异步过程调用是一种能在特定线程环境中异步执行的系统机制。往线程APC队列添加APC,系统会产生一个软中断。在线程下一次被调度的时候,就会执行APC函数,APC有两种形式,由系统产生的APC称为内核模式APC,由应用程序产生的APC被称为用户模式APC。每个线程都拥有自己的APC队列。应用程序可以使用函数把APC添加到指定线程的APC队列,函数定义如下:
1 | DWORD WINAPI QueueUserAPC( |
2 | _In_ PAPCFUNC pfnAPC, //APC函数地址 |
3 | _In_ HANDLE hThread, //目标线程 |
4 | _In_ ULONG_PTR dwData //APC函数的参数 |
5 | ); |
当用户模式APC被添加后,线程并不会直接调用APC函数,只有当线程处于“可变等待状态”时才会调用。如果希望线程执行APC函数,就要让线程进入可变等待状态。当线程调用SleepEx、SignalObjectAndWait、MsgWaitForMultipleObjectEx、WaitForMultipleObjectsEx或WaitForSingleObjectEx时就会进入可变等待状态。 ReadFileEx、WriteFileEx、和SetWaitableTimer等都是使用APC作为完成例程的回调机制。
原理:使用QueueUserAPC向目标进程的线程添加APC函数,而这个APC函数能够实现模块的加载功能。要使用这种方法的前提是目标进程能够进入可变等待状态,否则即便添加了APC也没有执行的机会
步骤:
1.向目标进程写入待注入的模块名称
2.枚举目标进程所有线程。(由于并不是每个线程都有机会进入可变等待状态,为了增加APC的机会,向目标进程的每个线程都添加APC是个比较保险的做法)
3.增加APC,把LoadLibrary作为APCProc,把第一步中DLL路径名称所在地址作为其参数
1 | int InjectDllWithApc(WCHAR* DllFullPath, ULONG ProcessId) |
2 | { |
3 | HANDLE hTatgetProcessHandle; |
4 | hTatgetProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId); |
5 | if (hTatgetProcessHandle == NULL) |
6 | { |
7 | printf("Failed To Open Process!!\n"); |
8 | return 0; |
9 | } |
10 | ULONG32 ulDllLength = (ULONG32)_tcslen(DllFullPath) + 1; |
11 | //申请内存 |
12 | WCHAR* pRemoteAddress = (WCHAR*)VirtualAllocEx(hTatgetProcessHandle, NULL, ulDllLength * sizeof(WCHAR), |
13 | MEM_COMMIT, PAGE_READWRITE); |
14 | if (pRemoteAddress == NULL) |
15 | { |
16 | printf("Alloc Virtual Address Failed!\n"); |
17 | CloseHandle(hTatgetProcessHandle); |
18 | return 0; |
19 | } |
20 | //DLL写入 |
21 | if (WriteProcessMemory(hTatgetProcessHandle, pRemoteAddress, (LPVOID)DllFullPath, ulDllLength * sizeof(WCHAR), NULL) == FALSE) |
22 | { |
23 | VirtualFreeEx(hTatgetProcessHandle, pRemoteAddress, ulDllLength, MEM_DECOMMIT); |
24 | CloseHandle(hTatgetProcessHandle); |
25 | return 0; |
26 | } |
27 | THREADENTRY32 ThreadEntry32 = { 0 }; |
28 | HANDLE hThreadSnap = INVALID_HANDLE_VALUE; |
29 | ThreadEntry32.dwSize = sizeof(THREADENTRY32); |
30 | HANDLE hThreadHandle; |
31 | BOOL bStatus; |
32 | DWORD dwReturn; |
33 | //创建快照 |
34 | hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); |
35 | if (hThreadSnap == INVALID_HANDLE_VALUE) |
36 | { |
37 | return 0; |
38 | } |
39 | if (!Thread32First(hThreadSnap, &ThreadEntry32)) |
40 | { |
41 | CloseHandle(hThreadSnap); |
42 | return 1; |
43 | } |
44 | do |
45 | { |
46 | //遍历线程 |
47 | if (ThreadEntry32.th32OwnerProcessID == ProcessId) |
48 | { |
49 | printf("TID:%d\n", ThreadEntry32.th32ThreadID); |
50 | hThreadHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, ThreadEntry32.th32ThreadID); |
51 | if (hThreadHandle) |
52 | { |
53 | //向线程插入APC |
54 | dwReturn = QueueUserAPC( |
55 | (PAPCFUNC)LoadLibrary, |
56 | hThreadHandle, |
57 | (ULONG_PTR)pRemoteAddress); |
58 | if (dwReturn > 0) |
59 | { |
60 | bStatus = TRUE; |
61 | } |
62 | //关闭句柄 |
63 | CloseHandle(hThreadHandle); |
64 | } |
65 | } |
66 | } while (Thread32Next(hThreadSnap, &ThreadEntry32)); |
67 | VirtualFreeEx(hTatgetProcessHandle, pRemoteAddress, ulDllLength, MEM_DECOMMIT); |
68 | CloseHandle(hThreadSnap); |
69 | CloseHandle(hTatgetProcessHandle); |
70 | return 0; |
71 | } |