DLL劫持并使用MinHook

首发于Enaium的个人博客


测试用例

首先我使用CLion写了一个简单的程序,这个程序会加载一个dinput8.dll,然后调用一个函数显示一段文字,然后等待用户按下任意键。这个程序的代码如下:

#include<windows.h>
#include<iostream>


int display(const char *text) {
    std::cout << text << std::endl;
    std::cout << "Press any key to continue..." << std::endl;
    std::cin.ignore();
    return 0;
}

int main() {
    LoadLibrary("dinput8.dll");
    printf("Address: %p\n", display);
    display("Hello World!");
}

我们所做的就是 Hook 这个display函数,然后在这个函数被调用时,将这个文字改为另一个文字。之后将项目进行编译,别忘了在CMakeLists.txt中添加set(CMAKE_EXE_LINKER_FLAGS "-static"),这样我们就可以得到一个静态链接的可执行文件。之后我们就可以开始 Hook 这个函数了。

DLL 劫持

首先我们需要了解一下 DLL 的加载机制,Windows 系统在加载 DLL 时,会按照一定的顺序搜索 DLL 文件,如果找到了就加载,如果没有找到就会报错。这个顺序是怎么样的呢?我们可以通过查看官方文档来了解。简单来说就是首先搜索应用程序目录,然后搜索系统目录,最后搜索环境变量中指定的路径。所以我们可以使用这个机制来劫持 DLL。

包装 DLL

上面我们知道了 DLL 的加载机制,那么我们就可以知道如何让程序加载我们自己的 DLL,加载我们的 DLL 后,我们需要让这个 DLL 也拥有原 DLL 的功能,这里我们需要对原 DLL 进行包装。我们可以使用wrap_dll这个工具来包装 DLL。这是个Python脚本,所以你必须安装了Python,之后这个脚本还需要dumpbin.exeundname.exe这两个工具,这两个工具是 Visual Studio 自带的,所以你需要安装了Visual Studio,并且需要将C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\bin\Hostx64\x64设置到环境变量中。之后你就可以使用这个脚本了。

python .\wrap_dll.py "C:\Windows\System32\dinput8.dll"

运行完成这个条命令后,你会得到一个dinput8项目,这个就是我们包装的 DLL。我们可以先把项目中的empty.hhook_macro.h给删掉,这两个文件对我们没有用。

MinHook

MinHook 是一个轻量级的钩子库,可以用来 Hook 函数。我们可以在minhook下载到这个库。下载完成后我们在 dinput8 这个项目中新建一个文件夹MinHook,然后将 MinHook 的include文件夹和src文件夹复制到这个文件夹中。之后我们在CMakeLists.txt中添加这个库。

ADD_LIBRARY(
    dinput8
    SHARED
    dinput8_asm.asm
    dinput8.cpp
    dinput8.def
    MinHook/include/MinHook.h
    MinHook/src/hde/hde32.c
    MinHook/src/hde/hde32.h
    MinHook/src/hde/hde64.c
    MinHook/src/hde/hde64.h
    MinHook/src/hde/pstdint.h
    MinHook/src/hde/table32.h
    MinHook/src/hde/table64.h
    MinHook/src/buffer.c
    MinHook/src/buffer.h
    MinHook/src/hook.c
    MinHook/src/trampoline.c
    MinHook/src/trampoline.h
)

之后我们就可以开始 Hook 这个函数了。

Hook 函数

首先我们需要再项目中执行cmake .,之后会生成一个Visual Studio的项目,我们使用Visual Studio打开这个项目,这里需要注意一下,需要通过打开解决方案的方式打开这个项目,不要直接打开这个项目文件夹。之后我们打开dinput8.cpp,删掉_hook_setup这个函数,接着我们需要将加载库的地址改为系统的LoadLibrary函数,这样我们就可以加载原 DLL 了。之后我们就可以开始 Hook 这个函数了。

#include <windows.h>
#include <stdio.h>

HINSTANCE mHinst = 0, mHinstDLL = 0;

extern "C" UINT_PTR mProcs[6] = {0};

LPCSTR mImportNames[] = {
  "DirectInput8Create",
  "DllCanUnloadNow",
  "DllGetClassObject",
  "DllRegisterServer",
  "DllUnregisterServer",
  "GetdfDIJoystick",
};

#ifndef _DEBUG
inline void log_info(const char* info) {
}
#else
FILE* debug;
inline void log_info(const char* info) {
  fprintf(debug, "%s\n", info);
  fflush(debug);
}
#endif

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
  mHinst = hinstDLL;
  if (fdwReason == DLL_PROCESS_ATTACH) {
    mHinstDLL = LoadLibrary("C:/Windows/System32/dinput8.dll");
    if (!mHinstDLL) {
      return FALSE;
    }
    for (int i = 0; i < 6; ++i) {
      mProcs[i] = (UINT_PTR)GetProcAddress(mHinstDLL, mImportNames[i]);
    }

#ifdef _DEBUG
    debug = fopen("./debug.log", "a");
#endif
  } else if (fdwReason == DLL_PROCESS_DETACH) {
#ifdef _DEBUG
    fclose(debug);
#endif
    FreeLibrary(mHinstDLL);
  }
  return TRUE;
}

