pub const WINDOWS_MASQUERADE_TARGETS: &[&str] = &[
"svchost.exe",
"lsass.exe",
"csrss.exe",
"spoolsv.exe",
"dllhost.exe",
"conhost.exe",
"wermgr.exe",
"services.exe",
"winlogon.exe",
"wininit.exe",
"smss.exe",
"taskhost.exe",
"taskhostw.exe",
"explorer.exe",
"system",
"registry",
];
pub const KNOWN_MALWARE_PROCESS_NAMES: &[&str] = &[
"xmrig",
"mimikatz",
"meterpreter",
"beacon",
"empire",
"cobaltstrike",
"ngrok",
"frp",
"chisel",
"ligolo",
"sliver",
"havoc",
"brute",
"pwncat",
"reptile",
"diamorphine",
];
pub fn is_masquerade_target(name: &str) -> bool {
let lower = name.to_ascii_lowercase();
WINDOWS_MASQUERADE_TARGETS
.iter()
.any(|t| t.to_ascii_lowercase() == lower)
}
pub fn is_known_malware_process(name: &str) -> bool {
let lower = name.to_ascii_lowercase();
KNOWN_MALWARE_PROCESS_NAMES
.iter()
.any(|t| t.to_ascii_lowercase() == lower)
}
pub const CREDENTIAL_ACCESS_TOOLS: &[&str] = &[
"mimikatz",
"mimikatz.exe",
"pypykatz",
"pypykatz.exe",
"wce",
"wce.exe",
"gsecdump",
"gsecdump.exe",
"fgdump",
"fgdump.exe",
"pwdump",
"pwdump7",
"pwdump7.exe",
"secretsdump",
"impacket-secretsdump",
"invoke-mimikatz",
"crackmapexec",
"cme",
"lsassy",
"procdump",
"procdump64",
"procdump.exe",
"procdump64.exe",
];
pub const LSASS_ACCESS_TOOLS: &[&str] = &[
"procdump",
"procdump64",
"procdump.exe",
"procdump64.exe",
"comsvcs.dll",
"werfault.exe",
"sqldumper.exe",
"lsassy",
"nanodump",
"handlekatz",
"rdrleakdiag.exe",
"out-minidump",
];
pub const WINDOWS_SINGLETON_PROCESSES: &[&str] = &[
"lsass.exe",
"services.exe",
"wininit.exe",
"csrss.exe",
"smss.exe",
"lsm.exe",
];
pub const WINDOWS_PARENT_RULES: &[(&str, &str)] = &[
("svchost.exe", "services.exe"),
("lsass.exe", "wininit.exe"),
("services.exe", "wininit.exe"),
("wininit.exe", "smss.exe"),
];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SpoofConfidence {
High,
Low,
}
pub const WINDOWS_PPID_RULES: &[(&str, &[&str], SpoofConfidence)] = &[
("smss.exe", &["system"], SpoofConfidence::High),
("csrss.exe", &["smss.exe"], SpoofConfidence::High),
("winlogon.exe", &["smss.exe"], SpoofConfidence::High),
("wininit.exe", &["smss.exe"], SpoofConfidence::High),
("lsass.exe", &["wininit.exe"], SpoofConfidence::High),
("services.exe", &["wininit.exe"], SpoofConfidence::High),
("lsm.exe", &["wininit.exe"], SpoofConfidence::High), ("svchost.exe", &["services.exe"], SpoofConfidence::High),
("taskhost.exe", &["services.exe"], SpoofConfidence::High),
("taskhostw.exe", &["services.exe"], SpoofConfidence::High),
("spoolsv.exe", &["services.exe"], SpoofConfidence::High),
("searchindexer.exe", &["services.exe"], SpoofConfidence::High),
("msdtc.exe", &["services.exe"], SpoofConfidence::High), ("trustedinstaller.exe", &["services.exe"], SpoofConfidence::High), ("vssvc.exe", &["services.exe"], SpoofConfidence::High), ("msmpeng.exe", &["services.exe"], SpoofConfidence::High), ("nissrv.exe", &["services.exe"], SpoofConfidence::High), ("audiodg.exe", &["svchost.exe"], SpoofConfidence::High), ("wmiprvse.exe", &["svchost.exe"], SpoofConfidence::High), ("runtimebroker.exe", &["svchost.exe"], SpoofConfidence::High), ("searchprotocolhost.exe", &["searchindexer.exe"], SpoofConfidence::High),
("searchfilterhost.exe", &["searchindexer.exe"], SpoofConfidence::High),
("userinit.exe", &["winlogon.exe"], SpoofConfidence::High),
("logonui.exe", &["winlogon.exe"], SpoofConfidence::High), ("dwm.exe", &["winlogon.exe"], SpoofConfidence::High), ("fontdrvhost.exe", &["wininit.exe", "winlogon.exe"], SpoofConfidence::High),
("explorer.exe", &["userinit.exe", "winlogon.exe"], SpoofConfidence::High), ("dllhost.exe", &["svchost.exe", "services.exe",
"explorer.exe", "mmc.exe"], SpoofConfidence::Low),
];
pub fn expected_parents(child_name: &str) -> Option<(&'static [&'static str], SpoofConfidence)> {
let lower = child_name.to_ascii_lowercase();
WINDOWS_PPID_RULES
.iter()
.find(|(child, _, _)| *child == lower.as_str())
.map(|(_, parents, conf)| (*parents, *conf))
}
pub const WINDOWS_NON_NETWORKING_PROCESSES: &[&str] = &[
"notepad.exe",
"calc.exe",
"mspaint.exe",
"write.exe",
"wordpad.exe",
"snippingtool.exe",
"osk.exe",
"magnify.exe",
"narrator.exe",
];
pub const WINDOWS_KERNEL_PDB_PREFIXES: &[&str] = &["ntkrnl", "ntoskrnl"];
pub const PE_MZ_MAGIC: [u8; 2] = [0x4D, 0x5A];
pub fn is_singleton_process(name: &str) -> bool {
let lower = name.to_ascii_lowercase();
WINDOWS_SINGLETON_PROCESSES.iter().any(|t| *t == lower)
}
pub fn expected_parent(child_name: &str) -> Option<&'static str> {
let lower = child_name.to_ascii_lowercase();
WINDOWS_PARENT_RULES
.iter()
.find(|(child, _)| *child == lower.as_str())
.map(|(_, parent)| *parent)
}
pub fn is_non_networking_process(name: &str) -> bool {
let lower = name.to_ascii_lowercase();
WINDOWS_NON_NETWORKING_PROCESSES.iter().any(|t| *t == lower)
}
pub fn is_credential_access_tool(name: &str) -> bool {
let lower = name.to_ascii_lowercase();
CREDENTIAL_ACCESS_TOOLS
.iter()
.any(|t| t.to_ascii_lowercase() == lower)
}
pub fn is_lsass_access_tool(name: &str) -> bool {
let lower = name.to_ascii_lowercase();
LSASS_ACCESS_TOOLS
.iter()
.any(|t| t.to_ascii_lowercase() == lower)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn masquerade_targets_contains_svchost() {
assert!(WINDOWS_MASQUERADE_TARGETS.contains(&"svchost.exe"));
}
#[test]
fn masquerade_targets_contains_lsass() {
assert!(WINDOWS_MASQUERADE_TARGETS.contains(&"lsass.exe"));
}
#[test]
fn malware_names_contains_mimikatz() {
assert!(KNOWN_MALWARE_PROCESS_NAMES.contains(&"mimikatz"));
}
#[test]
fn malware_names_contains_xmrig() {
assert!(KNOWN_MALWARE_PROCESS_NAMES.contains(&"xmrig"));
}
#[test]
fn detects_svchost_lowercase() {
assert!(is_masquerade_target("svchost.exe"));
}
#[test]
fn detects_lsass_uppercase() {
assert!(is_masquerade_target("LSASS.EXE"));
}
#[test]
fn detects_explorer_mixed_case() {
assert!(is_masquerade_target("Explorer.exe"));
}
#[test]
fn does_not_flag_random_process() {
assert!(!is_masquerade_target("mygame.exe"));
}
#[test]
fn empty_string_not_masquerade_target() {
assert!(!is_masquerade_target(""));
}
#[test]
fn detects_mimikatz() {
assert!(is_known_malware_process("mimikatz"));
}
#[test]
fn detects_meterpreter_uppercase() {
assert!(is_known_malware_process("METERPRETER"));
}
#[test]
fn detects_beacon() {
assert!(is_known_malware_process("beacon"));
}
#[test]
fn does_not_flag_chrome() {
assert!(!is_known_malware_process("chrome"));
}
#[test]
fn empty_string_not_malware_process() {
assert!(!is_known_malware_process(""));
}
#[test]
fn credential_tools_contains_mimikatz() {
assert!(CREDENTIAL_ACCESS_TOOLS.contains(&"mimikatz"));
}
#[test]
fn credential_tools_contains_pypykatz() {
assert!(CREDENTIAL_ACCESS_TOOLS.contains(&"pypykatz"));
}
#[test]
fn credential_tools_contains_procdump() {
assert!(CREDENTIAL_ACCESS_TOOLS.contains(&"procdump"));
}
#[test]
fn detects_mimikatz_exact() {
assert!(is_credential_access_tool("mimikatz"));
}
#[test]
fn detects_mimikatz_exe() {
assert!(is_credential_access_tool("mimikatz.exe"));
}
#[test]
fn detects_pypykatz_uppercase() {
assert!(is_credential_access_tool("PYPYKATZ"));
}
#[test]
fn detects_crackmapexec() {
assert!(is_credential_access_tool("crackmapexec"));
}
#[test]
fn does_not_flag_calc_as_cred_tool() {
assert!(!is_credential_access_tool("calc.exe"));
}
#[test]
fn empty_string_not_cred_tool() {
assert!(!is_credential_access_tool(""));
}
#[test]
fn lsass_tools_contains_procdump() {
assert!(LSASS_ACCESS_TOOLS.contains(&"procdump"));
}
#[test]
fn lsass_tools_contains_comsvcs() {
assert!(LSASS_ACCESS_TOOLS.contains(&"comsvcs.dll"));
}
#[test]
fn detects_procdump_exe() {
assert!(is_lsass_access_tool("procdump.exe"));
}
#[test]
fn detects_comsvcs_dll() {
assert!(is_lsass_access_tool("comsvcs.dll"));
}
#[test]
fn detects_nanodump() {
assert!(is_lsass_access_tool("nanodump"));
}
#[test]
fn lsass_tool_case_insensitive() {
assert!(is_lsass_access_tool("PROCDUMP64.EXE"));
}
#[test]
fn does_not_flag_notepad_as_lsass_tool() {
assert!(!is_lsass_access_tool("notepad.exe"));
}
#[test]
fn empty_string_not_lsass_tool() {
assert!(!is_lsass_access_tool(""));
}
#[test]
fn singleton_list_contains_lsass() {
assert!(WINDOWS_SINGLETON_PROCESSES.contains(&"lsass.exe"));
}
#[test]
fn singleton_list_contains_services() {
assert!(WINDOWS_SINGLETON_PROCESSES.contains(&"services.exe"));
}
#[test]
fn singleton_list_contains_wininit() {
assert!(WINDOWS_SINGLETON_PROCESSES.contains(&"wininit.exe"));
}
#[test]
fn is_singleton_process_case_insensitive() {
assert!(is_singleton_process("LSASS.EXE"));
}
#[test]
fn is_singleton_process_returns_false_for_svchost() {
assert!(!is_singleton_process("svchost.exe"));
}
#[test]
fn parent_rules_contain_svchost_services() {
assert!(WINDOWS_PARENT_RULES.contains(&("svchost.exe", "services.exe")));
}
#[test]
fn parent_rules_contain_lsass_wininit() {
assert!(WINDOWS_PARENT_RULES.contains(&("lsass.exe", "wininit.exe")));
}
#[test]
fn expected_parent_for_svchost_is_services() {
assert_eq!(expected_parent("svchost.exe"), Some("services.exe"));
}
#[test]
fn expected_parent_for_explorer_is_none() {
assert_eq!(expected_parent("explorer.exe"), None);
}
#[test]
fn non_networking_contains_notepad() {
assert!(WINDOWS_NON_NETWORKING_PROCESSES.contains(&"notepad.exe"));
}
#[test]
fn non_networking_contains_calc() {
assert!(WINDOWS_NON_NETWORKING_PROCESSES.contains(&"calc.exe"));
}
#[test]
fn is_non_networking_process_case_insensitive() {
assert!(is_non_networking_process("NOTEPAD.EXE"));
}
#[test]
fn is_non_networking_process_false_for_chrome() {
assert!(!is_non_networking_process("chrome.exe"));
}
#[test]
fn kernel_pdb_prefixes_contains_ntkrnl() {
assert!(WINDOWS_KERNEL_PDB_PREFIXES.contains(&"ntkrnl"));
}
#[test]
fn kernel_pdb_prefixes_contains_ntoskrnl() {
assert!(WINDOWS_KERNEL_PDB_PREFIXES.contains(&"ntoskrnl"));
}
#[test]
fn pe_mz_magic_is_4d5a() {
assert_eq!(PE_MZ_MAGIC, [0x4D, 0x5A]);
}
#[test]
fn ppid_rules_has_twenty_eight_entries() {
assert_eq!(WINDOWS_PPID_RULES.len(), 28);
}
#[test]
fn spoof_confidence_high_ne_low() {
assert_ne!(SpoofConfidence::High, SpoofConfidence::Low);
}
#[test]
fn ppid_rules_contains_svchost_services() {
assert!(WINDOWS_PPID_RULES
.iter()
.any(|(c, ps, _)| *c == "svchost.exe" && ps.contains(&"services.exe")));
}
#[test]
fn ppid_rules_contains_lsass_wininit() {
assert!(WINDOWS_PPID_RULES
.iter()
.any(|(c, ps, _)| *c == "lsass.exe" && ps.contains(&"wininit.exe")));
}
#[test]
fn ppid_rules_smss_parent_is_system() {
assert!(WINDOWS_PPID_RULES
.iter()
.any(|(c, ps, _)| *c == "smss.exe" && ps.contains(&"system")));
}
#[test]
fn ppid_rules_dllhost_allows_svchost_and_services() {
let (_, ps, _) = WINDOWS_PPID_RULES.iter().find(|(c, _, _)| *c == "dllhost.exe").unwrap();
assert!(ps.contains(&"svchost.exe"));
assert!(ps.contains(&"services.exe"));
}
#[test]
fn ppid_rules_dllhost_allows_explorer() {
let (_, ps, _) = WINDOWS_PPID_RULES.iter().find(|(c, _, _)| *c == "dllhost.exe").unwrap();
assert!(ps.contains(&"explorer.exe"), "COM thumbnail/preview handlers: explorer → dllhost");
}
#[test]
fn ppid_rules_dllhost_allows_mmc() {
let (_, ps, _) = WINDOWS_PPID_RULES.iter().find(|(c, _, _)| *c == "dllhost.exe").unwrap();
assert!(ps.contains(&"mmc.exe"), "MMC snap-ins activate COM surrogates");
}
#[test]
fn ppid_rules_dllhost_confidence_is_low() {
let (_, _, conf) = WINDOWS_PPID_RULES.iter().find(|(c, _, _)| *c == "dllhost.exe").unwrap();
assert_eq!(*conf, SpoofConfidence::Low);
}
#[test]
fn ppid_rules_lsass_confidence_is_high() {
let (_, _, conf) = WINDOWS_PPID_RULES.iter().find(|(c, _, _)| *c == "lsass.exe").unwrap();
assert_eq!(*conf, SpoofConfidence::High);
}
#[test]
fn ppid_rules_wininit_parent_is_smss() {
assert!(WINDOWS_PPID_RULES
.iter()
.any(|(c, ps, _)| *c == "wininit.exe" && ps.contains(&"smss.exe")));
}
#[test]
fn ppid_rules_lsm_parent_is_wininit() {
assert!(WINDOWS_PPID_RULES
.iter()
.any(|(c, ps, _)| *c == "lsm.exe" && ps.contains(&"wininit.exe")));
}
#[test]
fn ppid_rules_explorer_allows_userinit_and_winlogon() {
let (_, ps, _) = WINDOWS_PPID_RULES.iter().find(|(c, _, _)| *c == "explorer.exe").unwrap();
assert!(ps.contains(&"userinit.exe"), "normal logon: userinit → explorer");
assert!(ps.contains(&"winlogon.exe"), "auto-logon / shell replacement");
}
#[test]
fn ppid_rules_logonui_parent_is_winlogon() {
assert!(WINDOWS_PPID_RULES
.iter()
.any(|(c, ps, _)| *c == "logonui.exe" && ps.contains(&"winlogon.exe")));
}
#[test]
fn ppid_rules_dwm_parent_is_winlogon() {
assert!(WINDOWS_PPID_RULES
.iter()
.any(|(c, ps, _)| *c == "dwm.exe" && ps.contains(&"winlogon.exe")));
}
#[test]
fn ppid_rules_msdtc_parent_is_services() {
assert!(WINDOWS_PPID_RULES
.iter()
.any(|(c, ps, _)| *c == "msdtc.exe" && ps.contains(&"services.exe")));
}
#[test]
fn ppid_rules_trustedinstaller_parent_is_services() {
assert!(WINDOWS_PPID_RULES
.iter()
.any(|(c, ps, _)| *c == "trustedinstaller.exe" && ps.contains(&"services.exe")));
}
#[test]
fn ppid_rules_vssvc_parent_is_services() {
assert!(WINDOWS_PPID_RULES
.iter()
.any(|(c, ps, _)| *c == "vssvc.exe" && ps.contains(&"services.exe")));
}
#[test]
fn ppid_rules_msmpeng_parent_is_services() {
assert!(WINDOWS_PPID_RULES
.iter()
.any(|(c, ps, _)| *c == "msmpeng.exe" && ps.contains(&"services.exe")));
}
#[test]
fn ppid_rules_nissrv_parent_is_services() {
assert!(WINDOWS_PPID_RULES
.iter()
.any(|(c, ps, _)| *c == "nissrv.exe" && ps.contains(&"services.exe")));
}
#[test]
fn expected_parents_svchost_returns_services_high() {
let (parents, conf) = expected_parents("svchost.exe").unwrap();
assert_eq!(parents, &["services.exe"]);
assert_eq!(conf, SpoofConfidence::High);
}
#[test]
fn expected_parents_dllhost_is_low_confidence() {
let (parents, conf) = expected_parents("dllhost.exe").unwrap();
assert!(parents.contains(&"svchost.exe"));
assert!(parents.contains(&"explorer.exe"));
assert_eq!(conf, SpoofConfidence::Low);
}
#[test]
fn expected_parents_unknown_process_returns_none() {
assert!(expected_parents("calc.exe").is_none());
}
#[test]
fn expected_parents_case_insensitive() {
let (parents, _) = expected_parents("SVCHOST.EXE").unwrap();
assert!(parents.contains(&"services.exe"));
}
#[test]
fn expected_parents_fontdrvhost_returns_two_parents() {
let (p, _) = expected_parents("fontdrvhost.exe").unwrap();
assert!(p.contains(&"wininit.exe"));
assert!(p.contains(&"winlogon.exe"));
assert_eq!(p.len(), 2);
}
#[test]
fn expected_parents_runtimebroker_returns_svchost() {
let (p, _) = expected_parents("runtimebroker.exe").unwrap();
assert_eq!(p, &["svchost.exe"]);
}
#[test]
fn expected_parents_wmiprvse_returns_svchost() {
let (p, _) = expected_parents("wmiprvse.exe").unwrap();
assert_eq!(p, &["svchost.exe"]);
}
}