idkkkkkkkkkk

This commit is contained in:
Janis 2022-11-23 08:03:14 +01:00
parent 3c11b23ad8
commit 8b66a135c5

View file

@ -4,9 +4,12 @@
pub mod error {
use core::fmt::Display;
use crate::Version;
#[derive(Debug, PartialEq, Eq)]
pub enum BaseBlockVerifyError {
BadSignature,
IncompatibleVersion(Version),
IncompatibleMajorVersion,
IncompatibleMinorVersion,
BadFileType,
@ -16,14 +19,21 @@ pub mod error {
impl Display for BaseBlockVerifyError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(match self {
BaseBlockVerifyError::BadSignature => "Bad Signature",
BaseBlockVerifyError::IncompatibleMajorVersion => "Incompatible Major Version",
BaseBlockVerifyError::IncompatibleMinorVersion => "Incompatible Minor Version",
BaseBlockVerifyError::BadFileType => "Bad File Type (self.kind)",
BaseBlockVerifyError::BadFormat => "Bad Format",
BaseBlockVerifyError::Cluster => "Not the correct Cluster",
})
match self {
BaseBlockVerifyError::BadSignature => f.write_str("Bad Signature"),
BaseBlockVerifyError::IncompatibleVersion(version) => {
f.write_fmt(format_args!("{:?}", version))
}
BaseBlockVerifyError::IncompatibleMajorVersion => {
f.write_str("Incompatible Major Version")
}
BaseBlockVerifyError::IncompatibleMinorVersion => {
f.write_str("Incompatible Minor Version")
}
BaseBlockVerifyError::BadFileType => f.write_str("Bad File Type (self.kind)"),
BaseBlockVerifyError::BadFormat => f.write_str("Bad Format"),
BaseBlockVerifyError::Cluster => f.write_str("Not the correct Cluster"),
}
}
}
@ -100,11 +110,27 @@ pub mod cell_data {
data_len: u32,
data: Offset,
data_type: u32,
flags: u16,
flags: KeyValueFlags,
spare: u16,
value_name: ZstPtr,
}
bitflags! {
#[repr(C)]
#[derive(Zeroable, Pod)]
pub struct KeyValueFlags: u16 {
/// Value name is an ASCII string, possibly an extended ASCII string
/// (otherwise it is a UTF-16LE string)
const VALUE_COMP_NAME = 0x1;
/// Is a tombstone value (the flag is used starting from Insider
/// Preview builds of Windows 10 "Redstone 1"), a tombstone value
/// also has the Data type field set to REG_NONE, the Data size
/// field set to 0, and the Data offset field set to 0xFFFFFFFF
const IS_TOMBSTONE = 0x2;
}
}
/// KeySecurity items form a doubly linked list. A key security item may
/// act as a list header or a list entry (the only difference here is the
/// meaning of f_link and b_link fields).
@ -155,6 +181,12 @@ pub mod cell_data {
data: &'a [u8],
}
impl<'a> Cell<'a> {
pub fn into_any_cell(self) -> AnyCell<'a> {
self.into()
}
}
impl<'a> From<&'a super::RawCell> for Cell<'a> {
fn from(raw: &'a super::RawCell) -> Self {
let data = unsafe {
@ -446,21 +478,28 @@ pub mod cell_data {
impl KeyValue {
pub fn data_type(&self) -> DataType {
match self.data_type {
0x00 => DataType::None,
0x01 => DataType::Sz,
0x02 => DataType::ExpandSz,
0x03 => DataType::Binary,
0x04 => DataType::DWordLE,
0x05 => DataType::DWordBE,
0x06 => DataType::Link,
0x07 => DataType::MultiSz,
0x08 => DataType::ResourceList,
0x09 => DataType::FullResourceDescriptor,
0x0a => DataType::ResourceRequirementsList,
0x0b => DataType::QWordLE,
_ => DataType::Other(self.data_type),
self.data_type.into()
}
pub fn small_data(&self) -> Option<[u8; 4]> {
let data_len = self.data_len;
let data = self.data.0;
if data_len.bit(31) && self.data_len() <= 4 {
let mut ret = [0; 4];
ret[..self.data_len()]
.copy_from_slice(&bytemuck::bytes_of(&data)[..self.data_len()]);
Some(ret)
} else {
None
}
}
pub fn data_len(&self) -> usize {
let data_len = self.data_len;
BitRange::<u32>::bit_range(&data_len, 0, 31) as usize
}
}
@ -497,6 +536,26 @@ pub mod cell_data {
Other(u32),
}
impl From<u32> for DataType {
fn from(value: u32) -> Self {
match value {
0x00 => Self::None,
0x01 => Self::Sz,
0x02 => Self::ExpandSz,
0x03 => Self::Binary,
0x04 => Self::DWordLE,
0x05 => Self::DWordBE,
0x06 => Self::Link,
0x07 => Self::MultiSz,
0x08 => Self::ResourceList,
0x09 => Self::FullResourceDescriptor,
0x0a => Self::ResourceRequirementsList,
0x0b => Self::QWordLE,
_ => Self::Other(value),
}
}
}
#[cfg(test)]
mod test {
use super::*;
@ -512,8 +571,10 @@ pub mod cell_data {
}
}
use core::borrow::Borrow;
use bytemuck::{Pod, Zeroable};
use cell_data::Cell;
use cell_data::{AnyCell, Cell, KeyNode};
use error::BaseBlockVerifyError;
use ptr::ZstPtr;
@ -523,35 +584,137 @@ extern crate alloc;
#[repr(C, packed)]
#[derive(Debug, Copy, Clone, Pod, Zeroable)]
pub struct BaseBlockSequence {
/// This number is incremented by 1 in the beginning of a write operation on
/// the primary file
before: u32,
/// This number is incremented by 1 at the end of a write operation on the
/// primary file, a primary sequence number and a secondary sequence number
/// should be equal after a successful write operation
after: u32,
}
#[repr(C, packed)]
#[repr(C)]
#[derive(Debug, Copy, Clone, Pod, Zeroable)]
pub struct Offset(u32);
#[repr(C, packed)]
#[derive(Debug)]
pub struct Hive<'a> {
base_block: &'a BaseBlock,
hive_bins: &'a [u8],
}
impl<'a> Hive<'a> {
pub fn new(bytes: &'a [u8]) -> Option<Self> {
(bytes.len() >= 0x1000)
.then_some(())
.and_then(|_| {
let base_block = bytemuck::from_bytes::<BaseBlock>(bytes);
let bytes = &bytes[0x1000..];
let hive_bins_len = base_block.hive_bins_len();
(bytes.len() >= hive_bins_len).then_some((base_block, &bytes[..hive_bins_len]))
})
.map(|(base_block, hive_bins)| Self {
base_block,
hive_bins,
})
}
pub fn root_key(&self) -> Option<&KeyNode> {
let offset = self.base_block.root_cell().offset()?;
let a = bytemuck::from_bytes::<RawCell>(&self.hive_bins[offset..]);
let cell = Cell::from(a);
if let AnyCell::KeyNode(key_node) = cell.into_any_cell() {
Some(key_node)
} else {
None
}
}
}
#[repr(C, packed(2))]
#[derive(Debug, Copy, Clone, Pod, Zeroable)]
pub struct BaseBlock {
/// ASCII string signature equal to `b"regf"`
signature: u32,
sequence: BaseBlockSequence,
time_stmap: u64,
major: u32,
minor: u32,
/// FILETIME (UTC)
time_stamp: u64,
version: Version,
/// file_type, 0 means primary file
kind: u32,
/// 1 means direct memory load
format: u32,
/// Offset of a root cell in bytes, relative from the start of the hive bins data
root_cell: Offset,
length: u32,
cluster: u32,
/// Size of the hive bins data in bytes
hive_bins_len: u32,
/// Logical sector size of the underlying disk in bytes divided by 512
clustering_factor: u32,
/// UTF-16LE string (contains a partial file path to the primary file, or a file name of the primary file), used for debugging purposes
file_name: [u16; 32],
reserved: [u32; 99],
win10_fields1: Win10BaseBlock1,
reserved: [u32; 83],
/// XOR-32 checksum of the previous 508 bytes
checksum: u32,
reserved2: [u32; 0x37E],
reserved2: [u32; 0x372],
win10_fields2: Win10BaseBlock2,
boot_type: u32,
boot_recover: u32,
}
#[repr(C, packed)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Pod, Zeroable)]
pub struct Win10BaseBlock1 {
rm_id: u128,
log_id: u128,
flags: u32,
tm_id: u128,
guid_signature: [u8; 4],
last_reorganized_timestamp: u64,
}
#[repr(C, packed)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Pod, Zeroable)]
pub struct Win10BaseBlock2 {
thaw_tm_id: u128,
thaw_rm_id: u128,
thaw_log_id: u128,
}
#[repr(C, packed)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Pod, Zeroable)]
pub struct Version {
major: u32,
minor: u32,
}
impl Version {
pub const V1_3: Self = Self::new(1, 3);
pub const V1_4: Self = Self::new(1, 4);
pub const V1_5: Self = Self::new(1, 5);
pub const V1_6: Self = Self::new(1, 6);
pub const fn new(major: u32, minor: u32) -> Self {
Self { major, minor }
}
/// returns true if `&self` is any of `[Self::V1_3, Self::V1_4, Self::V1_5, Self::V1_6]`.
pub fn is_valid(&self) -> bool {
[Self::V1_3, Self::V1_4, Self::V1_5, Self::V1_6].contains(self)
}
pub fn major(&self) -> u32 {
self.major
}
pub fn minor(&self) -> u32 {
self.minor
}
}
#[repr(C, packed)]
#[derive(Debug, Copy, Clone, Pod, Zeroable)]
pub struct BinHeader {
@ -662,11 +825,22 @@ impl Offset {
impl BaseBlock {
const SIGNATURE: u32 = 0x66676572;
const MAJOR: u32 = 1;
const MINOR: u32 = 3;
const FILE_TYPE_PRIMARY: u32 = 0;
const BASE_FORMAT_MEMORY: u32 = 1;
#[cfg(feature = "alloc")]
pub fn debug_file_name(&self) -> Result<alloc::string::String, alloc::string::FromUtf16Error> {
use alloc::string::String;
let name = &self.file_name;
let name_len = name.iter().take_while(|&&b| b != 0).count();
String::from_utf16(&self.file_name[..name_len])
}
pub fn hive_bins_len(&self) -> usize {
self.hive_bins_len as usize
}
pub fn calculate_checksum(&self) -> u32 {
let bytes = bytemuck::bytes_of(self);
// let data = bytemuck::cast_slice::<_, u32>(&bytes[..508]);
@ -688,12 +862,9 @@ impl BaseBlock {
(self.signature == Self::SIGNATURE)
.then_some(())
.ok_or(BaseBlockVerifyError::BadSignature)?;
(self.major == Self::MAJOR)
(self.version.is_valid())
.then_some(())
.ok_or(BaseBlockVerifyError::IncompatibleMajorVersion)?;
(self.minor == Self::MINOR)
.then_some(())
.ok_or(BaseBlockVerifyError::IncompatibleMinorVersion)?;
.ok_or(BaseBlockVerifyError::IncompatibleVersion(self.version))?;
(self.kind == Self::FILE_TYPE_PRIMARY)
.then_some(())
.ok_or(BaseBlockVerifyError::BadFileType)?;
@ -701,7 +872,7 @@ impl BaseBlock {
.then_some(())
.ok_or(BaseBlockVerifyError::BadFormat)?;
(self.cluster == 1)
(self.clustering_factor == 1)
.then_some(())
.ok_or(BaseBlockVerifyError::BadSignature)?;
Ok(self.dirty())
@ -717,6 +888,10 @@ impl BaseBlock {
pub fn dirty(&self) -> bool {
self.sequence.dirty() || self.calculate_checksum() != self.checksum
}
pub fn root_cell(&self) -> Offset {
self.root_cell
}
}
impl BaseBlockSequence {