4

【C++】实现D3D9 的 Inline hook - 水风井

 1 year ago
source link: https://www.cnblogs.com/water-wells/p/16619679.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

【C++】实现D3D9 的 Inline hook

一些必备知识点:
    jmp 的硬编码是E9 ,在构造字节的时候,第一个字节为' \xe9'
    在修改内存前,应当先修改内存的属性,防止内存的属性是不可写的状态
    地址偏移的算法:自己函数的地址 - (原来函数的地址 + 字节数),此处替换5个字节,所以是自己函数的地址 - (原来函数的地址 + 5)
大致思路:
  写一个DLL,在DLL里面完成HOOK,随便写一个DLL注入器注入到进程即可
  DLL部分:
#include<process.h>
int __stdcall DllMain(void* _DllHandle, unsigned long _Reason, void*_Reserved)
{
    if (_Reason == DLL_PROCESS_ATTACH)
    {
        _beginthreadex(nullptr, 0, initialize_d3d9, nullptr, 0, nullptr);
    }
    return 1;
}// 注:initialize_d3d9是d3d9部分的函数
  HOOK部分:
    构造一个inline hook类,传入 被修改函数的地址 和 自己函数的地址 ,根据传入的两个参数算出偏移,保存原函数的前五个字节
    一个函数,修改原函数,将构造的字节写入到内存
    一个函数,还原原函数,将保存下来的字节写入内存
    一个函数,修改内存属性 
  HOOK部分完成
  D3D部分:
    初始化D3D9    用上面的函数HOOK掉D3D9的函数
  D3D部分完成
代码部分(HOOK部分):
#pragma once
#include<Windows.h>
constexpr int byte_length = 5; //修改函数的前五个字节,修改为jmp

class inline_hook
{
private:
    using uchar = unsigned char;
    using dword = DWORD;
    uchar m_original_byte[byte_length];
    uchar m_self_byte[byte_length];
    int m_original_address; //原始函数的地址
    int m_self_address;//自己函数的地址
    dword motify_memory_attribute(int address, dword attribute = PAGE_EXECUTE_READWRITE)
    {
        dword old_attributes;
        VirtualProtect(reinterpret_cast<void*>(address), byte_length, attribute, &old_attributes);
        return old_attributes;
    }
public:
    inline_hook(int original_address, int self_address) :m_original_address(original_address), m_self_address(self_address)
    {
        m_self_byte[0] = '\xe9'; //jmp的硬编码
        int offset = self_address - (original_address + byte_length);   //计算偏移
        memcpy(&m_self_byte[1], &offset, byte_length - 1); //构造字节
        dword attribute = motify_memory_attribute(original_address); //修改内存属性
        memcpy(m_original_byte, reinterpret_cast<void*>(original_address), byte_length); //将程序原来的函数代码保存到 m_original_byte 
        motify_memory_attribute(original_address, attribute); // 还原内存属性
    }
    void motify_address() //修改原来的函数
    {
        dword attribute = motify_memory_attribute(m_original_address); //修改内存属性
        //写入自己构造的字节
        memcpy(reinterpret_cast<void*>(m_original_address), m_self_byte, byte_length);// 将构造好的字节,覆盖到原来的函数的地方
        motify_memory_attribute(m_original_address, attribute); //还原内存属性
    }
    void restore_address() //恢复原来的函数
    {
        dword attribute = motify_memory_attribute(m_original_address);
        memcpy(reinterpret_cast<void*>(m_original_address), m_original_byte, byte_length);
        motify_memory_attribute(m_original_address, attribute);
    }
};

代码部分(D3D9部分):

/*需要包含的头文件*/
#include<d3d9.h>
#include<HOOK部分代码>
#pragma comment(lib,"d3d9.lib")
/*一些必要的全局变量*/
IDirect3D9 * g_direct3d9 = nullptr;
IDirect3DDevice9 *g_direct3ddevice9 = nullptr;
D3DPRESENT_PARAMETERS g_present;
HWND g_hwnd = FindWindowA(nullptr, "Counter-Strike: Global Offensive - Direct3D 9");
//HWND g_hwnd = FindWindowA("Valve001",nullptr);

