microsoft / windows-rs

Rust for Windows
https://kennykerr.ca/rust-getting-started/
Apache License 2.0
10.23k stars 476 forks source link

PVECTORED_EXCEPTION_HANDLER function cannot change the EIP content #3161

Closed lngex closed 1 month ago

lngex commented 1 month ago

Summary

Set the breakpoint address in register dr0 and set dr7 to 0x55. Then use AddVectoredExceptionHandler to add a callback function (PVECTORED_EXCEPTION_HANDLER), set EIP in the callback function and return EXCEPTION_CONTINUE_SEARCH. But the end result is that the breakpoint is triggered repeatedly. The expected result is that execution starts from EIP after the callback function ends.

Crate manifest

windows=0.58.0

Crate code

use std::io::Write;
use std::mem::size_of_val;
use std::ptr::addr_of_mut;

use windows::core::imp::CloseHandle;
use windows::Win32::Foundation::{EXCEPTION_SINGLE_STEP, FALSE, INVALID_HANDLE_VALUE};
use windows::Win32::System::Diagnostics::Debug::{AddVectoredExceptionHandler, CONTEXT, CONTEXT_ALL_X86, EXCEPTION_CONTINUE_EXECUTION, EXCEPTION_CONTINUE_SEARCH, EXCEPTION_POINTERS, GetThreadContext, PVECTORED_EXCEPTION_HANDLER, RemoveVectoredExceptionHandler, SetThreadContext};
use windows::Win32::System::Diagnostics::ToolHelp::{TH32CS_SNAPTHREAD, Thread32First, Thread32Next, THREADENTRY32};
use windows::Win32::System::Threading::{GetCurrentProcessId, OpenThread, ResumeThread, SuspendThread, THREAD_ALL_ACCESS};

const ADDRESS: u32 = 0x00430A11u32;

/// 硬件hook结构体
pub struct Hook {
    count: u32,
    /// 设置回调函数时返回的地址
    result_ptr: Option<*mut core::ffi::c_void>,
    /// hook的地址
    dr0: u32,
    dr1: u32,
    dr2: u32,
    dr3: u32,
    /// 0x55
    dr7: u32,
    /// 不用hook的线程id
    no_hook_thread: Vec<u32>,
    /// 异常时的回调函数
    hook_callback: PVECTORED_EXCEPTION_HANDLER,
}

impl Hook {
    /// 构建hook对象
    pub fn new(dr1: u32,
               dr2: u32,
               dr3: u32,
               no_hook_thread: Vec<u32>) -> Self {
        Self { count: 0, result_ptr: None, dr0: ADDRESS, dr1, dr2, dr3, dr7: 0x55, no_hook_thread, hook_callback: Some(sunlight) }
    }

    /// hook
    pub fn hook(&self) -> bool {
        unsafe {
            let result = windows::Win32::System::Diagnostics::ToolHelp::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
            if result.is_err() {
                // 如果出现错误返回失败
                return false;
            }
            let process_snapsho_handle = result.unwrap();
            if process_snapsho_handle == INVALID_HANDLE_VALUE {
                // 如果句柄无效返回失败
                return false;
            }
            // 创建一个变量存储线程信息
            let mut threadentry32: THREADENTRY32 = THREADENTRY32::default();
            threadentry32.dwSize = size_of_val(&threadentry32) as u32;
            let threadentry32_ptr = addr_of_mut!(threadentry32);
            let result1 = Thread32First(process_snapsho_handle, threadentry32_ptr);
            // 获取第一个线程
            if result1.is_ok() {
                loop {
                    if threadentry32.th32OwnerProcessID == GetCurrentProcessId() {
                        // 判断线程是否可以hook
                        if self.is_hook_thread(threadentry32.th32ThreadID) {
                            // 如果可以则获取线程权限以及上下文
                            match OpenThread(THREAD_ALL_ACCESS, FALSE, threadentry32.th32ThreadID) {
                                Ok(thread_handle) => {
                                    // 暂停线程
                                    SuspendThread(thread_handle);
                                    let mut context = CONTEXT::default();
                                    context.ContextFlags = CONTEXT_ALL_X86;
                                    let context_ptr = addr_of_mut!(context);
                                    // 获取线程上下文
                                    let _ = GetThreadContext(thread_handle, context_ptr);
                                    // 设置断点
                                    context.Dr0 = self.dr0;
                                    context.Dr1 = self.dr1;
                                    context.Dr2 = self.dr2;
                                    context.Dr3 = self.dr3;
                                    context.Dr7 = self.dr7;
                                    // 设置线程上下文
                                    let _ = SetThreadContext(thread_handle, context_ptr);
                                    ResumeThread(thread_handle);
                                    CloseHandle(thread_handle.0);
                                }
                                Err(_) => {}
                            }
                        }
                    }
                    // 如果线程还有则获取,没有跳出循环
                    if !Thread32Next(process_snapsho_handle, threadentry32_ptr).is_ok() {
                        break;
                    }
                }
            }
            // 释放句柄
            CloseHandle(process_snapsho_handle.0);
        }
        return true;
    }

