APC注入的原理是利用當線程被喚醒時APC中的注冊函數(shù)會被執(zhí)行的機制线椰,并以此去執(zhí)行我們的DLL加載代碼,進而完成DLL注入的目的纽窟,其具體流程如下:
1)當EXE里某個線程執(zhí)行到SleepEx()或者WaitForSingleObjectEx()時肖油,系統(tǒng)就會產(chǎn)生一個軟中斷(或者是Messagebox彈窗的時候不點OK的時候也能注入)。
2)當線程再次被喚醒時臂港,此線程會首先執(zhí)行APC隊列中的被注冊的函數(shù)森枪。
3)利用QueueUserAPC()這個API可以在軟中斷時向線程的APC隊列插入一個函數(shù)指針,如果我們插入的是Loadlibrary()執(zhí)行函數(shù)的話审孽,就能達到注入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;
//根據(jù)進程名獲取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);//遍歷系統(tǒng)中所有線程
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)
{
// 動態(tài)鏈接庫映射到某個進程的地址空間
case DLL_PROCESS_ATTACH:
MessageBox(NULL, L"in apc ok~", L"tit", MB_OK);
/**
* 當DLL剛被加載時觸發(fā)(LoadLibrary)佑力,此處專門用來做初始化工作式散,
* 如果初始化失敗可以返回 false 這樣DLL就不會被繼續(xù)加載了
**/
break;
// 應用程序創(chuàng)建新的線程
case DLL_THREAD_ATTACH:
break;
// 應用程序某個線程正常終止
case DLL_THREAD_DETACH:
break;
// 動態(tài)鏈接庫將被卸載
case DLL_PROCESS_DETACH:
/**
* 當DLL將要被卸載時觸發(fā)(FreeLibrary),此處專門用來做清理工作
* 如關閉文件,釋放內存空間等
**/
break;
}
return 1;
}
/*
void helloDLL(void)
{
//MessageBox(NULL, TEXT("Hello DLL~"), TEXT("Title"), MB_OK);
}*/