refactor: add usercall_trampoline! macro for naked asm generation
Browse filesReplaces hand-written naked trampolines in frontend.rs and scheme.rs
with a declarative macro that handles register capture + stack forwarding.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
crates/openwa-wormkit/src/hook.rs
CHANGED
|
@@ -1,4 +1,59 @@
|
|
| 1 |
//! MinHook helper for installing inline hooks on WA.exe functions.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
use std::ffi::c_void;
|
| 4 |
|
|
|
|
| 1 |
//! MinHook helper for installing inline hooks on WA.exe functions.
|
| 2 |
+
//!
|
| 3 |
+
//! Also provides the [`usercall_trampoline!`] macro for generating naked asm
|
| 4 |
+
//! trampolines that capture implicit register params from MSVC `__usercall`
|
| 5 |
+
//! functions and forward them to cdecl Rust implementations.
|
| 6 |
+
|
| 7 |
+
/// Generate a naked trampoline for a `__usercall` function.
|
| 8 |
+
///
|
| 9 |
+
/// Captures one implicit register param and optionally forwards stack params
|
| 10 |
+
/// to a `cdecl` Rust implementation function.
|
| 11 |
+
///
|
| 12 |
+
/// The impl_fn signature must be: `extern "cdecl" fn(reg_value, stack_args...) -> R`
|
| 13 |
+
///
|
| 14 |
+
/// # Variants
|
| 15 |
+
///
|
| 16 |
+
/// ```ignore
|
| 17 |
+
/// // 1 register, 0 stack params, plain ret
|
| 18 |
+
/// usercall_trampoline!(fn name; impl_fn = path; reg = eax);
|
| 19 |
+
///
|
| 20 |
+
/// // 1 register, 1 stack param, ret with cleanup
|
| 21 |
+
/// usercall_trampoline!(fn name; impl_fn = path; reg = esi;
|
| 22 |
+
/// stack_params = 1; ret_bytes = "0x4");
|
| 23 |
+
/// ```
|
| 24 |
+
macro_rules! usercall_trampoline {
|
| 25 |
+
// 1 register arg, 0 stack params, plain ret
|
| 26 |
+
(fn $name:ident; impl_fn = $impl:path; reg = $reg:ident) => {
|
| 27 |
+
#[unsafe(naked)]
|
| 28 |
+
unsafe extern "C" fn $name() {
|
| 29 |
+
core::arch::naked_asm!(
|
| 30 |
+
concat!("push ", stringify!($reg)),
|
| 31 |
+
"call {impl_fn}",
|
| 32 |
+
"add esp, 4",
|
| 33 |
+
"ret",
|
| 34 |
+
impl_fn = sym $impl,
|
| 35 |
+
);
|
| 36 |
+
}
|
| 37 |
+
};
|
| 38 |
+
|
| 39 |
+
// 1 register arg, 1 stack param, ret N
|
| 40 |
+
(fn $name:ident; impl_fn = $impl:path; reg = $reg:ident;
|
| 41 |
+
stack_params = 1; ret_bytes = $ret:literal) => {
|
| 42 |
+
#[unsafe(naked)]
|
| 43 |
+
unsafe extern "C" fn $name() {
|
| 44 |
+
core::arch::naked_asm!(
|
| 45 |
+
"push [esp+4]",
|
| 46 |
+
concat!("push ", stringify!($reg)),
|
| 47 |
+
"call {impl_fn}",
|
| 48 |
+
"add esp, 8",
|
| 49 |
+
concat!("ret ", $ret),
|
| 50 |
+
impl_fn = sym $impl,
|
| 51 |
+
);
|
| 52 |
+
}
|
| 53 |
+
};
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
pub(crate) use usercall_trampoline;
|
| 57 |
|
| 58 |
use std::ffi::c_void;
|
| 59 |
|
crates/openwa-wormkit/src/replacements/frontend.rs
CHANGED
|
@@ -14,24 +14,10 @@ use openwa_types::frontend::ScreenId;
|
|
| 14 |
/// Trampoline to the original FrontendChangeScreen (for fallback if needed).
|
| 15 |
static ORIG_FRONTEND_CHANGE_SCREEN: AtomicU32 = AtomicU32::new(0);
|
| 16 |
|
| 17 |
-
//
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
/// [ESP+0] = return address
|
| 22 |
-
/// [ESP+4] = screen_id
|
| 23 |
-
/// ESI = dialog this (MSVC __usercall convention)
|
| 24 |
-
#[unsafe(naked)]
|
| 25 |
-
unsafe extern "stdcall" fn trampoline(_screen_id: u32) {
|
| 26 |
-
core::arch::naked_asm!(
|
| 27 |
-
"push [esp+4]", // push screen_id
|
| 28 |
-
"push esi", // push dialog this
|
| 29 |
-
"call {impl_fn}", // cdecl call: impl(dialog, screen_id)
|
| 30 |
-
"add esp, 8", // clean our two pushes
|
| 31 |
-
"ret 0x4", // clean original screen_id from caller's stack
|
| 32 |
-
impl_fn = sym frontend_change_screen_impl,
|
| 33 |
-
);
|
| 34 |
-
}
|
| 35 |
|
| 36 |
/// Rust reimplementation of FrontendChangeScreen.
|
| 37 |
///
|
|
|
|
| 14 |
/// Trampoline to the original FrontendChangeScreen (for fallback if needed).
|
| 15 |
static ORIG_FRONTEND_CHANGE_SCREEN: AtomicU32 = AtomicU32::new(0);
|
| 16 |
|
| 17 |
+
// Naked trampoline: captures ESI (dialog this, __usercall) + 1 stack arg (screen_id).
|
| 18 |
+
crate::hook::usercall_trampoline!(fn trampoline;
|
| 19 |
+
impl_fn = frontend_change_screen_impl; reg = esi;
|
| 20 |
+
stack_params = 1; ret_bytes = "0x4");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
/// Rust reimplementation of FrontendChangeScreen.
|
| 23 |
///
|
crates/openwa-wormkit/src/replacements/scheme.rs
CHANGED
|
@@ -168,20 +168,9 @@ unsafe fn call_original_read_file(dest: u32, path: u32, flag: u32, out_ptr: u32)
|
|
| 168 |
// Scheme__ValidateExtendedOptions replacement (0x4D5110)
|
| 169 |
// ============================================================
|
| 170 |
|
| 171 |
-
//
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
/// Plain RET (no stack cleanup). Returns EAX: 0 = valid, 1 = invalid.
|
| 175 |
-
#[unsafe(naked)]
|
| 176 |
-
unsafe extern "C" fn trampoline_validate_ext_opts() {
|
| 177 |
-
core::arch::naked_asm!(
|
| 178 |
-
"push eax", // push options pointer as arg
|
| 179 |
-
"call {impl_fn}", // cdecl call
|
| 180 |
-
"add esp, 4", // clean up our push
|
| 181 |
-
"ret", // plain ret (no stack params to clean)
|
| 182 |
-
impl_fn = sym validate_extended_options_impl,
|
| 183 |
-
);
|
| 184 |
-
}
|
| 185 |
|
| 186 |
/// Rust implementation called by the naked trampoline.
|
| 187 |
unsafe extern "cdecl" fn validate_extended_options_impl(options_ptr: u32) -> u32 {
|
|
|
|
| 168 |
// Scheme__ValidateExtendedOptions replacement (0x4D5110)
|
| 169 |
// ============================================================
|
| 170 |
|
| 171 |
+
// Naked trampoline: captures EAX (110-byte extended options pointer). Returns 0=valid, 1=invalid.
|
| 172 |
+
crate::hook::usercall_trampoline!(fn trampoline_validate_ext_opts;
|
| 173 |
+
impl_fn = validate_extended_options_impl; reg = eax);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 174 |
|
| 175 |
/// Rust implementation called by the naked trampoline.
|
| 176 |
unsafe extern "cdecl" fn validate_extended_options_impl(options_ptr: u32) -> u32 {
|