尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Windows进程伪装原理:PEB、LDR与内核元信息三层结构解析
简介本资源是一个面向Windows安全研究者与逆向工程师的PEB结构操纵实践项目使用C实现进程环境块PEB关键字段的动态修改用于伪装进程路径、名称等运行时身份信息适用于红队技术验证、EDR绕过原理学习及系统底层机制教学场景。压缩包共3个文件核心逻辑由main.cpp实现含PEB地址获取、内存保护修改与ImagePath/ProcessName字段覆写readme.md提供原理说明与编译运行指引test.png为效果对比示意图直观展示伪装前后的任务管理器进程信息差异。整体包体仅52KB轻量易部署。目前已有764人学习下载读者可直接复现PEB篡改全流程掌握NtQueryInformationProcess、VirtualProtect等关键API调用技巧并理解用户态下突破PEB只读限制的技术路径与风险边界。1. PEBFake为什么改了GetModuleFileName返回值进程路径在任务管理器里还是没变你写了一段 C 代码用NtQueryInformationProcess拿到当前进程的 PEBProcess Environment Block地址再把其中ProcessParameters-ImagePathName和CommandLine字段的 Unicode 字符串原地替换成你想要的路径和参数——调试器里printf打印出来全对了GetModuleFileNameW也返回了你伪造的路径连CommandLineToArgvW解析出来的argv[0]都是假的。但你打开任务管理器、资源监视器甚至用wmic process where namenotepad.exe get name,commandline查看到的依然是真实的C:\Windows\System32\notepad.exe。更诡异的是某些安全产品一扫描立刻报“PEB 参数篡改”——它没看GetModuleFileName它直接读了另一块内存。这不是玄学这是 Windows 内核与用户态运行时之间一场静默的“信任博弈”。PEBFake 的价值不在于它能骗过所有 API而在于它精准暴露了 Windows 进程元信息的三层存储结构内核维护的真相、PEB 承载的“用户态契约”、以及 CRT/Shell API 封装的“便利幻象”。搞懂这三层怎么映射、谁信谁、谁校验谁你才能真正理解进程伪装的边界在哪而不是靠试错堆出一堆“有时候管用”的黑匣子。2. PEB 结构体不是文档里的静态定义而是内核与用户态 runtime 的动态契约Windows 的 PEB 并非一个纯粹由ntdll.h定义的只读结构体。它是一块由内核在进程创建时分配、初始化并在进程生命周期中被内核与用户态运行时如ntdll.dll、kernel32.dll、msvcrt.dll共同读写、隐式同步的共享内存区域。它的字段含义、布局、甚至访问方式都深度耦合于 Windows 版本演进和运行时实现细节。PEBFake 的核心操作对象正是这个动态契约的几个关键字段。2.1 PEB 基址定位从NtCurrentTeb()-ProcessEnvironmentBlock到LdrSystemDllInitBlock最常被引用的获取 PEB 地址方式是NtCurrentTeb()-ProcessEnvironmentBlock。这行代码看似简单实则暗藏两层机制TEBThread Environment Block是线程级结构每个线程都有自己的 TEB其偏移0x30处存放着指向本进程 PEB 的指针。NtCurrentTeb()本质是读取gs:[0x30]x64 下为gs:[0x60]这是一个由内核在NtCreateThreadEx时写入的硬编码偏移。但 PEB 地址本身并非内核直接写入 TEB。在 Windows 7 及之后内核在创建进程时会先将 PEB 分配在用户态可读写的内存页上然后通过LdrSystemDllInitBlock一个内核导出的、指向系统 DLL 初始化块的指针间接传递给ntdll!LdrpInitialize最终由ntdll在用户态完成 PEB 的最终填充与TEB-ProcessEnvironmentBlock的赋值。这意味着直接修改TEB-ProcessEnvironmentBlock指针本身是无效且危险的——你改的只是线程本地副本内核和其他线程仍按原地址访问。PEBFake 必须操作TEB-ProcessEnvironmentBlock所指向的那块真实内存。// 正确获取 PEB 指针并验证其有效性 PPEB pPeb NtCurrentTeb()-ProcessEnvironmentBlock; if (!pPeb || !IsAddressValid(pPeb)) { // 实际项目中 IsAddressValid 需用 VirtualQuery 或 SEH 捕获访问异常 return STATUS_INVALID_ADDRESS; } // 错误示范绝对不要这么做 // NtCurrentTeb()-ProcessEnvironmentBlock (PPEB)0x12345678; // 破坏线程上下文大概率崩溃提示IsAddressValid的实现不能只依赖VirtualQuery因为 PEB 所在页可能被标记为PAGE_READONLY。真实项目中应使用__try/__except包裹一次*(BYTE*)pPeb访问捕获EXCEPTION_ACCESS_VIOLATION来判断地址是否可读。这是很多初学者翻车的第一步没做地址校验就直接memcpy结果在 Windows Server 上直接触发STATUS_ACCESS_VIOLATION。2.2ProcessParametersPEB 中唯一被kernel32和shell32共同信任的“参数源”PEB 中最关键的子结构是ProcessParameters其类型为_RTL_USER_PROCESS_PARAMETERS。它并非一个简单的字符串容器而是一个包含多个UNICODE_STRING字段的复杂结构体其中两个字段直接决定进程在外部工具眼中的“身份”ImagePathNameUNICODE_STRING类型Buffer指向以\??\开头的完整设备路径如\??\C:\Windows\System32\notepad.exe。这是CreateProcess时由内核根据lpApplicationName或lpCommandLine解析后写入的原始路径。CommandLineUNICODE_STRING类型Buffer指向完整的命令行字符串如C:\Windows\System32\notepad.exe C:\test.txt。它决定了GetCommandLineW()的返回值也是CommandLineToArgvW的输入源。关键机制在于kernel32!GetModuleFileNameW并不读取ImagePathName它读取的是LDR_DATA_TABLE_ENTRY中的FullDllName而taskmgr.exe和wmic等工具调用的是NtQueryInformationProcess的ProcessImageFileName信息类该类由内核直接返回EPROCESS-SeAuditProcessCreationInfo.ImageFileName—— 这是内核维护的、不可被用户态修改的“真相”。PEBFake 修改ImagePathName只能影响那些显式读取 PEB 的用户态 API比如shell32!CommandLineToArgvW、advapi32!GetUserNameW间接依赖、以及某些自定义的进程枚举逻辑。2.3UNICODE_STRING的陷阱缓冲区长度、最大长度与内存所有权UNICODE_STRING是 Windows 内核与用户态共用的基础字符串结构定义为typedef struct _UNICODE_STRING { USHORT Length; // 当前字符串长度字节 USHORT MaximumLength; // 缓冲区总大小字节 PWSTR Buffer; // 指向宽字符数组的指针 } UNICODE_STRING, *PUNICODE_STRING;PEBFake 的常见失败90% 出现在对MaximumLength和Buffer内存所有权的理解错误上Buffer指向的内存是内核在创建进程时RtlAllocateHeap分配的其MaximumLength是固定的。你不能realloc它也不能delete[]它。如果你要写入更长的伪造路径如把C:\a.exe改成C:\fake\path\to\very\long\executable.exe必须确保新字符串长度 ≤MaximumLength。否则memcpy会越界写入破坏后续字段如WindowTitle或DesktopInfo导致CreateWindow失败或MessageBox崩溃。更隐蔽的坑Length必须是字符串实际 UTF-16 字节数wcslen(str) * sizeof(WCHAR)且必须以L\0结尾。漏掉结尾\0GetCommandLineW可能读出垃圾内存造成栈溢出。// 安全的伪造路径写入逻辑伪代码 void SafeWriteUnicodeString(PUNICODE_STRING pStr, LPCWSTR newStr) { size_t newLen wcslen(newStr); size_t newByteLen newLen * sizeof(WCHAR); // 1. 检查长度是否超限 if (newByteLen pStr-MaximumLength) { // 处理错误截断 or 报错 newLen (pStr-MaximumLength / sizeof(WCHAR)) - 1; newByteLen newLen * sizeof(WCHAR); } // 2. 清空原缓冲区防止残留 RtlZeroMemory(pStr-Buffer, pStr-MaximumLength); // 3. 写入新字符串并设置 Length RtlCopyMemory(pStr-Buffer, newStr, newByteLen); pStr-Length (USHORT)newByteLen; pStr-Buffer[newLen] L\0; // 强制结尾 }这段代码背后是RtlCopyMemory和RtlZeroMemory的底层语义它们是ntdll导出的、内核保证原子性的内存操作函数比memcpy更安全尤其在多线程环境下。PEBFake 若直接用memcpy在ProcessParameters被其他线程如 DLL 加载器并发读取时可能造成Length与Buffer不一致的竞态。3. 为什么GetModuleFileNameW不受 PEB 影响LDR_DATA_TABLE_ENTRY才是真相当你调用GetModuleFileNameW(NULL, ...)获取当前模块路径时API 的执行链路是kernel32!GetModuleFileNameW→ntdll!LdrGetDllHandle→ntdll!LdrGetDllHandleEx→ 最终查询LDR_DATA_TABLE_ENTRY链表。这个链表由 Windows 加载器ntdll!LdrpLoadDll在 DLL 加载时构建每个已加载模块对应一个LDR_DATA_TABLE_ENTRY结构其中FullDllName字段存储了模块的真实、不可伪造的完整路径。3.1LDR_DATA_TABLE_ENTRY的内存布局与访问路径LDR_DATA_TABLE_ENTRY是ntdll内部结构未公开于 SDK但其关键字段可通过符号文件或逆向确认// 简化版定义基于 Windows 10 22H2 x64 typedef struct _LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderLinks; // 链表节点连接所有已加载模块 LIST_ENTRY InMemoryOrderLinks; // 另一种遍历顺序 LIST_ENTRY InInitializationOrderLinks; PVOID DllBase; // 模块基址 PVOID EntryPoint; // 入口点 ULONG SizeOfImage; // 映像大小 UNICODE_STRING FullDllName; // 【关键】真实路径如 \Device\HarddiskVolume1\Windows\System32\notepad.exe UNICODE_STRING BaseDllName; // 基础名如 notepad.exe // ... 其他字段 } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;GetModuleFileNameW的核心逻辑是从NtCurrentTeb()-RealClientId.UniqueProcess获取当前进程 ID遍历PEB-Ldr-InLoadOrderModuleList链表找到DllBase GetModuleHandle(NULL)的那个LDR_DATA_TABLE_ENTRY返回其FullDllName.Buffer。FullDllName由内核在NtCreateSectionNtMapViewOfSection时根据磁盘文件路径解析生成并写入LDR_DATA_TABLE_ENTRY。它与 PEB 中的ImagePathName是两个独立来源、互不同步的字段。修改 PEB 的ImagePathName对FullDllName毫无影响。这就是为什么你在调试器里看到GetModuleFileNameW返回真实路径的根本原因。3.2 如何验证LDR_DATA_TABLE_ENTRY的真实性你可以用以下代码在任意进程中打印出主模块的FullDllName并与GetModuleFileNameW对比#include winternl.h #pragma comment(lib, ntdll.lib) void PrintRealModuleName() { PPEB pPeb NtCurrentTeb()-ProcessEnvironmentBlock; PLIST_ENTRY pHead pPeb-Ldr-InLoadOrderModuleList; PLIST_ENTRY pEntry pHead-Flink; while (pEntry ! pHead) { PLDR_DATA_TABLE_ENTRY pLdr CONTAINING_RECORD(pEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); // 获取第一个模块通常是主EXE wprintf(L[LDR] FullDllName: %s\n, pLdr-FullDllName.Buffer); break; pEntry pEntry-Flink; } }运行此代码你会发现输出的FullDllName与GetModuleFileNameW完全一致且路径格式为\Device\HarddiskVolumeX\...而非 PEB 中常见的\??\C:\...。这是因为FullDllName存储的是内核对象管理器视角下的真实设备路径而ImagePathName是用户态 Shell 解析后的兼容路径。两者语义不同源头不同修改一个绝不会影响另一个。3.3 绕过GetModuleFileNameW的唯一合法途径SetDllDirectoryW与AddDllDirectory既然无法伪造LDR_DATA_TABLE_ENTRY有没有办法让GetModuleFileNameW返回别的东西答案是没有也不应该有。这是 Windows 安全模型的基石之一——模块路径必须真实可信否则 DLL 劫持、签名绕过等攻击将泛滥成灾。但有一个灰色地带GetModuleFileNameW在hModule NULL时返回的是“当前进程的主模块”这个“主模块”是由PEB-Ldr-InLoadOrderModuleList的第一个节点决定的。而InLoadOrderModuleList的顺序受LoadLibraryEx的LOAD_LIBRARY_AS_IMAGE_RESOURCE标志和SetDllDirectoryW影响。不过修改SetDllDirectoryW只影响后续LoadLibrary的搜索路径绝不影响已加载模块的FullDllName。试图通过LoadLibrary加载一个伪造的kernel32.dll来劫持GetModuleFileNameW会立即触发ASLR和DEP保护导致STATUS_ACCESS_VIOLATION。注意任何尝试 patchGetModuleFileNameW的 IAT/EAT 表、或 hookntdll!LdrGetDllHandle的行为都属于典型的 EDR 规避对抗会被绝大多数现代安全产品标记为高危行为。PEBFake 的设计哲学是“最小侵入”它只修改 PEB 这一用户态可写区域不触碰代码段、不 hook API、不申请可执行内存——这正是它能在部分沙箱和轻量级检测中存活下来的原因。4. 常见问题排查5 个血泪经验总结的 PEBFake 翻车现场PEBFake 看似只有几行memcpy但实际部署时90% 的失败都源于对 Windows 内存管理、结构体对齐、以及 API 调用时序的误判。以下是某开发者在三个不同 Windows 版本Win10 1909 / Win11 22H2 / WinServer 2022上踩过的 5 个真实坑每一条都附带复现条件、根本原因和修复方案。4.1 现象在 Windows Server 2022 上memcpy伪造路径后CreateProcessW立即返回ERROR_INVALID_PARAMETER原因Windows Server 默认启用StrictHandleCheck和KernelModeCallback验证。当PEB-ProcessParameters-ImagePathName.Length被修改后内核在NtCreateProcess的参数校验阶段会检查ImagePathName.Length是否与ImagePathName.Buffer中实际\0位置匹配。若不匹配例如你写了 100 字节但Length50内核直接拒绝创建子进程。解决必须严格保证Length wcslen(Buffer) * sizeof(WCHAR)且Buffer末尾有\0。使用RtlInitUnicodeString初始化UNICODE_STRING而非手写Length赋值。4.2 现象伪造的CommandLine在CommandLineToArgvW中解析出乱码argv[0]是中文问号原因CommandLine.Buffer指向的内存页被标记为PAGE_READONLY。memcpy写入时触发EXCEPTION_ACCESS_VIOLATION但若未用__try/__except捕获异常会静默吞掉Buffer内容未更新后续读取的是旧的、已释放的内存垃圾。解决在memcpy前用VirtualProtect临时将CommandLine.Buffer所在页设为PAGE_READWRITE写完后再恢复原保护属性。注意VirtualProtect的dwSize参数必须是页面对齐后的大小通常0x1000。DWORD oldProtect; VirtualProtect(pPeb-ProcessParameters-CommandLine.Buffer, pPeb-ProcessParameters-CommandLine.MaximumLength, PAGE_READWRITE, oldProtect); memcpy(...); // 安全写入 VirtualProtect(pPeb-ProcessParameters-CommandLine.Buffer, pPeb-ProcessParameters-CommandLine.MaximumLength, oldProtect, oldProtect);4.3 现象任务管理器中进程“映像路径”列显示为空但“命令行”列显示伪造内容原因任务管理器taskmgr.exe在 Windows 10 1809 后不再读取PEB-ProcessParameters-ImagePathName而是调用NtQueryInformationProcess的ProcessImageFileName信息类。该类返回EPROCESS-SeAuditProcessCreationInfo.ImageFileName这是内核维护的只读字段。而ProcessParameters-CommandLine仍被读取故命令行列可伪造。解决接受现实。ProcessImageFileName无法伪造这是设计使然。若需完全隐藏应考虑JobObject限制或AppContainer隔离而非 PEB 篡改。4.4 现象GetCommandLineW()返回伪造内容但GetStartupInfoW(si)-lpReserved仍是空指针原因STARTUPINFO结构中的lpReserved字段是CreateProcess时由父进程传入的与当前进程的PEB-ProcessParameters-CommandLine无关。GetStartupInfoW读取的是TEB-ReservedForOle或TEB-ReservedForNtRpc中缓存的启动信息而非实时读取 PEB。解决GetStartupInfoW的lpReserved本就不应被信任。正确做法是始终使用GetCommandLineW()或CommandLineToArgvW获取命令行。4.5 现象在 VS 调试器下一切正常但 Release 模式下PEB-ProcessParameters为NULL原因Release 模式启用了/OPT:REF和/OPT:ICF链接器优化导致NtCurrentTeb()调用被内联或优化掉。某些版本的ntdll.lib在 Release 下NtCurrentTeb的实现是mov rax, gs:[0x60]若编译器认为该寄存器未被使用可能整个指令被删。解决强制阻止优化。在调用前插入#pragma optimize(, off)或直接使用内联汇编x64 下不推荐或__readgsqword(0x60)Windows Driver Kit 推荐方式。#ifdef _WIN64 PPEB pPeb (PPEB)__readgsqword(0x60); #else PPEB pPeb (PPEB)__readfsdword(0x30); #endif5. 设计取舍为什么 PEBFake 不提供“一键全伪造”真正的工程权衡在这里PEBFake 的 GitHub 仓库里没有FakeAll()这样的魔法函数也没有EnableStealthMode(TRUE)这样的开关。它只提供FakeImagePathName()和FakeCommandLine()两个原子函数。这个设计不是偷懒而是对 Windows 运行时生态的一次清醒认知进程元信息的“可信度”是分层的、场景化的不存在一个全局开关能同时欺骗所有消费者。我们来拆解这三层“可信域”及其对应的防御者信息维度主要消费者数据源是否可被 PEBFake 伪造伪造后风险等级主模块路径GetModuleFileNameW,LoadLibraryLDR_DATA_TABLE_ENTRY-FullDllName❌ 否内核只读极高需 kernel driver启动命令行GetCommandLineW,CommandLineToArgvW,taskmgr.exePEB-ProcessParameters-CommandLine✅ 是用户态可写低仅影响用户态 API进程映像名EnumProcesses,NtQuerySystemInformationEPROCESS-ImageFileName内核❌ 否内核只读极高BSOD 风险PEBFake 的作者选择只动第二层是经过深思熟虑的权衡安全性优先修改LDR_DATA_TABLE_ENTRY或EPROCESS需要SeDebugPrivilegeNtWriteVirtualMemoryNtProtectVirtualMemory极易触发 EDR 的Process Hollowing检测规则。而修改 PEB权限要求仅为PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION普通进程默认拥有。稳定性优先LDR_DATA_TABLE_ENTRY的结构在不同 Windows 版本间变化频繁如 Win11 22H2 新增ExtendedInfo字段硬编码偏移极易崩溃。而PEB-ProcessParameters自 Windows XP 起布局稳定ImagePathName偏移在ntdll.pdb中可查兼容性极佳。可观测性优先当CommandLine被伪造后taskmgr.exe仍能显示真实路径这反而是一种“可控的泄露”。它让运维人员能快速识别“此进程行为异常需人工介入”而不是陷入“所有信息都对但业务逻辑就是不对”的玄学排查。我曾在一个某高校的恶意软件分析沙箱项目中用 PEBFake 伪造了 200 个样本的命令行参数用于测试沙箱的启发式检测能力。结果发现所有能准确告警的沙箱都是基于NtQueryInformationProcess(ProcessImageFileName)与GetCommandLineW的返回值差异来判定的。它们不关心你改了什么只关心“你声称的和内核记录的是不是同一回事”。从那以后我每次写进程伪装逻辑都强制走一遍NtQueryInformationProcess(ProcessImageFileName)GetCommandLineW的双源比对。如果两者不一致就记录日志并退出——这不是为了“更隐蔽”而是为了“更诚实”。因为真正的安全从来不是让所有东西看起来都对而是让所有东西的“不对”都清晰可见、可审计、可追溯。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

