APC(Asynchronous Procedure Call)异步过程调用是一种Windows操作系统的核心机制,它允许在进程上下文中执行用户定义的函数,而无需创建线程或等待OS执行完成。该机制适用于一些频繁的、短暂的或非常细微的操作,例如改变线程优先级或通知线程处理任务。在APC机制中,当某些事件发生时(例如文件IO,网络IO或定时器触发),这些事件将被操作系统添加到一个APC队列中,该队列绑定到执行线程。在下一次发生ALERTABLE的事件时(例如调用SleepEx或SignalObjectAndWait时),OS将弹出APC函数并在执行线程上下文中调用该函数,并在执行完毕后恢复线程执行。

APC机制与DLL注入的关系在于,可以使用APC机制将某些代码注入到另一个进程中,并由该进程执行。读者可以使用NtQueueApcThreadQueueUserAPC等函数将用户定义的函数添加到目标进程中的APC队列中,目标线程将在调用SleepExSignalObjectAndWait等函数时执行注入函数。但读者需要注意,注入函数必须是一个简短的、没有长时间阻塞的代码块,通常会加载DLL或者在目标线程中打开其他进程。

QueueUserAPC 函数允许将一个用户定义的函数添加到指定线程对应的APC队列中。该函数中的APC指异步过程调用,是指通过消息的方式将一段代码投递给线程去执行的一种机制,常用于处理异步操作,该函数的函数原型如下:

DWORD WINAPI QueueUserAPC(
PAPCFUNC pfnAPC,
HANDLE hThread,
ULONG_PTR dwData
);

参数说明如下:

  • pfnAPC: 一个指向线程函数的函数指针,在本函数被执行时调用。
  • hThread: 目标线程的句柄。
  • dwData: 传递给线程函数的参数。

当调用QueueUserAPC函数时,该函数将在目标线程的APC队列中添加一个APC入口,APC的入口点为pfnAPC。当目标线程处于alertable状态时,即调用了如SleepEx等同于处理APCs的等待函数时,系统会将APC从队列中弹出,并调用pfnAPC。在DLL注入中,可以使用QueueUserAPC函数向目标进程内的线程的APC队列中插入一个我们定义的函数的指针,使该函数在目标线程执行时运行,从而实现DLL注入的目的。

APC一部注入原理可以总结为如下几个步骤,每个线程在可被唤醒时在其APC链中的函数将有机会执行被执行,每一个线程都具有一个APC链,那么只要在APC链中添加一个APC,就可以完成我们所需要的DLL注入的功能;

  • 1.WriteProcessMemory 将需要加载的DLL的完整路径写入目标进程空间
  • 2.获得LoadLibraryA函数的地址,当然也可以是LoadLibraryW函数的地址
  • 3.枚举目标进程中的所有线程,为每个线程添加一个APC函数,这样增加了注入成功的机会.

利用当线程被唤醒时APC中的注册函数会被执行的机制,并以此去执行我们的DLL加载代码,进而完成DLL注入的目的,通过APC注入的流程步骤大致如下;

  • 1.当进程里某个线程执行到SleepEx()或者WaitForSingleObjectEx()时,系统就会产生一个软中断
  • 2.当线程再次被唤醒时,此线程会首先执行APC队列中的被注册的函数
  • 3.利用QueueUserAPC()可以在软中断时向线程的APC队列插入一个函数指针,此处插入Loadlibrary()
  • 4.当插入函数被执行时则会加载Loadlibrary并将其指向的DLL模块插入到进程内

但读者需要注意一点,不论如何目标程序必须有执行SleepEx()或者WaitForSingleObjectEx()否则DLL不会加载,读者可自行做实验测试是否可以注入成功;

