模块伪装与调用栈欺骗"

admin 2026-10-18 00:46:52 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

模块伪装与调用栈欺骗

原创

pandazhengzheng
pandazhengzheng

安全分析与研究

2026年8月29日 22:00
广东

在小说阅读器读本章

去阅读

在公众号小说中沉浸阅读

目录

  1. 问题背景:内存扫描与调用栈回溯
  2. Windows 内存管理基础
  3. Module Stomping 原理
  4. Module Stomping 实现详解
  5. Module Stomping 的变体与进阶
  6. Module Stomping 的检测与规避
  7. 调用栈回溯的内部机制
  8. Call Stack Spoofing 原理
  9. Call Stack Spoofing 实现详解
  10. Call Stack Spoofing 的检测与规避
  11. 两种技术的组合与完整链路
  12. 小结

1. 问题背景:内存扫描与调用栈回溯

1.1 shellcode 落地的特征

无论用 APC、Fiber 还是其他执行载体,shellcode 都需要驻留在一段可执行内存中。这段内存的特征是 EDR 内存扫描的重点:

内存属性组合 含义 EDR 可疑度
MEM_PRIVATE + PAGE_EXECUTE_READWRITE 纯 shellcode,可写可执行 极高
MEM_PRIVATE + PAGE_EXECUTE_READ 纯 shellcode,只读执行 高
MEM_IMAGE + PAGE_EXECUTE_READ + 内容与磁盘一致 合法已加载模块 低
MEM_IMAGE + PAGE_EXECUTE_READ + 内容与磁盘不一致 module stomping/hollowing 中-高
MEM_MAPPED + PAGE_EXECUTE_READ 映射的可执行段(如反射加载的 PE) 中

EDR 的初筛规则:MEM_PRIVATE + PAGE_EXECUTE* 几乎必然被标记并扫描。因此,免杀的首要目标是让 shellcode 不驻留在 Private+Execute 内存。

1.2 调用栈的特征

当 shellcode 调用 ntdll 中的系统服务(即便用 Indirect Syscalls,最近一层返回地址合法,更深层仍可能暴露)时,EDR 回溯调用栈会发现:

  • 返回地址落在 MEM_PRIVATE(shellcode 直接调用);
  • 返回地址落在未在 PEB Ldr 链表登记的模块;
  • 调用链不自然(如 NtAllocateVirtualMemory 的调用方不是 kernel32/ntdll,而是某私有内存)。

Module Stomping 解决\"内存属性\"问题,Call Stack Spoofing 解决\"调用栈\"问题。两者常组合使用。


2. Windows 内存管理基础

2.1 内存区域的三种类型

  • MEM\_PRIVATE:由 VirtualAlloc/VirtualAllocEx 提交的私有内存,不与任何文件关联;
  • MEM\_MAPPED:由 MapViewOfFile/NtMapViewOfSection 映射的 section,可对应文件或 pagefile;
  • MEM\_IMAGE:由映像 loader 映射的 PE 文件(LoadLibrary、NtMapViewOfSection 以 SEC_IMAGE 创建的 section),有对应的磁盘文件(或内存中的 PE)。

关键:MEM_IMAGE 是 loader 的产物,操作系统与 EDR 将其视为\"已加载模块\",享有最低可疑度。

2.2 VAD(Virtual Address Descriptor)

内核通过 VAD 树管理进程的虚拟地址空间。每个 VAD 节点记录一段虚拟地址范围的属性(类型、保护、对应 section/control area)。NtQueryVirtualMemory 返回的 MemoryInformation 即来自 VAD。

关键:VAD 中的 Type 字段(Private/Mapped/Image)由映射方式决定,而非内容。一旦通过 SEC_IMAGE section 映射,VAD 即标记为 MEM_IMAGE,即便后续覆写内容,VAD 类型不变。

2.3 PEB Ldr 链表

进程的 PEB->Ldr 包含三条双向链表:

  • InLoadOrderModuleList:按加载顺序;
  • InMemoryOrderModuleList:按内存地址;
  • InInitializationOrderModuleList:按初始化顺序。

