在 Windows 中有多种方法实现 DLL 注入,可以使用消息钩子注入 DLL(参考《DLL 注入之 Windows 消息钩子》这篇文章),但是通过消息钩子的方法可控性差,不能准确的注入到指定的进程中。而使用远程线程注入的方法可以实现准确地在指定时刻将 DLL 注入到指定的进程中,其可控性较好。
0x01 注入原理
使用 Windows 远程线程机制,在本地进程中通过 CreateRemoteThread 函数在其他进程中开启并运行一个线程。CreateRemoteThread 函数原型如下:
主要关注三个参数:hProcess、lpStartAddress 和 lpParameter。hProcess 是要执行线程的目标进程句柄;lpStartAddress 是线程函数的起始地址,且该函数必须位于目标进程内;lpParameter 是传递给线程函数的参数。
为了使远程进程加载 DLL,把 LoadLibrary 函数作为 CreateRemoteThread 的线程函数,要加载的 DLL 路径作为线程函数的参数即可。
让远程进程执行 LoadLibrary 函数加载 DLL 文件,需解决两个问题:
1)获得远程进程中 LoadLibrary 函数的地址:Kernel32.dll 是系统基本库,且 Windows 系统中,所有进程加载 Kernel32.dll 模块基址是固定且一致的,所以只需获取本地进程中 LoadLibrary 地址。
2)向远程进程传递需加载 DLL 的路径:通过 Windows API 函数把路径写入远程进程中,使用以下API:OpenProcess、VirtualAllocEx、WriteProcessMemory、VirtualFreeEx。
0x02 注入过程
1. 获取目标进程句柄
使用 OpenProcess 函数打开远程进程的句柄。访问权限 dwDesiredAccess 需要设置为 PROCESS_ALL_ACCESS。
2. 在目标进程分配内存空间
使用 VirtualAllocEx 在目标进程中分配足够的内存空间,用于保存要加载 DLL 的路径。
3. 写入 DLL 路径至目标进程
用 WriteProcessMemory 函数把需加载的 DLL 路径写入到远程进程分配的内存空间。
4. 获取 LoadLibraryW 地址
Windows 系统中,LoadLibraryW 函数位于 kernel32.dll 中,并且系统核心 DLL 会加载到固定地址,所以系统中所有进程的 LoadLibraryW 函数地址是相同的。用 GetProcAddress 函数获取本地进程 LoadLibraryW 地址即可。
5. 在目标进程中运行远程线程
使用 CreateRemoteThread 函数是目标进程调用 LoadLibraryW 函数加载 DLL。
0x03 测试
1. 需注入 DLL 源码
|
|
2. 注入程序
|
|
3. 测试效果
运行 Injectmain.exe 将 DLL 注入到进程 3656(notepad.exe)中,注入成功将弹出消息框。
查看 notepad.exe 进程加载的模块列表,可以看到 InjectDll.dll 已被加载。
References:
[1] 逆向工程核心原理
[2] DLL注入浅析(下)