阶段五:核心逆向技术
📅 创建日期: 2025-01-XX
⏱️ 预计学时: 8-10 周
🎯 学习目标: 掌握 DLL 注入、API Hook、代码注入等核心技术
📋 前置要求: [[阶段四:Windows 开发]]
🔗 返回: [[逆向与驱动开发学习路径(详细版)]]
📚 本阶段内容概览
- [[#5.1 DLL注入技术|5.1 DLL注入技术]]
- [[#5.1.1 远程线程注入|远程线程注入]]
- [[#5.1.2 APC注入|APC注入]]
- [[#5.1.3 消息Hook注入|消息Hook注入]]
- [[#5.1.4 注册表注入|注册表注入]]
- [[#5.2 API Hook技术|5.2 API Hook技术]]
- [[#5.2.1 IAT Hook|IAT Hook]]
- [[#5.2.2 Inline Hook|Inline Hook]]
- [[#5.2.3 VTable Hook|VTable Hook]]
- [[#5.3 代码注入|5.3 代码注入]]
- [[#5.4 实战项目|5.4 实战项目]]
5.1 DLL注入技术
5.1.1 远程线程注入
📝 学习笔记
原理: 利用 CreateRemoteThread 在目标进程创建线程,执行 LoadLibrary 加载我们的 DLL。
BOOL InjectDLL(DWORD pid, const wchar_t* dllPath) {
// 1. 打开目标进程
HANDLE hProcess = OpenProcess(
PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION |
PROCESS_VM_WRITE | PROCESS_QUERY_INFORMATION,
FALSE, pid
);
if (!hProcess) return FALSE;
// 2. 在目标进程分配内存存储DLL路径
size_t pathSize = (wcslen(dllPath) + 1) * sizeof(wchar_t);
LPVOID remotePath = VirtualAllocEx(
hProcess, NULL, pathSize,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE
);
// 3. 写入DLL路径
WriteProcessMemory(hProcess, remotePath, dllPath, pathSize, NULL);
// 4. 获取LoadLibraryW地址
HMODULE hKernel32 = GetModuleHandle(L"kernel32.dll");
LPTHREAD_START_ROUTINE loadLibAddr =
(LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, "LoadLibraryW");
// 5. 创建远程线程执行LoadLibrary
HANDLE hThread = CreateRemoteThread(
hProcess, NULL, 0,
loadLibAddr, // 线程函数: LoadLibraryW
remotePath, // 参数: DLL路径
0, NULL
);
// 6. 等待完成并清理
WaitForSingleObject(hThread, INFINITE);
DWORD exitCode;
GetExitCodeThread(hThread, &exitCode); // 返回值是模块句柄
VirtualFreeEx(hProcess, remotePath, 0, MEM_RELEASE);
CloseHandle(hThread);
CloseHandle(hProcess);
return exitCode != 0;
}
DLL 入口点
// 注入的DLL
BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID reserved) {
switch (reason) {
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hModule);
// 初始化代码,可创建新线程执行主要逻辑
CreateThread(NULL, 0, MainThread, hModule, 0, NULL);
break;
case DLL_PROCESS_DETACH:
// 清理代码
break;
}
return TRUE;
}
[!warning] 注意事项
- DllMain中不宜执行复杂操作(有Loader Lock限制)
- 建议在DllMain中创建新线程执行主要逻辑
- 需要管理员权限注入到高权限进程
5.1.2 APC注入
📝 学习笔记
原理: 利用 APC(Asynchronous Procedure Call)机制,在目标线程进入 alertable 状态时执行我们的代码。
BOOL InjectDLL_APC(DWORD pid, DWORD tid, const wchar_t* dllPath) {
HANDLE hProcess = OpenProcess(
PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, pid);
HANDLE hThread = OpenThread(THREAD_SET_CONTEXT, FALSE, tid);
if (!hProcess || !hThread) return FALSE;
// 分配并写入DLL路径
size_t pathSize = (wcslen(dllPath) + 1) * sizeof(wchar_t);
LPVOID remotePath = VirtualAllocEx(hProcess, NULL, pathSize,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
WriteProcessMemory(hProcess, remotePath, dllPath, pathSize, NULL);
// 获取LoadLibraryW地址
PAPCFUNC loadLibAddr = (PAPCFUNC)GetProcAddress(
GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");
// 将APC加入队列
QueueUserAPC(loadLibAddr, hThread, (ULONG_PTR)remotePath);
CloseHandle(hThread);
CloseHandle(hProcess);
return TRUE;
}
[!tip] 适用场景
- 目标进程有处于alertable等待状态的线程
- 常见于有GUI的程序(GetMessage等会进入alertable状态)
5.1.3 消息Hook注入
📝 学习笔记
原理: 利用 SetWindowsHookEx 设置全局钩子,当目标进程处理相关消息时,系统自动加载我们的 DLL。
// Hook DLL
HHOOK g_hHook = NULL;
HMODULE g_hModule = NULL;
// 钩子回调(可以什么都不做)
LRESULT CALLBACK GetMsgProc(int code, WPARAM wParam, LPARAM lParam) {
return CallNextHookEx(g_hHook, code, wParam, lParam);
}
// 导出函数:安装钩子
extern "C" __declspec(dllexport)
BOOL InstallHook(DWORD threadId) {
g_hHook = SetWindowsHookEx(
WH_GETMESSAGE, // 钩子类型
GetMsgProc, // 回调函数
g_hModule, // DLL模块句柄
threadId // 目标线程ID (0=全局)
);
return g_hHook != NULL;
}
// 导出函数:卸载钩子
extern "C" __declspec(dllexport)
BOOL UninstallHook() {
return UnhookWindowsHookEx(g_hHook);
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID) {
if (reason == DLL_PROCESS_ATTACH) {
g_hModule = hModule;
// 这里执行注入后的代码
}
return TRUE;
}
常用钩子类型
| 类型 | 说明 |
|---|---|
| WH_GETMESSAGE | 消息获取 |
| WH_KEYBOARD | 键盘输入 |
| WH_MOUSE | 鼠标输入 |
| WH_CBT | 窗口操作 |
5.1.4 注册表注入
📝 学习笔记
AppInit_DLLs 方法
路径: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows
值:
- AppInit_DLLs: DLL完整路径
- LoadAppInit_DLLs: 1 (启用)
- RequireSignedAppInit_DLLs: 0 (允许未签名)
Image File Execution Options (IFEO)
路径: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\目标程序.exe
值:
- Debugger: 你的程序路径
[!warning] 注意
- 这些方法需要管理员权限修改注册表
- Windows 8+ 对AppInit_DLLs有签名要求
- IFEO主要用于调试器附加,不是传统注入
5.2 API Hook技术
5.2.1 IAT Hook
📝 学习笔记
原理: 修改导入地址表 (IAT) 中的函数指针,将 API 调用重定向到我们的函数。
// 保存原函数
typedef int (WINAPI* MessageBoxW_t)(HWND, LPCWSTR, LPCWSTR, UINT);
MessageBoxW_t OriginalMessageBoxW = NULL;
// Hook函数
int WINAPI HookedMessageBoxW(HWND hWnd, LPCWSTR text, LPCWSTR caption, UINT type) {
// 修改行为
return OriginalMessageBoxW(hWnd, L"Hooked!", caption, type);
}
// IAT Hook实现
BOOL HookIAT(HMODULE hModule, const char* dllName, const char* funcName,
LPVOID hookFunc, LPVOID* originalFunc) {
// 获取DOS头和NT头
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)hModule;
PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)
((BYTE*)hModule + dosHeader->e_lfanew);
// 获取导入表
PIMAGE_IMPORT_DESCRIPTOR importDesc = (PIMAGE_IMPORT_DESCRIPTOR)
((BYTE*)hModule + ntHeaders->OptionalHeader.DataDirectory
[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
// 遍历导入的DLL
while (importDesc->Name) {
char* moduleName = (char*)((BYTE*)hModule + importDesc->Name);
if (_stricmp(moduleName, dllName) == 0) {
// 找到目标DLL,遍历其函数
PIMAGE_THUNK_DATA origThunk = (PIMAGE_THUNK_DATA)
((BYTE*)hModule + importDesc->OriginalFirstThunk);
PIMAGE_THUNK_DATA iatThunk = (PIMAGE_THUNK_DATA)
((BYTE*)hModule + importDesc->FirstThunk);
while (origThunk->u1.AddressOfData) {
PIMAGE_IMPORT_BY_NAME importByName = (PIMAGE_IMPORT_BY_NAME)
((BYTE*)hModule + origThunk->u1.AddressOfData);
if (strcmp((char*)importByName->Name, funcName) == 0) {
// 找到目标函数
DWORD oldProtect;
VirtualProtect(&iatThunk->u1.Function, sizeof(LPVOID),
PAGE_READWRITE, &oldProtect);
*originalFunc = (LPVOID)iatThunk->u1.Function;
iatThunk->u1.Function = (ULONG_PTR)hookFunc;
VirtualProtect(&iatThunk->u1.Function, sizeof(LPVOID),
oldProtect, &oldProtect);
return TRUE;
}
origThunk++;
iatThunk++;
}
}
importDesc++;
}
return FALSE;
}
// 使用
HookIAT(GetModuleHandle(NULL), "user32.dll", "MessageBoxW",
HookedMessageBoxW, (LPVOID*)&OriginalMessageBoxW);
[!important] IAT Hook 特点
- 只能Hook目标模块导入的函数
- 不能Hook动态获取的函数(GetProcAddress)
- 实现简单,稳定性好
5.2.2 Inline Hook
📝 学习笔记
原理: 直接修改目标函数开头的指令,跳转到我们的 Hook 函数。
x64 Inline Hook (14 字节)
#pragma pack(push, 1)
struct JmpCode {
BYTE push; // 0x68
DWORD lowAddr; // 低32位地址
DWORD movRsp; // 0x042444C7
DWORD highAddr; // 高32位地址
BYTE ret; // 0xC3
};
#pragma pack(pop)
class InlineHook {
LPVOID targetFunc;
LPVOID hookFunc;
BYTE originalBytes[14];
BYTE* trampoline;
public:
BOOL Install(LPVOID target, LPVOID hook, LPVOID* original) {
targetFunc = target;
hookFunc = hook;
// 保存原始字节
memcpy(originalBytes, target, 14);
// 创建跳板(执行原始指令后跳回)
trampoline = (BYTE*)VirtualAlloc(NULL, 32,
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(trampoline, originalBytes, 14);
// 跳板末尾添加跳转回原函数+14的位置
JmpCode* jmpBack = (JmpCode*)(trampoline + 14);
ULONG_PTR returnAddr = (ULONG_PTR)target + 14;
jmpBack->push = 0x68;
jmpBack->lowAddr = (DWORD)returnAddr;
jmpBack->movRsp = 0x042444C7;
jmpBack->highAddr = (DWORD)(returnAddr >> 32);
jmpBack->ret = 0xC3;
*original = trampoline;
// 写入跳转到Hook函数
DWORD oldProtect;
VirtualProtect(target, 14, PAGE_EXECUTE_READWRITE, &oldProtect);
JmpCode* jmpHook = (JmpCode*)target;
jmpHook->push = 0x68;
jmpHook->lowAddr = (DWORD)(ULONG_PTR)hook;
jmpHook->movRsp = 0x042444C7;
jmpHook->highAddr = (DWORD)((ULONG_PTR)hook >> 32);
jmpHook->ret = 0xC3;
VirtualProtect(target, 14, oldProtect, &oldProtect);
FlushInstructionCache(GetCurrentProcess(), target, 14);
return TRUE;
}
BOOL Uninstall() {
DWORD oldProtect;
VirtualProtect(targetFunc, 14, PAGE_EXECUTE_READWRITE, &oldProtect);
memcpy(targetFunc, originalBytes, 14);
VirtualProtect(targetFunc, 14, oldProtect, &oldProtect);
FlushInstructionCache(GetCurrentProcess(), targetFunc, 14);
VirtualFree(trampoline, 0, MEM_RELEASE);
return TRUE;
}
};
使用示例
typedef int (WINAPI* MessageBoxW_t)(HWND, LPCWSTR, LPCWSTR, UINT);
MessageBoxW_t TrueMessageBoxW = NULL;
int WINAPI HookedMessageBoxW(HWND hWnd, LPCWSTR text, LPCWSTR caption, UINT type) {
// 调用原函数(通过跳板)
return TrueMessageBoxW(hWnd, L"Hooked!", caption, type);
}
// 安装Hook
InlineHook hook;
hook.Install(GetProcAddress(GetModuleHandle(L"user32.dll"), "MessageBoxW"),
HookedMessageBoxW, (LPVOID*)&TrueMessageBoxW);
[!tip] Inline Hook 库推荐
- MinHook: 轻量级,支持x86/x64
- Detours: 微软官方库,功能强大
- mhook: 简单易用
5.2.3 VTable Hook
📝 学习笔记
原理: 修改 C++ 对象的虚表指针或虚表中的函数指针。
// 目标类
class IInterface {
public:
virtual void Method1() = 0;
virtual void Method2() = 0;
};
// 原始方法类型
typedef void (__thiscall* Method1_t)(void* thisPtr);
Method1_t OriginalMethod1 = NULL;
// Hook函数
void __fastcall HookedMethod1(void* thisPtr) {
printf("Method1 called! this=%p\n", thisPtr);
OriginalMethod1(thisPtr); // 调用原方法
}
// VTable Hook
void HookVTable(void* object, int index, void* hookFunc, void** original) {
// 获取虚表指针
void** vtable = *(void***)object;
// 保存原函数
*original = vtable[index];
// 修改虚表项
DWORD oldProtect;
VirtualProtect(&vtable[index], sizeof(void*), PAGE_READWRITE, &oldProtect);
vtable[index] = hookFunc;
VirtualProtect(&vtable[index], sizeof(void*), oldProtect, &oldProtect);
}
// 使用
IInterface* obj = GetSomeObject();
HookVTable(obj, 0, HookedMethod1, (void**)&OriginalMethod1);
[!important] VTable Hook 特点
- 适合Hook COM接口或C++虚函数
- 只影响使用该虚表的对象
- 可以复制虚表实现更安全的Hook
5.3 代码注入
📝 学习笔记
Shellcode 注入
// 简单shellcode示例(调用MessageBox)
// 实际中需要自己编写位置无关代码
BYTE shellcode[] = {
// ... 机器码 ...
};
BOOL InjectShellcode(DWORD pid, BYTE* code, SIZE_T size) {
HANDLE hProcess = OpenProcess(
PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD,
FALSE, pid);
// 分配可执行内存
LPVOID remoteCode = VirtualAllocEx(hProcess, NULL, size,
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
// 写入shellcode
WriteProcessMemory(hProcess, remoteCode, code, size, NULL);
// 创建远程线程执行
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)remoteCode, NULL, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
VirtualFreeEx(hProcess, remoteCode, 0, MEM_RELEASE);
CloseHandle(hThread);
CloseHandle(hProcess);
return TRUE;
}
位置无关代码 (PIC) 编写要点
; 获取当前地址
call get_eip
get_eip:
pop rbx ; rbx = 当前地址
; 动态获取API地址
; 1. 通过PEB找kernel32.dll
mov rax, gs:[0x60] ; PEB
mov rax, [rax+0x18] ; Ldr
mov rax, [rax+0x20] ; InMemoryOrderModuleList
mov rax, [rax] ; 第二个模块
mov rax, [rax] ; 第三个模块 (kernel32)
mov rax, [rax+0x20] ; DllBase
; 2. 解析导出表找GetProcAddress
; ... 省略PE解析代码 ...
; 3. 使用GetProcAddress获取其他API
5.4 实战项目
项目一:进程监控工具
// 目标:Hook NtCreateProcess监控进程创建
// 1. 注入到目标进程
// 2. Hook ntdll!NtCreateProcessEx
// 3. 记录创建的进程信息
项目二:API调用记录器
// 目标:记录程序的关键API调用
// 1. Hook常用API(文件、网络、注册表操作)
// 2. 记录参数和返回值
// 3. 输出到日志文件
项目三:内存修改器框架
// 目标:实现基础的游戏修改器框架
// 1. 读写目标进程内存
// 2. 模式搜索定位数据
// 3. 实时修改数值
✅ 阶段检查点
DLL注入检查
- [ ] 实现远程线程注入
- [ ] 理解APC注入原理
- [ ] 了解消息Hook注入机制
- [ ] 能处理注入失败的常见原因
API Hook检查
- [ ] 实现IAT Hook
- [ ] 理解Inline Hook原理
- [ ] 能使用MinHook/Detours库
- [ ] 理解VTable Hook的应用场景
代码注入检查
- [ ] 理解Shellcode概念
- [ ] 了解位置无关代码编写
- [ ] 能注入并执行自定义代码
📖 学习资料
| 资源类型 | 名称 | 说明 |
|---|---|---|
| 📕 书籍 | Windows核心编程 | 进程/线程/内存基础 |
| 📕 书籍 | 逆向工程核心原理 | Hook技术详解 |
| 🔧 工具 | MinHook | 轻量级Hook库 |
| 🔧 工具 | Detours | 微软Hook库 |
| 🌐 网站 | Ring0开发 | 红队技术参考 |
🔗 导航
← [[阶段四:Windows 开发]] | [[逆向与驱动开发学习路径(详细版)| 返回主目录]] | [[阶段六:驱动开发]] →