如何使用windows-rs crate获取当前进程的PEB_LDR_DATA结构体
如何使用windows-rs crate获取当前进程的PEB_LDR_DATA结构体
我完全理解你更愿意用微软官方维护的windows-rs crate来实现这个需求——毕竟它的维护性和官方背书的兼容性确实更让人放心。下面我就带你一步步把原来基于ntapi的代码改成用windows-rs实现,轻松获取当前进程的PEB_LDR_DATA结构体。
首先,先确保你的Cargo.toml里正确添加了windows-rs依赖,并开启需要的功能特性:
[dependencies] windows = { version = "0.52", features = ["Win32_System_Threading", "Win32_Foundation"] }
接下来我们分部分实现核心逻辑:
1. 读取GS寄存器的辅助函数
这个函数和你原来的逻辑一致,用来直接从GS寄存器指定偏移处读取数据,这是获取TEB地址的关键:
use std::arch::asm; pub unsafe fn __readgsqword(offset: u32) -> u64 { let out: u64; asm!( "mov {}, gs:[{:e}]", lateout(reg) out, in(reg) offset, options(nostack, pure, readonly), ); out }
2. 获取当前进程的TEB指针
我们用windows-rs提供的NT_TIB和TEB类型,搭配标准库的std::mem::offset_of!来获取字段偏移,替代ntapi的自定义宏:
use windows::Win32::Foundation::NT_TIB; use windows::Win32::System::Threading::TEB; pub unsafe fn nt_current_teb() -> *mut TEB { // 获取NT_TIB中Self字段的偏移(x64平台下这个偏移是0) let teb_offset = std::mem::offset_of!(NT_TIB, Self) as u32; __readgsqword(teb_offset) as *mut TEB }
3. 通过TEB获取PEB指针
TEB结构体里直接包含了指向PEB的指针,我们直接读取这个字段即可:
use windows::Win32::System::Threading::PEB; pub unsafe fn nt_current_peb() -> *mut PEB { (*nt_current_teb()).ProcessEnvironmentBlock }
4. 获取PEB_LDR_DATA结构体
PEB的Ldr字段就是指向PEB_LDR_DATA的指针,直接读取就能拿到:
use windows::Win32::System::Threading::PEB_LDR_DATA; pub unsafe fn get_peb_ldr_data() -> *mut PEB_LDR_DATA { (*nt_current_peb()).Ldr }
扩展:遍历模块列表(类似你原来的get_module_addr函数)
如果要继续实现模块哈希查找的逻辑,我们可以用windows-rs的LIST_ENTRY和LDR_DATA_TABLE_ENTRY类型来完成链表遍历:
use windows::Win32::System::Threading::{LIST_ENTRY, LDR_DATA_TABLE_ENTRY}; use windows::Win32::Foundation::{ULONG, PVOID}; pub unsafe fn get_module_addr(hash: ULONG) -> PVOID { let ldr = get_peb_ldr_data(); if ldr.is_null() { return std::ptr::null_mut(); } // 获取加载顺序模块列表的表头 let header = &(*ldr).InLoadOrderModuleList as *const LIST_ENTRY; let mut entry = (*header).Flink; // 遍历模块链表 while entry != header { // LIST_ENTRY是LDR_DATA_TABLE_ENTRY的第一个成员,所以偏移-1得到完整的模块条目 let module_entry = entry.cast::<LIST_ENTRY>().offset(-1).cast::<LDR_DATA_TABLE_ENTRY>(); let dll_base = (*module_entry).DllBase; // 这里加入你的哈希校验逻辑,比如计算模块名称的哈希并和传入值比较 // if calculate_module_hash((*module_entry).BaseDllName.Buffer) == hash { // return dll_base; // } entry = (*entry).Flink; } // 未找到匹配模块时返回空指针 std::ptr::null_mut() }
一些重要注意事项
- 所有上述操作都必须在
unsafe块中执行,因为涉及直接操作原始指针和未安全的内存访问。 - 这段代码仅适配x64平台,如果需要支持x86,需要把寄存器从GS改为FS,并调整对应的字段偏移。
- windows-rs的结构体定义完全对齐Windows原生结构体,字段布局和偏移和winapi/ntapi完全一致,不用担心兼容性问题。
备注:内容来源于stack exchange,提问作者Tom Stock
相关产品推荐
相关产品推荐