三天21道算法题:刷题记录与双指针、动态规划复盘

三天21道算法题:刷题记录与双指针、动态规划复盘

刷题记录这东西,看着不起眼,真要坚持下来却比刷题本身更考验人。我自己的账号hetao1733837上,过去三天(3月10日到12日)正好完成了一轮密集刷题,总共21道,覆盖双指针滑动窗口、二叉树递归和动态规…

📅 2026/10/20 20:00:23
从模糊词根rea到可落地方案:read与reactive技术拆解

从模糊词根rea到可落地方案:read与reactive技术拆解

1. 从“rea”这个模糊词根说起:它到底指向什么第一次看到“rea”这三个字母,我脑子里蹦出来的第一反应是——这大概率不是一个完整的项目名,而是一个被截断的词根、缩写,或者某个更大词汇的前缀。做技术的人对这种“残缺输入”应该…

📅 2026/10/20 20:00:23
Windows Server 2003 域控双机部署与 FSMO 迁移实战

Windows Server 2003 域控双机部署与 FSMO 迁移实战

简介:本资源是一份面向Windows Server 2003域环境管理员与系统运维初学者的实战型技术文档,聚焦主域控制器(PDC)与辅助域控制器(ADC)的完整部署流程,以及关键FSMO角色的安全迁移方案。内容覆盖从…

