Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x64 Inline Hook #3

Open
jiushill opened this issue Jul 27, 2023 · 3 comments
Open

x64 Inline Hook #3

jiushill opened this issue Jul 27, 2023 · 3 comments

Comments

@jiushill
Copy link

jiushill commented Jul 27, 2023

// cargo +stable-i686-pc-windows-msvc run

use std::ffi::c_void;

use windows::{
    core::PCSTR,
    s,
    Win32::{
        Foundation::HWND,
        System::{
            Diagnostics::Debug::{ReadProcessMemory, WriteProcessMemory},
            LibraryLoader::{GetProcAddress, LoadLibraryA},
            Threading::GetCurrentProcess,
        },
        UI::WindowsAndMessaging::{MessageBoxA, MESSAGEBOX_RESULT, MESSAGEBOX_STYLE},
    },
};

static mut MESSAGE_BOX_ORIGINAL_BYTES: [u8; 12] = [0; 12];
static mut BYTES_WRITTEN: usize = 0;
static mut MESSAGE_BOX_ADDRESS: Option<unsafe extern "system" fn() -> isize> = None;

fn main() {
    unsafe {
        // Show messagebox before hooking
        //
        // Pop the message box before the function is hooked - just to make sure it works and to
        // prove that no functions are hooked so far -  it's the first instruction of the program
        MessageBoxA(HWND(0), s!("Hello World"), s!("Rust"), Default::default());

        let dll_handle = LoadLibraryA(s!("user32.dll")).unwrap();
        let bytes_read: usize = 0;

        // Get address of the `MessageBox` function in memory
        //
        // If we disassemble the bytes at that address, we will definitely see that there is code for
        // `MessageBoxA`
        MESSAGE_BOX_ADDRESS = GetProcAddress(dll_handle, s!("MessageBoxA"));

        // save the first 6 bytes of the original MessageBoxA function - will need for unhooking
        //
        // Note the first 6 bytes <xx xx xx xx xx xx>(mind the endian-ness, where `xx` is some hex address).
        // We need to save these bytes for future when we want to unhook MessageBoxA
        ReadProcessMemory(
            GetCurrentProcess(),
            MESSAGE_BOX_ADDRESS.unwrap() as *const c_void,
            MESSAGE_BOX_ORIGINAL_BYTES.as_ptr() as *mut c_void,
            12,
            Some(bytes_read as *mut usize),
        );

        // Create a patch `push <address of new MessageBoxA); ret`
        //
        // Let's now build the patch (hook) bytes:
        let hooked_message_box_address = (HookedMessageBox as *mut ()).cast::<c_void>();
        let offset = hooked_message_box_address as isize;
        let mut patch = [0; 12];
        patch[0] = 0x48;
        let temp  = offset.to_ne_bytes();
        println!("{:#?}",temp);
        patch[1]=0xb8;
        patch[2..9].copy_from_slice(&temp[..7]);
        patch[10] = 0xFF;
        patch[11]=0xE0;
        // ...that will translate into the following assembly instructions:
        //
        // ```asm
        // // push HookedMessageBox memory address onto the stack
        // push HookedMessageBox
        // // jump to HookedMessageBox
        // ret
        // ```

        // Patch the `MessageBoxA`
        //
        // We can now patch the `MessageBoxA` - memory pane in the bottom right shows the patch being
        // written to the beginning of `MessageBoxA` function and the top right shows the beginning of
        // the same function is re-written with a `push <address that jumps to our hooked function>;
        // ret` instructions
        WriteProcessMemory(
            GetCurrentProcess(),
            MESSAGE_BOX_ADDRESS.unwrap() as *const c_void,
            patch.as_ptr().cast::<c_void>(),
            12,
            Some(BYTES_WRITTEN as *mut usize),
        );

        // show messagebox after hooking
        MessageBoxA(HWND(0), s!("HelloX World"), s!("Rust"), Default::default());
    }
}

// The `HookedMessageBox` intercepts and prints out the arguments supplied to `MessageBoxA`, then
// unhooks `MessageBoxA` by swapping back the first 6 bytes to the original bytes of the `MessageBoxA`
// function and then calls the `MessageBoxA` with the supplied arguments:
#[no_mangle]
pub extern "stdcall" fn HookedMessageBox(
    hwnd: HWND,
    mut lptext: PCSTR,
    mut lpcaption: PCSTR,
    utype: MESSAGEBOX_STYLE,
) -> MESSAGEBOX_RESULT {
    unsafe {
        // Print intercepted values from the MessageBoxA function
        println!(
            "================\nHooked!!!\noriginal-lptext: {}\noriginal-lpcation: {}\n================",
            lptext.to_string().unwrap(),
            lpcaption.to_string().unwrap(),
        );

        // Change intercepted values from the MessageBoxA function
        lptext = s!("Hooked Hello World");
        lpcaption = s!("Hooked Rust");

        // Print new values from the MessageBoxA function
        println!(
            "\n================\nHooked!!!\nhooked-lptext: {}\nhooked-lpcation: {}\n================",
            lptext.to_string().unwrap(),
            lpcaption.to_string().unwrap(),
        );

        // Unpatch MessageBoxA
        WriteProcessMemory(
            GetCurrentProcess(),
            MESSAGE_BOX_ADDRESS.unwrap() as *const c_void,
            MESSAGE_BOX_ORIGINAL_BYTES.as_ptr().cast::<c_void>(),
            12,
            Some(BYTES_WRITTEN as *mut usize),
        );

        // call the original MessageBoxA
        MessageBoxA(hwnd, lptext, lpcaption, utype)
    }
}
@loongs-zhang
Copy link

@jiushill does it works in x86_64?

Also, I would like to ask if it supports stable-i686-pc-windows-gnu? @sn99

@sn99
Copy link
Contributor

sn99 commented Aug 7, 2024

@loongs-zhang You will have to try it out, it should work now for all intent and purposes.

@loongs-zhang
Copy link

Ok, thanks, I'll try it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants