use dyncvoke_core::{get_function_address, get_module_base_address, resolve_syscall};
fn collect_zw_exports(ntdll: usize) -> Vec<String> {
let mut out = Vec::new();
unsafe {
let pe_header = *((ntdll + 0x3C) as *const i32) as usize;
let opt_header = ntdll + pe_header + 0x18;
let magic = *(opt_header as *const i16);
let p_export = if magic == 0x010b {
opt_header + 0x60
} else {
opt_header + 0x70
};
let export_rva = *(p_export as *const i32) as usize;
let number_of_names = *((ntdll + export_rva + 0x18) as *const i32) as usize;
let names_rva = *((ntdll + export_rva + 0x20) as *const i32) as usize;
for x in 0..number_of_names {
let name_rva = *((ntdll + names_rva + x * 4) as *const i32) as usize;
let mut p = (ntdll + name_rva) as *const u8;
let mut name = String::new();
while *p != 0 {
name.push(*p as char);
p = p.add(1);
}
if name.starts_with("Zw") {
out.push(name.replacen("Zw", "Nt", 1));
}
}
}
out
}
fn canonical_ssn(stub: usize) -> Option<u16> {
if stub == 0 {
return None;
}
unsafe {
let p = stub as *const u8;
(*p == 0x4C
&& *p.add(1) == 0x8B
&& *p.add(2) == 0xD1
&& *p.add(3) == 0xB8
&& *p.add(6) == 0
&& *p.add(7) == 0)
.then(|| u16::from_le_bytes([*p.add(4), *p.add(5)]))
}
}
#[test]
fn resolve_syscall_matches_canonical_byte_derived_ssn_for_every_zw_export() {
let ntdll = get_module_base_address("ntdll.dll");
assert!(ntdll != 0, "ntdll.dll not loaded");
let names = collect_zw_exports(ntdll);
assert!(
names.len() > 200,
"expected ~489 Zw exports on Win11 24H2, got {}",
names.len()
);
let mut atypical: Vec<(String, u16)> = Vec::new();
let mut mismatches: Vec<(String, u16, u16)> = Vec::new();
for name in &names {
let stub = get_function_address(ntdll, name);
let (resolver_ssn, _) = match resolve_syscall(name) {
Ok(v) => v,
Err(e) => panic!("resolve_syscall({}) failed: {}", name, e),
};
match canonical_ssn(stub) {
Some(canonical) => {
if canonical != resolver_ssn {
mismatches.push((name.clone(), canonical, resolver_ssn));
}
}
None => {
atypical.push((name.clone(), resolver_ssn));
}
}
}
println!("total Zw exports: {}", names.len());
println!("atypical (recovered via Tartarus neighbor walk): {}", atypical.len());
for (name, ssn) in atypical.iter().take(10) {
println!(" atypical: {} -> SSN {}", name, ssn);
}
assert!(
mismatches.is_empty(),
"{} canonical-vs-resolver SSN mismatches: first few = {:?}",
mismatches.len(),
&mismatches[..mismatches.len().min(10)]
);
}