implement import forwarding...
This commit is contained in:
@@ -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,19 +181,68 @@ 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ┌─────────────────────────────────────────────────────────────────────────┐
|
||||
// │ Helper PRNG functions │
|
||||
// └─────────────────────────────────────────────────────────────────────────┘
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user