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 │
// └─────────────────────────────────────────────────────────────────────────┘
#[inline(always)]
pub unsafe fn import<
const Module: u64,
@@ -131,15 +130,25 @@ pub unsafe fn import<
"mov {x}, gs:[60h]", // TEB->PEB
"mov {x}, [{x} + 18h]", // PEB->LDR
"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
let offsets = const { XorSeed & 1 } as usize;
// in case of forwarded imports we will change these...
let mut module_hash = Module;
let mut import_hash = Import;
'search_for_import: loop {
let mut cursor = module_link as usize;
let end = (cursor as *const usize).add(offsets ^ 1).read();
let mut module: usize = 0usize;
#[cfg(debug_assertions)]
let mut found = false;
while cursor != end {
cursor = (cursor as *const usize).add(offsets).read();
@@ -152,11 +161,18 @@ pub unsafe fn import<
let hash = hash_utf16::<XorSeed>(core::slice::from_raw_parts(
name_ptr, (name_len >> 1) as usize));
if hash == Module { break; }
if hash == module_hash {
#[cfg(debug_assertions)]
if true {
found = true;
}
break;
}
}
debug_assert!(module != 0, "Module not found");
#[cfg(debug_assertions)]
debug_assert!(found, "Failed to find module");
let module = &*(module as *const ImageBase);
match module.exports() {
@@ -165,17 +181,66 @@ pub unsafe fn import<
core::arch::asm!("", options(noreturn));
}
Some(exports) => {
for export in exports {
if hash_utf8::<XorSeed2>(export.1) == Import {
return export.0;
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, "failed to find export");
debug_assert!(false, "function not found");
core::arch::asm!("", options(noreturn))
}
}
}
// ┌─────────────────────────────────────────────────────────────────────────┐
@@ -220,7 +285,7 @@ const PRIME: u64 = 0x100000001b3;
//noinspection DuplicatedCode
#[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
let mut hash = const {
@@ -238,6 +303,9 @@ pub const fn hash_utf8<const SALT: u64>(bytes: &[u8]) -> u64 {
};
let mut j = 0;
while j < bytes.len() {
let bytes = bytes[j];
let mut i = 0;
while i < bytes.len() {
let char = match bytes[i] {
@@ -249,6 +317,9 @@ pub const fn hash_utf8<const SALT: u64>(bytes: &[u8]) -> u64 {
hash = hash.wrapping_mul(PRIME);
i += 1;
}
j += 1;
}
hash
}
@@ -433,10 +504,11 @@ pub struct ExportIter<'a> {
image: &'a ImageBase,
export_dir: &'a ImageExportDirectory,
export_index: usize,
ext_range: core::ops::Range<usize>,
}
impl<'a> Iterator for ExportIter<'a> {
type Item = (usize, &'static [u8]);
type Item = (usize, &'static [u8], bool);
fn next(&mut self) -> Option<Self::Item> {
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_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;
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,
}
@@ -475,10 +549,15 @@ impl ImageBase {
let export_directory = unsafe {
&*((self + directory.virtual_address) as *const ImageExportDirectory)
};
let start = directory.virtual_address as usize;
let end = start + directory.size as usize;
Some(ExportIter {
image: self,
export_dir: export_directory,
export_index: 0,
ext_range: start..end,
})
}
}