什么是DLL注入
所谓DLL注入,就是将一个DLL(动态链接库)放入要被注入的进程的内存空间里,让它成为进程的一部分。
关于本教程
本教程是一个入门教程,实践的内容是用DLL注入一个自己写的简单的WinForms窗体程序,在DLL中使用了Detours库,用于修改被注入程序窗口提示函数的内存地址。
本教程使用的IDE为Visual Studio
关于Detours:
Detours是一个由微软开发的可以用于捕获系统API、拦截函数的函数库
需要创建的项目:
- 一个DLL项目
- 一个用于注入DLL的(中转)程序
- 一个用于被注入的靶子程序
开始!GET STARTED
第一步:创建一个DLL项目
在VS中创建一个DLL项目,新建源文件myhook.cpp
第二步:下载并准备好Detours
下载地址: https://github.com/microsoft/detours
解压后,打开x86/x64(根据自己的CPU选择) Native Tools Command Prompt
cd到当前目录后,输入nmake:
附完成后效果图:
目录结构如下:
将include目录下的detours.h与lib.X64目录下的detours.lib复制到项目根目录
右键项目,选择添加-现有项,选中以上两个文件。
第三步:写DLL代码
在myhook.cpp中写入如下代码:
#include "pch.h"#include "detours.h"// 该变量用于引用MessageBox函数static int (WINAPI* RealMessageBox)(HWND, LPCTSTR, LPCTSTR, UINT) = MessageBox;// 该函数是要替换的假MessageBox函数static int WINAPI MyMessageBox(HWND, LPCTSTR, LPCTSTR, UINT) {return RealMessageBox(NULL, L"My Hook successfully cracked!", L"Hey", MB_OK);}void StartHook() {DetourRestoreAfterWith();// 开始Detour事务DetourTransactionBegin();// 更新线程信息(执行事务的线程)DetourUpdateThread(GetCurrentThread());// 将拦截的函数附加到原函数的地址上DetourAttach((PVOID*)&RealMessageBox, MyMessageBox);// 提交事务DetourTransactionCommit();}
(提示C++新手:WINAPI是宏)
再打开dllmain.cpp,编辑代码如下(新增处已表明注释):
#include "pch.h"#include "framework.h"VOID __declspec(dllexport) exportFunction() { // 添加导出函数(内容随便写)OutputDebugString(L"导出函数执行!");}extern void StartHook(); // 添加myhook.cpp中的函数的声明BOOL APIENTRY DllMain( HMODULE hModule, DWORDul_reason_for_call, LPVOID lpReserved ){switch (ul_reason_for_call){case DLL_PROCESS_ATTACH: StartHook(); // 添加启动钩子case DLL_THREAD_ATTACH:case DLL_THREAD_DETACH:case DLL_PROCESS_DETACH:break;}return TRUE;}
完工,Ctrl+B生成。
第四步:创建用于被注入的(靶子)程序 [快速完成]
这里我们创建了一个简单的WinForms(C#)项目
双击打开Form1.cs设计,拖入一个按钮控件(如果找不到工具箱,请在上方选择视图-工具箱):
双击按钮,进入button1_Click()函数,写入:
private void button1_Click(object sender, EventArgs e){MessageBox.Show("正常的窗口文字");}
效果:
好了,上方选择Release,Ctrl+B,环节完毕。
第五步:开始写DLL注入程序(中转站)[远程线程注入]
创建一个普通的C++控制台项目
先上代码:
#include #include using namespace std;int main(){HANDLE ProcessHandle;HANDLE ThreadHandle;LPVOID MemoryPointer;wchar_t dllPath[] = L"D:\\hzyfiles\\C-workspace(VS)\\DLL注入-完整过程\\Dll1\\x64\\Release\\Dll1.dll"; // 这里写自己的路径DWORD pid;cout << "输入程序PID:";cin >> pid;cout << "正在打开进程...";ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, false, pid); // 打开进程if (!ProcessHandle) {cout << "失败!" << endl;return 1;}cout << "成功!" << endl;cout << "正在开辟虚拟内存...";MemoryPointer = VirtualAllocEx(ProcessHandle, NULL, sizeof(dllPath), MEM_COMMIT, PAGE_READWRITE);if (!MemoryPointer) {cout << "失败!" << endl;return 1;}cout << "成功!" << endl;cout << "正在写入内存...";if (!WriteProcessMemory(ProcessHandle, MemoryPointer, (LPVOID)dllPath, sizeof(dllPath), NULL)) {cout << "失败!" << endl;return 1;}cout << "成功!" << endl;PTHREAD_START_ROUTINE LoadLibraryFunctionPointer = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"Kernel32"),"LoadLibraryW");if (!LoadLibraryFunctionPointer) {cout << "Kernel32-LoadLibraryW函数未找到!" << endl;return 1;}cout << "正在创建远程线程...";ThreadHandle = CreateRemoteThread(ProcessHandle, NULL, 0, LoadLibraryFunctionPointer, MemoryPointer, 0, NULL);if (!ThreadHandle) {cout << "失败!" << endl;return 1;}cout << "成功!" << endl;CloseHandle(ProcessHandle);CloseHandle(ThreadHandle);return 0;}
如果你是新手,我们可以一起分析一下这段代码的核心思路:
在靶子程序中执行LoadLibraryW函数,来达到将自己的DLL注入到靶子程序的效果。
核心步骤如下:
- 打开进程,获得进程句柄(OpenProcess)
- 在进程的虚拟内存中开辟一块空间(大小即为我们DLL路径所需要的内存空间)(目的:让靶子程序能够识别这块内存,从而将其带入为LoadLibraryW的参数)(VirtualAllocEx)
- 将DLL的路径写入这块内存(WriteProcessMemory)
- 远程在靶子进程内创建一个线程,在线程内执行LoadLibrary函数(CreateRemoteThread)
打开WinForms程序,记录其PID
附:CMD中的PID获取命令:tasklist | findstr [程序名],记录第二列的数字即可
程序执行后,我们再点击按钮,可以看到:
NICE
总结(仅重点)
- Detours库可以执行修改函数地址的操作
- DLL注入(远程线程注入)的核心只有四步,见上一节