#include <windows.h>
#include <iostream>
#include <TlHelp32.h>
#include <tchar.h> // 传入进程名称返回该进程PID
DWORD FindProcessID(LPCTSTR szProcessName)
{
DWORD dwPID = 0xFFFFFFFF;
HANDLE hSnapShot = INVALID_HANDLE_VALUE;
PROCESSENTRY32 pe;
pe.dwSize = sizeof(PROCESSENTRY32);
hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, NULL);
Process32First(hSnapShot, &pe);
do
{
if (!_tcsicmp(szProcessName, (LPCTSTR)pe.szExeFile))
{
dwPID = pe.th32ProcessID;
break;
}
} while (Process32Next(hSnapShot, &pe));
CloseHandle(hSnapShot);
return dwPID;
} // APC注入
BOOL ApcInjectDll(DWORD dwPid, char* szDllName)
{
// 得到文件完整路径长度
int nDllLen = lstrlen(szDllName) + sizeof(char); // 打开目标进程
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (hProcess == NULL)
{
return FALSE;
} // 在对端开辟内存空间
PVOID pDllAddr = VirtualAllocEx(hProcess, NULL, nDllLen, MEM_COMMIT, PAGE_READWRITE);
if (pDllAddr == NULL)
{
CloseHandle(hProcess);
return FALSE;
} // 根据不同位数写出DLL文件路径
#ifdef _WIN64
size_t dwWriteNum = 0;
WriteProcessMemory(hProcess, pDllAddr, szDllName, nDllLen, &dwWriteNum);
#else
DWORD dwWriteNum = 0;
WriteProcessMemory(hProcess, pDllAddr, szDllName, nDllLen, &dwWriteNum);
#endif
CloseHandle(hProcess); THREADENTRY32 te = { 0 };
te.dwSize = sizeof(THREADENTRY32); // 得到线程快照
HANDLE handleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (INVALID_HANDLE_VALUE == handleSnap)
{
CloseHandle(hProcess);
return FALSE;
} // 得到LoadLibraryA函数的地址
FARPROC pFunAddr = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
DWORD dwRet = 0; // 得到第一个线程
if (Thread32First(handleSnap, &te))
{
do
{
// 进程ID对比是否为传入的进程
if (te.th32OwnerProcessID == dwPid)
{
// 打开线程,得到线程句柄
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID);
if (hThread)
{
// 向线程插入APC队列
dwRet = QueueUserAPC((PAPCFUNC)pFunAddr, hThread, (ULONG_PTR)pDllAddr); // 关闭句柄
CloseHandle(hThread);
}
}
// 循环下一个线程
} while (Thread32Next(handleSnap, &te));
} // 关闭句柄
CloseHandle(handleSnap);
return TRUE;
} int main(int argc, char *argv[])
{
DWORD pid = FindProcessID("lyshark.exe");
std::cout << "进程PID: " << pid << std::endl; bool flag = ApcInjectDll(pid, (char *)"d://hook.dll");
std::cout << "注入状态: " << flag << std::endl; return 0;
}

3.2 DLL注入:远程APC异步注入的更多相关文章

  1. HOOK -- DLL的远程注入技术详解(1)

    DLL的远程注入技术是目前Win32病毒广泛使用的一种技术.使用这种技术的病毒体通常位于一个DLL中,在系统启动的时候,一个EXE程序会将这个DLL加载至某些系统进程(如Explorer.exe)中运 ...

  2. 使用远程线程来注入DLL

    使用远程线程来注入DLL DLL注入技术要求我们目标进程中的一个线程调用LoadLibrary来载入我们想要的DLL (1)用OpenProcess函数打开目标进程(2)用VirtualAllocEx ...

  3. DLL的远程注入技术

    DLL的远程注入技术是目前Win32病毒广泛使用的一种技术.使用这种技术的病毒体通常位于一个DLL中,在系统启动的时候,一个EXE程序会将这个DLL加载至某些系统进程(如Explorer.exe)中运 ...

  4. 《windows核心编程系列》十九谈谈使用远程线程来注入DLL。

    windows内的各个进程有各自的地址空间.它们相互独立互不干扰保证了系统的安全性.但是windows也为调试器或是其他工具设计了一些函数,这些函数可以让一个进程对另一个进程进行操作.虽然他们是为调试 ...

  5. windows:shellcode 代码远程APC注入和加载

    https://www.cnblogs.com/theseventhson/p/13197776.html  上一章介绍了通用的shellcode加载器,这个加载器自己调用virtualAlloc分配 ...

  6. WinAPI【远程注入】三种注入方案【转】

    来源:http://www.cnblogs.com/okwary/archive/2008/12/20/1358788.html 导言: 我 们在Code project(www.codeprojec ...

  7. [Android]在Dagger 2中使用RxJava来进行异步注入(翻译)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客: # 在Dagger 2中使用RxJava来进行异步注入 > 原文: 几星期前我写了一篇关于在Dagger 2中使用*Producers*进行 ...

  8. Dll注入:注册表注入

    在系统中每一个进程加载User32.dll时,会受到DLL_PROCESS_ATTACH通知,当User32.dll对其进行处理时,会取得注册表键值HKEY_LOCAL_MACHINE\Softwar ...

  9. 威胁预警|Solr velocity模板注入远程命令执行已加入watchbog武器库,漏洞修补时间窗口越来越短

    概述 近日,阿里云安全团队监测到挖矿团伙watchbog更新了其使用的武器库,增加了最新Solr Velocity 模板注入远程命令执行漏洞的攻击方式,攻击成功后会下载门罗币挖矿程序进行牟利.建议用户 ...

  10. Dll注入技术之ComRes注入

    DLL注入技术之ComRes注入 ComRes注入的原理是利用Windows 系统中C:\WINDOWS\system32目录下的ComRes.dll这个文件,当待注入EXE如果使用CoCreateI ...

