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,
pub last_written: Option<chrono::DateTime<chrono::Utc>>,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct Dcc2SlotEntry {
pub slot_name: String,
pub is_populated: bool,
pub data_size: usize,
pub last_written: Option<chrono::DateTime<chrono::Utc>>,
}
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 Ok(Some(key)) = hive.open_key(key_path) else {
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 Ok(Some(secrets_key)) = hive.open_key("Policy\\Secrets") else {
return Vec::new();
};
let Ok(subkeys) = secrets_key.subkeys() else {
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,
last_written: secret_key.last_written(),
});
}
entries
}
pub fn parse_dcc2_slots(hive: &Hive<Cursor<Vec<u8>>>) -> Vec<Dcc2SlotEntry> {
let Ok(Some(cache_key)) = hive.open_key("Cache") else {
return Vec::new();
};
let Ok(subkeys) = cache_key.subkeys() else {
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_or(0, |d| d.len())
} else {
0
};
slots.push(Dcc2SlotEntry {
slot_name,
is_populated: data_size > 0,
data_size,
last_written: slot_key.last_written(),
});
}
slots
}