APC注入DLL源码分析
获取LoadLibrary地址
GetModuleHandleA 函数 (libloaderapi.h)
官方文档:https://learn.microsoft.com/zh-cn/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandlea?redirectedfrom=MSDN
检索指定模块的模块句柄。模块必须已由调用进程加载。
HMODULE GetModuleHandleA(
[in, optional] LPCSTR lpModuleName
);
参数 | 作用 |
---|---|
[in, optional] lpModuleName | 加载的模块的名称 (.dll 或 .exe 文件) 。如果省略文件扩展名,则会追加默认库扩展名 .dll。文件名字符串可以包含尾随点字符 (.) ,以指示模块名称没有扩展名。字符串不必指定路径。指定路径时,请务必使用反斜杠 () ,而不是使用 /) (正斜杠。 |
GetProcAddress 函数 (libloaderapi.h)
官方文档:https://learn.microsoft.com/zh-cn/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress?redirectedfrom=MSDN
从指定的动态链接库 (DLL) 检索导出函数 (也称为过程) 或变量的地址。
FARPROC GetProcAddress(
[in] HMODULE hModule,
[in] LPCSTR lpProcName
);
参数 | 作用 |
---|---|
[in] hModule | 包含函数或变量的 DLL 模块的句柄。 |
[in] lpProcName | 函数或变量名称,或函数的序号值。 |
APC注入dll
OpenThread 函数 (processthreadapi.h)
官方文档:https://learn.microsoft.com/zh-cn/windows/win32/api/processthreadsapi/nf-processthreadsapi-openthread
打开现有线程对象。
HANDLE OpenThread(
[in] DWORD dwDesiredAccess,
[in] BOOL bInheritHandle,
[in] DWORD dwThreadId
);
参数 | 作用 |
---|---|
[in] dwDesiredAccess | 对线程对象的访问。针对线程的安全描述符检查此访问权限。 |
[in] bInheritHandle | 如果此值为 TRUE,则此进程创建的进程将继承句柄。否则,进程不会继承此句柄。 |
[in] dwThreadId | 要打开的线程的标识符。 |
QueueUserAPC 函数 (processthreadsapi.h)
官方文档:https://learn.microsoft.com/zh-cn/windows/win32/api/processthreadsapi/nf-processthreadsapi-queueuserapc
将用户模式 异步过程调用 (APC) 对象添加到指定线程的 APC 队列。
DWORD QueueUserAPC(
[in] PAPCFUNC pfnAPC,
[in] HANDLE hThread,
[in] ULONG_PTR dwData
);
参数 | 作用 |
---|---|
[in] pfnAPC | 指向应用程序提供的 APC 函数的指针,该函数在指定线程执行可警报等待操作时调用。 |
[in] hThread | 线程的句柄。句柄必须具有 THREAD_SET_CONTEXT 访问权限。 |
[in] dwData | 传递给 pfnAPC 参数指向的 APC 函数的单个值。 |
ResumeThread 函数 (processthreadsapi.h)
官方文档:https://learn.microsoft.com/zh-cn/windows/win32/api/processthreadsapi/nf-processthreadsapi-resumethread
递减线程的挂起计数。当暂停计数减为零时,将恢复线程的执行。
DWORD ResumeThread(
[in] HANDLE hThread
);
参数 | 作用 |
---|---|
[in] hThread | 要重启的线程的句柄。 |
功能实现步骤
1.获取当前程序DEBUG权限
2. 根据pid通过OpenProcess获取进程句柄
3. VirtualAllocEx申请内存区块并通过WriteProcessMemory写入dllPath指向内容
4. 获取loadLibrary地址
5. 线程遍历
6. APC注入DLL
// 1.获取当前程序DEBUG权限
if (!CurrentProcessAdjustToken()) {
_putts(TEXT("Invalid AdjustToken"));
}
DWORD pid = 0;
pid = _wtoi(argv[1]);
if (pid <= 0) {
_putts(TEXT("Invalid pid"));
return 0;
}
// 2. 根据pid通过OpenProcess获取进程句柄
HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, NULL, pid);
// 3. VirtualAllocEx申请内存区块并通过WriteProcessMemory写入dllPath指向内容
const wchar_t* dllPath = argv[2];
_putts(dllPath);
LPVOID mem = NULL;
mem = VirtualAllocEx(process, NULL, wcslen(dllPath) * sizeof(WCHAR), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!mem) {
_putts(TEXT("VirtualAllocEx error"));
return 0;
}
if (!WriteProcessMemory(process, mem, dllPath, wcslen(dllPath) * sizeof(WCHAR), NULL)) {
DisplayErrorMessage((LPTSTR)"WriteProcessMemory error: ", GetLastError());
return 0;
}
// 4. 获取loadLibrary地址
FARPROC pFuncAddr = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");
// 5. 线程遍历
HANDLE hSnap = NULL;
hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (NULL == hSnap) {
DisplayErrorMessage((LPTSTR)"Create Snapshot error: ", GetLastError());
return 0;
}
THREADENTRY32 te = { 0 };
te.dwSize = sizeof(THREADENTRY32);
Thread32First(hSnap, &te);
do {
if (te.th32OwnerProcessID == pid) {
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID);
// 6. APC注入DLL
QueueUserAPC((PAPCFUNC)pFuncAddr, hThread, (ULONG_PTR)mem);
ResumeThread(hThread);
CloseHandle(hThread);
}
} while (Thread32Next(hSnap, &te));
CloseHandle(hSnap);
CloseHandle(process);
原文始发于微信公众号(蟹堡安全团队):APC注入DLL源码分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论