use azathoth_core::os::Current::consts::IMAGE_DIRECTORY_ENTRY_EXPORT;
use azathoth_core::os::Current::structs::{IMAGE_EXPORT_DIRECTORY, LDR_DATA_TABLE_ENTRY, LIST_ENTRY};
use azathoth_utils::hasher::{FuncIdentifier, Hasher};
use crate::ident2val;
use crate::windows::utils::{copy_to_buf, get_nt_headers, get_peb, ptr_to_str, rva, strip_dll_suffix, to_ascii_uppercase_buf, ustr_to_str};
pub unsafe fn get_proc_address<'a, H, I>(base_address: *mut u8, hasher: &H, ident: I) -> Option<usize>
where
H: Hasher,
I: Into<
FuncIdentifier<'a>>
{
unsafe {
let base = base_address as usize;
let nt_headers = get_nt_headers(base_address)?;
let export_entry =
(*nt_headers).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT as usize];
let export_rva = export_entry.VirtualAddress;
let export_size = export_entry.Size;
let export_dir = rva::<IMAGE_EXPORT_DIRECTORY>(base, export_rva);
let num_names = (*export_dir).NumberOfNames as usize;
let num_funcs = (*export_dir).NumberOfFunctions as usize;
let names = core::slice::from_raw_parts(rva::<u32>(base, (*export_dir).AddressOfNames), num_names);
let ordinals = core::slice::from_raw_parts(
rva::<u16>(base, (*export_dir).AddressOfNameOrdinals),
num_names,
);
let funcs = core::slice::from_raw_parts(
rva::<u32>(base, (*export_dir).AddressOfFunctions),
num_funcs,
);
let identifier = ident2val(ident, hasher);
for i in 0..num_names {
let name_ptr = (base + names[i] as usize) as *const u8;
let name = ptr_to_str(name_ptr)?;
if identifier == hasher.hash(name) {
let ordinal = ordinals[i] as usize;
if ordinal >= funcs.len() {
return None;
}
let func_rva = funcs[ordinal];
let func_addr = base + func_rva as usize;
if func_rva >= export_rva && func_rva < export_rva + export_size {
let forwarder_str = ptr_to_str(rva::<u8>(base, func_rva))?;
return resolve_forwarder(forwarder_str, hasher);
}
return Some(func_addr);
}
}
None
}
}
pub unsafe fn load_library<'a, H, I>(ident: I, hasher: &H) -> Option<*mut u8>
where
H: Hasher,
I: Into<FuncIdentifier<'a>> {
unsafe {
let peb = get_peb();
let ldr = (*peb).Ldr;
let list_head = &mut (*ldr).InLoadOrderModuleList as *mut LIST_ENTRY;
let mut current = (*list_head).flink;
let target_hash = match ident.into() {
FuncIdentifier::Hashed(hash) => hash,
FuncIdentifier::Name(name) => {
hasher.hash(strip_dll_suffix(name))
},
FuncIdentifier::Bytes(bytes) => {
let s = core::str::from_utf8_unchecked(&bytes);
hasher.hash(strip_dll_suffix(s))
},
};
while current != list_head {
let entry = current as *const LDR_DATA_TABLE_ENTRY;
let base_name = &(*entry).BaseDllName;
if let Some(name) = ustr_to_str(base_name) {
let mut buf = [0u8; 64];
let _ = to_ascii_uppercase_buf(name, &mut buf)?;
let str_from_buf = core::str::from_utf8(&buf).ok()?;
let cleaned_str = str_from_buf.trim_end_matches(|c: char| c == '\0' || c.is_ascii_whitespace());
let mut buf2 = [0u8; 64];
let str_from_buf2 = copy_to_buf(cleaned_str, &mut buf2)?;
let str_to_hash = strip_dll_suffix(str_from_buf2);
let hash = hasher.hash(str_to_hash);
if hash == target_hash {
return Some((*entry).DllBase as *mut u8);
}
}
current = (*current).flink;
}
None
}
}
pub unsafe fn resolve_forwarder<H: Hasher>(forwarder: &str, hasher: &H) -> Option<usize> {
let idx = forwarder.find('.')?;
let dll_name_part = &forwarder[..idx];
let symbol_part = &forwarder[idx + 1..];
let base = unsafe { load_library(dll_name_part, hasher)? };
if base.is_null() {
return None;
}
unsafe { get_proc_address(base, hasher, symbol_part)}
}
pub unsafe fn find_api<'a, H, I, T>(base: *mut u8, ident: I, hasher: &H) -> Option<T>
where
H: Hasher,
I: Into<FuncIdentifier<'a>>,
{
unsafe {
let addr = get_proc_address(base, hasher, ident)?;
Some(core::mem::transmute_copy::<_, T>(&addr))
}
}