extern "C" void DirectInput8Create_wrapper();
extern "C" void DllCanUnloadNow_wrapper();
extern "C" void DllGetClassObject_wrapper();
extern "C" void DllRegisterServer_wrapper();
extern "C" void DllUnregisterServer_wrapper();
extern "C" void GetdfDIJoystick_wrapper();

接着我们可以在当fdwReason等于DLL_PROCESS_ATTACH时,也就是当 DLL 被加载时,我们进行 Hook。

首先我们需要编写一个原函数的函数指针,这个函数指针的类型需要和原函数的类型一样,这里我们使用typedef来定义这个函数指针。

typedef int (*display_t)(const char*);

之后我们需要定义一个函数指针,这个函数指针用来指向我们 Hook 后的函数。

display_t display = nullptr;

之后我们创建一个Hook函数,这个函数的参数和返回值都需要和原函数一样。

int display_hook(const char *text) {
    return display("Hello MinHook!");
}

最后我们使用MinHookHook这个函数。

首先我们需要初始化MinHook,之后使用MH_CreateHook来创建一个 Hook,传入原函数的地址,Hook 函数的地址,和一个函数指针的指针,之后我们就可以使用MH_EnableHook来启用这个 Hook 了。

MH_Initialize();
MH_CreateHook((LPVOID)0x00007ff62fb51634, &display_hook, reinterpret_cast<LPVOID*>(&display));
MH_EnableHook(nullptr);

这里的0x00007ff62fb51634是我们运行这个测试程序后得到的display函数的地址,你可以通过打印这个函数的地址来得到这个地址。除了这个方法我们还可以使用x64dbg这个工具来得到这个地址。

我们打开x64dbg通过符号切换到这个程序,之后使用字符串搜索Hello World!,之后我们找到call这个指令,然后我们就可以得到这个函数的地址了。

20241103121643
在这里插入图片描述

20241103122021

之后编译我们的项目,然后将生成的 DLL 放到我们的测试程序的目录下,之后我们就可以运行这个程序了。

20241103122428

项目地址: dll-hijack-minhook

根据C:\gkoudai\口袋贵金属.exe软件,在GkoudaiDataCollector.py编写使用DirectX钩子技术(D3D11的hook)来自动获取口袋贵金属.exe进程和窗口句柄为00050412(Intermediate D3D Window)中的的卖价、买价格和交易量的代码实时显示在程序面板上的优化以下方案:口袋贵金属.exe 行情数据采集方案(DirectX Hook 实现) 核心思路:通过注入 DLL 挂钩目标进程的 D3D 渲染管线(优先 D3D9,兼容主流桌面应用),拦截绘制调用后解析显存/绘制数据中的行情字段,实时回显到面板。 一、开发环境 - 语言:Python 3.8+(主面板)+ C++ 17(D3D Hook DLL,Python 无法直接实现底层挂钩) - 依赖: - Python: pywin32 (进程/窗口操作)、 PyQt5 (实时面板)、 ctypes (调用 DLL) - C++:DirectX SDK(D3D11 库)、MinHook(挂钩框架)、Detours(备选挂钩库) 二、核心实现步骤(分模块) 1. Python 主程序(GkoudaiDataCollector.py) 负责进程注入、窗口句柄绑定、数据接收和面板显示: 2. C++ D3D Hook DLL(核心逻辑) 负责注入进程、挂钩 D3D 绘制函数(如  EndScene )、解析行情数据: 三、关键说明 1. D3D 版本适配:优先 D3D9(口袋贵金属这类桌面软件常用),若检测到 D3D10/11,需替换对应接口(如  ID3D11DeviceContext::Present )。 2. 数据解析优化:示例中用正则解析显存文本,实际需用 Spy++ 抓取行情控件的坐标/文本格式,或通过 D3D 绘制对象遍历(更精准)。 3. 注入安全:需关闭目标软件的反作弊(若有),否则注入可能被拦截;建议以管理员权限运行采集程序。 4. 稳定性:Hook 可能导致目标软件崩溃,需添加异常捕获和资源释放逻辑。 四、编译与运行步骤 1. 编译 C++ DLL:用 Visual Studio 配置 DirectX SDK 和 MinHook,编译为 32/64 位 DLL(匹配目标软件架构)。 2. 放置 DLL 与 Python 脚本同目录,运行  GkoudaiDataCollector.py (需管理员权限)。 3. 确保口袋贵金属.exe 已启动,且窗口句柄为  0x00050412 (若句柄变化,需在 Python 中添加窗口查找逻辑)。
最新发布
11-11
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Enaium

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值