当前位置: 首页>编程语言>正文

rustdesk 改端口 rust windows api

hook 方式

这里采用编译成 dll 后注入,这里有两点需要注意:

  1. 目标程序为 32/64 位 rust 编译工具链也需要对应 32/64 位
  2. 编译需要用 release 模式,防止注入编译器在 debug 下为了调试附加的数据

Cargo.toml 中需要说明将其编译为库且类型为动态的,如果编译时显示找不到编译目标则手动添加一行路径:

[lib]
crate-type = ["dylib"]
path = "src/lib.rs"

当然你还可以通过创建远程线程来 hook,不过过程比较繁琐这里就提一下。

以下所有例子都以 hook MessageBoxW 为目标。

IAT Hook

IAT hook 即为导入表 hook,原理是 PE 文件中有个导入表,其中记录了该模块调用了哪些外部API,模块被加载到内存后,PE 加载器会修改该表,地址改成外部 API 重定位后的真实地址,我们只要直接把里面的地址改成我们新函数的地址,就可以完成对相应 API 的 Hook。

直接上代码,特别提醒一点所有的字符串结尾是没有 \0 的,要想像 C 一样正常工作需要自己添加:

extern crate libc;
extern crate winapi;

// 代表 c 中的 nullptr,只不过 rust 中分为可变和不可变两种
use std::ptr::{null, null_mut};
// 这几个东西是什么也不多说了,熟悉 Windows.h 的应该都知道:
use winapi::shared::{
    minwindef::LPVOID,
    ntdef::LPCWSTR,
    windef::HWND,
};

// 定义一个别名,省的后面类型转换的时候写一大堆
type MessageBoxWHook = *const unsafe extern "system" fn(HWND, LPCWSTR, LPCWSTR, u32) -> i32;

// 保存原函数地址,当然这里可以用指针具体写法后面单独介绍
static mut MESSAGE_BOX_W_HOOK_ADDRESS: u64 = 0;

// for Debug
unsafe fn clear_last_error() {
    use winapi::um::errhandlingapi::SetLastError;
    SetLastError(0);
}

