implement import forwarding...

This commit is contained in:
Numbers
2025-05-18 03:39:35 +02:00
parent 5571f608d9
commit 71fa023c73

View File

@@ -115,7 +115,6 @@ impl<const M: u64, const I: u64, const X: u64, V> core::ops::Deref for LazyImpor
// ┌─────────────────────────────────────────────────────────────────────────┐ // ┌─────────────────────────────────────────────────────────────────────────┐
// │ the function actually responsible for everything │ // │ the function actually responsible for everything │
// └─────────────────────────────────────────────────────────────────────────┘ // └─────────────────────────────────────────────────────────────────────────┘
#[inline(always)] #[inline(always)]
pub unsafe fn import< pub unsafe fn import<
const Module: u64, const Module: u64,
@@ -128,56 +127,122 @@ pub unsafe fn import<
// get the ldr data table entries // get the ldr data table entries
let module_link: *const usize; let module_link: *const usize;
core::arch::asm!( core::arch::asm!(
"mov {x}, gs:[60h]", // TEB->PEB "mov {x}, gs:[60h]", // TEB->PEB
"mov {x}, [{x} + 18h]", // PEB->LDR "mov {x}, [{x} + 18h]", // PEB->LDR
"lea {x}, [{x} + 10h]", // LDR->InLoadOrderModuleList "lea {x}, [{x} + 10h]", // LDR->InLoadOrderModuleList
x = out(reg) module_link); x = out(reg) module_link,
);
// 0x0 = next, 0x8 = prev, use the xor seed to flip the direction of the iterator // 0x0 = next, 0x8 = prev, use the xor seed to flip the direction of the iterator
let offsets = const { XorSeed & 1 } as usize; let offsets = const { XorSeed & 1 } as usize;
let mut cursor = module_link as usize; // in case of forwarded imports we will change these...
let end = (cursor as *const usize).add(offsets ^ 1).read(); let mut module_hash = Module;
let mut module: usize = 0usize; let mut import_hash = Import;
while cursor != end { 'search_for_import: loop {
cursor = (cursor as *const usize).add(offsets).read();
// extract the appropriate fields let mut cursor = module_link as usize;
let name_len = ((cursor + 0x58) as *const u16).read(); let end = (cursor as *const usize).add(offsets ^ 1).read();
let name_ptr = ((cursor + 0x60) as *const *const u16).read(); let mut module: usize = 0usize;
module = ((cursor + 0x30) as *const usize).read();
// calculate the hash for the module #[cfg(debug_assertions)]
let hash = hash_utf16::<XorSeed>(core::slice::from_raw_parts( let mut found = false;
name_ptr, (name_len >> 1) as usize));
if hash == Module { break; } while cursor != end {
} cursor = (cursor as *const usize).add(offsets).read();
debug_assert!(module != 0, "Module not found"); // extract the appropriate fields
let name_len = ((cursor + 0x58) as *const u16).read();
let name_ptr = ((cursor + 0x60) as *const *const u16).read();
module = ((cursor + 0x30) as *const usize).read();
let module = &*(module as *const ImageBase); // calculate the hash for the module
let hash = hash_utf16::<XorSeed>(core::slice::from_raw_parts(
name_ptr, (name_len >> 1) as usize));
match module.exports() { if hash == module_hash {
None => {
debug_assert!(false, "Module has no exports"); #[cfg(debug_assertions)]
core::arch::asm!("", options(noreturn)); if true {
found = true;
}
break;
}
} }
Some(exports) => {
for export in exports { #[cfg(debug_assertions)]
if hash_utf8::<XorSeed2>(export.1) == Import { debug_assert!(found, "Failed to find module");
return export.0; let module = &*(module as *const ImageBase);
match module.exports() {
None => {
debug_assert!(false, "Module has no exports");
core::arch::asm!("", options(noreturn));
}
Some(exports) => {
for (vaddr, name, forwarded) in exports {
if hash_utf8::<XorSeed2,1>([name]) == import_hash {
// the import is forwarded... oh boy...
if forwarded {
// strlen and convert to a slice
let fwd = {
let ptr = vaddr as *const u8;
let len = (0usize..)
.position(|i| ptr.add(i).read() == 0)
.unwrap_or(0);
core::slice::from_raw_parts(ptr, len)
};
let Some(split) = fwd.iter().position(|i| *i == b'.') else {
debug_assert!(false, "Forwarded input lacks . separator");
core::arch::asm!("", options(noreturn))
};
// split our bytes into two slices at the .
let (module, import) = (&fwd[..split], &fwd[split+1..]);
// to prevent bullshit where people can sig scan for .dll or xref it
// I do this hacky encrypt
let dll_crypt = const {
let mut p = Prng::new(XorSeed);
p.skip(4);
u32::from_le_bytes(*b".dll") ^ p.next() as u32
};
// use blackbox so it's not decrypted at compile time
let dll = core::hint::black_box(dll_crypt) ^ const {
let mut p = Prng::new(XorSeed);
p.skip(4);
p.next() as u32
};
let dll = dll.to_le_bytes();
module_hash = hash_utf8::<XorSeed,2>([module, &dll]);
import_hash = hash_utf8::<XorSeed2, 1>([import]);
// restart the loop but with our new hashes now...
continue 'search_for_import;
}
return vaddr;
}
} }
} }
} }
debug_assert!(false, "function not found");
core::arch::asm!("", options(noreturn))
} }
debug_assert!(false, "failed to find export");
core::arch::asm!("", options(noreturn))
} }
} }
// ┌─────────────────────────────────────────────────────────────────────────┐ // ┌─────────────────────────────────────────────────────────────────────────┐
// │ Helper PRNG functions │ // │ Helper PRNG functions │
// └─────────────────────────────────────────────────────────────────────────┘ // └─────────────────────────────────────────────────────────────────────────┘
@@ -220,7 +285,7 @@ const PRIME: u64 = 0x100000001b3;
//noinspection DuplicatedCode //noinspection DuplicatedCode
#[inline(always)] #[inline(always)]
pub const fn hash_utf8<const SALT: u64>(bytes: &[u8]) -> u64 { pub const fn hash_utf8<const SALT: u64, const S: usize>(bytes: [&[u8]; S]) -> u64 {
// make the initial state based on the salt // make the initial state based on the salt
let mut hash = const { let mut hash = const {
@@ -238,17 +303,23 @@ pub const fn hash_utf8<const SALT: u64>(bytes: &[u8]) -> u64 {
}; };
let mut i = 0; let mut j = 0;
while i < bytes.len() { while j < bytes.len() {
let char = match bytes[i] { let bytes = bytes[j];
0x40..=0x5A => bytes[i] + 0x20, let mut i = 0;
_ => bytes[i], while i < bytes.len() {
} as u64; let char = match bytes[i] {
0x40..=0x5A => bytes[i] + 0x20,
_ => bytes[i],
} as u64;
hash = hash ^ (char); hash = hash ^ (char);
hash = hash.wrapping_mul(PRIME); hash = hash.wrapping_mul(PRIME);
i += 1; i += 1;
}
j += 1;
} }
hash hash
} }
@@ -433,10 +504,11 @@ pub struct ExportIter<'a> {
image: &'a ImageBase, image: &'a ImageBase,
export_dir: &'a ImageExportDirectory, export_dir: &'a ImageExportDirectory,
export_index: usize, export_index: usize,
ext_range: core::ops::Range<usize>,
} }
impl<'a> Iterator for ExportIter<'a> { impl<'a> Iterator for ExportIter<'a> {
type Item = (usize, &'static [u8]); type Item = (usize, &'static [u8], bool);
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
match self.export_index < self.export_dir.number_of_name_pointers as usize { match self.export_index < self.export_dir.number_of_name_pointers as usize {
@@ -457,11 +529,13 @@ impl<'a> Iterator for ExportIter<'a> {
let export_name = self.image.offset(*export_names.add(self.export_index)); let export_name = self.image.offset(*export_names.add(self.export_index));
let export_ordinal = *export_ordinals.add(self.export_index); let export_ordinal = *export_ordinals.add(self.export_index);
let export_rva = self.image.offset(*export_functions.add(export_ordinal as usize)) as usize; let export_rva = *export_functions.add(export_ordinal as usize);
let export_va = self.image.offset(export_rva) as usize;
self.export_index += 1; self.export_index += 1;
Some((export_rva, u8_nul_terminated(export_name))) let is_forwarded = self.ext_range.contains(&(export_rva as usize));
Some((export_va, u8_nul_terminated(export_name), is_forwarded))
} }
false => None, false => None,
} }
@@ -475,10 +549,15 @@ impl ImageBase {
let export_directory = unsafe { let export_directory = unsafe {
&*((self + directory.virtual_address) as *const ImageExportDirectory) &*((self + directory.virtual_address) as *const ImageExportDirectory)
}; };
let start = directory.virtual_address as usize;
let end = start + directory.size as usize;
Some(ExportIter { Some(ExportIter {
image: self, image: self,
export_dir: export_directory, export_dir: export_directory,
export_index: 0, export_index: 0,
ext_range: start..end,
}) })
} }
} }