随机推荐

  1. 用ChatGPT 玩转哔哩哔哩

    用ChatGPT 玩转哔哩哔哩 哔哔终结者 BibiGPT 哔哩哔哩 BiliGPT,一款智能AI工具,帮助用户一键总结 哔哩哔哩视频内容,非常省心,软件基于GPT-3.5 AI,如果无法使用,可以使 ...

  2. 深挖 Python 元组 pt.2

    哈喽大家好,我是咸鱼 在<深挖 Python 元组 pt.1>中我们了解 Python 元组的一些概念(索引和切片等),以及如何创建元组,最重要的是我们还介绍了元组的不可变特性 那么今天我 ...

  3. 【库函数】在什么时候使用 string_view 代替 string

    前言 C++17增加了std::string_view,它在很多情况会优于使用std::string . 尤其是用做函数形参的时候,使用std::string_view基本一定优于老式的const s ...

  4. JavaScript 基础 - Day01

    了解变量.数据类型.运算符等基础概念,能够实现数据类型的转换,结合四则运算体会如何编程. 体会现实世界中的事物与计算机的关系 理解什么是数据并知道数据的分类 理解变量存储数据的"容器&quo ...

  5. acwing算法提高课程笔记—数字三角形模型,最长上升子序列模型

    转自自网络,仅作为学习使用 1015摘花生 /*Hello Kitty想摘点花生送给她喜欢的米老鼠. 她来到一片有网格状道路的矩形花生地(如下图),从西北角进去,东南角出来. 地里每个道路的交叉点上都 ...

  6. 【每日一题】31.「土」秘法地震 (二维前缀和 / DP)

    补题链接:Here 题意就是要找每一个 \(k * k\) 的小正方形里至少有一个1的数量 显然我们可以通过二维前缀和处理出(1, 1) 到 (n, m) 的数量 然后通过枚举处理出答案,具体思想是容 ...

  7. 0x03~04 前缀和与差分、二分

    A题:HNOI2003]激光炸弹 按照蓝书上的教程做即可,注意这道题卡空间用int 而不是 long long. int g[5010][5010]; int main() { ios_base::s ...

  8. 编写Java代码时应该避免的6个坑

    通常情况下,我们都希望我们的代码是高效和兼容的,但是实际情况下代码中常常含有一些隐藏的坑,只有等出现异常时我们才会去解决它.本文是一篇比较简短的文章,列出了开发人员在编写 Java 程序时常犯的错误, ...

  9. freeswitch透传带SDP的180

    概述 freeswitch是一款简单好用的VOIP开源软交换平台. freeswitch对于180/183的消息处理有默认的规则,但是在3GPP的标准中,消息流程会更加复杂,场景更多变. 这样就需要我 ...

  10. I/O多路复用与socket

    前言 简单来讲I/O多路复用就是用一个进程来监听多个文件描述符(fd),我们将监听的fd通过系统调用注册到内核中,如果有一个或多个fd可读或可写,内核会通知应用程序来对这些fd做读写操作,select ...