inline_hook*g_Reset_hook = nullptr; //hook Reset 函数
inline_hook*g_EndScene_hook = nullptr;
inline_hook*g_DrawIndexedPrimitive_hook = nullptr;

/*如果需要劫持Reset函数,则写一个函数代替原来的Reset*/

HRESULT __stdcall self_Reset(IDirect3DDevice9 *g_direct3ddevice9, D3DPRESENT_PARAMETERS* pPresentationParameters)
{
printf("join\n"); //自己需要实现的功能写在这里
HRESULT result = D3D_OK;
g_Reset_hook->restore_address();
if (g_direct3ddevice9)
{
ImGui_ImplDX9_InvalidateDeviceObjects();
HRESULT result = g_direct3ddevice9->Reset(pPresentationParameters); //调用原来的Reset函数
ImGui_ImplDX9_CreateDeviceObjects();
}
g_Reset_hook->motify_address();
return result;
}
 

/*DLL注入后执行此函数*/

unsigned int __stdcall initialize_d3d9(void*data)
{
AllocConsole();
freopen("CON", "w", stdout);
g_direct3d9 = Direct3DCreate9(D3D_SDK_VERSION);
check_error(g_direct3d9, "Direct3DCreate9失败");
memset(&g_present, 0, sizeof(g_present));
g_present.Windowed = TRUE;
g_present.SwapEffect = D3DSWAPEFFECT_DISCARD;
g_present.BackBufferFormat = D3DFMT_UNKNOWN;
g_present.EnableAutoDepthStencil = TRUE;
g_present.AutoDepthStencilFormat = D3DFMT_D16;
HRESULT result = g_direct3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hwnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &g_present, &g_direct3ddevice9);
check_error(result == 0, "CreateDevice失败");
int *direct3d9_table = (int*)*(int *)g_direct3d9;
int *direct3ddevice9_table = (int*)*(int *)g_direct3ddevice9;//这里是一个表,储存了direct的函数,通过劫持这些函数,转接到自己的函数,通过速览IDirect3DDevice9的定义可以看到函数
g_Reset_hook = new inline_hook(direct3ddevice9_table[16], (int)self_Reset); //hook掉了reset
g_Reset_hook->motify_address(); //还原了Reset
//direct3ddevice9_table[下标],下标通过速览定义IDirect3DDevice9结构可知,看需要hook掉的函数在结构的第几位,减去一则是下标
return 0;
}

此时以DLL编译,完成后用DLL注入器注入,则完成D3D9的inline hook,需要HOOK其他函数,则仿照Reset_hook 的写法

g_Reset_hook = new inline_hook(direct3ddevice9_table[16], (int)self_Reset); //hook掉了reset
g_Reset_hook->motify_address(); //还原了Reset

 HOOK 掉 DrawIndexedPrimitive举例

先写自己的函数

HRESULT __stdcall self_DrawIndexedPrimitive(IDirect3DDevice9 *g_direct3ddevice9,D3DPRIMITIVETYPE type, INT BaseVertexIndex, UINT MinVertexIndex, UINT NumVertices, UINT startIndex, UINT primCount)
{
    HRESULT result = D3D_OK;
    {
        /*自己的功能*/
    }
    g_DrawIndexedPrimitive_hook->restore_address();

    result = g_direct3ddevice9->DrawIndexedPrimitive(type, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount);

    g_DrawIndexedPrimitive_hook->motify_address();
    
    return result;
}

在 initialize_d3d9 函数中增加

g_DrawIndexedPrimitive_hook = new inline_hook(direct3ddevice9_table[82], (int)self_DrawIndexedPrimitive); //hook掉了DrawIndexedPrimitive
g_DrawIndexedPrimitive_hook->motify_address();

至此,完成对DrawIndexedPrimitive的HOOK

ENDING.............


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK