APC注入的原理是利用当线程被唤醒时APC中的注册函数会被执行的机制,并以此去执行我们的DLL加载代码,进而完成DLL注入的目的,其具体流程如下:
1)当EXE里某个线程执行到SleepEx()或者WaitForSingleObjectEx()时,系统就会产生一个软中断(或者是Messagebox弹窗的时候不点OK的时候也能注入)。
2)当线程再次被唤醒时,此线程会首先执行APC队列中的被注册的函数。
3)利用QueueUserAPC()这个API可以在软中断时向线程的APC队列插入一个函数指针,如果我们插入的是Loadlibrary()执行函数的话,就能达到注入DLL的目的。
程序如下:
// TESTAPC2.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#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 GetPidFormName(wstring wsProcessname)
{
HANDLE hSnaoshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnaoshot == INVALID_HANDLE_VALUE)
{
return false;
}
PROCESSENTRY32W pe = { sizeof(pe) };
BOOL bok;
for (bok = Process32FirstW(hSnaoshot, &pe); bok; bok = Process32NextW(hSnaoshot,&pe))
{
wstring wsNowProcName = pe.szExeFile;
if (StrStrI(wsNowProcName.c_str(), wsProcessname.c_str()) != NULL)
{
CloseHandle(hSnaoshot);
return pe.th32ProcessID;
}
}
CloseHandle(hSnaoshot);
return 0;
}
//dll 文件注入到进程wsProcessname
BOOL Injection_APC(const wstring &wsProcessname, const WCHAR wcCacheInDllPath[])
{
DWORD dwProcessId = GetPidFormName(wsProcessname);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
if (!hProcess)
{
return FALSE;
}
PVOID lpData = VirtualAllocEx(hProcess, NULL, 1024, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
DWORD dwRet;
if (lpData)
{
//在远程进程申请空间写入待注入dll 的路径
WriteProcessMemory(hProcess, lpData, (LPVOID)wcCacheInDllPath,MAX_PATH, &dwRet);
CloseHandle(hProcess);
}
//开始注入
THREADENTRY32 te = { sizeof(te) };
HANDLE handleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);//遍历系统中所有线程
if (handleSnap == INVALID_HANDLE_VALUE)
{
return false;
}
bool bstat = false;
if (Thread32First(handleSnap, &te))
{
do {
if (te.th32OwnerProcessID == dwProcessId)
{
HANDLE handleThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID);
if (handleThread)
{
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()
{
Injection_APC(L"testapc.exe", L"testapcdll.dll");
return 0;
}
测试exe程序:
#include<windows.h>
int main()
{
MessageBox(NULL, L"start", L"tit", MB_OK);
SleepEx(1000 * 60 * 5, true);
MessageBox(NULL, L"end", L"tit", MB_OK);
Sleep(-1);
}
测试dll 程序:
#include<windows.h>
#include"dll.h"
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved)
{
switch (dwReason)
{
// 动态链接库映射到某个进程的地址空间
case DLL_PROCESS_ATTACH:
MessageBox(NULL, L"in apc ok~", L"tit", MB_OK);
/**
* 当DLL刚被加载时触发(LoadLibrary),此处专门用来做初始化工作,
* 如果初始化失败可以返回 false 这样DLL就不会被继续加载了
**/
break;
// 应用程序创建新的线程
case DLL_THREAD_ATTACH:
break;
// 应用程序某个线程正常终止
case DLL_THREAD_DETACH:
break;
// 动态链接库将被卸载
case DLL_PROCESS_DETACH:
/**
* 当DLL将要被卸载时触发(FreeLibrary),此处专门用来做清理工作
* 如关闭文件,释放内存空间等
**/
break;
}
return 1;
}
/*
void helloDLL(void)
{
//MessageBox(NULL, TEXT("Hello DLL~"), TEXT("Title"), MB_OK);
}*/