emulation

This commit is contained in:
nora 2025-01-31 18:20:37 +01:00
parent 5595df2249
commit 38797253f6
3 changed files with 283 additions and 56 deletions

227
src/emulated.rs Normal file
View file

@ -0,0 +1,227 @@
macro_rules! define_emulation_entry {
($($name:ident,)*) => {
pub(crate) fn supports_dll(dll_name: &str) -> bool {
[
$($name::DLL_NAME,)*
]
.contains(&dll_name.to_lowercase().as_str())
}
pub(crate) fn emulate(dll_name: &str, function_name: &str) -> Option<usize> {
None
$(.or($name::emulate(dll_name, function_name)))*
}
};
}
define_emulation_entry!(
kernel32,
vcruntime140,
api_ms_win_crt_runtime_l1_1_0,
api_ms_win_crt_math_l1_1_0,
api_ms_win_crt_stdio_l1_1_0,
api_ms_win_crt_locale_l1_1_0,
api_ms_win_crt_heap_l1_1_0,
);
macro_rules! emulate {
($dllname:literal, mod $modname:ident {
$(
fn $name:ident($($args:tt)*) $(-> $ret:ty)? {
$($body:tt)*
}
)*
}) => {
mod $modname {
pub(super) const DLL_NAME: &str = $dllname;
pub(super) fn emulate(dll_name: &str, function_name: &str) -> Option<usize> {
if dll_name.to_lowercase() != $dllname {
return None;
}
$(
if function_name == stringify!($name) {
unsafe {
return Some(std::mem::transmute($name as extern "system" fn()));
}
}
)*
None
}
$(
// TODO: Windows API adapter...
#[allow(non_snake_case)]
extern "system" fn $name($($args)*) $(-> $ret)? {
$($body)*
}
)*
}
};
}
emulate!(
"kernel32.dll",
mod kernel32 {
fn QueryPerformanceCounter() {
todo!("QueryPerformanceCounter")
}
fn GetCurrentProcessId() {
todo!("GetCurrentProcessId")
}
fn GetCurrentThreadId() {
todo!("GetCurrentThreadId")
}
fn GetSystemTimeAsFileTime() {
todo!("GetSystemTimeAsFileTime")
}
fn InitializeSListHead() {
todo!("InitializeSListHead")
}
fn RtlCaptureContext() {
todo!("RtlCaptureContext")
}
fn RtlLookupFunctionEntry() {
todo!("RtlLookupFunctionEntry")
}
fn RtlVirtualUnwind() {
todo!("RtlVirtualUnwind")
}
fn IsDebuggerPresent() {
todo!("IsDebuggerPresent")
}
fn UnhandledExceptionFilter() {
todo!("UnhandledExceptionFilter")
}
fn SetUnhandledExceptionFilter() {
todo!("SetUnhandledExceptionFilter")
}
fn IsProcessorFeaturePresent() {
todo!("IsProcessorFeaturePresent")
}
fn GetModuleHandleW() {
todo!("GetModuleHandleW")
}
}
);
emulate!(
"vcruntime140.dll",
mod vcruntime140 {
fn __C_specific_handler() {
todo!("__C_specific_handler")
}
fn __current_exception() {
todo!("__current_exception")
}
fn __current_exception_context() {
todo!("__current_exception_context")
}
fn memset() {
todo!("memset")
}
fn memcpy() {
todo!("memcpy")
}
}
);
emulate!(
"api-ms-win-crt-runtime-l1-1-0.dll",
mod api_ms_win_crt_runtime_l1_1_0 {
fn _initterm_e() {
todo!("_initterm_e")
}
fn exit() {
todo!("exit")
}
fn _exit() {
todo!("_exit")
}
fn _initterm() {
todo!("_initterm")
}
fn __p___argc() {
todo!("__p___argc")
}
fn __p___argv() {
todo!("__p___argv")
}
fn _initialize_narrow_environment() {
todo!("_initialize_narrow_environment")
}
fn _c_exit() {
todo!("_c_exit")
}
fn _register_thread_local_exe_atexit_callback() {
todo!("_register_thread_local_exe_atexit_callback")
}
fn _seh_filter_exe() {
todo!("_seh_filter_exe")
}
fn _configure_narrow_argv() {
todo!("_configure_narrow_argv")
}
fn _set_app_type() {
todo!("_set_app_type")
}
fn _initialize_onexit_table() {
todo!("_initialize_onexit_table")
}
fn _register_onexit_function() {
todo!("_register_onexit_function")
}
fn _crt_atexit() {
todo!("_crt_atexit")
}
fn terminate() {
todo!("terminate")
}
fn _cexit() {
todo!("_cexit")
}
fn _get_initial_narrow_environment() {
todo!("_get_initial_narrow_environment")
}
}
);
emulate!(
"api-ms-win-crt-math-l1-1-0.dll",
mod api_ms_win_crt_math_l1_1_0 {
fn __setusermatherr() {
todo!("__setusermatherr")
}
}
);
emulate!(
"api-ms-win-crt-stdio-l1-1-0.dll",
mod api_ms_win_crt_stdio_l1_1_0 {
fn _set_fmode() {
todo!("_set_fmode")
}
fn __p__commode() {
todo!("__p__commode")
}
}
);
emulate!(
"api-ms-win-crt-locale-l1-1-0.dll",
mod api_ms_win_crt_locale_l1_1_0 {
fn _configthreadlocale() {
todo!("_configthreadlocale")
}
}
);
emulate!(
"api-ms-win-crt-heap-l1-1-0.dll",
mod api_ms_win_crt_heap_l1_1_0 {
fn _set_new_mode() {
todo!("_set_new_mode")
}
}
);

