Dll注入技术之APC注入
2016-06-27 15:53
351 查看
APC注入的原理是利用当线程被唤醒时APC中的注册函数会被执行的机制,并以此去执行我们的DLL加载代码,进而完成DLL注入的目的,其具体流程如下: 1)当EXE里某个线程执行到SleepEx()或者WaitForSingleObjectEx()时,系统就会产生一个软中断。 2)当线程再次被唤醒时,此线程会首先执行APC队列中的被注册的函数。 3)利用QueueUserAPC()这个API可以在软中断时向线程的APC队列插入一个函数指针,如果我们插入的是Loadlibrary()执行函数的话,就能达到注入DLL的目的。 1.编写测试文件 新建MFC工程,添加按钮控件,双击写代码如下所示: void CMfcTextApcInjectDlg::OnBnClickedSleepex() { // TODO: 在此添加控件通知处理程序代码 SleepEx(5000,TRUE); }
摘自:windows编程循序渐进
异步过程调用是一种能在特定线程环境中异步执行的系统机制。往线程APC队列添加APC,系统会产生一个软中断。在线程下一次被调度的时候,就会执行APC函数,APC有两种形式,由系统产生的APC称为内核模式APC,由应用程序产生的APC被称为用户模式APC。
每个线程都拥有自己的APC队列。应用程序可以使用函数把APC添加到指定线程的APC队列,函数定义如下:
[cpp] view plain copy
DWORD WINAPI QueueUserAPC(
__in PAPCFUNC pfnAPC,//APC函数地址
__in HANDLE hThread,//目标线程
__in ULONG_PTR dwData//APC函数的参数
);
其中APC函数原型如下:
[cpp] view plain copy
VOID CALLBACK APCProc(
[in] ULONG_PTR dwParam
);
当用户模式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路径名称所在地址作为其参数
代码:
#define _WIN32_WINNT 0x0400 #include <windows.h> #include <TlHelp32.h> #include <iostream> #include <string> using namespace std; #define DEF_BUF_SIZE 1024 // 用于存储注入模块DLL的路径全名 char szDllPath[DEF_BUF_SIZE] = { 0 }; // 使用APC机制向指定ID的进程注入模块 BOOL InjectModuleToProcessById(DWORD dwProcessId) { DWORD dwRet = 0; BOOL bStatus = FALSE; LPVOID lpData = NULL; UINT uLen = strlen(szDllPath) + 1; // 打开目标进程 HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); if (hProcess) { // 分配空间 lpData = VirtualAllocEx(hProcess, NULL, uLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (lpData) { // 写入需要注入的模块路径全名 bStatus = WriteProcessMemory(hProcess, lpData, szDllPath, uLen, &dwRet); } CloseHandle(hProcess); } if (bStatus == FALSE) return FALSE; // 创建线程快照 THREADENTRY32 te32 = { sizeof(THREADENTRY32) }; HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (hThreadSnap == INVALID_HANDLE_VALUE) return FALSE; bStatus = FALSE; // 枚举所有线程 if (Thread32First(hThreadSnap, &te32)) { do{ // 判断是否目标进程中的线程 if (te32.th32OwnerProcessID == dwProcessId) { // 打开线程 HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID); if (hThread) { // 向指定线程添加APC DWORD dwRet = QueueUserAPC((PAPCFUNC)LoadLibraryA, hThread, (ULONG_PTR)lpData); if (dwRet > 0) bStatus = TRUE; CloseHandle(hThread); break; //由于测试程序只有只有主线程,所以直接break掉 } } } while (Thread32Next(hThreadSnap, &te32)); } CloseHandle(hThreadSnap); return bStatus; } int main(void) { CopyMemory(szDllPath, "c:\\test.dll", ARRAYSIZE("c:\\test.dll")); DWORD dwProcessId = 0; while (cout << "请输入目标进程ID:" && cin >> dwProcessId && dwProcessId > 0) { BOOL bRet = InjectModuleToProcessById(dwProcessId); cout << (bRet ? "注入成功!" : "注入失败!") << endl; } return 0; }
相关文章推荐
- intellij git tag
- Win7系统下tomcat7.0配置教程
- java责任链设计模式及在tomca容器中的应用,第一部分
- svn post-commit hook faild(exit code 1) with output /Username not found 错误
- android OkHttp的使用
- LeetCode最小的k个数
- [分类算法] :SVM支持向量机
- 视频网站
- 【翻译+笔记】【高质量Objective-C-2】第1条:了解Objective-C语言的起源-Maoyingyong
- Java8新特性——Lambda
- 回测框架pybacktest简介(一)
- Problem-B
- 隐马尔可夫模型(三)——隐马尔可夫模型的评估问题(前向算法)
- Map遍历
- 深入理解HTTP Session
- 关于闰年和平年的每个月有多少天
- 自由落体和平抛运动
- ASP.NET 实现人民币大写转换函数
- "rpm -qa" C 接口的实现过程
- Linux 搭建Nexus