use std::collections::HashSet;
use crate::{
error::Error,
records::{
item::ItemConditions,
windows::{Bitness, WindowsVersionRange},
},
util::{
encoding::{read_ansi_bytes, read_setup_string},
read::Reader,
},
version::Version,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
#[allow(missing_docs)]
pub enum RegistryHive {
ClassesRoot,
CurrentUser,
LocalMachine,
Users,
PerformanceData,
CurrentConfig,
DynData,
Unknown(u32),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
#[allow(missing_docs)]
pub enum RegistryValueType {
None,
String,
ExpandString,
DWord,
Binary,
MultiString,
QWord,
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
#[non_exhaustive]
#[allow(missing_docs)]
pub enum RegistryFlag {
CreateValueIfDoesntExist,
UninsDeleteValue,
UninsClearValue,
UninsDeleteEntireKey,
UninsDeleteEntireKeyIfEmpty,
PreserveStringType,
DeleteKey,
DeleteValue,
NoError,
DontCreateKey,
Bits32,
Bits64,
}
#[derive(Clone, Debug)]
pub struct RegistryEntry {
pub subkey: String,
pub value_name: String,
pub value: Vec<u8>,
pub conditions: ItemConditions,
pub legacy_permissions: Vec<u8>,
pub winver: WindowsVersionRange,
pub hive: RegistryHive,
pub hive_raw: u32,
pub permission_index: i16,
pub value_type: Option<RegistryValueType>,
pub value_type_raw: u8,
pub bitness: Option<Bitness>,
pub bitness_raw: u8,
pub flags: HashSet<RegistryFlag>,
pub options_raw: Vec<u8>,
}
impl RegistryEntry {
pub(crate) fn read(reader: &mut Reader<'_>, version: &Version) -> Result<Self, Error> {
let subkey = read_setup_string(reader, version, "Registry.Subkey")?;
let value_name = read_setup_string(reader, version, "Registry.ValueName")?;
let value = read_ansi_bytes(reader, "Registry.ValueData")?;
let conditions = ItemConditions::read(reader, version)?;
let legacy_permissions = if version.at_least(4, 0, 11) && !version.at_least(4, 1, 0) {
read_ansi_bytes(reader, "Registry.LegacyPermissions")?
} else {
Vec::new()
};
let winver = WindowsVersionRange::read(reader, version)?;
let raw_root = reader.u32_le("Registry.RootKey")?;
let hive_raw = raw_root & !0x8000_0000;
let hive = decode_hive(hive_raw);
let permission_index = if version.at_least(4, 1, 0) {
reader
.array::<2>("Registry.PermissionIndex")
.map(i16::from_le_bytes)?
} else {
-1
};
let value_type_raw = reader.u8("Registry.Typ")?;
let value_type = decode_value_type(value_type_raw, version);
let (bitness, bitness_raw) = if version.at_least_4(7, 0, 0, 3) {
let raw = reader.u8("Registry.Bitness")?;
(Bitness::decode(raw), raw)
} else {
(None, 0)
};
let table = registry_flag_table(version);
let raw = reader.set_bytes(table.len(), true, "Registry.Options")?;
let flags = super::decode_packed_flags(&raw, &table);
Ok(Self {
subkey,
value_name,
value,
conditions,
legacy_permissions,
winver,
hive,
hive_raw,
permission_index,
value_type,
value_type_raw,
bitness,
bitness_raw,
flags,
options_raw: raw,
})
}
}
fn decode_hive(raw: u32) -> RegistryHive {
match raw {
0 => RegistryHive::ClassesRoot,
1 => RegistryHive::CurrentUser,
2 => RegistryHive::LocalMachine,
3 => RegistryHive::Users,
4 => RegistryHive::PerformanceData,
5 => RegistryHive::CurrentConfig,
6 => RegistryHive::DynData,
n => RegistryHive::Unknown(n),
}
}
fn decode_value_type(b: u8, version: &Version) -> Option<RegistryValueType> {
let table: &[RegistryValueType] = if version.at_least(5, 2, 5) {
&[
RegistryValueType::None,
RegistryValueType::String,
RegistryValueType::ExpandString,
RegistryValueType::DWord,
RegistryValueType::Binary,
RegistryValueType::MultiString,
RegistryValueType::QWord,
]
} else {
&[
RegistryValueType::None,
RegistryValueType::String,
RegistryValueType::ExpandString,
RegistryValueType::DWord,
RegistryValueType::Binary,
RegistryValueType::MultiString,
]
};
table.get(usize::from(b)).copied()
}
fn registry_flag_table(version: &Version) -> Vec<RegistryFlag> {
let mut t = vec![
RegistryFlag::CreateValueIfDoesntExist,
RegistryFlag::UninsDeleteValue,
RegistryFlag::UninsClearValue,
RegistryFlag::UninsDeleteEntireKey,
RegistryFlag::UninsDeleteEntireKeyIfEmpty,
];
if version.at_least(1, 2, 6) {
t.push(RegistryFlag::PreserveStringType);
}
if version.at_least(1, 3, 9) {
t.push(RegistryFlag::DeleteKey);
t.push(RegistryFlag::DeleteValue);
}
if version.at_least(1, 3, 12) {
t.push(RegistryFlag::NoError);
}
if version.at_least(1, 3, 16) {
t.push(RegistryFlag::DontCreateKey);
}
if version.at_least(5, 1, 0) && !version.at_least_4(7, 0, 0, 3) {
t.push(RegistryFlag::Bits32);
t.push(RegistryFlag::Bits64);
}
t
}