这篇文章主要介绍“C++ DLL注入怎么实现”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“C++ DLL注入怎么实现”文章能帮助大家解决问题。
先上源码:
#include "Inject_Main.h"#include "resource.h"#include <Windows.h>#include <TlHelp32.h>#include <string>#include <TCHAR.H>using namespace std;/// <summary>/// 通过进程名称获取该进程句柄/// </summary>/// <param name="processName"></param>/// <returns>成功返回 DWORD,失败返回 0</returns>DWORD GetProcessByName(CONST TCHAR* processName) { // 获取到整个系统的进程 HANDLE processALL = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); // 定义一个容器,该容器用来接收,进程信息 PROCESSENTRY32W processInfo = { 0 }; processInfo.dwSize = sizeof(PROCESSENTRY32W); // 根据进程名称,循环判断是否是指定的进程 do { if (_tcscmp(processInfo.szExeFile, processName) == 0) { // 释放进程快照,防止内存泄露 CloseHandle(processALL); // 如果是返回指定进程句柄 return processInfo.th42ProcessID; } // 一个迭代函数 } while (Process32Next(processALL, &processInfo)); // 释放进程快照,防止内存泄露 CloseHandle(processALL); return 0;}/// <summary>/// 获取指定 DLL 的内存地址/// </summary>/// <param name="pid"></param>/// <param name="moduleName"></param>/// <returns></returns>HMODULE GetProcessModuleHandle(DWORD pid, CONST TCHAR* moduleName) { MODULEENTRY32 moduleEntry; HANDLE handle = NULL; handle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); if (!handle) { CloseHandle(handle); return NULL; } ZeroMemory(&moduleEntry, sizeof(MODULEENTRY32)); moduleEntry.dwSize = sizeof(MODULEENTRY32); if (!Module32First(handle, &moduleEntry)) { CloseHandle(handle); return NULL; } do { if (_tcscmp(moduleEntry.szModule, moduleName) == 0) { // 释放进程快照,防止内存泄露 CloseHandle(handle); return moduleEntry.hModule; } } while (Module32Next(handle, &moduleEntry)); CloseHandle(handle); return 0;}/// <summary>/// 把指定DLL注入到指定进程中/// </summary>/// <param name="processName">processName 进程名称</param>/// <param name="dllPath">dllPath dll路径</param>void InjectDll(const wchar_t* processName, const char* dllPath) { // 获取指定进程的句柄 DWORD dword = GetProcessByName(processName); if (dword == 0) { MessageBox(NULL, TEXT("没有找到指定进程"), TEXT("错误"), 0); return; } // 打开指定进程 HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dword); if (hProcess == NULL) { MessageBox(NULL, TEXT("指定进程打开失败"), TEXT("错误"), 0); return; } LPVOID DLLAddress = VirtualAllocEx(hProcess, NULL, strlen(dllPath), MEM_COMMIT, PAGE_READWRITE); if (WriteProcessMemory(hProcess, DLLAddress, dllPath, strlen(dllPath), NULL) == 0) { MessageBox(NULL, TEXT("路径写入失败"), TEXT("错误"), 0); return; } // 获取 Kernel32.dll 这个模块 HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); // 在 Kernel32.dll 模块中找到 LoadLibrary 这个函数的内存地址 LPVOID loadADD = GetProcAddress(k32, "LoadLibraryA"); HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)loadADD, DLLAddress, 0, NULL); // 释放指定的模块 CloseHandle(hThread); CloseHandle(hProcess);}/// <summary>/// 把指定进程中的DLL卸载掉/// </summary>/// <param name="processName"></param>/// <param name="dllPath"></param>void UnInjectDll(const wchar_t* processName) { // 通过进程名称获取该进程句柄 DWORD dword = GetProcessByName(processName); if (dword == 0) { MessageBox(NULL, TEXT("没有找到指定进程"), TEXT("错误"), 0); return; } // 获取指定进程中指定模块的内存地址 HMODULE hmodule = GetProcessModuleHandle(dword, L"WX_Read_Write.dll"); // 打开指定进程 HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dword); if (hProcess == NULL) { MessageBox(NULL, TEXT("指定进程打开失败"), TEXT("错误"), 0); return; } // 获取 Kernel32.dll 这个模块 HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); // 在 Kernel32.dll 模块中找到 LoadLibrary 这个函数的内存地址 LPVOID loadADD = GetProcAddress(k32, "FreeLibrary"); HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)loadADD, (LPVOID)hmodule, 0, NULL); // 释放指定的模块 CloseHandle(hThread); CloseHandle(hProcess);}/// <summary>/// /// </summary>/// <param name="hwndDlg"></param>/// <param name="uMsg"></param>/// <param name="wParam"></param>/// <param name="lParam"></param>/// <returns></returns>INT_PTR CALLBACK dialogProc(_In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam){ wchar_t processName[100] = L"WeChat.exe"; char dllPath[400] = { "C://Users//qiaoas//documents//visual studio 2015//Projects//ConsoleApplication1//Debug//WX_Read_Write.dll" }; switch (uMsg) { case WM_INITDIALOG: break; case WM_CLOSE: EndDialog(hwndDlg, 0); // 关闭窗体 break; case WM_COMMAND: if (wParam == Btn_Inject_DLL) { if (sizeof(processName) == 0) { MessageBox(NULL, TEXT("进程名称不能为空"), TEXT("错误"), 0); } if (sizeof(dllPath) == 0) { MessageBox(NULL, TEXT("DLL路径不能为空"), TEXT("错误"), 0); } InjectDll(processName, dllPath); // 注入DLL } if (wParam == Btn_unInject_DLL) { UnInjectDll(processName); // 卸载DLL } break; default: break; } return FALSE;}/// <summary>/// 初始化/// </summary>/// <param name="hInstance"></param>/// <param name="hPrevInstance"></param>/// <param name="lpCmdLine"></param>/// <param name="nCmdShow"></param>/// <returns></returns>int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow){ DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, &dialogProc); return 0;}
初学C++,代码可能有些地方写的不够好,但是注入卸载是完全没问题的。
注入逻辑解释:
使用
CreateRemoteThread
函数可以为目标进程创建一个新的线程。在一个进程为另一个进程创建的线程就是远程线程。
使用
LoadLibrary
函数把指定的DLL加载到进程中因此就可以在创建远程线程的同时调用
LoadLibrary
函数,把指定的DLL
加载到目标进程中。
为什么创建远程线程的时候调用 LoadLibrary 函数就能把 DLL 注入到目标进程中?
LoadLibrary
函数是 Kernel32.dll 中的一个成员Kernel32.dll
这个DLL是创建进程必须的一个DLL,并且所有进程在内存中指向的 Kernel32.dll 是同一个地址所以只要获取到当前进程中
LoadLibrary
函数的地址就够了
为什么要在目标进程中开辟一块内存,再把DLL路径写入到块内存中?
LoadLibrary
函数需要一个参数,就是DLL的路径把当前进程中的一个地址传到另一个进程中,鬼知道另一个进程获取这个地址中的数据时,读取到的是否是我们想要的。
因此需要把
DLL
的路径直接写入到目标进程中。VirtualAllocEx
函数,在目标进程中开辟一块空间,用来存放DLL路径WriteProcessMemory
函数,把DLL的路径写入进去GetModuleHandle
获取 Kernel32.dll 模块GetProcAddress
获取 LoadLibraryA 函数在内存中的地址CreateRemoteThread
创建远程线程,并调用 LoadLibraryA 函数
LoadLibrary
、LoadLibraryA
、LoadLibraryW
这三者的区别。
LoadLibrary
是一个宏,可以根据字符集的不同,自动决定是使用 LoadLibraryA 还是 LoadLibraryW
LoadLibrary 宏定义的源码:
WINBASEAPI_Ret_maybenull_HMODULEWINAPILoadLibraryA( _In_ LPCSTR lpLibFileName );WINBASEAPI_Ret_maybenull_HMODULEWINAPILoadLibraryW( _In_ LPCWSTR lpLibFileName );#ifdef UNICODE#define LoadLibrary LoadLibraryW#else#define LoadLibrary LoadLibraryA#endif // !UNICODE
卸载逻辑:
使用 CreateRemoteThread 函数创建一个远程线程
调用 FreeLibrary 函数,卸载DLL
FreeLibrary 函数在 Kernel32.dll 模块中,逻辑同上
FreeLibrary 函数需要 DLL 的内存地址
遍历进程快照可以获取到指定模块的内存地址
卸载和注入的思路都是一样的
确认DLL是否注入到目标进程中:
方式一:使用 procexp
方式二:Cheat Engine
确认 Kernel32.dll 中的 FreeLibrary 和 LoadLibraryA 在多个进程中是否指向同一块内存地址:
可以通过CE查看多个进程中 Kernel32.dll 的内存地址是否相同
再通过 Kernel32.dll 中函数的内存地址,确认 FreeLibrary 和 LoadLibraryA 这两个函数
关于“C++ DLL注入怎么实现”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注编程网行业资讯频道,小编每天都会为大家更新不同的知识点。