mirror of
https://github.com/Noratrieb/portability.git
synced 2026-01-16 08:45:03 +01:00
emulation
This commit is contained in:
parent
e8ba83c443
commit
8c23716f8f
3 changed files with 250 additions and 156 deletions
186
src/emulated.rs
186
src/emulated.rs
|
|
@ -1,4 +1,9 @@
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
mod base_defs {
|
mod base_defs {
|
||||||
|
use std::{ffi::CStr, fmt::Debug};
|
||||||
|
|
||||||
pub(crate) type HANDLE = usize;
|
pub(crate) type HANDLE = usize;
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
|
@ -23,6 +28,25 @@ mod base_defs {
|
||||||
f.write_str(&self.to_string())
|
f.write_str(&self.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub(crate) struct LPCSTR(pub *const std::ffi::c_char);
|
||||||
|
impl LPCSTR {
|
||||||
|
pub(crate) fn as_cstr(&self) -> &CStr {
|
||||||
|
unsafe { CStr::from_ptr(self.0) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::fmt::Debug for LPCSTR {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
Debug::fmt(self.as_cstr(), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub(super) struct CRITICAL_SECTION {
|
||||||
|
pub(super) mutex: std::sync::atomic::AtomicU64,
|
||||||
|
pub(super) pad: [u8; 40 - 8],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! define_emulation_entry {
|
macro_rules! define_emulation_entry {
|
||||||
|
|
@ -56,6 +80,16 @@ define_emulation_entry!(
|
||||||
ws2_32,
|
ws2_32,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
macro_rules! make_body {
|
||||||
|
($name:ident $($argname:ident),* @ delegate($dll:ident)) => {
|
||||||
|
crate::emulated::$dll::$name($($argname),*)
|
||||||
|
};
|
||||||
|
($name:ident $($argname:ident),* @ $($body:tt)*) => {
|
||||||
|
#[allow(unused_unsafe)]
|
||||||
|
unsafe { $($body)* }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! emulate {
|
macro_rules! emulate {
|
||||||
($dllname:literal, mod $modname:ident {
|
($dllname:literal, mod $modname:ident {
|
||||||
$(
|
$(
|
||||||
|
|
@ -79,7 +113,7 @@ macro_rules! emulate {
|
||||||
if function_name == stringify!($name) {
|
if function_name == stringify!($name) {
|
||||||
unsafe {
|
unsafe {
|
||||||
// NOTE: The ABI string is a lie.....
|
// NOTE: The ABI string is a lie.....
|
||||||
return Some(std::mem::transmute($name::trampoline as unsafe extern "C" fn($($argty),*)));
|
return Some(std::mem::transmute($name as unsafe extern "win64" fn($($argty),*) $(-> $ret)?));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
|
|
@ -87,43 +121,13 @@ macro_rules! emulate {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use crate::emulated::base_defs::*;
|
||||||
|
|
||||||
$(
|
$(
|
||||||
#[allow(non_snake_case)]
|
$(#[$attr])*
|
||||||
mod $name {
|
pub(super) unsafe extern "win64" fn $name($($argname: $argty),*) $(-> $ret)? {
|
||||||
#[allow(unused_imports)]
|
make_body! { $name $($argname),* @ $($body)* }
|
||||||
use crate::emulated::base_defs::*;
|
|
||||||
|
|
||||||
// https://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventions
|
|
||||||
cfg_if::cfg_if! {
|
|
||||||
if #[cfg(windows)] {
|
|
||||||
pub(super) unsafe extern "C" fn trampoline($($argname: $argty)*) {
|
|
||||||
unsafe { inner($($argname),*) }
|
|
||||||
}
|
|
||||||
} else if #[cfg(all(target_os = "linux", target_arch = "x86_64"))] {
|
|
||||||
#[naked_function::naked]
|
|
||||||
#[export_name = concat!("portability_callconv_trampoline_", stringify!($modname), "__", stringify!($name))]
|
|
||||||
pub(super) unsafe extern "C" fn trampoline($($argname: $argty),*) {
|
|
||||||
// Map Windows arguments to System V arguments.
|
|
||||||
// For now we only support 4 integer arguments.
|
|
||||||
asm!(
|
|
||||||
"mov rdi, rcx", // arg 1
|
|
||||||
"mov rsi, rdx", // arg 2
|
|
||||||
"mov rdx, r8", // arg 3
|
|
||||||
"mov rcx, r9", // arg 4
|
|
||||||
"jmp {}",
|
|
||||||
sym inner,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
compile_error!("unsupported architecture or operating system");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$(#[$attr])*
|
|
||||||
unsafe extern "C" fn inner($($argname: $argty),*) $(-> $ret)? {
|
|
||||||
#[allow(unused_unsafe)]
|
|
||||||
unsafe { $($body)* }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
|
|
@ -165,9 +169,17 @@ emulate!(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
emulate!(
|
emulate!(
|
||||||
"api-ms-win-core-synch-l1-2-0.dll",
|
"api-ms-win-core-synch-l1-2-0.dll",
|
||||||
mod api_ms_win_core_synch_l1_2_0 {
|
mod api_ms_win_core_synch_l1_2_0 {
|
||||||
|
fn InitializeCriticalSectionEx(
|
||||||
|
lpCriticalSection: *mut (),
|
||||||
|
dwSpinCount: u32,
|
||||||
|
flags: u32,
|
||||||
|
) -> bool {
|
||||||
|
delegate(kernel32)
|
||||||
|
}
|
||||||
fn WaitOnAddress() {
|
fn WaitOnAddress() {
|
||||||
todo!("WaitOnAddress")
|
todo!("WaitOnAddress")
|
||||||
}
|
}
|
||||||
|
|
@ -293,9 +305,8 @@ emulate!(
|
||||||
fn DecodePointer() {
|
fn DecodePointer() {
|
||||||
todo!("DecodePointer")
|
todo!("DecodePointer")
|
||||||
}
|
}
|
||||||
fn DeleteCriticalSection() {
|
/// <https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-deletecriticalsection>
|
||||||
todo!("DeleteCriticalSection")
|
fn DeleteCriticalSection(_lpCriticalSection: *mut ()) {}
|
||||||
}
|
|
||||||
fn DeleteFiber() {
|
fn DeleteFiber() {
|
||||||
todo!("DeleteFiber")
|
todo!("DeleteFiber")
|
||||||
}
|
}
|
||||||
|
|
@ -314,8 +325,19 @@ emulate!(
|
||||||
fn EncodePointer() {
|
fn EncodePointer() {
|
||||||
todo!("EncodePointer")
|
todo!("EncodePointer")
|
||||||
}
|
}
|
||||||
fn EnterCriticalSection() {
|
/// <https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-entercriticalsection>
|
||||||
todo!("EnterCriticalSection")
|
fn EnterCriticalSection(lpCriticalSection: *mut CRITICAL_SECTION) {
|
||||||
|
// a shitty spinlock
|
||||||
|
while (&*lpCriticalSection)
|
||||||
|
.mutex
|
||||||
|
.compare_exchange_weak(
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
std::sync::atomic::Ordering::Acquire,
|
||||||
|
std::sync::atomic::Ordering::Relaxed,
|
||||||
|
)
|
||||||
|
.is_err()
|
||||||
|
{}
|
||||||
}
|
}
|
||||||
fn EnumSystemLocalesW() {
|
fn EnumSystemLocalesW() {
|
||||||
todo!("EnumSystemLocalesW")
|
todo!("EnumSystemLocalesW")
|
||||||
|
|
@ -335,8 +357,10 @@ emulate!(
|
||||||
fn FindNextFileW() {
|
fn FindNextFileW() {
|
||||||
todo!("FindNextFileW")
|
todo!("FindNextFileW")
|
||||||
}
|
}
|
||||||
fn FlsAlloc() {
|
/// <https://learn.microsoft.com/en-us/windows/win32/api/fibersapi/nf-fibersapi-flsalloc>
|
||||||
todo!("FlsAlloc")
|
fn FlsAlloc(_callback: extern "win64" fn()) -> u32 {
|
||||||
|
const FLS_OUT_OF_INDEXES: u32 = -1_i32 as u32;
|
||||||
|
FLS_OUT_OF_INDEXES
|
||||||
}
|
}
|
||||||
fn FlsFree() {
|
fn FlsFree() {
|
||||||
todo!("FlsFree")
|
todo!("FlsFree")
|
||||||
|
|
@ -448,8 +472,8 @@ emulate!(
|
||||||
fn GetFullPathNameW() {
|
fn GetFullPathNameW() {
|
||||||
todo!("GetFullPathNameW")
|
todo!("GetFullPathNameW")
|
||||||
}
|
}
|
||||||
fn GetLastError() {
|
fn GetLastError() -> u32 {
|
||||||
todo!("GetLastError")
|
1
|
||||||
}
|
}
|
||||||
fn GetLocaleInfoEx() {
|
fn GetLocaleInfoEx() {
|
||||||
todo!("GetLocaleInfoEx")
|
todo!("GetLocaleInfoEx")
|
||||||
|
|
@ -469,8 +493,9 @@ emulate!(
|
||||||
fn GetModuleHandleExW() {
|
fn GetModuleHandleExW() {
|
||||||
todo!("GetModuleHandleExW")
|
todo!("GetModuleHandleExW")
|
||||||
}
|
}
|
||||||
fn GetModuleHandleW() {
|
fn GetModuleHandleW() -> u64 {
|
||||||
todo!("GetModuleHandleW")
|
tracing::error!("TODO GetModuleHandleW");
|
||||||
|
0
|
||||||
}
|
}
|
||||||
fn GetNativeSystemInfo() {
|
fn GetNativeSystemInfo() {
|
||||||
todo!("GetNativeSystemInfo")
|
todo!("GetNativeSystemInfo")
|
||||||
|
|
@ -482,8 +507,17 @@ emulate!(
|
||||||
todo!("GetOverlappedResult")
|
todo!("GetOverlappedResult")
|
||||||
}
|
}
|
||||||
/// <https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress>
|
/// <https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress>
|
||||||
fn GetProcAddress(hModule: u64, lpProcName: LPCWSTR) {
|
fn GetProcAddress(hModule: u64, lpProcName: LPCSTR) -> usize {
|
||||||
todo!("GetProcAddress: {lpProcName:?}")
|
let dll = crate::GLOBAL_STATE
|
||||||
|
.state
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.hmodule_to_dll
|
||||||
|
.get(&hModule)
|
||||||
|
.cloned()
|
||||||
|
.unwrap();
|
||||||
|
// TODO: error handling...
|
||||||
|
crate::va_for_dll_export_by_name(&dll, lpProcName.as_cstr(), 0)
|
||||||
}
|
}
|
||||||
fn GetProcessHeap() {
|
fn GetProcessHeap() {
|
||||||
todo!("GetProcessHeap")
|
todo!("GetProcessHeap")
|
||||||
|
|
@ -578,8 +612,19 @@ emulate!(
|
||||||
fn InitializeCriticalSectionAndSpinCount() {
|
fn InitializeCriticalSectionAndSpinCount() {
|
||||||
todo!("InitializeCriticalSectionAndSpinCount")
|
todo!("InitializeCriticalSectionAndSpinCount")
|
||||||
}
|
}
|
||||||
fn InitializeCriticalSectionEx() {
|
fn InitializeCriticalSectionEx(
|
||||||
todo!("InitializeCriticalSectionEx")
|
lpCriticalSection: *mut (),
|
||||||
|
_dwSpinCount: u32,
|
||||||
|
_flags: u32,
|
||||||
|
) -> bool {
|
||||||
|
lpCriticalSection
|
||||||
|
.cast::<CRITICAL_SECTION>()
|
||||||
|
.write(CRITICAL_SECTION {
|
||||||
|
mutex: Default::default(),
|
||||||
|
pad: Default::default(),
|
||||||
|
});
|
||||||
|
const _: () = assert!(size_of::<CRITICAL_SECTION>() == 40);
|
||||||
|
true
|
||||||
}
|
}
|
||||||
fn InitializeProcThreadAttributeList() {
|
fn InitializeProcThreadAttributeList() {
|
||||||
todo!("InitializeProcThreadAttributeList")
|
todo!("InitializeProcThreadAttributeList")
|
||||||
|
|
@ -590,11 +635,13 @@ emulate!(
|
||||||
fn InterlockedFlushSList() {
|
fn InterlockedFlushSList() {
|
||||||
todo!("InterlockedFlushSList")
|
todo!("InterlockedFlushSList")
|
||||||
}
|
}
|
||||||
fn IsDebuggerPresent() {
|
/// <https://learn.microsoft.com/en-us/windows/win32/api/debugapi/nf-debugapi-isdebuggerpresent>
|
||||||
todo!("IsDebuggerPresent")
|
fn IsDebuggerPresent() -> bool {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
fn IsProcessorFeaturePresent() {
|
/// <https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-isprocessorfeaturepresent>
|
||||||
todo!("IsProcessorFeaturePresent")
|
fn IsProcessorFeaturePresent(_ProcessorFeature: u32) -> bool {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
fn IsThreadAFiber() {
|
fn IsThreadAFiber() {
|
||||||
todo!("IsThreadAFiber")
|
todo!("IsThreadAFiber")
|
||||||
|
|
@ -633,8 +680,8 @@ emulate!(
|
||||||
&crate::GLOBAL_STATE.executable_path(),
|
&crate::GLOBAL_STATE.executable_path(),
|
||||||
);
|
);
|
||||||
match result {
|
match result {
|
||||||
crate::LoadedDll::Emulated => 1,
|
Some(result) => result.hmodule(),
|
||||||
crate::LoadedDll::Real(img, _, _) => img.base as u64,
|
None => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn LoadLibraryW() {
|
fn LoadLibraryW() {
|
||||||
|
|
@ -762,8 +809,9 @@ emulate!(
|
||||||
fn SetThreadStackGuarantee() {
|
fn SetThreadStackGuarantee() {
|
||||||
todo!("SetThreadStackGuarantee")
|
todo!("SetThreadStackGuarantee")
|
||||||
}
|
}
|
||||||
fn SetUnhandledExceptionFilter() {
|
/// <https://learn.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-setunhandledexceptionfilter>
|
||||||
todo!("SetUnhandledExceptionFilter")
|
fn SetUnhandledExceptionFilter(_lpTopLevelExceptionFilter: *mut ()) -> *mut () {
|
||||||
|
std::ptr::null_mut()
|
||||||
}
|
}
|
||||||
fn SetWaitableTimer() {
|
fn SetWaitableTimer() {
|
||||||
todo!("SetWaitableTimer")
|
todo!("SetWaitableTimer")
|
||||||
|
|
@ -804,8 +852,10 @@ emulate!(
|
||||||
fn TryAcquireSRWLockExclusive() {
|
fn TryAcquireSRWLockExclusive() {
|
||||||
todo!("TryAcquireSRWLockExclusive")
|
todo!("TryAcquireSRWLockExclusive")
|
||||||
}
|
}
|
||||||
fn UnhandledExceptionFilter() {
|
/// <https://learn.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-unhandledexceptionfilter>
|
||||||
todo!("UnhandledExceptionFilter")
|
fn UnhandledExceptionFilter(_ExceptionInfo: *const ()) -> u64 {
|
||||||
|
const EXCEPTION_CONTINUE_SEARCH: u64 = 0x0;
|
||||||
|
EXCEPTION_CONTINUE_SEARCH
|
||||||
}
|
}
|
||||||
fn UnlockFile() {
|
fn UnlockFile() {
|
||||||
todo!("UnlockFile")
|
todo!("UnlockFile")
|
||||||
|
|
@ -866,14 +916,20 @@ emulate!(
|
||||||
fn NtWriteFile() {
|
fn NtWriteFile() {
|
||||||
todo!("NtWriteFile")
|
todo!("NtWriteFile")
|
||||||
}
|
}
|
||||||
|
/// <https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtlcapturecontext>
|
||||||
fn RtlCaptureContext() {
|
fn RtlCaptureContext() {
|
||||||
todo!("RtlCaptureContext")
|
tracing::error!("TODO: RtlCaptureContext - looks like someone feels like crashing...")
|
||||||
}
|
}
|
||||||
fn RtlGetLastNtStatus() {
|
fn RtlGetLastNtStatus() {
|
||||||
todo!("RtlGetLastNtStatus")
|
todo!("RtlGetLastNtStatus")
|
||||||
}
|
}
|
||||||
fn RtlLookupFunctionEntry() {
|
/// <https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtllookupfunctionentry>
|
||||||
todo!("RtlLookupFunctionEntry")
|
fn RtlLookupFunctionEntry(
|
||||||
|
_ControlPc: u64,
|
||||||
|
_ImageBase: *mut (),
|
||||||
|
_HistoryTable: *mut (),
|
||||||
|
) -> *const () {
|
||||||
|
std::ptr::null()
|
||||||
}
|
}
|
||||||
fn RtlNtStatusToDosError() {
|
fn RtlNtStatusToDosError() {
|
||||||
todo!("RtlNtStatusToDosError")
|
todo!("RtlNtStatusToDosError")
|
||||||
|
|
|
||||||
196
src/lib.rs
196
src/lib.rs
|
|
@ -2,11 +2,15 @@ mod emulated;
|
||||||
mod sys;
|
mod sys;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
ffi::{CStr, CString},
|
ffi::{CStr, CString},
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::Mutex,
|
sync::{
|
||||||
|
atomic::{AtomicU64, Ordering},
|
||||||
|
LazyLock, Mutex,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, bytemuck::Zeroable, bytemuck::Pod)]
|
#[derive(Clone, Copy, Debug, bytemuck::Zeroable, bytemuck::Pod)]
|
||||||
|
|
@ -252,7 +256,9 @@ pub fn execute(pe: &[u8], executable_path: &Path) {
|
||||||
tracing::debug!("YOLO to {:#x}", entrypoint);
|
tracing::debug!("YOLO to {:#x}", entrypoint);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let result = sys::call_entrypoint_via_stdcall(entrypoint);
|
let entrypoint =
|
||||||
|
std::mem::transmute::<usize, unsafe extern "win64" fn() -> u32>(entrypoint);
|
||||||
|
let result = entrypoint();
|
||||||
tracing::info!("result: {result}");
|
tracing::info!("result: {result}");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -280,29 +286,59 @@ impl<'pe> DerefMut for Image<'pe> {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
enum LoadedDll {
|
enum LoadedDll {
|
||||||
Emulated,
|
Emulated {
|
||||||
Real(Image<'static>, ExportDirectoryTable, Vec<CString>),
|
name: String,
|
||||||
|
hmodule: u64,
|
||||||
|
},
|
||||||
|
Real {
|
||||||
|
name: String,
|
||||||
|
img: Image<'static>,
|
||||||
|
edt: ExportDirectoryTable,
|
||||||
|
export_names: Vec<CString>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LoadedDll {
|
||||||
|
fn hmodule(&self) -> u64 {
|
||||||
|
match self {
|
||||||
|
Self::Emulated { hmodule, .. } => *hmodule,
|
||||||
|
Self::Real { img, .. } => img.base as u64,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TheGlobalState {
|
struct TheGlobalState {
|
||||||
loaded_libraries: Vec<(String, LoadedDll)>,
|
loaded_libraries: Vec<(String, LoadedDll)>,
|
||||||
executable_path: Option<PathBuf>,
|
executable_path: Option<PathBuf>,
|
||||||
|
hmodule_to_dll: HashMap<u64, LoadedDll>,
|
||||||
|
next_emulated_hmodule_idx: AtomicU64,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GlobalStateWrapper {
|
struct GlobalStateWrapper {
|
||||||
state: Mutex<TheGlobalState>,
|
state: std::sync::LazyLock<Mutex<TheGlobalState>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GlobalStateWrapper {
|
impl GlobalStateWrapper {
|
||||||
fn executable_path(&self) -> PathBuf {
|
fn executable_path(&self) -> PathBuf {
|
||||||
self.state.lock().unwrap().executable_path.clone().unwrap()
|
self.state.lock().unwrap().executable_path.clone().unwrap()
|
||||||
}
|
}
|
||||||
|
fn get_emulated_hmodule_idx(&self) -> u64 {
|
||||||
|
self.state
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.next_emulated_hmodule_idx
|
||||||
|
.fetch_add(1, Ordering::Relaxed)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static GLOBAL_STATE: GlobalStateWrapper = GlobalStateWrapper {
|
static GLOBAL_STATE: GlobalStateWrapper = GlobalStateWrapper {
|
||||||
state: Mutex::new(TheGlobalState {
|
state: LazyLock::new(|| {
|
||||||
loaded_libraries: Vec::new(),
|
Mutex::new(TheGlobalState {
|
||||||
executable_path: None,
|
loaded_libraries: Vec::new(),
|
||||||
|
executable_path: None,
|
||||||
|
hmodule_to_dll: HashMap::new(),
|
||||||
|
next_emulated_hmodule_idx: AtomicU64::new(1),
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -465,7 +501,8 @@ fn load_inner<'pe>(pe: &'pe [u8], executable_path: &Path, is_dll: bool) -> Image
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let dll = load_dll(dll_name, executable_path);
|
let dll = load_dll(dll_name, executable_path)
|
||||||
|
.unwrap_or_else(|| panic!("could not find dll {dll_name}"));
|
||||||
|
|
||||||
let import_lookups = bytemuck::cast_slice::<u8, u64>(
|
let import_lookups = bytemuck::cast_slice::<u8, u64>(
|
||||||
&image[import_directory.import_address_table_rva as usize..],
|
&image[import_directory.import_address_table_rva as usize..],
|
||||||
|
|
@ -477,36 +514,21 @@ fn load_inner<'pe>(pe: &'pe [u8], executable_path: &Path, is_dll: bool) -> Image
|
||||||
break;
|
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.rva
|
|
||||||
..(img.opt_header.export_table.rva + img.opt_header.export_table.size))
|
|
||||||
.contains(&export_rva)
|
|
||||||
{
|
|
||||||
todo!("symbol forwarding")
|
|
||||||
}
|
|
||||||
|
|
||||||
export_rva as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
let ordinal_name_flag = import_lookup >> 63;
|
let ordinal_name_flag = import_lookup >> 63;
|
||||||
let resolved_va = if ordinal_name_flag == 1 {
|
let resolved_va = if ordinal_name_flag == 1 {
|
||||||
let ordinal_number = import_lookup & 0xFFFF;
|
let ordinal_number = import_lookup & 0xFFFF;
|
||||||
tracing::debug!("import by ordinal: {ordinal_number}");
|
tracing::debug!("import by ordinal: {ordinal_number}");
|
||||||
|
|
||||||
match &dll {
|
match &dll {
|
||||||
LoadedDll::Emulated => {
|
LoadedDll::Emulated { .. } => {
|
||||||
tracing::error!("unsupported: emulated import via ordinal for {dll_name}. resolving them to 0");
|
tracing::error!("unsupported: emulated import via ordinal for {dll_name}. resolving them to 0");
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
LoadedDll::Real(img, export_directory_table, _) => {
|
LoadedDll::Real {
|
||||||
|
img,
|
||||||
|
edt: export_directory_table,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#export-ordinal-table
|
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#export-ordinal-table
|
||||||
let unbiased_ordinal =
|
let unbiased_ordinal =
|
||||||
ordinal_number as usize - export_directory_table.ordinal_base as usize;
|
ordinal_number as usize - export_directory_table.ordinal_base as usize;
|
||||||
|
|
@ -528,37 +550,7 @@ fn load_inner<'pe>(pe: &'pe [u8], executable_path: &Path, is_dll: bool) -> Image
|
||||||
CStr::from_bytes_until_nul(&image[hint_name_table_rva as usize + 2..]).unwrap();
|
CStr::from_bytes_until_nul(&image[hint_name_table_rva as usize + 2..]).unwrap();
|
||||||
tracing::debug!("import by name: hint={hint} name={func_name:?}");
|
tracing::debug!("import by name: hint={hint} name={func_name:?}");
|
||||||
|
|
||||||
match &dll {
|
va_for_dll_export_by_name(&dll, func_name, hint as usize)
|
||||||
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
|
|
||||||
} else {
|
|
||||||
names.binary_search(&func_name.to_owned()).unwrap_or_else(|_| {
|
|
||||||
panic!("could not find function {func_name:?} in dll {dll_name}")
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#export-ordinal-table
|
|
||||||
let ordinal_table = bytemuck::cast_slice::<u8, u16>(
|
|
||||||
&img[export_directory_table.ordinal_table_rva as usize..]
|
|
||||||
[..2 * export_directory_table.number_of_name_pointers as usize],
|
|
||||||
);
|
|
||||||
let unbiased_ordinal = ordinal_table[idx];
|
|
||||||
|
|
||||||
let export_rva = compute_export_rva(
|
|
||||||
export_directory_table,
|
|
||||||
unbiased_ordinal as usize,
|
|
||||||
img,
|
|
||||||
);
|
|
||||||
|
|
||||||
img.base + export_rva as usize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(size_of::<usize>(), size_of::<u64>());
|
assert_eq!(size_of::<usize>(), size_of::<u64>());
|
||||||
|
|
@ -597,7 +589,63 @@ fn load_inner<'pe>(pe: &'pe [u8], executable_path: &Path, is_dll: bool) -> Image
|
||||||
image
|
image
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_dll(dll_name: &str, executable_path: &Path) -> LoadedDll {
|
fn va_for_dll_export_by_name(dll: &LoadedDll, func_name: &CStr, hint: usize) -> usize {
|
||||||
|
match &dll {
|
||||||
|
LoadedDll::Emulated { name, .. } => emulated::emulate(name, func_name.to_str().unwrap())
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
panic!("could not find function {func_name:?} in dll {name:?}");
|
||||||
|
}),
|
||||||
|
LoadedDll::Real {
|
||||||
|
name,
|
||||||
|
img,
|
||||||
|
edt: export_directory_table,
|
||||||
|
export_names: names,
|
||||||
|
} => {
|
||||||
|
let idx = if names.get(hint) == Some(&func_name.to_owned()) {
|
||||||
|
hint as usize
|
||||||
|
} else {
|
||||||
|
names
|
||||||
|
.binary_search(&func_name.to_owned())
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
panic!("could not find function {func_name:?} in dll {name}")
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#export-ordinal-table
|
||||||
|
let ordinal_table = bytemuck::cast_slice::<u8, u16>(
|
||||||
|
&img[export_directory_table.ordinal_table_rva as usize..]
|
||||||
|
[..2 * export_directory_table.number_of_name_pointers as usize],
|
||||||
|
);
|
||||||
|
let unbiased_ordinal = ordinal_table[idx];
|
||||||
|
|
||||||
|
let export_rva =
|
||||||
|
compute_export_rva(export_directory_table, unbiased_ordinal as usize, img);
|
||||||
|
|
||||||
|
img.base + export_rva as usize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.rva
|
||||||
|
..(img.opt_header.export_table.rva + img.opt_header.export_table.size))
|
||||||
|
.contains(&export_rva)
|
||||||
|
{
|
||||||
|
todo!("symbol forwarding")
|
||||||
|
}
|
||||||
|
|
||||||
|
export_rva as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_dll(dll_name: &str, executable_path: &Path) -> Option<LoadedDll> {
|
||||||
tracing::debug!("loading dll {dll_name}");
|
tracing::debug!("loading dll {dll_name}");
|
||||||
|
|
||||||
let already_loaded = GLOBAL_STATE
|
let already_loaded = GLOBAL_STATE
|
||||||
|
|
@ -609,14 +657,17 @@ fn load_dll(dll_name: &str, executable_path: &Path) -> LoadedDll {
|
||||||
.find(|(name, _)| name == dll_name)
|
.find(|(name, _)| name == dll_name)
|
||||||
.map(Clone::clone);
|
.map(Clone::clone);
|
||||||
if let Some((_, already_loaded)) = already_loaded {
|
if let Some((_, already_loaded)) = already_loaded {
|
||||||
return already_loaded;
|
return Some(already_loaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
let dll = find_dll(&dll_name, executable_path);
|
let dll = find_dll(&dll_name, executable_path);
|
||||||
let dll = match dll {
|
let dll = match dll {
|
||||||
Some(DllLocation::Emulated) => {
|
Some(DllLocation::Emulated) => {
|
||||||
tracing::debug!("emulating {dll_name:?}");
|
tracing::debug!("emulating {dll_name:?}");
|
||||||
LoadedDll::Emulated
|
LoadedDll::Emulated {
|
||||||
|
name: dll_name.to_owned(),
|
||||||
|
hmodule: GLOBAL_STATE.get_emulated_hmodule_idx(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Some(DllLocation::Found(path)) => {
|
Some(DllLocation::Found(path)) => {
|
||||||
let file = std::fs::File::open(&path).unwrap();
|
let file = std::fs::File::open(&path).unwrap();
|
||||||
|
|
@ -659,10 +710,15 @@ fn load_dll(dll_name: &str, executable_path: &Path) -> LoadedDll {
|
||||||
tracing::trace!(?name, "DLL {dll_name} has export");
|
tracing::trace!(?name, "DLL {dll_name} has export");
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadedDll::Real(img, export_directory_table, names)
|
LoadedDll::Real {
|
||||||
|
name: dll_name.to_owned(),
|
||||||
|
img,
|
||||||
|
edt: export_directory_table,
|
||||||
|
export_names: names,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
panic!("could not find dll {dll_name:?}");
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -672,8 +728,14 @@ fn load_dll(dll_name: &str, executable_path: &Path) -> LoadedDll {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.loaded_libraries
|
.loaded_libraries
|
||||||
.push((dll_name.to_owned(), dll.clone()));
|
.push((dll_name.to_owned(), dll.clone()));
|
||||||
|
GLOBAL_STATE
|
||||||
|
.state
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.hmodule_to_dll
|
||||||
|
.insert(dll.hmodule(), dll.clone());
|
||||||
|
|
||||||
dll
|
Some(dll)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_header(pe: &[u8]) -> (&CoffHeader, usize) {
|
fn parse_header(pe: &[u8]) -> (&CoffHeader, usize) {
|
||||||
|
|
|
||||||
24
src/sys.rs
24
src/sys.rs
|
|
@ -56,11 +56,6 @@ mod imp {
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn call_entrypoint_via_stdcall(fnptr: *const ()) -> u32 {
|
|
||||||
let fnptr = unsafe { std::mem::transmute::<_, unsafe extern "stdcall" fn() -> u32>(fnptr) };
|
|
||||||
unsafe { fnptr() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
|
|
@ -91,25 +86,6 @@ mod imp {
|
||||||
Err(io::Error::last_os_error())
|
Err(io::Error::last_os_error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn call_entrypoint_via_stdcall(fnptr: usize) -> u32 {
|
|
||||||
let mut out: u32;
|
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
|
||||||
if #[cfg(target_arch = "x86_64")] {
|
|
||||||
std::arch::asm!(
|
|
||||||
"call {}",
|
|
||||||
"mov {1:e}, eax",
|
|
||||||
in(reg) fnptr,
|
|
||||||
out(reg) out,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
compile_error!("unsupported architecture");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) use imp::*;
|
pub(crate) use imp::*;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue