目录
[么是系统级别的 LoadPE 过程?](#么是系统级别的 LoadPE 过程?)
[PE 手动加载绕过监控的思路](#PE 手动加载绕过监控的思路)
[一、C 语言](#一、C 语言)
[二、汇编语言 ------ 提供底层控制与规避能力](#二、汇编语言 —— 提供底层控制与规避能力)
[一、LoadPE 的核心定位与免杀本质](#一、LoadPE 的核心定位与免杀本质)
[1. 静态免杀(Static Evasion)](#1. 静态免杀(Static Evasion))
[2. 动态免杀(Dynamic Evasion)------ LoadPE 的主战场](#2. 动态免杀(Dynamic Evasion)—— LoadPE 的主战场)
[二、高级免杀方案设计(含 LoadPE 组合)](#二、高级免杀方案设计(含 LoadPE 组合))
[方案一:基础静动结合 LoadPE](#方案一:基础静动结合 LoadPE)
[方案二:LoadPE + 进程伪装 + 模块镂空](#方案二:LoadPE + 进程伪装 + 模块镂空)
[方案三:LoadPE + APC Early Bird + Shellcode(高阶)](#方案三:LoadPE + APC Early Bird + Shellcode(高阶))
[方案四:LoadPE + 反射 DLL 注入 + 反调试(生产级)](#方案四:LoadPE + 反射 DLL 注入 + 反调试(生产级))

- 这个技术点必须要有较强的windows编程能力以及深入理解PE结构以及汇编堆栈知识点切记哦! 懂则还行 不懂就非常难 熟悉就一般 精通这些就简单
系统LoadPE
什么是系统级别的LodePE
在 Windows 系统中,.exe、.dll 和 .sys 文件本质上是同一种东西------PE (Portable Executable) 文件。它们是操作系统能够理解和执行的标准二进制格式。
要理解它们的本质,首先要明白操作系统加载它们的过程,也就是 LoadPE。
么是系统级别的 LoadPE 过程?
- 当你双击一个
.exe文件,或者一个程序调用一个.dll时,Windows 的加载器 (Loader) 会自动执行一系列复杂的操作,将磁盘上的 PE 文件变成一个可以在内存中运行的程序。 - 这个过程就是
LoadPE。
过程:
-
读取文件头
加载器首先读取 PE 文件的头部信息(如 DOS 头和 NT 头)。这些头部就像是文件的"说明书",告诉加载器这个文件是什么架构(x86 或 x64)、代码和数据在哪里、入口点(程序开始执行的地方)是哪个。
-
分配内存
根据"说明书"中的指示(
SizeOfImage),加载器会在进程的虚拟内存空间中开辟一块足够大的区域。 -
映射节区 (Sections)
PE 文件的内容被划分为不同的"节"(Sections),例如
.text(代码)、.data(数据)、.rsrc(资源)等。加载器会按照头部信息,将这些节从磁盘文件复制到刚刚分配的内存中对应的位置。 -
修复重定位 (Relocations)
由于现代操作系统启用了地址空间布局随机化 (ASLR) 安全机制,程序每次加载到内存的地址都可能不同。重定位表的作用就是修正程序中所有硬编码的地址,确保它们指向正确的内存位置。
-
解析导入表 (Import Address Table, IAT)
程序通常会调用系统或其他 DLL 中的函数(如
MessageBoxA)。导入表记录了这些外部依赖。加载器会负责找到这些 DLL,并获取所需函数的真实地址,填入程序的导入地址表中,这样程序才能正确调用它们。 -
跳转到入口点
完成以上所有准备工作后,加载器最终会将 CPU 的执行权交给程序的入口点,程序正式开始运行。
简单来说,.exe、.dll、.sys 都是 PE 格式,它们的区别主要在于文件头中的某个标志位,这个标志位告诉加载器如何对待它:是创建一个新进程(.exe),还是将其映射到现有进程空间(.dll),或是加载到内核空间(.sys),这个过程称之为系统级别的LoadPE, 也成为之Windows 系统自带的加载器
LoadPE的应用(手动映射过程)
为什么要实现手动PE
在 Windows 系统中,绝大多数恶意软件,免杀,游戏修改器(Loader、注入器、内存补丁等)都需要调用一系列系统 API,例如:
-
LoadLibrary/GetProcAddress(动态加载 DLL 和函数) -
VirtualAlloc/VirtualProtect(内存分配与权限修改) -
CreateRemoteThread、WriteProcessMemory等注入相关函数
现代杀毒软件(尤其是行为监控类杀软)对这些高敏感 API 进行了严格的监控。
一旦检测到进程调用这些函数,就会触发告警、拦截甚至直接查杀。
- 传统方式是直接通过
GetProcAddress从kernel32.dll、ntdll.dll等模块中获取函数地址,这种"明目张胆"的调用很容易被行为监控捕获。
那么,有没有办法不直接调用这些被监控的 API,却依然能完成同样的功能呢?
答案就是:手动加载 PE(Manual PE Loading),也称为 PE 隐藏加载 或 PE 映射加载。
核心原理
Windows 加载一个 PE 文件(.exe 或 .dll)到内存中的过程,本质上是由操作系统内核(ntdll + kernel32)完成的,主要步骤包括:
-
读取 PE 文件
-
解析 DOS 头 → NT 头 → 节表
-
分配内存并拷贝文件头和各个节
-
处理重定位(Relocation)
-
解析导入表(Import Table),加载依赖的 DLL 并填充 IAT(Import Address Table)
-
处理 TLS、异常表等
-
调用入口点(Entry Point)
其中,第 5 步"解析导入表并调用 LoadLibrary + GetProcAddress" 是关键。
-
手动加载 PE 的核心思路是:我们自己模拟操作系统加载 PE 的整个过程,把这个加载行为完全放在用户态自己的代码中完成。
-
这样做的最大好处是:
-
在加载目标 PE 的过程中,我们可以完全控制导入表的解析方式。
-
我们可以不通过系统的
LoadLibrary和GetProcAddress来解析和填充 IAT,而是自己实现一套加载逻辑。 -
甚至可以直接从已加载的 kernel32.dll、ntdll.dll 等模块的导出表中手动解析函数地址,完全避开被杀软重点监控的 API 调用路径。
PE 手动加载绕过监控的思路
典型的 PE 加载器(Loader)会执行以下步骤:
1. 打开并映射目标 PE 文件 (如 PlantsVsZombies.exe 或某个 DLL)到内存。
2. 解析 PE 结构:找到 DOS 头、NT 头、节表、导入目录等。
3. 在当前进程内存中分配空间 (通常使用 VirtualAlloc + PAGE_EXECUTE_READWRITE)。
4. 拷贝文件头和所有节到分配的内存中。
5. 手动解析导入表(Import Directory) ------ 这就是绕过监控的关键点:传统方式(会被监控):
HMODULE hMod = LoadLibrary("kernel32.dll");
FARPROC pFunc = GetProcAddress(hMod, "VirtualProtect");
手动加载绕过方式:
-
遍历目标 PE 的导入表(IMAGE_IMPORT_DESCRIPTOR)
-
对于每一个需要导入的 DLL:
-
不调用 LoadLibrary,而是检查当前进程中是否已经加载了该 DLL(通过已知的模块列表或手动遍历 PEB 的 InMemoryOrderModuleList)。
-
如果已加载,直接获取模块句柄(HMODULE)。
-
不调用 GetProcAddress,而是自己解析该 DLL 的导出表(Export Directory),通过名称或序号找到目标函数的地址。
-
-
将解析到的函数地址直接填充到目标 PE 的 IAT(Import Address Table)中
-
处理重定位表(如果需要支持 ASLR)。
-
修改内存保护属性,跳转到目标 PE 的入口点运行。
为什么这种方式可有效绕过监控
-
杀软的行为监控主要 hook 的是 公开的、高频使用的 API(LoadLibrary、GetProcAddress、VirtualProtect 等)。
-
当我们手动解析导出表时,我们直接操作内存中的 PE 结构(IMAGE_EXPORT_DIRECTORY),通过遍历 Export Name Pointer Table、Ordinal Table 等结构来获取函数地址。
-
这个过程没有走标准的 API 调用路径,很多杀软的 hook 点无法覆盖到这种底层手动解析行为。
-
整个加载过程看起来更像是"自己映射一段数据到内存并执行",而不是"加载 DLL 并动态获取函数"。
这也就是常说的 PE 隐藏加载 或 无 Import 加载(Import-less Loading)的核心思想。
手动映射的局限性
仍然需要少量敏感 API
- 手动加载器本身通常仍需要
VirtualAlloc、VirtualProtect、CreateFileMapping等基础 API。这些调用依然可能被监控,但数量远少于完整加载过程,且可以进一步通过 API Hashing 、直接 syscall 等方式继续隐藏。
兼容性问题
- 需要正确处理重定位、延迟导入(Delay Import)、TLS 回调、异常处理等复杂情况,否则目标程序容易崩溃。
反检测与对抗升级
- 高级杀软已开始监控内存中的异常 PE 结构、异常的内存执行流、IAT 填充行为等。单纯的 PE 手动加载已不是万能方案,需要结合更多技术(如 syscall、间接调用、代码混淆等)使用。
代码复杂度较高
- 手动实现 PE 加载器需要深入理解 PE 格式,对编程能力要求较高。
总结
- PE 手动加载技术的核心原理是:
- 自己完整模拟 Windows 操作系统的 PE 加载流程 ,尤其是手动解析导入表和导出表
- 从而避免直接调用
LoadLibrary和GetProcAddress等被杀软重点监控的敏感 API。
- 通过这种方式
- 我们把"加载 DLL 并获取函数地址"这个容易被检测的行为
- 变成了"自己解析内存中的 PE 结构并填充地址"的低调操作
- 显著降低了被行为监控触发的概率。
这也是许多高级 Loader、内存执行框架(如 Reflective DLL Injection 的变种)采用的核心技术之一。
我会实现什么版本
编写的版本架构设计
- 在手动实现 PE Loader 时,选择 C 还是汇编并非非此即彼的问题。
- 最佳策略是 "C 为主、汇编为辅",充分发挥两者优势:C 语言负责构建清晰、可维护的复杂逻辑框架,汇编语言则专注于底层控制、性能关键路径和反检测能力。
- 其实这里就涉及到内联汇编或者是混合编程的一个设计的过程
一、C 语言
C 语言适合作为 Loader 的主体语言
- 复杂数据结构处理 PE 文件格式包含 DOS 头、NT 头、可选头、节表、导入表、导出表、重定位表等复杂结构。使用 C 的 struct 和 typedef 可以清晰、直观地映射这些格式,提升代码可读性和维护性。
- 核心加载流程实现 Loader 的主要步骤(如文件读取、内存分配、节区映射、权限设置、IAT 修复、重定位处理、TLS 执行等)逻辑复杂。C 语言的结构化控制流(if/else、for、while)和函数封装使流程清晰,便于调试和扩展。
- Windows API 调用 方便调用 VirtualAlloc、LoadLibrary、GetProcAddress 等 API,代码简洁。
二、汇编语言 ------ 提供底层控制与规避能力
汇编语言用于解决 C 语言难以实现或容易被检测的关键环节:
- 直接系统调用 (Direct Syscall) 核心应用之一。绕过 EDR/XDR 对 NtAllocateVirtualMemory、NtProtectVirtualMemory 等 API 的 Inline Hook 监控。
- C 的局限:难以直接发起系统调用。
- 汇编优势:手动设置寄存器(RAX/R10 存放 syscall 号,RDX、R8 等存放参数),执行 syscall 指令,直接与内核交互,实现"隐身"操作。
- Shellcode 执行 最直接、最可控的执行方式。使用 call 或 jmp 跳转到 Shellcode 内存地址。 C 语言虽可通过函数指针调用,但在 x64 下稳定性与控制力度不如汇编。
- 反调试与反分析 插入花指令(junk code)、实现反调试技巧(如检测硬件断点、时间检查)、手动栈帧操作等,增加逆向分析难度。
优势:对寄存器和指令的精确控制、极致性能、绕过安全软件监控。
三、推荐的实现架构
一个成熟的 PE Loader 通常采用以下分层设计:
- C 语言主框架
- PE 文件解析
- 内存管理与节区映射
- 加载流程编排
- 错误处理与日志
- 汇编封装函数(内联或单独 .asm 文件)
- DirectSyscall 系列函数(NtAllocateVirtualMemory、NtWriteVirtualMemory 等)
- ExecuteShellcode / CallPayload 函数
- 反调试与花指令模块
- 接口设计 使用 __declspec(naked) 或单独汇编模块,将汇编功能封装成 C 可调用的函数,保持主代码的简洁性。
总结
C 语言提供结构与效率,汇编语言提供深度与隐蔽性。
两者结合才能打造出既稳定可靠、又具备强对抗能力的 Loader PE。
- C 为主 → 保证开发效率和代码质量
- 汇编为辅 → 突破限制,实现关键"隐身"能力
LoadePE静态还是动态免杀的过程
一、LoadPE 的核心定位与免杀本质
LoadPE(手动实现 PE Loader)属于典型的「动态加载 / 运行时注入」技术 ,主要用于动态免杀 ,但可与静态免杀技术深度结合,形成**"静动结合"**的高级免杀链。
1. 静态免杀(Static Evasion)
- 发生在文件落地前(磁盘上的 PE 文件被杀软扫描时)。
- LoadPE 本身作为加载器(Loader),其可执行文件(.exe)需要先通过静态特征免杀。
- 关键技术 :
- 代码混淆、花指令、虚拟机保护(VMProtect)、函数调用序列欺骗
- Shellcode 加密(对称/非对称/异或+凯撒等)
- 移除/混淆 PE 特征(Import Table 打散、字符串加密等)
2. 动态免杀(Dynamic Evasion)------ LoadPE 的主战场
- 发生在运行时(进程内存中执行时)。
- LoadPE 的核心价值在于不依赖系统 LoadLibrary ,而是手动映射 PE 到内存,实现"无文件落地"或"内存加载"。
LoadPE 动态加载完整流程(清晰版):
- 读取 PE 文件(磁盘或资源节中嵌入)
- 解析 PE 结构(DOS头 → NT头 → Optional头 → Section Table)
- 申请内存(VirtualAlloc 或 Direct Syscall NtAllocateVirtualMemory,权限 RWX)
- 映射节区(拷贝各 Section 到对应内存地址)
- 修复重定位表(Relocation Table)------ 处理 ASLR
- 修复导入表(Import Address Table)------ 解析 DLL 和函数地址
- 处理 TLS 回调(线程本地存储)
- 执行入口点(调用 PE 的 AddressOfEntryPoint)
二、高级免杀方案设计(含 LoadPE 组合)
方案一:基础静动结合 LoadPE
- Loader 本身做强静态混淆(花指令 + 函数调用欺骗 + 代码虚拟化)
- Payload(Shellcode 或 PE)做多层加密(异或 → AES → 动态解密)
- 使用 Direct Syscall 完成所有内存操作
- 效果:绕过大部分静态引擎 + 部分行为监控
方案二:LoadPE + 进程伪装 + 模块镂空
- Loader 注入到高信任进程(explorer.exe、svchost.exe 等)
- 使用模块镂空(Hollowing):把合法进程的内存空间清空,再 LoadPE 映射恶意 PE
- 结合 PPID Spoofing + Parent Process Masquerading
- 额外加入 ETW 绕过 + AMS I 绕过
方案三:LoadPE + APC Early Bird + Shellcode(高阶)
- Loader 创建挂起进程(CreateProcess + CREATE_SUSPENDED)
- 使用 Early Bird APC 将 LoadPE 的执行逻辑投递到主线程
- LoadPE 在 APC 中完成内存映射和 PE 执行
- 优点:极早执行,绕过很多用户态监控
方案四:LoadPE + 反射 DLL 注入 + 反调试(生产级)
- 将目标 PE 编译成 Reflective DLL
- LoadPE 实现反射式加载(无需文件,直接在内存中解析并执行)
- 加入:
- 反调试(检测硬件断点、单步、时间差)
- PPL 绕过 / Kernel Callback 绕过
- 动态 Patch EDR 驱动保护函数
总结建议:
- LoadPE 本身是动态免杀的核心技术 ,但必须配合强静态免杀才能落地。
- 真正厉害的 Loader 一定是 C + 汇编混合,Direct Syscall 是标配。