Loading...

文章背景图

阶段五:核心逆向技术

这篇文章详细介绍了Windows逆向工程中的核心逆向技术,主要包括DLL注入、API Hook和代码注入三大核心技术。 DLL注入部分讲解了四种方法:远程线程注入(使用CreateRemoteThread)、APC注入(利用异步过程调用)、消息Hook注入(通过SetWindowsHookEx)和注册表注入(修改AppInit_DLLs或IFEO)。 API Hook技术介绍了三种主要方式:IAT Hook(修改导入地址表)、Inline Hook(直接修改函数指令)和VTable Hook(修改虚函数表)。 代码注入部分重点讲解了Shellcode注入和位置无关代码的编写要点。文章还提供了三个实战项目建议:进程监控工具、API调用记录器和内存修改器框架,并给出了详细的技术检查点和推荐学习资料。 全文以实际代码示例为基础,深入浅出地讲解了各项技术的实现原理和应用场景,适合具有一定Windows开发基础的学习者。文章预计学习时间为8-10周,是逆向工程学习路径中的重要阶段。

a0yark a0yark
|
2025-12-03
|
1
|
-
|
- min
|

阶段五:核心逆向技术

📅 创建日期: 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 开发]] | [[逆向与驱动开发学习路径(详细版)| 返回主目录]] | [[阶段六:驱动开发]] →

分享文章

未配置分享平台

请在主题设置中启用分享平台

评论