Windows中的2种类型的APC: 用户模式 APC执行在用户模式下的线程的上下文中,必须得到线程的允许才能得到执行 同时用户APC还需要目标进程处在Alertable等待状态才能被成功的调度执行 在内核模式中 可以执行KeWaitForSingleObject.....,在用户模式下可以调用SleepEx..... 当用户APC 投递到线程中 调用上面函数 执行条件满足时去执行APC历程 执行完APC历程 继续执行线程 内核模式 分为普通内核APC和特殊内核APC 当APC投递到线程上 特殊的内核APC不需要 得到现车允许即可得到执行 普通内核APC需要特定环境才能得到执行 特殊内核APC甚至可以唤醒 阻塞的线程 APC执行优先级 特殊内核APC > 普通内核APC > 用户APC 每个线程都含有2个APC队列 用户模式APC队列 和 内核模式APC队列 APC注入的原理是利用当线程被唤醒时APC中的注册函数会被执行的机制,并以此去执行我们的DLL加载代码,进而完成DLL注入的目的,其具体流程如下: 1)当EXE里某个线程执行到SleepEx()或者WaitForSingleObjectEx()时,系统就会产生一个软中断。 2)当线程再次被唤醒时,此线程会首先执行APC队列中的被注册的函数。 3)利用QueueUserAPC()这个API可以在软中断时向线程的APC队列插入一个函数指针,如果我们插入的是Loadlibrary()执行函数的话,就能达到注入DLL的目的。 //打开远程进程 handle = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcessId); if (handle) { //在远程进程申请空间 lpData = VirtualAllocEx(handle, NULL, 1024, MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (lpData) { //在远程进程申请空间中写入待注入DLL的路径 bRet = WriteProcessMemory(handle, lpData, (LPVOID)sDllName, 1024,&dwRet); } //关闭句柄 CloseHandle(handle); } 当我们准备好 用于注入DLL的LoadLibrary()函数后,接下来需要使用QueueUserAPC()函数将此函数插入到软中断线程的APC队列中。但是由于 QueueUserAPC()函数的第三个参数是线程ID,因此我们需要根据现有进程ID,并通过遍历对比得到线程ID,具体API如下表所示: CreateToolhelp32Snapshot 创建线程快照 Thread32First 得到第一个线程快照 Thread32Next 循环下一个线程快照 THREADENTRY32 te = {0}; te.dwSize = sizeof(THREADENTRY32); //得到线程快照 HANDLE handleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,0); if (INVALID_HANDLE_VALUE == handleSnap) { return FALSE; } BOOL bStat = FALSE; //得到第一个线程 if (Thread32First(handleSnap,&te)) { do { //进行进程ID对比 if (te.th32OwnerProcessID == dwProcessId) { //得到线程句柄 HANDLE handleThread = OpenThread( THREAD_ALL_ACCESS, FALSE, te.th32ThreadID); if (handleThread) { //向线程插入APC dwRet = QueueUserAPC( (PAPCFUNC)LoadLibrary, handleThread, (ULONG_PTR)lpData); if (dwRet > 0) { bStat = TRUE; } //关闭句柄 CloseHandle(handleThread); } } //循环下一个线程 } while (Thread32Next(handleSnap,&te)); } CloseHandle(handleSnap);