use core::ffi::c_void;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct ListEntry {
pub flink: *mut ListEntry,
pub blink: *mut ListEntry,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct UnicodeString {
pub length: u16,
pub maximum_length: u16,
pub buffer: *mut u16,
}
#[repr(C)]
pub struct LdrDataTableEntry {
pub in_load_order_links: ListEntry,
pub in_memory_order_links: ListEntry,
pub in_initialization_order_links: ListEntry,
pub dll_base: *mut c_void,
pub entry_point: *mut c_void,
pub size_of_image: u32,
pub full_dll_name: UnicodeString,
pub base_dll_name: UnicodeString,
}
#[repr(C)]
pub struct PebLdrData {
pub _length_and_init: [u8; 8],
pub _ss_handle: *mut c_void,
pub in_load_order_module_list: ListEntry,
}
#[repr(C)]
pub struct Peb {
pub _reserved1: [u8; 2],
pub being_debugged: u8,
pub _reserved2: [u8; 1],
pub _reserved3: [*mut c_void; 2],
pub ldr: *mut PebLdrData,
}
#[inline(always)]
pub fn current_peb() -> *mut Peb {
let peb: *mut Peb;
#[cfg(target_arch = "x86_64")]
unsafe {
core::arch::asm!(
"mov {peb}, gs:[0x60]",
peb = out(reg) peb,
options(nostack, preserves_flags, readonly)
);
}
#[cfg(target_arch = "x86")]
unsafe {
core::arch::asm!(
"mov {peb}, fs:[0x30]",
peb = out(reg) peb,
options(nostack, preserves_flags, readonly)
);
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))]
compile_error!("dyncvoke PEB walker only supports x86 / x86_64");
peb
}
pub const fn hash_name(bytes: &[u8]) -> u32 {
let mut hash: u32 = 5381;
let mut i = 0;
while i < bytes.len() {
let mut c = bytes[i];
if c >= b'A' && c <= b'Z' {
c += 32;
}
hash = hash.wrapping_mul(33).wrapping_add(c as u32);
i += 1;
}
hash
}
unsafe fn unicode_eq_ascii_ci(us: &UnicodeString, target: &[u8]) -> bool {
if us.buffer.is_null() {
return target.is_empty() && us.length == 0;
}
let len = (us.length / 2) as usize;
if len != target.len() {
return false;
}
for i in 0..len {
let wide = *us.buffer.add(i);
if wide >= 0x80 {
return false;
}
let mut byte = wide as u8;
if byte >= b'A' && byte <= b'Z' {
byte += 32;
}
let mut tgt = target[i];
if tgt >= b'A' && tgt <= b'Z' {
tgt += 32;
}
if byte != tgt {
return false;
}
}
true
}
unsafe fn hash_unicode_string(us: &UnicodeString) -> u32 {
if us.buffer.is_null() {
return 0;
}
let len = (us.length / 2) as usize;
let mut hash: u32 = 5381;
for i in 0..len {
let wide = *us.buffer.add(i);
if wide >= 0x80 {
return 0;
}
let mut byte = wide as u8;
if byte >= b'A' && byte <= b'Z' {
byte += 32;
}
hash = hash.wrapping_mul(33).wrapping_add(byte as u32);
}
hash
}
unsafe fn walk_modules<F>(mut visit: F) -> usize
where
F: FnMut(&LdrDataTableEntry) -> bool,
{
let peb = current_peb();
if peb.is_null() {
return 0;
}
let ldr = (*peb).ldr;
if ldr.is_null() {
return 0;
}
let head = &(*ldr).in_load_order_module_list as *const ListEntry as *mut ListEntry;
let mut cursor = (*head).flink;
while !cursor.is_null() && cursor != head {
let entry = cursor as *mut LdrDataTableEntry;
if visit(&*entry) {
return (*entry).dll_base as usize;
}
cursor = (*cursor).flink;
}
0
}
pub fn get_module_by_name(name: &str) -> usize {
unsafe { walk_modules(|entry| unicode_eq_ascii_ci(&entry.base_dll_name, name.as_bytes())) }
}
pub fn get_module_by_hash(hash: u32) -> usize {
unsafe { walk_modules(|entry| hash_unicode_string(&entry.base_dll_name) == hash) }
}