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 {
|
pub mod error {
|
||||||
use core::fmt::Display;
|
use core::fmt::Display;
|
||||||
|
|
||||||
|
use crate::Version;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum BaseBlockVerifyError {
|
pub enum BaseBlockVerifyError {
|
||||||
BadSignature,
|
BadSignature,
|
||||||
|
IncompatibleVersion(Version),
|
||||||
IncompatibleMajorVersion,
|
IncompatibleMajorVersion,
|
||||||
IncompatibleMinorVersion,
|
IncompatibleMinorVersion,
|
||||||
BadFileType,
|
BadFileType,
|
||||||
|
@ -16,14 +19,21 @@ pub mod error {
|
||||||
|
|
||||||
impl Display for BaseBlockVerifyError {
|
impl Display for BaseBlockVerifyError {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
f.write_str(match self {
|
match self {
|
||||||
BaseBlockVerifyError::BadSignature => "Bad Signature",
|
BaseBlockVerifyError::BadSignature => f.write_str("Bad Signature"),
|
||||||
BaseBlockVerifyError::IncompatibleMajorVersion => "Incompatible Major Version",
|
BaseBlockVerifyError::IncompatibleVersion(version) => {
|
||||||
BaseBlockVerifyError::IncompatibleMinorVersion => "Incompatible Minor Version",
|
f.write_fmt(format_args!("{:?}", version))
|
||||||
BaseBlockVerifyError::BadFileType => "Bad File Type (self.kind)",
|
}
|
||||||
BaseBlockVerifyError::BadFormat => "Bad Format",
|
BaseBlockVerifyError::IncompatibleMajorVersion => {
|
||||||
BaseBlockVerifyError::Cluster => "Not the correct Cluster",
|
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_len: u32,
|
||||||
data: Offset,
|
data: Offset,
|
||||||
data_type: u32,
|
data_type: u32,
|
||||||
flags: u16,
|
flags: KeyValueFlags,
|
||||||
spare: u16,
|
spare: u16,
|
||||||
value_name: ZstPtr,
|
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
|
/// 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
|
/// act as a list header or a list entry (the only difference here is the
|
||||||
/// meaning of f_link and b_link fields).
|
/// meaning of f_link and b_link fields).
|
||||||
|
@ -155,6 +181,12 @@ pub mod cell_data {
|
||||||
data: &'a [u8],
|
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> {
|
impl<'a> From<&'a super::RawCell> for Cell<'a> {
|
||||||
fn from(raw: &'a super::RawCell) -> Self {
|
fn from(raw: &'a super::RawCell) -> Self {
|
||||||
let data = unsafe {
|
let data = unsafe {
|
||||||
|
@ -446,21 +478,28 @@ pub mod cell_data {
|
||||||
|
|
||||||
impl KeyValue {
|
impl KeyValue {
|
||||||
pub fn data_type(&self) -> DataType {
|
pub fn data_type(&self) -> DataType {
|
||||||
match self.data_type {
|
self.data_type.into()
|
||||||
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),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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),
|
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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -512,8 +571,10 @@ pub mod cell_data {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use core::borrow::Borrow;
|
||||||
|
|
||||||
use bytemuck::{Pod, Zeroable};
|
use bytemuck::{Pod, Zeroable};
|
||||||
use cell_data::Cell;
|
use cell_data::{AnyCell, Cell, KeyNode};
|
||||||
use error::BaseBlockVerifyError;
|
use error::BaseBlockVerifyError;
|
||||||
use ptr::ZstPtr;
|
use ptr::ZstPtr;
|
||||||
|
|
||||||
|
@ -523,35 +584,137 @@ extern crate alloc;
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
#[derive(Debug, Copy, Clone, Pod, Zeroable)]
|
#[derive(Debug, Copy, Clone, Pod, Zeroable)]
|
||||||
pub struct BaseBlockSequence {
|
pub struct BaseBlockSequence {
|
||||||
|
/// This number is incremented by 1 in the beginning of a write operation on
|
||||||
|
/// the primary file
|
||||||
before: u32,
|
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,
|
after: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C, packed)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone, Pod, Zeroable)]
|
#[derive(Debug, Copy, Clone, Pod, Zeroable)]
|
||||||
pub struct Offset(u32);
|
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)]
|
#[derive(Debug, Copy, Clone, Pod, Zeroable)]
|
||||||
pub struct BaseBlock {
|
pub struct BaseBlock {
|
||||||
|
/// ASCII string signature equal to `b"regf"`
|
||||||
signature: u32,
|
signature: u32,
|
||||||
sequence: BaseBlockSequence,
|
sequence: BaseBlockSequence,
|
||||||
time_stmap: u64,
|
/// FILETIME (UTC)
|
||||||
major: u32,
|
time_stamp: u64,
|
||||||
minor: u32,
|
version: Version,
|
||||||
|
/// file_type, 0 means primary file
|
||||||
kind: u32,
|
kind: u32,
|
||||||
|
/// 1 means direct memory load
|
||||||
format: u32,
|
format: u32,
|
||||||
|
/// Offset of a root cell in bytes, relative from the start of the hive bins data
|
||||||
root_cell: Offset,
|
root_cell: Offset,
|
||||||
length: u32,
|
/// Size of the hive bins data in bytes
|
||||||
cluster: u32,
|
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],
|
file_name: [u16; 32],
|
||||||
reserved: [u32; 99],
|
win10_fields1: Win10BaseBlock1,
|
||||||
|
reserved: [u32; 83],
|
||||||
|
/// XOR-32 checksum of the previous 508 bytes
|
||||||
checksum: u32,
|
checksum: u32,
|
||||||
reserved2: [u32; 0x37E],
|
reserved2: [u32; 0x372],
|
||||||
|
win10_fields2: Win10BaseBlock2,
|
||||||
boot_type: u32,
|
boot_type: u32,
|
||||||
boot_recover: 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)]
|
#[repr(C, packed)]
|
||||||
#[derive(Debug, Copy, Clone, Pod, Zeroable)]
|
#[derive(Debug, Copy, Clone, Pod, Zeroable)]
|
||||||
pub struct BinHeader {
|
pub struct BinHeader {
|
||||||
|
@ -662,11 +825,22 @@ impl Offset {
|
||||||
|
|
||||||
impl BaseBlock {
|
impl BaseBlock {
|
||||||
const SIGNATURE: u32 = 0x66676572;
|
const SIGNATURE: u32 = 0x66676572;
|
||||||
const MAJOR: u32 = 1;
|
|
||||||
const MINOR: u32 = 3;
|
|
||||||
const FILE_TYPE_PRIMARY: u32 = 0;
|
const FILE_TYPE_PRIMARY: u32 = 0;
|
||||||
const BASE_FORMAT_MEMORY: u32 = 1;
|
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 {
|
pub fn calculate_checksum(&self) -> u32 {
|
||||||
let bytes = bytemuck::bytes_of(self);
|
let bytes = bytemuck::bytes_of(self);
|
||||||
// let data = bytemuck::cast_slice::<_, u32>(&bytes[..508]);
|
// let data = bytemuck::cast_slice::<_, u32>(&bytes[..508]);
|
||||||
|
@ -688,12 +862,9 @@ impl BaseBlock {
|
||||||
(self.signature == Self::SIGNATURE)
|
(self.signature == Self::SIGNATURE)
|
||||||
.then_some(())
|
.then_some(())
|
||||||
.ok_or(BaseBlockVerifyError::BadSignature)?;
|
.ok_or(BaseBlockVerifyError::BadSignature)?;
|
||||||
(self.major == Self::MAJOR)
|
(self.version.is_valid())
|
||||||
.then_some(())
|
.then_some(())
|
||||||
.ok_or(BaseBlockVerifyError::IncompatibleMajorVersion)?;
|
.ok_or(BaseBlockVerifyError::IncompatibleVersion(self.version))?;
|
||||||
(self.minor == Self::MINOR)
|
|
||||||
.then_some(())
|
|
||||||
.ok_or(BaseBlockVerifyError::IncompatibleMinorVersion)?;
|
|
||||||
(self.kind == Self::FILE_TYPE_PRIMARY)
|
(self.kind == Self::FILE_TYPE_PRIMARY)
|
||||||
.then_some(())
|
.then_some(())
|
||||||
.ok_or(BaseBlockVerifyError::BadFileType)?;
|
.ok_or(BaseBlockVerifyError::BadFileType)?;
|
||||||
|
@ -701,7 +872,7 @@ impl BaseBlock {
|
||||||
.then_some(())
|
.then_some(())
|
||||||
.ok_or(BaseBlockVerifyError::BadFormat)?;
|
.ok_or(BaseBlockVerifyError::BadFormat)?;
|
||||||
|
|
||||||
(self.cluster == 1)
|
(self.clustering_factor == 1)
|
||||||
.then_some(())
|
.then_some(())
|
||||||
.ok_or(BaseBlockVerifyError::BadSignature)?;
|
.ok_or(BaseBlockVerifyError::BadSignature)?;
|
||||||
Ok(self.dirty())
|
Ok(self.dirty())
|
||||||
|
@ -717,6 +888,10 @@ impl BaseBlock {
|
||||||
pub fn dirty(&self) -> bool {
|
pub fn dirty(&self) -> bool {
|
||||||
self.sequence.dirty() || self.calculate_checksum() != self.checksum
|
self.sequence.dirty() || self.calculate_checksum() != self.checksum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn root_cell(&self) -> Offset {
|
||||||
|
self.root_cell
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BaseBlockSequence {
|
impl BaseBlockSequence {
|
||||||
|
|
Loading…
Reference in a new issue