diff --git a/src/lazy_importer.rs b/src/lazy_importer.rs index 677df07..ae36889 100644 --- a/src/lazy_importer.rs +++ b/src/lazy_importer.rs @@ -115,7 +115,6 @@ impl core::ops::Deref for LazyImpor // ┌─────────────────────────────────────────────────────────────────────────┐ // │ the function actually responsible for everything │ // └─────────────────────────────────────────────────────────────────────────┘ - #[inline(always)] pub unsafe fn import< const Module: u64, @@ -128,56 +127,122 @@ pub unsafe fn import< // get the ldr data table entries let module_link: *const usize; core::arch::asm!( - "mov {x}, gs:[60h]", // TEB->PEB - "mov {x}, [{x} + 18h]", // PEB->LDR - "lea {x}, [{x} + 10h]", // LDR->InLoadOrderModuleList - x = out(reg) module_link); + "mov {x}, gs:[60h]", // TEB->PEB + "mov {x}, [{x} + 18h]", // PEB->LDR + "lea {x}, [{x} + 10h]", // LDR->InLoadOrderModuleList + 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; - let mut cursor = module_link as usize; - let end = (cursor as *const usize).add(offsets ^ 1).read(); - let mut module: usize = 0usize; + // in case of forwarded imports we will change these... + let mut module_hash = Module; + let mut import_hash = Import; - while cursor != end { - cursor = (cursor as *const usize).add(offsets).read(); + 'search_for_import: loop { - // 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 mut cursor = module_link as usize; + let end = (cursor as *const usize).add(offsets ^ 1).read(); + let mut module: usize = 0usize; - // calculate the hash for the module - let hash = hash_utf16::(core::slice::from_raw_parts( - name_ptr, (name_len >> 1) as usize)); + #[cfg(debug_assertions)] + let mut found = false; - 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::(core::slice::from_raw_parts( + name_ptr, (name_len >> 1) as usize)); - match module.exports() { - None => { - debug_assert!(false, "Module has no exports"); - core::arch::asm!("", options(noreturn)); + if hash == module_hash { + + #[cfg(debug_assertions)] + if true { + found = true; + } + break; + } } - Some(exports) => { - for export in exports { - if hash_utf8::(export.1) == Import { - return export.0; + + #[cfg(debug_assertions)] + debug_assert!(found, "Failed to find module"); + 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::([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::([module, &dll]); + import_hash = hash_utf8::([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 │ // └─────────────────────────────────────────────────────────────────────────┘ @@ -220,7 +285,7 @@ const PRIME: u64 = 0x100000001b3; //noinspection DuplicatedCode #[inline(always)] -pub const fn hash_utf8(bytes: &[u8]) -> u64 { +pub const fn hash_utf8(bytes: [&[u8]; S]) -> u64 { // make the initial state based on the salt let mut hash = const { @@ -238,17 +303,23 @@ pub const fn hash_utf8(bytes: &[u8]) -> u64 { }; - let mut i = 0; - while i < bytes.len() { - let char = match bytes[i] { - 0x40..=0x5A => bytes[i] + 0x20, - _ => bytes[i], - } as 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] { + 0x40..=0x5A => bytes[i] + 0x20, + _ => bytes[i], + } as u64; - hash = hash ^ (char); - hash = hash.wrapping_mul(PRIME); - i += 1; + hash = hash ^ (char); + 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, } impl<'a> Iterator for ExportIter<'a> { - type Item = (usize, &'static [u8]); + type Item = (usize, &'static [u8], bool); fn next(&mut self) -> Option { 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, }) } -} +} \ No newline at end of file