DLL注入--远程线程注入

  • A+
所属分类:逆向工程

DLL注入是一种将自身代码附加到目标进程中运行的一种技术,通过让目标进程主动加载指定的DLL,执行DllMain函数从而执行预期的代码,主要用于隐藏代码,绕过一些防御措施,控制目标程序的代码逻辑。DLL注入有非常多种方式,本篇演示远程线程注入DLL的方式。

远程线程注入是一种很‘古老’,但使用最为频繁使用的一种方式。通过函数CreateRemoteThread的方式在对方进程中开启一个线程执行代码。

首先我们先制作一个DLL,弹出一个窗口代表注入成功,并且通过事件通知注入者注入已经完毕。DllMain函数在进程、线程,加载、卸载DLL的时候被调用。返回FALSEDLL被卸载,返回TRUEDLL会被驻留在内存中等待FreeLibrary将其释放。

#include <stdio.h>#include <tchar.h>#include <Windows.h>
#define GLOBAL_EVENT _T("Global\Inject")BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: { MessageBox(NULL, _T("注入成功"), _T("注入成功"), MB_ICONINFORMATION); HANDLE hEvt = OpenEvent(EVENT_ALL_ACCESS, FALSE, GLOBAL_EVENT); if(hEvt) SetEvent(hEvt); break; } case DLL_THREAD_ATTACH: break;
case DLL_THREAD_DETACH: break;
case DLL_PROCESS_DETACH: break; } return FALSE; }


注入代码流程:

1. 打开进程句柄

2. 获得LoadLibraryA函数地址

3. 分配一块可读写的内存空间

4. 将所需DLL的路径写入内存

5. 通过远程线程执行LoadLibraryA函数,并且指定参数为DLL路径的内存地址

首先打开进程句柄

OpenProcess函数声明:

WINBASEAPIHANDLEWINAPIOpenProcess(    _In_ DWORD dwDesiredAccess,//访问权限    _In_ BOOL bInheritHandle, //是否继承此句柄至子进程    _In_ DWORD dwProcessId  //进程ID    );

我们需要分配内存、内存写入、创建远程线程,因此需要

PROCESS_CREATE_THREADPROCESS_VM_WRITEPROCESS_VM_OPERATION 权限打开进程

hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_WRITE | PROCESS_VM_OPERATION , FALSE, dwProcessId)

 

随后获得LoadLibraryA的地址

如果与目标进程之间因某种保护导致kernel32.dll的基址不一致时,可使用枚举进程模块地址的方式加上函数的RVA获得对方进程中的LoadLibraryA函数的地址,这里假设kernel32.dll基址一致。

通过LoadLibrary函数加载kernel32.dll库并从导出函数中找到LoadLibraryA的函数地址,这看上去像是套娃,最终是获得LoadLibraryA的地址,那为什么不直接&LoadLibrary的形式直接获取呢。原因是通过后者的方法获得的实际上不是LoadLibrary的地址,而是一个跳转到LoadLibrary函数指令的一个自身的代码地址。当然同样也可以通过运算e9跳转指令来获得跳转的目标地址,也就是LoadLibrary的地址。这里追求简单直接通过GetProcAddress获得:

hKernel = LoadLibrary(_T("kernel32.dll"));LoadLibraryAddress = GetProcAddress(hKernel, "LoadLibraryA");

在对方进程中分配一块内存

VirtualAllocEx函数声明:

LPVOID  //如果成功则是远程内存地址,失败会为NULLWINAPIVirtualAllocEx(    _In_ HANDLE hProcess,    //进程句柄    _In_opt_ LPVOID lpAddress,  //操作的目标地址(使用随机地址分配填NULL)    _In_ SIZE_T dwSize,    //分配大小 必须是内存页大小的整倍数(一般为0x1000)    _In_ DWORD flAllocationType,//操作类型,保留或者提交,这里不保留    _In_ DWORD flProtect    //内存属性,可读可写    );


在目标进程中分配一块0x1000大小可读可写的内存。

