[逆向工程]C++實現DLL注入:原理、實現與防御全解析(二十五)
引言
DLL注入(DLL Injection)是Windows系統下實現進程間通信、功能擴展、監控調試的核心技術之一。本文將從原理分析、代碼實現、實戰調試到防御方案,全方位講解如何用C++實現DLL注入,并提供可直接編譯的完整項目代碼。
一、資源準備
1.資源準備
gmp.exe 被注入的程序
injector.exe 注入器
mandaohook.dll 需注入的dll
2.任務目標
將編寫好的mandaohook.dll通過injector.exe注入器注入到gmp.exe可運行程序中。gmp.exe是一個密碼學工具箱。
二、DLL注入的核心原理
DLL注入的本質是強制目標進程加載指定的DLL文件,其核心流程為:
- 獲取目標進程句柄:通過進程ID或進程名定位目標
- 在目標進程中分配內存:用于存儲DLL路徑
- 寫入DLL路徑:將DLL的完整路徑寫入目標內存
- 創建遠程線程:通過
LoadLibrary
加載DLL
三、關鍵API函數解析
API函數 | 作用描述 | 關鍵參數說明 |
---|---|---|
OpenProcess | 打開目標進程 | dwProcessId :目標進程ID |
VirtualAllocEx | 在目標進程分配內存 | lpAddress :分配內存地址 |
WriteProcessMemory | 向目標內存寫入數據 | lpBaseAddress :目標內存地址 |
CreateRemoteThread | 在目標進程創建遠程線程 | lpStartAddress :線程入口點 |
GetProcAddress | 獲取LoadLibrary 函數地址 | lpProcName :函數名 |
四、完整C++實現代碼
1. 注入器代碼(Injector.cpp)
#include <Windows.h>
#include <TlHelp32.h>
#include <iostream>
#include <memory>// 自動釋放資源模板
template<typename T>
struct HandleDeleter {void operator()(T* handle) const {if (handle) CloseHandle(handle);}
};
using UniqueHandle = std::unique_ptr<void, HandleDeleter<void>>;// 查找進程ID(優化版)
DWORD FindProcessId(const wchar_t* processName) {PROCESSENTRY32W pe = { sizeof(pe) };UniqueHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));if (!snapshot.get()) return 0;if (Process32FirstW(snapshot.get(), &pe)) {do {if (_wcsicmp(processName, pe.szExeFile) == 0) {return pe.th32ProcessID;}} while (Process32NextW(snapshot.get(), &pe));}return 0;
}// 注入主函數
int main() {// 1. 查找進程const DWORD pid = FindProcessId(L"gmp.exe");if (!pid) {std::wcerr << L"錯誤:未找到進程!" << std::endl;return EXIT_FAILURE;}// 2. 打開進程UniqueHandle hProcess(OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION |PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ,FALSE, pid));if (!hProcess) {std::wcerr << L"打開進程失敗(代碼:" << GetLastError() << L")" << std::endl;return EXIT_FAILURE;}// 3. 獲取DLL路徑wchar_t dllPath[MAX_PATH];if (!GetFullPathNameW(L"mandaohook.dll", MAX_PATH, dllPath, nullptr)) {std::wcerr << L"獲取路徑失敗(代碼:" << GetLastError() << L")" << std::endl;return EXIT_FAILURE;}// 4. 分配內存const size_t pathSize = (wcslen(dllPath) + 1) * sizeof(wchar_t);UniqueHandle pRemoteMem(VirtualAllocEx(hProcess.get(), nullptr, pathSize, MEM_COMMIT, PAGE_READWRITE));if (!pRemoteMem) {std::wcerr << L"內存分配失敗(代碼:" << GetLastError() << L")" << std::endl;return EXIT_FAILURE;}// 5. 寫入路徑if (!WriteProcessMemory(hProcess.get(), pRemoteMem.get(), dllPath, pathSize, nullptr)) {std::wcerr << L"寫入內存失敗(代碼:" << GetLastError() << L")" << std::endl;return EXIT_FAILURE;}// 6. 獲取LoadLibrary地址const auto pLoadLibrary = reinterpret_cast<LPTHREAD_START_ROUTINE>(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "LoadLibraryW"));if (!pLoadLibrary) {std::wcerr << L"獲取函數地址失敗(代碼:" << GetLastError() << L")" << std::endl;return EXIT_FAILURE;}// 7. 創建遠程線程UniqueHandle hThread(CreateRemoteThread(hProcess.get(), nullptr, 0,pLoadLibrary, pRemoteMem.get(), 0, nullptr));if (!hThread) {std::wcerr << L"創建線程失敗(代碼:" << GetLastError() << L")" << std::endl;return EXIT_FAILURE;}// 8. 等待注入完成WaitForSingleObject(hThread.get(), INFINITE);std::wcout << L"注入成功!" << std::endl;return EXIT_SUCCESS;
}
編譯:
g++ -shared -o mandaohook.dll mandaohook.cpp -luser32 -lgdi32 -Wall
2.目標DLL頭文件(mandaohook.h)
#pragma once
#include <Windows.h>LRESULT CALLBACK HookProc(int code, WPARAM wParam, LPARAM lParam);
3. 目標DLL代碼(mandaohook.cpp)
/*********************** myhook.cpp ***********************/
#include "mandaohook.h"// 全局變量
HHOOK g_hHook = NULL;
HWND g_hNotepadppWnd = NULL;// 前向聲明線程函數
DWORD WINAPI ThreadProc(LPVOID lpParameter);//-----------------------------------------------------------------------------
// DLL入口函數
//-----------------------------------------------------------------------------
BOOL APIENTRY DllMain(HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
{switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:// 創建線程避免阻塞DllMainCreateThread(nullptr, 0, ThreadProc, hModule, 0, nullptr);break;case DLL_PROCESS_DETACH:if (g_hHook) {UnhookWindowsHookEx(g_hHook);g_hHook = nullptr;}break;}return TRUE;
}// 線程處理函數
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{HMODULE hModule = (HMODULE)lpParameter;// 使用 OutputDebugStringW 輸出調試信息OutputDebugStringW(L"DLL成功注入gmp.exe!");MessageBoxW(nullptr, L"DLL成功注入gmp.exe!", L"提示", MB_OK);g_hNotepadppWnd = FindWindowExW(nullptr, nullptr, L"gmp.exe", nullptr);if (g_hNotepadppWnd) {g_hHook = SetWindowsHookExW(WH_KEYBOARD_LL,HookProc,hModule,0);}return 0;
}//-----------------------------------------------------------------------------
// 鉤子處理函數
//-----------------------------------------------------------------------------
LRESULT CALLBACK HookProc(int code, WPARAM wParam, LPARAM lParam)
{if (code == HC_ACTION) {const auto* pKbd = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);if (pKbd->vkCode == VK_F12 && (pKbd->flags & LLKHF_UP)) {MessageBoxW(g_hNotepadppWnd, L"安全提示:F12功能已被攔截!", L"安全防護", MB_ICONWARNING | MB_OK);return 1;}}return CallNextHookEx(g_hHook, code, wParam, lParam);
}
編譯:
g++ -shared -o mandaohook.dll mandaohook.cpp -luser32 -lwininet -Wall -municode
五、測試結果
gmp.exe injector.exe mandaohook.dll放入同一個文件夾下:
1.先打開gmp.exe 工具
2.再打開DebugView
3.執行injector.exe 注入器注入
4.查看注入信息
可以先查看下gmp.exe PID
查看DebugView已注入。
六、技術難點與解決方案
1. 權限問題
- 癥狀:
OpenProcess
返回ERROR_ACCESS_DENIED
- 解決:以管理員權限運行注入器
- 代碼實現:
#include <ShellAPI.h> ShellExecuteW(nullptr, L"runas", L"Injector.exe", nullptr, nullptr, SW_SHOW);
2. 路徑轉換問題
- 癥狀:DLL路徑包含中文字符導致寫入失敗
- 解決:使用寬字符API并驗證路徑
if (!PathFileExistsW(dllPath)) {// 處理路徑不存在的情況 }
3. 64/32位進程兼容
- 癥狀:跨架構注入失敗(如64位注入器操作32位進程)
- 解決:使用
Wow64DisableWow64FsRedirection
PVOID oldValue = nullptr; Wow64DisableWow64FsRedirection(&oldValue); // 執行文件操作 Wow64RevertWow64FsRedirection(oldValue);
七、防御DLL注入方案
- 進程保護:調用
SetProcessMitigationPolicy
PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY policy = {}; policy.MicrosoftSignedOnly = 1; SetProcessMitigationPolicy(ProcessSignaturePolicy, &policy, sizeof(policy));
- 鉤子檢測:定期檢查
LoadLibrary
調用棧 - 內存保護:啟用DEP和ASLR
#pragma comment(linker, "/DYNAMICBASE:YES") #pragma comment(linker, "/NXCOMPAT")
八、實戰調試技巧
- 調試輸出:使用
OutputDebugStringW
OutputDebugStringW(L"[DEBUG] DLL已加載");
- 日志文件:寫入臨時文件監控行為
FILE* f = _wfopen(L"C:\\inject_log.txt", L"a+"); fwprintf(f, L"PID:%d 已注入\n", GetCurrentProcessId()); fclose(f);
- Process Monitor:監控進程的DLL加載事件
如果本教程對您有幫助,請點贊??收藏?關注支持!歡迎在評論區留言交流技術細節!