前言
首先想法是 强制创建一个目标进程的线程,把我自己的恶意DLL加载进去 被注入的DLL拥有目标进程内存的访问权限,所以我们可以通过该
向某个进程注入DLL时的方法主要有以下三种:
• 创建远程线程(CreateRemoteThread() API) • 使用注册表(AppInit_DLLs值) • 消息钩取(SetWindowsHookEx())
小试牛刀
目前尝试第一种: 1. 使用VirtualAllocEx()在目标进程的地址空间中创建一块我们DLL所在路径长度的内存空间。 2. 使用WriteProcessMemory()将DLL路径写入分配的内存。 3. 一旦DLL路径写入内存中,再使用CreateRemoteThread()(或者其他无正式说明的功能),它再调用LoadLibrary函数将DLL注入目标进程中。
代码:
- 使用VirtualAllocEx()在目标进程的地址空间中创建一块我们DLL所在路径长度的内存空间。
1void process(int pid, char* path){ 2//获取进程的句柄 如果成功返回程序句柄,失败返回NULL 3HANDLE processHANDLE = OpenProcess(PROCESS_ALL_ACCESS, false,pid); 4//申请一块内存给DLL路径 5LPVOID pReturnAddress = VirtualAllocEx(processHANDLE, NULL, strlen(path) + 1, MEM_COMMIT, PAGE_READWRITE);
- 然后使用WriteProcessMemory()把我们要载如的DLL路径写入到刚刚创建的DLL内存空间中
1//写入路径到上一行代码申请的内存中去 2WriteProcessMemory(processHANDLE,pReturnAddress,path,strlen(path)+1,NULL);
- 一旦DLL路径写入内存中,再使用CreateRemoteThread(或者其他无正式说明的功能),它再调用LoadLibrary()函数将我们的DLL注入目标进程中。
1//获取函数模块句柄 2HMODULE hmodule = GetModuleHandleA("KERNEL32.dll"); 3//获取DLL载入函数的地址 4LPTHREAD_START_ROUTINE lpStartAddress =(LPTHREAD_START_ROUTINE)GetProcAddress(hmodule,"LoadLibraryA"); 5/* CreateRemoteThread() 6hProcess:要在其中创建线程的进程的句柄 7lpThreadAttributes:指向SECURITY_ATTRIBUTES结构的指针,一般为NULL 8dwStackSize:一般为0 9lpStartAddress:最重要的一个参数,表示远程进程中线程的起始地址 10lpParameter:指向要传递给线程函数的变量的指针。 11dwCreationFlags:控制线程创建的标志。 12*/ 13CreateRemoteThread(processHANDLE,NULL,0,lpStartAddress,pReturnAddress,0,NULL);
第四个参数lpParameter
指向要传递给线程要执行的函数的变量(参数—)路径 也就是dll的路径)的指针。 整体代码如下:
1 /* 本程序是利用Windows API 实现远程线程注入 2021.4.19/20:24 */ 2#include <iostream> 3#include <windows.h> 4 5void process(int pid, char* path){ 6 //获取进程的句柄 如果成功返回程序句柄,失败返回NULL 7 HANDLE processHANDLE = OpenProcess(PROCESS_ALL_ACCESS, false,pid); 8 //申请一块内存给DLL路径 9 LPVOID pReturnAddress = VirtualAllocEx(processHANDLE, NULL, strlen(path) + 1, MEM_COMMIT, PAGE_READWRITE); 10 //写入路径到上一行代码申请的内存中去 11 WriteProcessMemory(processHANDLE,pReturnAddress,path,strlen(path)+1,NULL); 12 //获取函数模块句柄 13 HMODULE hmodule = GetModuleHandleA("KERNEL32.dll"); 14 //获取DLL载入函数的地址 15 LPTHREAD_START_ROUTINE lpStartAddress =(LPTHREAD_START_ROUTINE)GetProcAddress(hmodule,"LoadLibraryA"); 16 17 /* CreateRemoteThread() 18 hProcess:要在其中创建线程的进程的句柄 19 lpThreadAttributes:指向SECURITY_ATTRIBUTES结构的指针,一般为NULL 20 dwStackSize:一般为0 21 lpStartAddress:最重要的一个参数,表示远程进程中线程的起始地址 22 lpParameter:指向要传递给线程函数的变量的指针。 23 dwCreationFlags:控制线程创建的标志。 24 */ 25 CreateRemoteThread(processHANDLE,NULL,0,lpStartAddress,pReturnAddress,0,NULL); 26} 27int main() { 28 29 const char *path ="C:\\Users\\86132\\Desktop\\libDLL.dll"; //这里做了一个强制类型转换 30 process(11620,(char*)path); //第一个参数是被注入进程的PID,第二个则是要注入dll的路径 31 return 0; 32} 33
远程线程注入 优化
在多线程的情况下,有时候我们希望等待某一个线程完成后,再让程序继续调用其他线程 防止程序线程的阻塞
基于上面的注入代码做一个优化 我们利用**GreateRemoteTread()**函数,如果成功会返回线程的句柄
接下里介绍这个:
WaitForSingleObject函数
等待直到指定的对象处于发信号状态或超时间隔过去。
参数: hHandle 对象的句柄,必须具有SYNCHRONIZE访问权限
dwMilliseconds 超时间隔(以毫秒为单位)。 如果指定了非零值,则函数将等待,直到发出信号通知对象或间隔过去为止。 如果dwMilliseconds为零,则如果未用信号通知对象,则函数不会进入等待状态;否则,函数将进入等待状态。它总是立即返回。 如果dwMilliseconds为INFINITE,则仅在发出对象信号时该函数才会返回。也就是有多久等多久
1//创建远程线程 并获取句柄 hThread 2HANDLE hThread = CreateRemoteThread(processHANDLE,NULL,0,lpStartAddress,pReturnAddress,0,NULL); 3//等待线程事件 4WaitForSingleObject(hThread,2000);
这里等待2秒 还有一个小细节,利用closehandle()函数关闭打开的对象句柄。防止内存泄漏
1//关掉句柄,防止内存泄漏 2CloseHandle(hmodule); 3CloseHandle(processHANDLE);
