模块 12 - 未文档化的结构
恶意软件开发课程 - 未文档化的结构
模块 12 - 未文档化的结构#
[!IMPORTANT] 本知识库声明
本知识库由本人整理自互联网 MalDev Academy 泄露资源,并由本人手动翻译为中文,过程中增加了大量关键技术提示与实践心得。
- 内容完整性:并未修改任何核心代码与技术逻辑,仅做汉化与注释加强。
- 版权归属:原始知识产权归原作者/官方所有。
- 支持正版:本仓库仅供内部学习交流,如果您有经济能力,请务必支持正版课程。
- 权利申诉:如相关内容侵犯了您的权益,请联系我,我将立即核实并删除。
未文档化的结构#
简介#
在查看 Windows 文档中的结构时,可能会遇到结构中的几个保留 (reserved) 成员。这些保留成员通常表示为 BYTE 或 PVOID 数据类型的数组。Microsoft 实施此做法是为了保持机密性并防止用户了解结构以避免对这些保留成员进行修改。
话虽如此,在整个课程中,将需要使用这些未文档化的成员。因此,某些模块将避免使用 Microsoft 的文档,而是使用具有完整未文档化结构的其他网站,这些结构可能是通过逆向工程获得的。
💡 初学者提示:为什么Microsoft隐藏结构成员?
Reserved(保留)成员的真相:
c// Microsoft 官方文档中的PEB typedef struct _PEB { BYTE Reserved1[2]; // 隐藏了什么? BYTE BeingDebugged; BYTE Reserved2[1]; // 又隐藏了什么? PVOID Reserved3[2]; PPEB_LDR_DATA Ldr; ... } PEB;为什么要隐藏?
- 向后兼容性:微软可以自由更改这些成员
- 防止滥用:避免恶意软件利用
- 内部实现细节:不想公开的API
- 维护灵活性:不同Windows版本可能不同
类比理解:
plaintext手机 (Windows) ├── 公开功能:打电话、发短信 (文档化) ├── 隐藏功能:调试模式、工程菜单 (未文档化) └── 为什么隐藏?避免普通用户误操作但对于我们(恶意软件开发/安全研究):
- 需要访问这些隐藏成员
- 用于实现高级技术
- 例如:API Hashing、反调试、进程隐藏等
PEB 结构示例#
如前模块所述,进程环境块或 PEB 是一个包含有关 Windows 进程信息的数据结构。但是,Microsoft 的文档 ↗ 中的 PEB 结构显示了几个保留成员。这使得访问结构的成员变得困难。
typedef struct _PEB {
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PVOID Reserved4[3];
PVOID AtlThunkSListPtr;
PVOID Reserved5;
ULONG Reserved6;
PVOID Reserved7;
ULONG Reserved8;
ULONG AtlThunkSListPtr32;
PVOID Reserved9[45];
BYTE Reserved10[96];
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
BYTE Reserved11[128];
PVOID Reserved12[1];
ULONG SessionId;
} PEB, *PPEB;c查找保留成员#
确定 PEB 的保留成员包含什么的一种方法是通过 WinDbg ↗ 中的 !peb 命令。