每个节点是一个 LDR_DATA_TABLE_ENTRY,记录模块基址、大小、名称、路径等。EDR 通过遍历这些链表判断\"某地址是否属于已加载模块\"。

关键:MEM_IMAGE 内存不一定在 Ldr 链表中——通过 NtMapViewOfSection 以 SEC_IMAGE 映射但不调用 LdrLoadDll,VAD 是 MEM_IMAGE 但 Ldr 中无登记。EDR 会检查此不一致。


3. Module Stomping 原理

3.1 核心思路

Module Stomping 的目标是:让 shellcode 驻留在一个被操作系统视为合法映像(MEM_IMAGE)的内存区域中,从而通过 EDR 的\"是否为私有可执行内存\"与\"是否为已加载模块\"两项初筛。

具体做法:

  1. 获取一个合法的 MEM_IMAGE 区域:加载一个 DLL(或映射一个 PE section);
  2. 在该区域中找到一段可写(或可改为可写)的可执行空间:通常是 .text 段;
  3. 将 shellcode 覆写进去;
  4. 跳转到该区域执行 shellcode。

此时 shellcode 所在内存的 VAD 类型为 MEM_IMAGE,保护属性为 PAGE_EXECUTE_READ,从 VAD 与保护属性看与合法代码无异。

3.2 为什么有效

  • VAD 类型检查通过:MEM_IMAGE 而非 MEM_PRIVATE;
  • 保护属性检查通过:PAGE_EXECUTE_READ 而非 PAGE_EXECUTE_READWRITE;
  • RIP 落点检查通过:执行时 RIP 落在 MEM_IMAGE 区域;
  • 初级内存扫描通过:EDR 的\"扫描所有 Private+Execute 内存\"不会扫描此区域(它是 Image+Execute)。

3.3 剩余检测面

  • 映像完整性校验:EDR 将内存中的映像与磁盘文件比对,若 .text 被覆写则不一致;
  • Ldr 链表检查:若映射方式未登记到 Ldr,EDR 发现\"VAD 是 Image 但 Ldr 无记录\";
  • 被 stomp 的 DLL 功能破坏:若该 DLL 被其他代码调用,覆写会破坏功能。

4. Module Stomping 实现详解

4.1 选择目标 DLL

选择标准:

  • 磁盘存在且签名合法:能通过 LoadLibrary 正常加载;
  • 低关注度:不是 EDR 重点监控的 DLL(如 ntdll、kernel32);
  • 不会被频繁调用:覆写其 .text 不会破坏进程功能;
  • .text 足够大:能容纳 shellcode。

典型选择:

  • C:\\Windows\\System32\\ 下的冷门 DLL:如 xpsservices.dll、wmvcore.dll、msftedit.dll;
  • 一个攻击者自带但签名合法的 DLL(若有签名能力);
  • 一个已加载但未被使用的 DLL。

4.2 加载目标 DLL

方式 A:LoadLibraryEx

