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 │
|
// │ 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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user