有关更完整的 PEB 结构,请参阅 Process Hacker 的 PEB structure ↗。
📚 知识扩展:如何找到未文档化的结构
方法1:使用WinDbg调试器
plaintext1. 启动WinDbg 2. 附加到进程 或 打开crash dump 3. 使用命令: !peb (查看PEB) !teb (查看TEB) dt nt!_PEB (显示PEB结构) dt nt!_EPROCESS (显示EPROCESS) 输出示例: 0:000> dt nt!_PEB +0x000 InheritedAddressSpace : UChar +0x001 ReadImageFileExecOptions : UChar +0x002 BeingDebugged : UChar +0x003 BitField : UChar +0x004 Mutant : Ptr64 Void +0x008 ImageBaseAddress : Ptr64 Void +0x010 Ldr : Ptr64 _PEB_LDR_DATA ...方法2:逆向工程
plaintext1. 在IDA Pro/Ghidra中打开 ntdll.dll 2. 查找使用PEB的函数 3. 分析偏移量和访问模式 4. 推断结构成员方法3:查阅第三方文档(推荐)
plaintext见下一节"替代文档"Reserved成员到底是什么?
c// Microsoft 的 PEB BYTE Reserved1[2]; // 实际上是(ntdll.dll 10.0.19041) BYTE InheritedAddressSpace; // +0x000 BYTE ReadImageFileExecOptions; // +0x001 // Microsoft 的 PEB PVOID Reserved3[2]; // 实际上是 PVOID Mutant; // +0x004 PVOID ImageBaseAddress; // +0x008
替代文档#
如前所述,某些模块将避免使用 Microsoft 的文档,而是使用其他文档源。
- Process Hacker’s Header Files ↗
- undocumented.ntinternals.net ↗ - 某些结构可能已过时
- ReactOS’s Documentation ↗
- Vergilius Project ↗ - 虽然主要用于 Windows 内核结构,但它仍然是一个有价值的资源。
📚 知识扩展:推荐的文档资源详解
1. Process Hacker (phnt)
plaintextURL: https://github.com/winsiderss/systeminformer/tree/master/phnt/include 优点: ✅ 最完整、最新 ✅ 主动维护 ✅ 支持最新Windows版本 ✅ 包含所有架构(x86, x64, ARM64) ✅ 社区验证 文件: - ntpebteb.h → PEB, TEB结构 - ntldr.h → 模块加载器结构 - ntpsapi.h → 进程/线程API - ntexapi.h → 执行API2. Vergilius Project
plaintextURL: https://www.vergiliusproject.com/ 优点: ✅ 在线查询,无需下载 ✅ 支持多个Windows版本对比 ✅ 可视化偏移量 ✅ 适合快速查询 使用示例: 搜索 "_PEB" → 选择Windows版本(10.0.19041 x64) → 查看完整结构定义3. ReactOS Documentation
plaintextURL: https://doxygen.reactos.org/ 优点: ✅ 开源实现的Windows克隆 ✅ 详细的注释 ✅ 函数实现可参考 注意: ⚠️ 可能与真实Windows有细微差别 ⚠️ 主要用于理解概念4. undocumented.ntinternals.net(历史资料)
plaintext注意: ⚠️ 网站已关闭(需要通过Wayback Machine访问) ⚠️ 信息可能过时(针对旧版本Windows) ⚠️ 仅作历史参考实用示例:对比不同来源
Microsoft官方(Winternl.h):
ctypedef struct _PEB { BYTE Reserved1[2]; BYTE BeingDebugged; // ... 很多Reserved } PEB;Process Hacker(ntpebteb.h):
ctypedef struct _PEB { BOOLEAN InheritedAddressSpace; BOOLEAN ReadImageFileExecOptions; BOOLEAN BeingDebugged; union { BOOLEAN BitField; struct { BOOLEAN ImageUsesLargePages : 1; BOOLEAN IsProtectedProcess : 1; BOOLEAN IsImageDynamicallyRelocated : 1; BOOLEAN SkipPatchingUser32Forwarders : 1; BOOLEAN IsPackagedProcess : 1; BOOLEAN IsAppContainer : 1; BOOLEAN IsProtectedProcessLight : 1; BOOLEAN IsLongPathAwareProcess : 1; }; }; HANDLE Mutant; PVOID ImageBaseAddress; PPEB_LDR_DATA Ldr; // ... 完整定义 } PEB;差距明显!
注意事项#
选择结构定义时,重要的是要注意以下几点。
- 某些结构定义仅适用于特定架构,即 x86 或 x64。如果是这种情况,请确保选择适当的结构定义。
- 在某些情况下,由于嵌套结构的概念,可能需要定义多个结构。例如,PEB 之类的结构可能包含一个充当指向另一个结构的指针的成员。因此,重要的是包括后一个结构的定义,以确保程序正确解释它。
- 使用结构的自定义定义时,不可能同时包含其在 Windows SDK 中找到的原始定义。例如,Microsoft 的 PEB 结构定义位于 Winternl.h ↗ 中。如果打算使用上述文档源之一的不同定义,那么尝试在程序中包含
Winternl.h将导致 Visual Studio 的编译器抛出重定义错误。为避免这种情况,请仅选择一个结构定义。
💡 初学者提示:使用未文档化结构的实践指南
常见问题1:架构不匹配
c// ❌ 错误:在64位程序中使用32位结构 #ifdef _WIN64 typedef struct _PEB { // ... 使用32位偏移量 } PEB; #endif // ✅ 正确:根据架构选择 #ifdef _WIN64 #include "peb64.h" // 64位PEB定义 #else #include "peb32.h" // 32位PEB定义 #endif常见问题2:嵌套结构
c// PEB 包含 Ldr成员 typedef struct _PEB { ... PPEB_LDR_DATA Ldr; // 指向另一个结构! ... } PEB; // 所以必须也定义 PEB_LDR_DATA typedef struct _PEB_LDR_DATA { ULONG Length; BOOLEAN Initialized; HANDLE SsHandle; LIST_ENTRY InLoadOrderModuleList; LIST_ENTRY InMemoryOrderModuleList; LIST_ENTRY InInitializationOrderModuleList; ... } PEB_LDR_DATA, *PPEB_LDR_DATA; // 还需要 LIST_ENTRY typedef struct _LIST_ENTRY { struct _LIST_ENTRY* Flink; struct _LIST_ENTRY* Blink; } LIST_ENTRY, *PLIST_ENTRY;常见问题3:重定义错误
c// ❌ 错误:同时包含两个定义 #include <Windows.h> // 包含 Winternl.h // 自定义PEB定义 typedef struct _PEB { ... } PEB; // 错误:'_PEB': 重定义;不同的基类型 // ✅ 解决方案1:不包含官方头文件 // #include <Windows.h> // 注释掉 #include <windef.h> // 只包含需要的部分 // 自定义PEB定义 typedef struct _PEB { ... } PEB; // ✅ 解决方案2:使用预处理器 #define _WINTERNL_ // 防止包含 Winternl.h #include <Windows.h> // 自定义PEB定义 typedef struct _PEB { ... } PEB;推荐的项目结构:
plaintextMyMalware/ ├── include/ │ ├── ntdefs.h (基本NT定义) │ ├── ntpebteb.h (PEB/TEB完整定义) │ ├── ntldr.h (加载器结构) │ └── ntstatus.h (NTSTATUS值) ├── src/ │ └── main.c └── README.md // main.c #define _WINTERNL_ // 阻止Windows SDK定义 #include <Windows.h> #include "include/ntpebteb.h" // 使用自定义定义实用模板:
c// ntdefs.h - 基础定义 #pragma once #define _WINTERNL_ #include <Windows.h> // 防止重定义 #ifndef NT_SUCCESS #define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) #endif // ... 其他未文档化定义版本兼容性检查:
c// 不同Windows版本PEB偏移可能不同 DWORD GetWindowsVersion() { RTL_OSVERSIONINFOW ver = {0}; ver.dwOSVersionInfoSize = sizeof(ver); RtlGetVersion(&ver); printf("Windows %d.%d.%d\n", ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber); return ver.dwBuildNumber; } // 根据版本使用不同的结构 #if (WINDOWS_BUILD >= 19041) // Windows 10 20H1+ // 使用新版本PEB #else // 使用旧版本PEB #endif
🎯 本模块学习要点
关键概念:
- ✅ Microsoft 文档中的 Reserved 成员是未文档化的
- ✅ 需要使用第三方资源获取完整结构
- ✅ 注意架构差异(x86 vs x64)
- ✅ 处理嵌套结构定义
- ✅ 避免重定义错误
推荐资源(按优先级):
- ⭐⭐⭐⭐⭐ Process Hacker (phnt) - 最完整、最新
- ⭐⭐⭐⭐ Vergilius Project - 在线查询方便
- ⭐⭐⭐ ReactOS - 理解概念和实现
- ⭐⭐ undocumented.ntinternals.net - 历史参考
实践建议:
plaintext1. 创建统一的头文件目录 2. 从Process Hacker复制需要的结构 3. 添加 #pragma once 防止重复包含 4. 使用 #define _WINTERNL_ 防止冲突 5. 测试不同Windows版本的兼容性常用的未文档化结构:
_PEB- 进程环境块_TEB- 线程环境块_LDR_DATA_TABLE_ENTRY- 模块信息_EPROCESS- 内核进程结构_ETHREAD- 内核线程结构下一步: 学习Payload在不同PE节中的存储