HMODULE hMod = LoadLibraryEx(\"xpsservices.dll\", NULL, DONT_RESOLVE_DLL_REFERENCES);

DONT_RESOLVE_DLL_REFERENCES 加载 DLL 但不调用 DllMain、不解析导入表,减少副作用。DLL 的 .text 被映射为 PAGE_EXECUTE_READ。

方式 B:NtMapViewOfSection(手动映射)

HANDLE hFile = CreateFile(..., \"xpsservices.dll\", ...);
HANDLE hSection;
NtCreateSection(&hSection, SECTION_ALL_ACCESS, NULL, NULL, PAGE_READONLY, SEC_IMAGE, hFile);
PVOID base = NULL;
SIZE_T viewSize = 0;
NtMapViewOfSection(hSection, NtCurrentProcess(), &base, 0, 0, NULL, &viewSize, ViewShare, 0, PAGE_READONLY);

手动映射不经过 loader,不登记到 Ldr 链表。VAD 是 MEM_IMAGE 但 Ldr 无记录。

权衡:

  • LoadLibraryEx 登记 Ldr,但触发映像加载回调(EDR 感知\"加载了冷门 DLL\");
  • NtMapViewOfSection 不触发映像加载回调,但 Ldr 无记录,EDR 可能发现不一致。

4.3 定位 .text 段

解析 PE 头:

PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)base;
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((BYTE*)base + dos->e_lfanew);
PIMAGE_SECTION_HEADER sec = IMAGE_FIRST_SECTION(nt);
for (int i = 0; i < nt->FileHeader.NumberOfSections; i++) {
    if (memcmp(sec[i].Name, \".text\", 5) == 0) {
        textVa = (BYTE*)base + sec[i].VirtualAddress;
        textSize = sec[i].Misc.VirtualSize;
        break;
    }
}

4.4 覆写 shellcode

DWORD oldProt;
VirtualProtect(textVa, shellcodeSize, PAGE_READWRITE, &oldProt);
memcpy(textVa, shellcode, shellcodeSize);
VirtualProtect(textVa, shellcodeSize, PAGE_EXECUTE_READ, &oldProt);

关键:

  • 先 PAGE_READWRITE 写入,再改回 PAGE_EXECUTE_READ,\\避免 PAGE_EXECUTE_READWRITE\\(RWX 是强特征);
  • 但 VirtualProtect 改为可写、再改为可执行,两次都触发 ETW-TI EtwTiLogProtectExecVm;
  • 改回 PAGE_EXECUTE_READ 后,shellcode 所在内存属性为 MEM_IMAGE + PAGE_EXECUTE_READ,与合法代码无异。

4.5 跳转执行

// 通过函数指针跳转
((void(*)())textVa)();

或结合 Fiber / APC,将执行入口设为 textVa。


5. Module Stomping 的变体与进阶

5.1 映射未落盘的 PE(避免完整性校验)

EDR 的映像完整性校验依赖磁盘文件。若映像本就不存在于磁盘(或磁盘文件无法被 EDR 读取),校验无法进行:

  1. 在内存中构造一个完整的 PE(DOS header + NT header + 节区 + 必要的导出表);
  2. NtCreateSection 以 SEC_IMAGE 创建一个 section,基于 pagefile 而非文件;
  3. NtMapViewOfSection 映射到进程;
  4. 此时 VAD 是 MEM_IMAGE,但无磁盘文件可比对,EDR 的完整性校验失败(无法校验)而非命中(不一致)。

注意:SEC_IMAGE 基于 pagefile 的 section 在某些 Windows 版本受限。可改为映射一个临时创建的文件中的 PE,加载后立即删除文件。

5.2 Stomping 已加载的 DLL(Ldr 一致)


模块伪装与调用栈欺骗" 网络安全文章

模块伪装与调用栈欺骗"

本文详述模块伪装与调用栈欺骗两种免杀技术。模块伪装将shellcode写入合法DLL的.text段,使内存属性为mem\\_image,绕过EDR内存扫描。调用栈欺骗伪造调用栈回溯,规避检测。组合使用可有效规避内存扫描与调用栈回溯。建议选择冷门DLL、手动映射避免LDR登记,并注意完整性校验。
网安公司的AI收入,到底有多少了?" 网络安全文章

网安公司的AI收入,到底有多少了?"

2026年半年报显示18家网安上市公司均谈及AI,但仅天融信、安恒、绿盟、永信至诚、启明星辰5家披露可量化收入或订单,且口径不一、不可直接加总;AI收入分为保护大模型的SecurityforAI与赋能产品的AIforSecurity两类,二季度18家公司收入合计仍下降8%,AI尚未成为行业第二增长曲线,建议后续单独跟踪AI收入的定义、确认进度与收入质量。
突发!OpenAI宣布断供Cursor" 网络安全文章

突发!OpenAI宣布断供Cursor"

OpenAI宣布终止与Cursor的合作,原因是Cursor被SpaceX收购后,OpenAI基于马斯克旗下公司违反合同的历史,无法信任其合规使用模型。此举也与OpenAI即将推出的Astra模型有关,该模型在网络安全方面达到关键级能力,促使OpenAI收紧分发渠道。事件凸显模型访问权正成为商业竞争武器,开发者需注意工具与模型解耦以降低风险。
评论:0   参与:  0