View file

@ -1,11 +1,7 @@
mod emulated;
mod sys; mod sys;
use std::{ use std::{ffi::CStr, fmt::Debug, path::PathBuf};
ffi::CStr,
fmt::Debug,
fs::File,
path::{Path, PathBuf},
};
#[derive(Clone, Copy, Debug, bytemuck::Zeroable, bytemuck::Pod)] #[derive(Clone, Copy, Debug, bytemuck::Zeroable, bytemuck::Pod)]
#[repr(C)] #[repr(C)]
@ -300,53 +296,74 @@ pub fn execute(pe: &[u8]) {
&pe[section.pointer_to_raw_data as usize..][..section.size_of_raw_data as usize], &pe[section.pointer_to_raw_data as usize..][..section.size_of_raw_data as usize],
); );
//crate::sys::protect( // NOTE: we might actually want to do this later in the process?
// section_a.as_ptr().cast(), // also it doesn't work on windows right now for some reason.
// section.virtual_size as usize, if false {
// mode, crate::sys::protect(
//) section_a.as_ptr().cast(),
//.unwrap(); section.virtual_size as usize,
mode,
)
.unwrap();
}
} }
let import_directory_table: &[ImportDirectoryTableEntry] = bytemuck::cast_slice( let import_directory_table = bytemuck::cast_slice::<_, ImportDirectoryTableEntry>(
&a[optional_header.import_table.virtual_address as usize..] &a[optional_header.import_table.virtual_address as usize..]
[..optional_header.import_table.size as usize], [..optional_header.import_table.size as usize],
); )
.to_vec();
for import_directory in import_directory_table { for import_directory in import_directory_table {
dbg!(import_directory); dbg!(import_directory);
let name = CStr::from_bytes_until_nul(&a[import_directory.name_rva as usize..]).unwrap(); let dll_name = CStr::from_bytes_until_nul(&a[import_directory.name_rva as usize..])
if name.is_empty() { .unwrap()
.to_owned();
if dll_name.is_empty() {
// Trailing null import directory. // Trailing null import directory.
break; break;
} }
dbg!(name); dbg!(&dll_name);
let dll = find_dll(name); let dll = find_dll(&dll_name);
match dll { match dll {
Some(path) => eprintln!(" found {name:?} at {path:?}"), Some(DllLocation::Emulated) => eprintln!(" emulating {dll_name:?}"),
None => eprintln!(" COULD NOT FIND {name:?}"), Some(DllLocation::Found(path)) => todo!("unsupported, loading dll at {path:?}"),
None => panic!("could not find dll {dll_name:?}"),
} }
let import_lookups = bytemuck::cast_slice::<u8, u64>( let import_lookups = bytemuck::cast_slice::<u8, u64>(
&a[import_directory.import_lookup_table_rva as usize..], &a[import_directory.import_address_table_rva as usize..],
); )
for import_lookup in import_lookups { .to_vec();
if *import_lookup == 0 { for (i, import_lookup) in import_lookups.iter().enumerate() {
let import_lookup = *import_lookup;
if import_lookup == 0 {
break; break;
} }
let ordinal_name_flag = import_lookup >> 63; let ordinal_name_flag = import_lookup >> 63;
if ordinal_name_flag == 1 { if ordinal_name_flag == 1 {
let ordinal_number = import_lookup & 0xFFFF; let ordinal_number = import_lookup & 0xFFFF;
eprintln!(" import by ordinal: {ordinal_number}"); eprintln!(" import by ordinal: {ordinal_number}");
todo!("unsupported, import by ordinal");
} else { } else {
let hint_name_table_rva = import_lookup & 0xFFFF_FFFF; let hint_name_table_rva = import_lookup & 0xFFFF_FFFF;
let hint = let hint =
bytemuck::cast_slice::<u8, u16>(&a[hint_name_table_rva as usize..][..2])[0]; bytemuck::cast_slice::<u8, u16>(&a[hint_name_table_rva as usize..][..2])[0];
let name = let func_name =
CStr::from_bytes_until_nul(&a[hint_name_table_rva as usize + 2..]).unwrap(); CStr::from_bytes_until_nul(&a[hint_name_table_rva as usize + 2..]).unwrap();
eprintln!(" import by name: hint={hint} name={name:?}"); eprintln!(" import by name: hint={hint} name={func_name:?}");
let resolved_va =
emulated::emulate(dll_name.to_str().unwrap(), func_name.to_str().unwrap())
.unwrap_or_else(|| {
panic!("could not find function {func_name:?} in dll {dll_name:?}")
});
assert_eq!(size_of::<usize>(), size_of::<u64>());
a[import_directory.import_address_table_rva as usize..][i * size_of::<u64>()..]
[..size_of::<u64>()]
.copy_from_slice(&resolved_va.to_ne_bytes());
} }
} }
} }
@ -382,28 +399,24 @@ fn parse_header(pe: &[u8]) -> (&CoffHeader, usize) {
) )
} }
fn find_dll(name: &CStr) -> Option<PathBuf> { #[derive(Debug)]
enum DllLocation {
Emulated,
#[expect(dead_code)]
Found(PathBuf),
}
fn find_dll(name: &CStr) -> Option<DllLocation> {
// https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order // https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order
let name = name.to_str().unwrap(); let name = name.to_str().unwrap();
if name.starts_with("api-") { if name.starts_with("api-") && emulated::supports_dll(name) {
// This is an API set, essentially a virtual alias // This is an API set, essentially a virtual alias
// https://learn.microsoft.com/en-us/windows/win32/apiindex/windows-apisets // https://learn.microsoft.com/en-us/windows/win32/apiindex/windows-apisets
return None; return Some(DllLocation::Emulated);
} }
let system = sys::system_directory().unwrap(); if emulated::supports_dll(name) {
eprintln!(" searching {system:?} for {name}"); return Some(DllLocation::Emulated);
let from_system = std::fs::read_dir(system).unwrap().find(|child| {
child
.as_ref()
.unwrap()
.file_name()
.to_str()
.unwrap()
.eq_ignore_ascii_case(name)
});
if let Some(from_system) = from_system {
return Some(from_system.unwrap().path());
} }
None None

View file

@ -94,21 +94,8 @@ mod imp {
.map_err(Into::into) .map_err(Into::into)
} }
} }
pub(crate) fn system_directory() -> io::Result<PathBuf> {
let mut buf = vec![0; 1024];
let ret = unsafe { GetSystemDirectoryW(Some(&mut buf)) };
if ret == 0 {
Err(io::Error::last_os_error())
} else {
Ok(std::char::decode_utf16(buf)
.map(Result::unwrap)
.take_while(|c| *c != '\0')
.collect::<String>()
.into())
}
}
} }
#[cfg(unix)] #[cfg(unix)]
mod imp { mod imp {
compile_error!("no unix yet lol skill issue"); compile_error!("no unix yet lol skill issue");