// for Debug
unsafe fn show_last_error() {
    use winapi::um::{
        errhandlingapi::GetLastError,
        winuser::{MB_OK, MessageBoxA},
    };

    let e = GetLastError();
    let e = format!("{}

EAT Hook

", e.to_string()); MessageBoxA(null_mut(), e.as_ptr() as _, "

Inline Hook

".as_ptr() as _, MB_OK); } // 我们自己的 MessageBoxW unsafe extern "system" fn hook_message_box_w(h_wnd: HWND, _: LPCWSTR, _: LPCWSTR, u_type: u32) -> i32 { (*(&MESSAGE_BOX_W_HOOK_ADDRESS as *const _ as MessageBoxWHook))( h_wnd, "Ops hooked by Xavier!

VMT Hook

".encode_utf16().collect::<Vec<_>>().as_ptr(), "Ops hooked by Xavier!ID3D11Device".encode_utf16().collect::<Vec<_>>().as_ptr(), u_type, ) } // 遍历表找到 MessageBoxW unsafe fn detour(module_name: *const i8, old_func_offset: u64, new_func_address: u64) -> u64 { use std::mem::size_of; use libc::strcmp; use winapi::{ um::{ libloaderapi::GetModuleHandleA, memoryapi::VirtualProtect, winnt::{PAGE_EXECUTE_READWRITE, PIMAGE_DOS_HEADER, PIMAGE_IMPORT_DESCRIPTOR, PIMAGE_NT_HEADERS}, winuser::{MB_OK, MessageBoxA}, }, }; let module_address = GetModuleHandleA(module_name) as u64; let old_func_address = module_address + old_func_offset; let image_base = GetModuleHandleA(null()) as u64; let p_dos_header = image_base as PIMAGE_DOS_HEADER; let p_nt_headers = (image_base + (*p_dos_header).e_lfanew as u64) as PIMAGE_NT_HEADERS; let mut p_import_descriptor = (image_base + (*p_nt_headers).OptionalHeader.DataDirectory[1].VirtualAddress as u64) as PIMAGE_IMPORT_DESCRIPTOR; while (*p_import_descriptor).FirstThunk != 0 { // MessageBoxA(null_mut(), (image_base + (*p_import_descriptor).Name as u64) as _, "struct PresentPointers { context: *mut ID3D11DeviceContext, device: *mut ID3D11Device, swap_chain: *mut IDXGISwapChain, present: *mut Present, } impl PresentPointers { const INIT: Self = PresentPointers { context: null_mut(), device: null_mut(), swap_chain: null_mut(), present: null_mut(), }; }".as_ptr() as _, MB_OK); if strcmp(module_name, (image_base + (*p_import_descriptor).Name as u64) as *const i8) != 0 { p_import_descriptor = p_import_descriptor.offset(1); continue; } MessageBoxA(null_mut(), (image_base + (*p_import_descriptor).Name as u64) as _, "".as_ptr() as _, MB_OK); let mut p_func = (image_base + (*p_import_descriptor).FirstThunk as u64) as *mut u64; for i in 0.. { if p_func.is_null() { return 0; } // MessageBoxA(null_mut(), (image_base + (*((image_base + *(*p_import_descriptor).u.OriginalFirstThunk() as u64) as *const u64).offset(i)) + 2) as _, "".as_ptr() as _, MB_OK); if old_func_address == *p_func { MessageBoxA(null_mut(), (image_base + (*((image_base + *(*p_import_descriptor).u.OriginalFirstThunk() as u64) as *const u64).offset(i)) + 2) as _, "".as_ptr() as _, MB_OK); let mut old_protection = 0u32; VirtualProtect(p_func as _, size_of::<LPVOID>(), PAGE_EXECUTE_READWRITE, &mut old_protection as _); *p_func = new_func_address; VirtualProtect(p_func as _, size_of::<LPVOID>(), old_protection, &mut old_protection as _); return old_func_address; } p_func = p_func.offset(1); } return 0; } 0 } unsafe extern "system" fn init_hook(_: LPVOID) -> u32 { MESSAGE_BOX_W_HOOK_ADDRESS = detour("USER32.dll".as_ptr() as _, 0x72AD0, hook_message_box_w as _); 0 } // 告诉编译器不要对函数名进行混淆,使得能被 cffi 调用 #[no_mangle] #[allow(non_snake_case)] pub extern "system" fn DllMain(_: winapi::shared::minwindef::HINSTANCE, reason: u32, _: LPVOID) -> i32 { use winapi::um::processthreadsapi::CreateThread; // 1 实际上就是 DLL_PROCESS_ATTACH 的值 match reason { 1 => unsafe { CreateThread(null_mut(), 0, Some(init_hook), null_mut(), 0, null_mut()); } 0 => (), _ => (), } // Windows 编程中的 BOOL TRUE 实际上就是 i32 1 1 }

编译后可注入记事本,然后搜索,搜索结果的对话框会被 hook。

rustdesk 改端口 rust windows api,rustdesk 改端口 rust windows api_hook,第1张

EAT hook 即为导出表 hook,但是一般用的不多。原理是调用模块 IAT 中的函数地址是通过被调用模块 EAT 获取的,等于变相修改了 IAT,只要修改了被调用模块 EAT 中的函数地址,就可以 hook 到了。但是在我反复测试多次后得出,就是 EAT hook 要足够早最好是在程序启动前,前面说到这是变相修改 IAT 所以要在 IAT 使用它之前修改。

这个是最难的但是没有它不能 hook 到的函数,原理很简单,在需要 hook 的函数执行前把当前参数入栈备份,写入跳转指令,跳转到我们函数执行完后跳转回去,出栈参数还原跳转前的寄存器状态,以上所有操作都是在汇编层面。这里有两个难点,第一是找到 hook 函数的地址,第二则是跳转指令的写入,这里需要破坏部分原有的指令,如果目标是 64 位的,那么寻址空间变大又分为长跳短跳,C 的 MSVC 编译器不能编译 64 位的 ASM,要么安装 intel 的编译器要么先反汇编成字节码,Rust 中的 asm! 宏我也只尝试过 32 位的。所以很多人会选择使用库,比如 minihook,easyhook,plyhook 还有微软的 detours。Rust 的选择则较少,本例使用 detour-rs。关于第一个难点由于本例 hook 的 MessageBoxW 在 USER.DLL 中,这个系统模块在所有程序的地址都是一样的,所以找到它的地址没什么难度。

VMT hook 即为虚函数表 hook,原理需要大家复习一下 C 的内容了。这里特别提一点,有类的对象会共用一张虚表但是有的类的对象各自拥有一张虚表,有时候修改自己创建出来的类对象的虚表是没用的。但是尽管虚表不同,虚表里面存的地址一定是一样的,所以不修改这个地址而修改这个地址的内容即可达到 hook 的目的,这样实际上就是 VMT hook 和 inline hook 的结合。接下来这个 D3D11 hook 的例子作为补充,刚好 就是每个对象单独拥有一张虚表的情况。

前面说到有一个替代储存地址的方案,那就是储存一个裸指针,但是你是无法直接定义这种全局裸指针的,下面提供一个方法,也是我在本例中用到的方法:


https://www.xamrdz.com/lan/5st1932525.html

相关文章: