idkkkkkkkkkk
This commit is contained in:
parent
3c11b23ad8
commit
8b66a135c5
257
src/lib.rs
257
src/lib.rs
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue