首页 > 解决方案 > 给定该函数的 RVA,如何使用 WinAppDbg 挂钩和调用现有函数?

问题描述

我正在尝试使用 WinAppDbg 为游戏编写插件,其中涉及挂钩和调用该游戏中的现有函数。经过一些逆向工程工作,我得到了一些目标函数或类方法的 RVA 和签名。但很快我发现WinAppDbg的文档只告诉我如何hook现有函数而不是调用它们。此外,通常目标函数的地址来自 Process.resolve_label,但我只有 RVA。如何将函数与它的 RVA 挂钩?谢谢!

------------------ 更新 1------------------------

为了弄清楚我要做什么,请考虑以下情况:

有一个类方法调用bool Player::attack(this, Animal *some_animal)some_game.exe当我攻击任何动物时都会调用它。我想监视攻击事件,并可能随时模拟/触发攻击事件。

在检查了“some_game.pdb”文件后,我发现目标函数的 RVAPlayer::attack0x00678840. 我制作了一个myplugin.dll文件,将在游戏过程中注入。

myplugin.dll然后像这样调用这个函数就很简单了

#include <windows.h>

auto module_addr = reinterpret_cast<unsigned int>(GetModuleHandle(NULL));
attack_func = reinterpret_cast<void(*)(void*, void*)>(0x00678840+module_addr);
attack_func(...)

在Microsoft Detours 库的帮助下,挂钩也很容易,首先定义一个辅助挂钩函数void RegisterStaticHook(RVA sym, void* hook, void** org);

#include <windows.h>
// Detours
#include <detours/detours.h>

typedef uint64_t VA;
typedef unsigned int RVA;

template<typename Type>
using Ptr = Type*;

enum class HookErrorCode {
    ERR_SUCCESS,
    ERR_TRANSACTION_BEGIN,
    ERR_UPDATE_THREAD,
    ERR_ATTACH,
    ERR_DETACH,
    ERR_TRANSACTION_COMMIT
};
template<typename T = Ptr<void>>
auto Hook(Ptr<T> p, T f) {
    int error = DetourTransactionBegin();
    if (error != NO_ERROR) {
        return HookErrorCode::ERR_TRANSACTION_BEGIN;
    }
    error = DetourUpdateThread(GetCurrentThread());
    if (error != NO_ERROR) {
        return HookErrorCode::ERR_UPDATE_THREAD;
    }
    error = DetourAttach(
        reinterpret_cast<Ptr<PVOID>>(p),
        reinterpret_cast<PVOID>(f)
    );
    if (error != NO_ERROR) {
        return HookErrorCode::ERR_ATTACH;
    }
    error = DetourTransactionCommit();
    if (error != NO_ERROR) {
        return HookErrorCode::ERR_TRANSACTION_COMMIT;
    }
    return HookErrorCode::ERR_SUCCESS;
}

void RegisterStaticHook(RVA sym, void* hook, void** org) {
    auto base = reinterpret_cast<VA>(GetModuleHandle(NULL));
    *org = reinterpret_cast<void*>(base + sym);
    auto ret = Hook<void*>(org, hook);
    if (ret != HookErrorCode::ERR_SUCCESS) {
        std::cout << "[Error] ";
        switch (ret) {
        case HookErrorCode::ERR_TRANSACTION_BEGIN:
            std::cout << "DetourTransactionBegin";
            break;
        case HookErrorCode::ERR_UPDATE_THREAD:
            std::cout << "DetourUpdateThread";
            break;
        case HookErrorCode::ERR_ATTACH:
            std::cout << "DetourAttach";
            break;
        case HookErrorCode::ERR_DETACH:
            std::cout << "DetourDetach";
            break;
        case HookErrorCode::ERR_TRANSACTION_COMMIT:
            std::cout << "DetourTransactionCommit";
            break;
        default:
            break;
        }
        std::cout << "failed!" << std::endl;
    }
}

my_attack然后像这样使用“RegisterStaticHook”挂钩:

void** org;
bool my_attack(void* player_this, void* animal){
...
}

RegisterStaticHook(0x00678840, my_attack, org);

我的问题是如何用 WinAppDbg 做到这一点。

标签: pythondebuggingreverse-engineering

解决方案


TL;DR:WinAppDbg 并不真正支持这一点,而且这样做会有点困难。请改用 Microsoft Detours。

长解释:

WinAppDbg 是一个调试器,这意味着您要修补/挂钩等的目标程序运行在与 Python 脚本不同的 Windows 进程中。它只能以 Win32 API 允许的方式与被调试的进程交互,不幸的是,没有在目标进程中执行函数的机制。

您可以通过一些工作在某种程度上模拟此功能。基本上这意味着修改寄存器和堆栈以执行挂钩函数的开头,确保堆栈中的所有指针实际上都指向进程中的有效内存,并可能分配一些蹦床代码以允许 WinAppDbg 在函数执行后中断执行已经回来了。由于架构差异,这对于 32 位和 64 位都必须重新完成。这基本上就是像 GDB 这样的调试器所做的。

另一方面,Detours 通过将 DLL 直接注入您要定位的进程的内存来工作 - 因此执行代码轻而易举,因为您不必处理奇怪的 Win32 API 调用或跨进程映射内存。它也快了很多。但是你必须用 C 而不是 Python 编写代码。


推荐阅读