    /// 安装hook函数
    pub fn set_hook_fn(&mut self) {
        if self.count == 0 {
            self.count = 1;
            unsafe {
                let handler = AddVectoredExceptionHandler(0, self.hook_callback);
                self.result_ptr = Some(handler);
            }
        }
    }

    /// 取消hokk
    pub fn unhook(&mut self) {
        self.dr0 = 0;
        self.dr1 = 0;
        self.dr2 = 0;
        self.dr3 = 0;
        self.dr7 = 0;
        let _ = self.hook();
        unsafe { RemoveVectoredExceptionHandler(self.result_ptr.unwrap()) };
        if self.count == 1 {
            self.count = 0;
        }
    }

    /// 判断线程是否可以hook
    fn is_hook_thread(&self, thread_id: u32) -> bool {
        !self.no_hook_thread.contains(&thread_id)
    }
}

///  阳光回调函数
unsafe extern "system" fn sunlight(exceptioninfo: *mut EXCEPTION_POINTERS) -> i32 {
    let mut exceptioninfo_value = exceptioninfo.read();
    let mut record_value = exceptioninfo_value.ExceptionRecord.read();
    if record_value.ExceptionCode == EXCEPTION_SINGLE_STEP {
        if (record_value.ExceptionAddress as u32) == 0x00430A11 {
            let mut context = exceptioninfo_value.ContextRecord.read();
            context.Ecx = 100;
            // Execute the assembly of the current hook
            // 00430A11 add dword ptr ds:[eax+5560],ecx
            let ptr = (context.Eax + 0x5560) as *mut u32;
            ptr.write(ptr.read() + context.Ecx);
            // Execute next assembly after changes
            // 00430A17 mov ecx,dword ptr ds:[eax+5560]
            let eip_ptr = addr_of_mut!(context.Eip);
            eip_ptr.write(context.Eip + 6);
            return EXCEPTION_CONTINUE_EXECUTION;
        }
    }
    return EXCEPTION_CONTINUE_SEARCH;
}
riverar commented 1 month ago

I suspect you may have forgotten to clear the Trap Flag (TF) in EFLAGS/RFLAGS, so you're single stepping the target. (EFlags &= 0b_0001_0000_0000)

riverar commented 1 month ago

Closing this as there doesn't appear to be a bug in the crate. Feel free to continue the discussion if needed.

For future API questions, please consider https://stackoverflow.com/questions/tagged/windows-rs.

lngex commented 1 month ago

I suspect you may have forgotten to clear the Trap Flag (TF) in EFLAGS/RFLAGS, so you're single stepping the target. (EFlags &= 0b_0001_0000_0000)

I tried to set E Flags, but it also got stuck in a loop. I saw in the C++ example that TF was not handled, so I suspect it's a bug

kennykerr commented 1 month ago

I don't believe SEH works in Rust at the present.

lngex commented 1 month ago

I don't believe SEH works in Rust at the present.

I don't believe SEH works in Rust at the present.

VEH is used here, and as far as I know, it only requires calling the AddVectoredExceptionHandler to add the processor and set breakpoints

riverar commented 1 month ago

Here's a simple working sample:

[package]
name = "app"
version = "0.0.0"
edition = "2021"
publish = false

[dependencies.windows]
version = "0.58.0"
features = [
    "Win32_Foundation",
    "Win32_System_Diagnostics_Debug",
    "Win32_System_Kernel"
]
use windows::Win32::{
    Foundation::EXCEPTION_SINGLE_STEP,
    System::Diagnostics::Debug::{
        AddVectoredExceptionHandler, RaiseException, EXCEPTION_CONTINUE_EXECUTION,
        EXCEPTION_CONTINUE_SEARCH, EXCEPTION_POINTERS,
    },
};

fn main() -> windows::core::Result<()> {
    println!("Start");

    unsafe {
        let _ = AddVectoredExceptionHandler(0, Some(handler));
        RaiseException(EXCEPTION_SINGLE_STEP.0 as _, 0, None);
    };

    println!("End");
    Ok(())
}

unsafe extern "system" fn handler(info: *mut EXCEPTION_POINTERS) -> i32 {
    let exception_record = info.read().ExceptionRecord.read();
    match exception_record.ExceptionCode {
        EXCEPTION_SINGLE_STEP => {
            println!("[*] Swallowing exception");
            EXCEPTION_CONTINUE_EXECUTION
        }
        _ => EXCEPTION_CONTINUE_SEARCH,
    }
}

If you're still having trouble, please create an issue with a minimal standalone repro case.