use std::io::Cursor;
use winreg_core::hive::Hive;
#[derive(Debug, Clone, serde::Serialize)]
pub struct LsaSecretEntry {
pub name: String,
pub has_current: bool,
pub has_old: bool,
pub curr_size: usize,
pub old_size: usize,
pub is_interesting: bool,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct Dcc2SlotEntry {
pub slot_name: String,
pub is_populated: bool,
pub data_size: usize,
}
pub fn is_interesting_secret(name: &str) -> bool {
matches!(
name,
"$MACHINE.ACC" | "DefaultPassword" | "DPAPI_SYSTEM" | "NL$KM" | "RasDialParams"
) || name.starts_with("_SC_")
}
fn read_binary_value_size(hive: &Hive<Cursor<Vec<u8>>>, key_path: &str) -> usize {
let key = match hive.open_key(key_path) {
Ok(Some(k)) => k,
_ => return 0,
};
if let Ok(values) = key.values() {
for val in &values {
if let Ok(data) = val.raw_data() {
return data.len();
}
}
}
0
}
pub fn parse_secrets(hive: &Hive<Cursor<Vec<u8>>>) -> Vec<LsaSecretEntry> {
let secrets_key = match hive.open_key("Policy\\Secrets") {
Ok(Some(k)) => k,
_ => return Vec::new(),
};
let subkeys = match secrets_key.subkeys() {
Ok(k) => k,
Err(_) => return Vec::new(),
};
let mut entries = Vec::with_capacity(subkeys.len());
for secret_key in subkeys {
let name = secret_key.name();
let currval_path = format!("Policy\\Secrets\\{name}\\CurrVal");
let oldval_path = format!("Policy\\Secrets\\{name}\\OldVal");
let curr_size = read_binary_value_size(hive, &currval_path);
let old_size = read_binary_value_size(hive, &oldval_path);
let has_current = curr_size > 0;
let has_old = old_size > 0;
let is_interesting = is_interesting_secret(&name);
entries.push(LsaSecretEntry {
name,
has_current,
has_old,
curr_size,
old_size,
is_interesting,
});
}
entries
}
pub fn parse_dcc2_slots(hive: &Hive<Cursor<Vec<u8>>>) -> Vec<Dcc2SlotEntry> {
let cache_key = match hive.open_key("Cache") {
Ok(Some(k)) => k,
_ => return Vec::new(),
};
let subkeys = match cache_key.subkeys() {
Ok(k) => k,
Err(_) => return Vec::new(),
};
let mut slots = Vec::with_capacity(subkeys.len());
for slot_key in subkeys {
let slot_name = slot_key.name();
let data_size = if let Ok(values) = slot_key.values() {
values
.into_iter()
.find_map(|v| v.raw_data().ok())
.map(|d| d.len())
.unwrap_or(0)
} else {
0
};
slots.push(Dcc2SlotEntry {
slot_name,
is_populated: data_size > 0,
data_size,
});
}
slots
}