尝试使用 C++ 挂钩 DirectX9 - 不断崩溃

逆向工程 调试 C++ 函数挂钩
2021-06-13 23:55:25

几天前我在 StackOverflow 上发布了一个线程,但很少有人关注,所以我认为这可能是一个更好的地方。

正如标题所说,我正在尝试挂钩/绕道 DirectX9 函数,以便我可以在屏幕上呈现一些信息。

我能够通过在 IDA 中找到偏移地址来绕过我自己的应用程序中的常规函数​​,我相信我至少在合理的程度上了解钩子/绕道是什么以及它是如何工作的。

问题是,我什至不知道如何在 IDA 中找到 EndScene 函数,而且我在创建虚拟设备和获取该函数的 V-Table 指针方面所做的任何尝试都没有奏效。

这是我的代码,请有人指出我在这里做错了什么。我想解决这个问题并了解我做错了什么,这样我就可以避免将来犯同样的错误。

// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
#include <iostream>
#include <Windows.h>
#include <intrin.h>  
#include <tchar.h>
#include <tlhelp32.h>
#include <Psapi.h>
#include <winsock2.h>
#include <vector>
#include <ws2tcpip.h>
#pragma comment( lib, "Ws2_32.lib" )
#include <d3d9.h>
#pragma comment(lib, "d3d9.lib")
#include <d3dx9.h>
#pragma comment(lib, "d3dx9.lib")
#include <detours.h>
#pragma comment(lib, "detours.lib")

using namespace std;

D3DCOLOR RED = D3DCOLOR_ARGB(255, 255, 0, 0);

typedef HRESULT(__stdcall* EndScene)(IDirect3DDevice9*);
static EndScene EndScene_orig = NULL;

HRESULT __stdcall EndScene_hook(IDirect3DDevice9* pDevice)
{
//  D3DRECT rec = { 100,100,200,200 };
//  pDevice->Clear(1, &rec, D3DCLEAR_TARGET, RED, 0, 0);
//  MessageBoxA(0, "In EndScene", "", 0); //    <<<<----- This function is called over and over when not commented.
    return EndScene_orig(pDevice);
}

void WINAPI InitHook()
{

    HWND game_window = FindWindow(NULL, _T("Skinned Mesh"));

    auto d3dpp = D3DPRESENT_PARAMETERS{};
    auto d3d = Direct3DCreate9(D3D_SDK_VERSION);
    if (d3d)
    {
        d3dpp.BackBufferCount = 1;
        d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
        d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
        d3dpp.hDeviceWindow = game_window;
        d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
        d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
        d3dpp.BackBufferFormat = D3DFMT_R5G6B5;
        d3dpp.Windowed = TRUE;
        IDirect3DDevice9* Device{};
        if (SUCCEEDED(d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, game_window, D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &Device)))
        {
//          MessageBoxA(0, "We made it here...", "", 0);

            DWORD** pVTable = *reinterpret_cast<DWORD***>(Device);

            DetourTransactionBegin();
            DetourUpdateThread(GetCurrentThread());

            EndScene_orig = (EndScene)pVTable[42];
            DetourAttach(&(LPVOID&)pVTable[42], EndScene_hook);

            if (DetourTransactionCommit() == NO_ERROR)
                cout << "Detoured successfully" << endl;
        }
    }

}


void SetupConsole()
{
    AllocConsole();
    freopen("CONOUT$", "wb", stdout);
    freopen("CONOUT$", "wb", stderr);
    freopen("CONIN$", "rb", stdin);
    SetConsoleTitle("CSGOHAX");
}


BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        SetupConsole();
        DisableThreadLibraryCalls(hModule);
        CreateThread(0, 0, (LPTHREAD_START_ROUTINE)InitHook, 0, 0, NULL);
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
1个回答

我想通了,在 IDA 中反转二进制文件,找到模块地址和 EndScene 函数以及它的地址,计算偏移量。然后用ollydbg又找到了这个函数,做了个签名,现在可以用签名扫描功能动态找到了。

所以我可以用这个签名来获取函数地址。

DWORD dwEndScene = FindPattern("d3d9.dll",
    "\x6A\x18\xB8\x00\x00\x00\x00\xE8\x00\x00\x00\x00\x8B\x7D\x08\x8B\xDF\x8D\x47\x04\xF7\xDB\x1B\xDB\x23\xD8\x89\x5D\xE0\x33\xF6\x89\x75\xE4\x39\x73\x18\x75\x73",
    "xxx????x????xxxxxxxxxxxxxxxxxxxxxxxxxxx");

然后我就绕道而行

DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
EndScene_orig = (oEndScene)(dwEndScene);
DetourAttach(&(LPVOID&)EndScene_orig, EndScene_hook);
if (DetourTransactionCommit() == NO_ERROR)
    cout << "Detoured successfully" << endl;

这比我以前尝试使用虚拟设备在 V 表中查找函数要容易得多。