use std::arch::asm;
use std::ops::Add;
use std::os::windows::ffi::OsStringExt;
use std::slice::from_raw_parts;
use std::ffi::{c_void, OsString};
use windows::Win32::System::SystemServices::{IMAGE_DOS_HEADER, IMAGE_DOS_SIGNATURE, IMAGE_EXPORT_DIRECTORY, IMAGE_NT_SIGNATURE};
use windows::Win32::System::Diagnostics::Debug::IMAGE_NT_HEADERS64;
use std::fmt;
struct ExportResolver<'a> {
module: &'a str,
base_address: usize, function: &'a str,
address: usize,
}
#[derive(Debug)]
pub struct ExportList<'a> {
list: Vec<ExportResolver<'a>>,
}
impl fmt::Debug for ExportResolver<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"ExportResolver {{ module: \"{}\", function: \"{}\", address: {:#x} }}",
self.module, self.function, self.address
)
}
}
#[derive(Debug)]
pub enum ExportError {
FunctionNotFound { module: String, function: String },
FunctionNotInList { function: String },
}
impl fmt::Display for ExportError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ExportError::FunctionNotFound { module, function } => {
write!(f, "Failed to get function address for {} in {}", function, module)
},
ExportError::FunctionNotInList { function } => {
write!(f, "Function {} could not be found in the list of resolved functions, are you sure it's there?.", function)
}
}
}
}
impl std::error::Error for ExportError {}
impl<'a> ExportList<'a> {
pub fn new() -> ExportList<'a> {
ExportList {
list: Vec::new(),
}
}
pub fn add(&mut self, module: &'a str, function: &'a str) -> Result<(), ExportError> {
for item in &self.list {
if item.module == module {
let result = get_function_from_exports(module, function, Some(item.base_address))
.ok_or_else(|| ExportError::FunctionNotFound {
module: module.to_string(),
function: function.to_string(),
})?;
self.list.push(result);
return Ok(());
}
}
let result = get_function_from_exports(module, function, None)
.ok_or_else(|| ExportError::FunctionNotFound {
module: module.to_string(),
function: function.to_string(),
})?;
self.list.push(result);
Ok(())
}
pub fn get_function_address(&self, function_name: &str) -> Result<usize, ExportError> {
self.list
.iter()
.find(|f| f.function == function_name)
.map(|f| f.address)
.ok_or_else(|| ExportError::FunctionNotInList {
function: function_name.to_string(),
})
}
}
#[allow(unused_variables)]
#[allow(unused_assignments)]
fn get_module_base(module_name: &str) -> Option<usize> {
let mut peb: usize;
let mut ldr: usize;
let mut in_memory_order_module_list: usize;
let mut current_entry: usize;
unsafe {
asm!(
"mov {peb}, gs:[0x60]",
"mov {ldr}, [{peb} + 0x18]",
"mov {in_memory_order_module_list}, [{ldr} + 0x10]", peb = out(reg) peb,
ldr = out(reg) ldr,
in_memory_order_module_list = out(reg) in_memory_order_module_list,
);
current_entry = in_memory_order_module_list;
loop {
let dll_base = *(current_entry.add(0x30) as *const usize);
let module_name_address = *(current_entry.add(0x60) as *const usize);
let module_length = *(current_entry.add(0x58) as *const u16);
if module_name_address != 0 && module_length > 0 {
let dll_name_slice = from_raw_parts(module_name_address as *const u16, (module_length / 2) as usize);
let dll_name = OsString::from_wide(dll_name_slice);
if dll_name.to_string_lossy().eq_ignore_ascii_case(module_name) {
return Some(dll_base);
}
} else {
return None;
}
current_entry = *(current_entry as *const usize);
if current_entry == in_memory_order_module_list {
return None;
}
}
}
}
fn get_function_from_exports<'a>(dll_name: &'a str, needle: &'a str, dll_base: Option<usize>) -> Option<ExportResolver<'a>> {
let dll_base: *mut c_void = match dll_base {
Some(base) => base as *mut c_void,
None => {
match get_module_base(dll_name) {
Some(a) => a as *mut c_void,
None => {
return None;
},
}
},
};
let dos_header: IMAGE_DOS_HEADER = unsafe { read_memory(dll_base as *const IMAGE_DOS_HEADER) };
if dos_header.e_magic != IMAGE_DOS_SIGNATURE {
return None;
}
let nt_headers = unsafe { read_memory(dll_base.offset(dos_header.e_lfanew as isize) as *const IMAGE_NT_HEADERS64) };
if nt_headers.Signature != IMAGE_NT_SIGNATURE {
return None;
}
let export_dir_rva = nt_headers.OptionalHeader.DataDirectory[0].VirtualAddress;
let export_offset = unsafe {dll_base.add(export_dir_rva as usize) };
let export_dir: IMAGE_EXPORT_DIRECTORY = unsafe { read_memory(export_offset as *const IMAGE_EXPORT_DIRECTORY) };
let address_of_functions_rva = export_dir.AddressOfFunctions as usize;
let address_of_names_rva = export_dir.AddressOfNames as usize;
let ordinals_rva = export_dir.AddressOfNameOrdinals as usize;
let functions = unsafe { dll_base.add(address_of_functions_rva as usize) } as *const u32;
let names = unsafe { dll_base.add(address_of_names_rva as usize) } as *const u32;
let ordinals = unsafe { dll_base.add(ordinals_rva as usize) } as *const u16;
let number_of_names = export_dir.NumberOfNames;
for i in 0..number_of_names {
let name_rva = unsafe { *names.offset(i.try_into().unwrap()) as usize };
let name_addr = unsafe { dll_base.add(name_rva) };
let function_name = unsafe {
let char = name_addr as *const u8;
let mut len = 0;
while *char.add(len) != 0 {
len += 1;
}
std::slice::from_raw_parts(char, len)
};
let function_name = std::str::from_utf8(function_name).unwrap_or("Invalid UTF-8");
if function_name.eq("Invalid UTF-8") {
return None;
}
if function_name.eq(needle) {
let ordinal = unsafe { *ordinals.offset(i.try_into().unwrap()) as usize };
let fn_rva = unsafe { *functions.add(ordinal) as usize };
let fn_addr = unsafe { dll_base.add(fn_rva) } as *const c_void;
let result = ExportResolver {
module: dll_name,
base_address: dll_base as usize,
function: needle,
address: fn_addr as usize,
};
return Some(result);
}
}
None
}
unsafe fn read_memory<T>(address: *const T) -> T {
std::ptr::read(address)
}