APC注入(QueueUserAPC)--Ring3
日期: 2020-05-19 分类: 个人收藏 581次阅读
APC英文全称(Asynchronous Procedure Call),我们一般译为异步过程调用。它是一种Windows的软中断机制,一般分为两种:
- 内核APC:由系统产生的APC。
- 用户APC:由应用层程序产生的APC。
当一个线程从等待状态(线程调用SleepEx、SignalObjectAndWait、WaitForSingleObjectEx、WaitForMultipleObjectsEx等函数是会进入可唤醒状态)中苏醒时,线程会检查有没有APC需要去执行,如果有APC,则去执行这些异步过程调用函数。而我们在Ring3层,可以利用QueueUserAPC函数APC过程添加到目标线程的APC队列中,当线程恢复执行之前,APC会被执行,完成我们的注入。
当我们添加APC后,线程不会立即就调用APC函数,只有当线程被唤醒时,才会调用,所以为了增加代码被唤醒得几率,在程序中向所有线程插入APC。
写这篇文章之际,我也翻看了之前的笔记,回顾了自己做的过程中遇到的问题,有一个可有意思的现象,就是当我向线程添加APC队列时,感觉对操作线程的顺序有要求。
例一:Win7 x86 x64 Taskmgr.exe Explorer.exe在按照以下代码插入各个线程APC队列时,会导致目标进程奔溃。当然测试自己写的Test.exe目标程序,还是可以通过的。
if (Thread32First(SnapshotHandle, &ThreadEntry32))
{
do
{
if (ThreadEntry32.th32OwnerProcessID == ProcessId)
{
HANDLE ThreadHandle = OpenThread(THREAD_SET_CONTEXT, FALSE, ThreadEntry32.th32ThreadID);
if (ThreadHandle)
{
QueueUserAPC((PAPCFUNC)__LoadLibrary, ThreadHandle,
(ULONG_PTR)VirtualAddress);
CloseHandle(ThreadHandle);
}
}
} while (Thread32Next(SnapshotHandle, &ThreadEntry32));
}
例二:Win7 x86 x64 Taskmgr.exe Explorer.exe在按照以下代码插入各个线程APC队列时,注入成功。我试着将线程Id先保存起来,然后倒着插入APC队列。
DWORD v1[15] = { 0 };//为了测试,直接写死的15
int j = 0;
if (Thread32First(SnapshotHandle, &ThreadEntry32))
{
do
{
if (ThreadEntry32.th32OwnerProcessID == ProcessId)
{
v1[j++] = ThreadEntry32.th32ThreadID;
}
} while (Thread32Next(SnapshotHandle, &ThreadEntry32));
}
for (j = 15; j > 0; j--)
{
HANDLE ThreadHandle = OpenThread(THREAD_SET_CONTEXT, FALSE,v1[j]);
if (ThreadHandle)
{
QueueUserAPC((PAPCFUNC)__LoadLibrary, ThreadHandle,
(ULONG_PTR)VirtualAddress);
CloseHandle(ThreadHandle);
}
}
我当初很是奇怪,不知道为什么会发生这种状况,我所看过的书籍也是按照第一种方式注入的,但是我在自己的虚拟机测试并不能成功。最后调试、尝试了很多次之后,我还是妥协了,最后按照这种倒着插APC的方式,进行注入。
最终,我使用vector动态数组来存储线程Id。
最终代码:
#include"QueueUserApc.h"
#include"Helper.h"
#ifdef UNICODE
LPFN_LOADLIBRARYW __LoadLibrary = NULL;
#else
LPFN_LOADLIBRARYA __LoadLibrary = NULL;
#endif
int _tmain()
{
//控制台识别中文
setlocale(LC_ALL, "Chinese-simplified");
TCHAR ProcessImageName[MAX_PATH] = { 0 };//保存进程名字
TCHAR CurrentFullPath[MAX_PATH] = { 0 }; //当前进程的完整路径
TCHAR TargetProcessFullPath[MAX_PATH] = { 0 };//目标进程的完整路径
ULONG_PTR TargetProcessPathLength = MAX_PATH;
ULONG ProcessId = 0;//目标进程Id
HANDLE ProcessHandle = INVALID_HANDLE_VALUE;//进程句柄
LPVOID VirtualAddress = NULL;
SIZE_T ReturnLength = 0;
BOOL IsOk = FALSE;
//注入的启动程序和目标程序的位数
BOOL SourceIsWow64 = FALSE;
BOOL TargetIsWow64 = FALSE;
_tprintf(_T("输入一个进程ImageName\r\n"));
TCHAR RcceiveChar = _gettchar();//接受字符串
int i = 0;//用来偏移ProcessName字符数组
while (RcceiveChar != '\n')
{
ProcessImageName[i++] = RcceiveChar;
RcceiveChar = _gettchar();
//ProcessImageName = 0x000000db28fceed0 "Taskmgr.exe"
}
GetCurrentDirectory(MAX_PATH, CurrentFullPath);//保存当前进程的完整路径
IsWow64Process(GetCurrentProcess(), &SourceIsWow64);//得到当前进程位数
//SourceIsWow64 = 0x00000000
ProcessId = KtGetProcessIdentify(ProcessImageName);//通过进程名得到进程Id
//ProcessId = 0x00003aa0
if (ProcessId == 0)
{
return 0;
}
IsOk = KtGetProcessFullPath(TargetProcessFullPath,
&TargetProcessPathLength, ProcessId, FALSE);
if (IsOk == FALSE)
{
return 0;
}
//判断目标进程位数
KtIsWow64Process(TargetProcessFullPath, &TargetIsWow64);
//TargetIsWow64 = 0x00000000
if (SourceIsWow64 == TRUE && TargetIsWow64 == TRUE)
{
_tcscat_s(CurrentFullPath, _T("\\Dll.dll"));
}
else if (SourceIsWow64 == FALSE && TargetIsWow64 == FALSE)
{
_tcscat_s(CurrentFullPath, _T("\\Dll.dll"));
}
//_tcscat_s(CurrentFullPath, _T("\\Dll.dll"));//Win 7 32位测试用
ProcessHandle = KtOpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);
ULONG BufferLength = 0;
//在目标进程空间中申请内存
BufferLength = (_tcslen(CurrentFullPath) + 1) * sizeof(TCHAR);
//目标进程空间中申请内存
VirtualAddress = VirtualAllocEx(ProcessHandle, NULL, BufferLength, MEM_COMMIT, PAGE_READWRITE);
if (VirtualAddress == NULL)
{
KtCloseHandle(ProcessHandle);
return 0;
}
//目标进程空间中写入数据
if (KtProcessMemoryWriteSafe(ProcessHandle, VirtualAddress, CurrentFullPath, BufferLength, &ReturnLength) == FALSE)
{
VirtualFreeEx(ProcessHandle, VirtualAddress, BufferLength, MEM_RELEASE);
KtCloseHandle(ProcessHandle);
return 0;
}
//获得目标进程下的所有线程
vector<HANDLE> ThreadId{};
if (KtGetThreadIdentify((HANDLE)ProcessId, ThreadId) == FALSE)
{
VirtualFreeEx(ProcessHandle, VirtualAddress, BufferLength, MEM_RELEASE);
KtCloseHandle(ProcessHandle);
return 0;
}
HMODULE Kernel32ModuleBase = NULL;
Kernel32ModuleBase = GetModuleHandle(_T("KERNEL32.DLL"));
//Kernel32ModuleBase = kernel32.dll!0x00007ffe83fa0000 (加载符号以获取其他信息) {unused=0x00905a4d }
if (Kernel32ModuleBase == NULL)
{
VirtualFreeEx(ProcessHandle, VirtualAddress, BufferLength, MEM_RELEASE);
KtCloseHandle(ProcessHandle);
return 0;
}
#ifdef UNICODE
__LoadLibrary = (LPFN_LOADLIBRARYW)GetProcAddress(Kernel32ModuleBase, "LoadLibraryW");
#else
__LoadLibrary = (LPFN_LOADLIBRARYA)GetProcAddress(Kernel32ModuleBase, "LoadLibraryA");
#endif
if (__LoadLibrary == NULL) {
KtCloseHandle(ProcessHandle);
return 0;
}
for (i = ThreadId.size() - 1; i >= 0; i--)
{
HANDLE ThreadHandle = KtOpenThread(THREAD_SET_CONTEXT, FALSE, ThreadId[i]);
if (ThreadHandle)
{
/*
很奇怪,得按照逆序来插入APC队列
如果按照toolhelper32直接枚举出来的ThreadId,依此顺序插入,
会导致目标进程奔溃(测试Win7 x86 x64 Taskmgr.exe和Explorer.exe)
按照逆序插入没什么问题
*/
//向目标进程中的各个线程的APC队列插入执行体
QueueUserAPC((PAPCFUNC)__LoadLibrary,
ThreadHandle,
(ULONG_PTR)VirtualAddress);
CloseHandle(ThreadHandle);
}
}
ThreadId.~vector();//执行析构,释放内存
if (VirtualAddress != NULL)
{
VirtualFreeEx(ProcessHandle, VirtualAddress, BufferLength, MEM_RELEASE);
VirtualAddress = NULL;
}
if (ProcessHandle)
{
KtCloseHandle(ProcessHandle);
ProcessHandle = NULL;
}
return 0;
}
“If you keep on believing,the dreams that you wish will come true.”
除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog
标签:Ring3注入和Hook
精华推荐