import ordinal

This commit is contained in:
nora 2025-02-01 17:24:57 +01:00
parent 64c30e201d
commit 402efaee61

View file

@ -2,7 +2,7 @@ mod emulated;
mod sys; mod sys;
use std::{ use std::{
ffi::CStr, ffi::{CStr, CString},
fmt::Debug, fmt::Debug,
ops::Deref, ops::Deref,
path::{Path, PathBuf}, path::{Path, PathBuf},
@ -228,8 +228,7 @@ const IMAGE_FILE_MACHINE_ARM64: u16 = 0xaa64;
pub fn execute(pe: &[u8], executable_path: &Path) { pub fn execute(pe: &[u8], executable_path: &Path) {
let image = load(pe, executable_path, false); let image = load(pe, executable_path, false);
let entrypoint = let entrypoint = image.base + image.opt_header.address_of_entry_point as usize;
image.opt_header.image_base as usize + image.opt_header.address_of_entry_point as usize;
tracing::debug!("YOLO to {:#x}", entrypoint); tracing::debug!("YOLO to {:#x}", entrypoint);
unsafe { unsafe {
@ -253,6 +252,10 @@ impl<'pe> Deref for Image<'pe> {
#[tracing::instrument(skip(pe, is_dll))] #[tracing::instrument(skip(pe, is_dll))]
fn load<'pe>(pe: &'pe [u8], executable_path: &Path, is_dll: bool) -> Image<'pe> { fn load<'pe>(pe: &'pe [u8], executable_path: &Path, is_dll: bool) -> Image<'pe> {
load_inner(pe, executable_path, is_dll)
}
fn load_inner<'pe>(pe: &'pe [u8], executable_path: &Path, is_dll: bool) -> Image<'pe> {
let (coff_header, after_header) = parse_header(pe); let (coff_header, after_header) = parse_header(pe);
match (std::env::consts::ARCH, coff_header.machine) { match (std::env::consts::ARCH, coff_header.machine) {
@ -375,7 +378,7 @@ fn load<'pe>(pe: &'pe [u8], executable_path: &Path, is_dll: bool) -> Image<'pe>
enum LoadedDll { enum LoadedDll {
Emulated, Emulated,
Real(Image<'static>), Real(Image<'static>, ExportDirectoryTable, Vec<CString>),
} }
let dll = find_dll(&dll_name, executable_path); let dll = find_dll(&dll_name, executable_path);
@ -391,44 +394,8 @@ fn load<'pe>(pe: &'pe [u8], executable_path: &Path, is_dll: bool) -> Image<'pe>
std::mem::ManuallyDrop::new(unsafe { memmap2::Mmap::map(&file).unwrap() }); std::mem::ManuallyDrop::new(unsafe { memmap2::Mmap::map(&file).unwrap() });
let mmap = unsafe { &*(&**mmap as *const [u8]) }; let mmap = unsafe { &*(&**mmap as *const [u8]) };
let image = load(&mmap, &path, true); let img: Image<'static> = load(&mmap, &path, true);
LoadedDll::Real(image)
}
None => {
panic!("could not find dll {dll_name:?}");
}
};
let import_lookups = bytemuck::cast_slice::<u8, u64>(
&image.loaded[import_directory.import_address_table_rva as usize..],
)
.to_vec();
for (i, import_lookup) in import_lookups.iter().enumerate() {
let import_lookup = *import_lookup;
if import_lookup == 0 {
break;
}
let ordinal_name_flag = import_lookup >> 63;
if ordinal_name_flag == 1 {
let ordinal_number = import_lookup & 0xFFFF;
tracing::debug!(" import by ordinal: {ordinal_number}");
todo!("unsupported, import by ordinal");
} else {
let hint_name_table_rva = import_lookup & 0xFFFF_FFFF;
let hint = bytemuck::cast_slice::<u8, u16>(
&image.loaded[hint_name_table_rva as usize..][..2],
)[0];
let func_name =
CStr::from_bytes_until_nul(&image.loaded[hint_name_table_rva as usize + 2..])
.unwrap();
tracing::debug!("import by name: hint={hint} name={func_name:?}");
let resolved_va = match &dll {
LoadedDll::Emulated => emulated::emulate(dll_name, func_name.to_str().unwrap())
.unwrap_or_else(|| {
panic!("could not find function {func_name:?} in dll {dll_name:?}");
}),
LoadedDll::Real(img) => {
// Read the single export directory table from the front // Read the single export directory table from the front
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#export-directory-table // https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#export-directory-table
@ -445,7 +412,10 @@ fn load<'pe>(pe: &'pe [u8], executable_path: &Path, is_dll: bool) -> Image<'pe>
&img[img.opt_header.export_table.va as usize..] &img[img.opt_header.export_table.va as usize..]
[..size_of::<ExportDirectoryTable>()], [..size_of::<ExportDirectoryTable>()],
)[0]; )[0];
tracing::debug!(?export_directory_table, "Export Directory Table of {dll_name}"); tracing::debug!(
?export_directory_table,
"Export Directory Table of {dll_name}"
);
// This is not aligned..? // This is not aligned..?
let mut names = vec![]; let mut names = vec![];
@ -455,14 +425,88 @@ fn load<'pe>(pe: &'pe [u8], executable_path: &Path, is_dll: bool) -> Image<'pe>
{ {
let name = u32::from_ne_bytes(img[name_ptr..][..4].try_into().unwrap()); let name = u32::from_ne_bytes(img[name_ptr..][..4].try_into().unwrap());
let name = CStr::from_bytes_until_nul(&img[name as usize..]).unwrap(); let name = CStr::from_bytes_until_nul(&img[name as usize..]).unwrap();
names.push(name); names.push(name.to_owned());
tracing::debug!(?name, "DLL {dll_name} has export"); tracing::debug!(?name, "DLL {dll_name} has export");
} }
let idx = if names.get(hint as usize) == Some(&func_name) { LoadedDll::Real(img, export_directory_table, names)
}
None => {
panic!("could not find dll {dll_name:?}");
}
};
let import_lookups = bytemuck::cast_slice::<u8, u64>(
&image.loaded[import_directory.import_address_table_rva as usize..],
)
.to_vec();
for (i, import_lookup) in import_lookups.iter().enumerate() {
let import_lookup = *import_lookup;
if import_lookup == 0 {
break;
}
fn compute_export_rva(
export_directory_table: &ExportDirectoryTable,
unbiased_ordinal: usize,
img: &Image<'_>,
) -> usize {
let eat_addr_rva = export_directory_table.export_address_table_rva as usize
+ (unbiased_ordinal * 4);
let export_rva = u32::from_ne_bytes(img[eat_addr_rva..][..4].try_into().unwrap());
if (img.opt_header.export_table.va
..(img.opt_header.export_table.va + img.opt_header.export_table.size))
.contains(&export_rva)
{
todo!("symbol forwarding")
}
export_rva as usize
}
let ordinal_name_flag = import_lookup >> 63;
let resolved_va = if ordinal_name_flag == 1 {
let ordinal_number = import_lookup & 0xFFFF;
tracing::debug!(" import by ordinal: {ordinal_number}");
match &dll {
LoadedDll::Emulated => panic!("unsupported: emulated import via ordinal"),
LoadedDll::Real(img, export_directory_table, _) => {
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#export-ordinal-table
let unbiased_ordinal =
ordinal_number as usize - export_directory_table.ordinal_base as usize;
let export_rva = compute_export_rva(
export_directory_table,
unbiased_ordinal as usize,
img,
);
img.base + export_rva as usize
}
}
} else {
let hint_name_table_rva = import_lookup & 0xFFFF_FFFF;
let hint = bytemuck::cast_slice::<u8, u16>(
&image.loaded[hint_name_table_rva as usize..][..2],
)[0];
let func_name =
CStr::from_bytes_until_nul(&image.loaded[hint_name_table_rva as usize + 2..])
.unwrap();
tracing::debug!("import by name: hint={hint} name={func_name:?}");
match &dll {
LoadedDll::Emulated => emulated::emulate(dll_name, func_name.to_str().unwrap())
.unwrap_or_else(|| {
panic!("could not find function {func_name:?} in dll {dll_name:?}");
}),
LoadedDll::Real(img, export_directory_table, names) => {
let idx =
if names.get(hint as usize) == Some(&func_name.to_owned()) {
hint as usize hint as usize
} else { } else {
names.binary_search(&func_name).unwrap_or_else(|_| { names.binary_search(&func_name.to_owned()).unwrap_or_else(|_| {
panic!("could not find function {func_name:?} in dll {dll_name}") panic!("could not find function {func_name:?} in dll {dll_name}")
}) })
}; };
@ -474,20 +518,15 @@ fn load<'pe>(pe: &'pe [u8], executable_path: &Path, is_dll: bool) -> Image<'pe>
); );
let unbiased_ordinal = ordinal_table[idx]; let unbiased_ordinal = ordinal_table[idx];
let eat_addr_rva = export_directory_table.export_address_table_rva as usize let export_rva = compute_export_rva(
+ (unbiased_ordinal as usize * 4); export_directory_table,
let export_rva = unbiased_ordinal as usize,
u32::from_ne_bytes(img[eat_addr_rva..][..4].try_into().unwrap()); img,
);
if (img.opt_header.export_table.va
..(img.opt_header.export_table.va + img.opt_header.export_table.size))
.contains(&export_rva)
{
todo!("symbol forwarding of {func_name:?} in {dll_name}")
}
img.base + export_rva as usize img.base + export_rva as usize
} }
}
}; };
assert_eq!(size_of::<usize>(), size_of::<u64>()); assert_eq!(size_of::<usize>(), size_of::<u64>());
@ -496,7 +535,6 @@ fn load<'pe>(pe: &'pe [u8], executable_path: &Path, is_dll: bool) -> Image<'pe>
.copy_from_slice(&resolved_va.to_ne_bytes()); .copy_from_slice(&resolved_va.to_ne_bytes());
} }
} }
}
tracing::debug!("applying section protections"); tracing::debug!("applying section protections");
for section in section_table { for section in section_table {