Address = VirtualAllocEx(hProcess, NULL, 0x1000, MEM_COMMIT, PAGE_READWRITE);

随后通过WriteProcessMemory向分配的内存中写入DLL全路径。

WriteProcessMemory函数声明:

BOOL  //是否成功WINAPIWriteProcessMemory(    _In_ HANDLE hProcess,    //进程句柄    _In_ LPVOID lpBaseAddress,  //进程内存地址    _In_reads_bytes_(nSize) LPCVOID lpBuffer, //写入的数据基地址    _In_ SIZE_T nSize,      // 数据大小    _Out_opt_ SIZE_T* lpNumberOfBytesWritten    //成功写入数据量    );


DLL_NAME为欲注入的DLL全路径

WriteProcessMemory(hProcess, Address, DLL_NAME, sizeof(DLL_NAME), &dwWriteBytes);

随后通过CreateRemoteThread函数创建远程线程

CreateRemoteThread函数声明:

HANDLE //线程句柄WINAPICreateRemoteThread(    _In_ HANDLE hProcess, //进程句柄    _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, //线程安全属性    _In_ SIZE_T dwStackSize,            //线程栈大小    _In_ LPTHREAD_START_ROUTINE lpStartAddress,    //入口函数地址    _In_opt_ LPVOID lpParameter,          //参数    _In_ DWORD dwCreationFlags,            //创建属性    _Out_opt_ LPDWORD lpThreadId          //返回线程ID    );


创建一个入口点为LoadLibraryA函数并且参数为欲注入DLL的地址。默认线程安全性,默认栈大小。

CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryAddress, Address, 0, &dwThreadId)

随后远程线程被创建,执行DllMain后弹出提示窗口

 

DLL注入--远程线程注入


完整代码:

注入代码:

#include <stdio.h>#include <windows.h>#include <tchar.h>
#define DLL_NAME "X:\xxxx\DllInject\x64\Debug\DllInject.dll" // DLL全路径#define GLOBAL_EVENT _T("Global\Inject")
int main(){ BOOLEAN isSuccess = FALSE; DWORD dwProcessId; HANDLE hProcess = NULL; HMODULE hKernel = NULL; HANDLE hLoadEvt = NULL; PVOID Address = NULL;
printf("输入注入对象进程ID:"); scanf_s("%d", &dwProcessId);
do { hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, dwProcessId); if (!hProcess) { printf("无法打开进程%x,因为%d错误n", dwProcessId, GetLastError()); break; }
hKernel = LoadLibrary(_T("kernel32.dll")); if (!hKernel) { printf("打开kernel32.dll失败!n"); break; } PVOID LoadLibraryAddress = GetProcAddress(hKernel, "LoadLibraryA"); if (!LoadLibraryAddress) { printf("获得函数地址失败!n"); break; }
Address = VirtualAllocEx(hProcess, NULL, 0x1000, MEM_COMMIT, PAGE_READWRITE); if (!Address) { printf("分配远程内存失败!n"); break; }
SIZE_T dwWriteBytes; if (!WriteProcessMemory(hProcess, Address, DLL_NAME, sizeof(DLL_NAME), &dwWriteBytes)) { printf("写内存失败!n"); break; } hLoadEvt = CreateEvent(NULL, FALSE, FALSE, GLOBAL_EVENT); if (!hLoadEvt) { printf("创建事件失败!n"); break; }
DWORD dwThreadId; if (!CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryAddress, Address, 0, &dwThreadId)) { printf("创建远程线程失败!n"); break; } printf("创建成功!n"); if (!WaitForSingleObject(hLoadEvt, INFINITE)); isSuccess = TRUE; } while (FALSE);
if (!isSuccess) printf("错误%dn", GetLastError());
if (hLoadEvt) CloseHandle(hLoadEvt);
if (hProcess) { if (Address) VirtualFreeEx(hProcess, Address, 0, MEM_RELEASE); CloseHandle(hProcess); }
if (hKernel) FreeLibrary(hKernel);
return 0;}

本文始发于微信公众号(锋刃科技):DLL注入--远程线程注入

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: