You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 10:38:06