原理:
往线程APC队列添加APC,系统会产生一个软中断。在线程下一次被调度的时候,就会执行APC函数,APC有两种形式,由系统产生的APC称为内核模式APC,由应用程序产生的APC被称为用户模式APC。
每个线程都拥有自己的APC队列。应用程序可以使用函数把APC添加到指定线程的APC队列,函数定义如下:
DWORD WINAPI QueueUserAPC(
__in PAPCFUNC pfnAPC,//APC函数地址
__in HANDLE hThread,
__in ULONG_PTR dwData//APC函数的参数
);
其中APC函数原型如下:
VOID CALLBACK APCProc(
[in] ULONG_PTR dwParam
);
当用户模式APC被添加后,线程并不会直接调用APC函数,只有当线程处于“唤醒”时才会调用,线程调用SleepEx、SignalObjectAndWait、MsgWaitForMultipleObjectsEx、WaitForMultipleObjectsEx、WaitForSingleObjectEx这些函数的时候会进入可唤醒状态,为了增加机会,应向所有线程加入APC
BOOL InjectModuleToProcessById(DWORD dwProcessId, PCHAR pszDllPath, DWORD dwSize)
{
DWORD dwRet = 0 ;
BOOL bStatus = FALSE ;
LPVOID lpData = NULL ;
UINT uLen = strlen(pszDllPath) + 1;
// 打开目标进程
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId) ;
if (hProcess)
{
// 分配空间
lpData = VirtualAllocEx(hProcess, NULL, uLen, MEM_COMMIT, PAGE_READWRITE);
DWORD dwErr = GetLastError();
if (lpData)
{
// 写入需要注入的模块路径全名
bStatus = WriteProcessMemory(hProcess, lpData, pszDllPath, uLen, &dwRet) ;
}
CloseHandle(hProcess) ;
}
if (bStatus == FALSE)
return FALSE ;
// 创建线程快照
THREADENTRY32 te32 = {sizeof(THREADENTRY32)} ;
HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0) ;
if (hThreadSnap == INVALID_HANDLE_VALUE)
return FALSE ;
bStatus = FALSE ;
// 枚举所有线程
if (Thread32First(hThreadSnap, &te32))
{
do{
// 判断是否目标进程中的线程
if (te32.th32OwnerProcessID == dwProcessId)
{
// 打开线程
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID) ;
if (hThread)
{
// 向指定线程添加APC
DWORD dwRet = QueueUserAPC ((PAPCFUNC)LoadLibraryA, hThread, (ULONG_PTR)lpData) ;
if (dwRet > 0)
bStatus = TRUE ;
CloseHandle (hThread) ;
}
}
}while (Thread32Next ( hThreadSnap, &te32 )) ;
}
CloseHandle (hThreadSnap) ;
return bStatus;
}