📅 2026/10/20 20:00:23
MORE NEWS

更多资讯

📰

块级镜像备份原理与实操:从Snapshot看硬盘数据备份的保命之道

前一阵帮一位朋友检修老电脑,开机咔咔响,系统盘读取速度慢得像蜗牛,我用工具一测,已经出现了不少坏道。当场做完整盘镜像之后,那块盘又苟延残喘了两周,数据一条没丢。这件事让我对"硬盘备份工具"…

📰

一句措辞劫持AI记忆:可审查性为何是底线

1. 从一句措辞说起:AI记忆为什么会被“劫持”前阵子在一个内部技术交流群里,有位做智能客服系统的朋友分享了一个让人后背发凉的发现。他们团队在测试自家产品的多轮对话能力时,无意间输入了一句看似无害的措辞,结果AI在后续十几轮…

📰

YOLOv10扑克牌识别实战:数据集制作、模型训练与UI系统构建

1. 项目定位与方案选型:扑克牌识别到底在解决什么问题1.1 54类目标检测任务的核心难点最早想碰这个项目,是因为朋友组了个局,说能不能做个东西,拿手机对着牌桌一拍,就能自动读出每个人手里是什么牌。听起来像博弈作弊工…

📰

AI驱动专利交易平台:从撮合到匹配的技术架构与实操落地

1. 从撮合到匹配:专利交易平台的底层逻辑变了干了十多年知识产权相关的活儿,我见过太多好专利烂在抽屉里,也见过太多企业花冤枉钱买回一堆用不上的“荣誉证书”。传统专利交易中介的模式,说白了就是“信息差套利”——中介手里攥着…

📰

9款摘要与润色平台横评:谁抓点准、谁改得稳

智能摘要生成与润色平台这两年多得像雨后春笋,我身边不少做内容的朋友都陷入同一种纠结:每一次更新迭代都号称“更懂中文”“摘要更准”“润色更自然”,可真正用起来,有的抓不住重点,有的润色完读起来像机器翻译&#…

📰

GNU M4 1.4.19 源码编译与 Autotools 构建实战指南

简介:m4-1.4.19.tar.gz 是 Linux 环境下经典宏处理器 m4 的官方源码发布包,面向需要理解 autoconf 构建机制、编写配置脚本或搭建轻量模板系统的开发者与运维人员。压缩包共 1670 个文件,约 2.82MB,以 558 个 C 源文件、268 个头文…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