UE Hook函数
不改引擎源代码的情况下,如何修改一个函数?
如何Hook FMaterialEditor::PasteNodesHere 这个函数?

1.某些函数是导出的,某些函数是未导出的。
2.dll加载到内存后,整个dll的地址会发生变化。
3.函数地址是相对于dll基地址的。
第一个问题:不管函数有没有导出,在dll里面都可以获得函数的地址,秒了。
PasteNodesHere在.text段里面,位置是 00000001800B0860,
但是UE的dll 的 ImageBase都是000180000000,
于是能算出 函数基于ImageBase的偏移值,
0001800B0860 - 000180000000 = B0860,转为十进制是 723040,
PasteNodesHere 的偏移值是 B0860,十进制为723040。
实际的函数地址还需要dll在内存中的首地址 即可计算出PasteNodesHere在内存中的地址,
例如: DLL首地址 + 723040 = PasteNodesHere。
我说的对,但是首地址怎么获取?
GetModuleHandleA 是 Windows API 中的一个函数,用于获取指定模块(如 DLL 或 EXE)的句柄。
句柄可能在底层通过指针实现(如句柄指向内核对象的内存地址),但对外完全隐藏了细节。
示例: 获取UnrealEditor-MaterialEditor.dll的首地址
constexpr const char* MaterialDll = "UnrealEditor-MaterialEditor.dll";
HMODULE MatModule = GetModuleHandleA(MaterialDll);
uintptr_t BaseAddress = reinterpret_cast<uintptr_t>(HModule);
BaseAddress是这个dll的首地址
PasteNodesHere 的偏移值是 B0860,十进制为723040。
实际的函数地址还需要dll在内存中的首地址 即可计算出PasteNodesHere在内存中的地址,
例如: DLL首地址 + 723040 = PasteNodesHere。
所以 PasteNodesHere的实际内存地址是:
FunctionAddress = BaseAddress + 723040 = PasteNodesHere.
增加计算FunctionAddress :
constexpr const char* MaterialDll = "UnrealEditor-MaterialEditor.dll";
HMODULE MatModule = GetModuleHandleA(MaterialDll);
uint64 FunctionOffset = 723040;
uintptr_t BaseAddress = reinterpret_cast<uintptr_t>(HModule);
void* FunctionAddress = reinterpret_cast<void*>(BaseAddress + FunctionOffset);
函数地址已经得到了,然后呢? 如何截胡这个函数?
正常执行流程:
Call PasteNodesHere()---> 原函数执行 ---> 执行完毕
截胡后的流程:
Call PasteNodesHere()---> 执行自定义函数 --->原函数执行 ---> 执行完毕
Call PasteNodesHere()---> 原函数执行 ---> 执行自定义函数 --->执行完毕
Call PasteNodesHere()---> 执行自定义函数 --->执行完毕
Call PasteNodesHere()---> 执行完毕
这几种截胡都可能实现,首要目标是获取原函数地址,目标完成了,那到底怎么实现截胡?
微软的 Detours 库是一个用于拦截和修改函数调用的高效工具,广泛应用于调试、性能分析、安全检测、API 扩展等场景。
● 高效稳定:直接操作二进制代码,性能开销低。
● 灵活性高:支持动态挂载与卸载,适应多种场景。
● 开源免费:社区活跃,文档和示例丰富。
-
Detours 库的核心功能
● 函数钩子(Hook):拦截目标函数的执行,将控制流重定向到自定义的替换函数。
● 动态代码修补:在运行时修改二进制代码,支持对已编译程序的无缝扩展。
● 多平台支持:兼容 Windows x86/x64 架构,适用于用户模式和内核模式编程。 -
工作原理
Detours 通过以下步骤实现函数拦截: -
备份原函数入口:保存目标函数的前几条指令(通常为跳转或调用指令)。
-
插入跳转指令:在目标函数入口处写入跳转指令(如 JMP),将执行流转向自定义的钩子函数。
-
执行替换逻辑:在钩子函数中处理自定义逻辑,可选择调用原始函数(通过备份的指令)或完全替代其行为。
-
恢复原函数:在需要时,可移除钩子并恢复原始代码。
通过 Detours 库,无需修改源码或重新编译目标程序,即可实现强大的运行时行为监控和扩展。
DetourAttach 通常需要配合以下函数实现线程安全:
● DetourTransactionBegin():开始一个事务,暂停所有线程的执行。
● DetourUpdateThread(GetCurrentThread()):更新当前线程的上下文,确保指令缓存一致性。
● DetourTransactionCommit():提交事务,恢复线程并应用所有修改。
Build.cs配置

PasteNodesHere函数的Hook流程:
void HookFunction(HMODULE HModule, uintptr_t FunctionOffset, PVOID& OriginalFunction,PVOID InHookFunction,Hook_Normal_Tag )
{
if (!HModule) {
return ;
}
uintptr_t BaseAddress = reinterpret_cast<uintptr_t>(HModule);
void* FunctionAddress = reinterpret_cast<void*>(BaseAddress + FunctionOffset);
OriginalFunction = FunctionAddress;
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)OriginalFunction, InHookFunction);
DetourTransactionCommit();
}
//FMaterialEditor::PasteNodesHere(const FVector2D& Location, const class UEdGraph* Graph)
using PasteNodesHereFuncType = void(__fastcall*)(FMaterialEditor* , const FVector2D& , const UEdGraph* );
PasteNodesHereFuncType OriginalPasteNodesHere = nullptr;
void __fastcall HookPasteNodesHere(FMaterialEditor* This, const FVector2D& Location, const UEdGraph* Graph)
{
OriginalPasteNodesHere(This, Location, Graph);
//Hook
auto NewNodes = This->GetSelectedNodes();
for (const auto Node : NewNodes)
{
UMaterialGraphNode* MatNode = Cast<UMaterialGraphNode>(Node);
if (Cast<UMaterialExpressionMaterialFunctionCall>(MatNode->MaterialExpression))
{
MatNode->RecreateAndLinkNode();
}
This->AddToSelection(MatNode->MaterialExpression);
}
}
void HookMatEditor()
{
constexpr const char* MaterialDll = "UnrealEditor-MaterialEditor.dll";
HMODULE MatModule = GetModuleHandleA(MaterialDll);
const UMatHelperSettings* Settings = GetDefault<UMatHelperSettings>();
HookFunction(MatModule, 723040, (PVOID&)OriginalPasteNodesHere, HookPasteNodesHere);
}
分析:
0.定义要替换的函数
OriginalPasteNodesHere 用来保存原始函数
HookPasteNodesHere 是要用来做替换的函数,
Hook成功以后,再执行PasteNodesHere 就执行到了 HookPasteNodesHere 函数。
这个代码里面 Hook后的执行流程:
Call PasteNodesHere()---> 原函数执行 ---> 执行自定义函数 --->执行完毕
//FMaterialEditor::PasteNodesHere(const FVector2D& Location, const class UEdGraph* Graph)
using PasteNodesHereFuncType = void(__fastcall*)(FMaterialEditor* , const FVector2D& , const UEdGraph* );
PasteNodesHereFuncType OriginalPasteNodesHere = nullptr;
void __fastcall HookPasteNodesHere(FMaterialEditor* This, const FVector2D& Location, const UEdGraph* Graph)
{
OriginalPasteNodesHere(This, Location, Graph);
//Hook
auto NewNodes = This->GetSelectedNodes();
for (const auto Node : NewNodes)
{
UMaterialGraphNode* MatNode = Cast<UMaterialGraphNode>(Node);
if (Cast<UMaterialExpressionMaterialFunctionCall>(MatNode->MaterialExpression))
{
MatNode->RecreateAndLinkNode();
}
This->AddToSelection(MatNode->MaterialExpression);
}
}
1.计算函数在内存中的实际位置.
uintptr_t BaseAddress = reinterpret_cast<uintptr_t>(HModule);
void* FunctionAddress = reinterpret_cast<void*>(BaseAddress + FunctionOffset);
2.保存原函数
PVOID就是void*,通过PVOID&引用传递,就能在函数体内修改外部传进来的函数指针,
最终让外部传来的函数指针 指向原始函数.
void HookFunction(HMODULE HModule, uintptr_t FunctionOffset, PVOID& OriginalFunction,PVOID InHookFunction,Hook_Normal_Tag )
{
if (!HModule) {
return ;
}
uintptr_t BaseAddress = reinterpret_cast<uintptr_t>(HModule);
void* FunctionAddress = reinterpret_cast<void*>(BaseAddress + FunctionOffset);
OriginalFunction = FunctionAddress;
}
3.Hook
核心在于Detour函数,替换执行逻辑
DetourAttach(&(PVOID&)OriginalFunction, InHookFunction);
连续调用4个Detour开头的函数,问就是 公式做题就是快。
void HookFunction(HMODULE HModule, uintptr_t FunctionOffset, PVOID& OriginalFunction,PVOID InHookFunction,Hook_Normal_Tag )
{
if (!HModule) {
return ;
}
uintptr_t BaseAddress = reinterpret_cast<uintptr_t>(HModule);
void* FunctionAddress = reinterpret_cast<void*>(BaseAddress + FunctionOffset);
OriginalFunction = FunctionAddress;
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)OriginalFunction, InHookFunction);
DetourTransactionCommit();
}
Hook完成了。。。
封装Hook函数库:
FunctionHook.h
#pragma once
#include "Windows/AllowWindowsPlatformTypes.h"
#include <detours.h>
#include "Windows/HideWindowsPlatformTypes.h"
namespace FunctionHook
{
struct Hook_Debug_Tag {};
struct Hook_Normal_Tag {};
void HookFunction(HMODULE HModule, uintptr_t FunctionOffset, void*& OriginalFunction, void* InHookFunction, Hook_Normal_Tag = Hook_Normal_Tag{});
void HookFunction(HMODULE HModule, uintptr_t FunctionOffset, void*& OriginalFunction, void* InHookFunction, Hook_Debug_Tag);
void HookFunction(const char* ModuleName, uintptr_t FunctionOffset, void*& OriginalFunction, void* InHookFunction, Hook_Normal_Tag Tag = Hook_Normal_Tag{});
void HookFunction(const char* ModuleName, uintptr_t FunctionOffset, void*& OriginalFunction, void* InHookFunction, Hook_Debug_Tag Tag);
template<typename MODULE, typename T>
void HookFunction(MODULE Module, uintptr_t Offset, T*& Original, T* Hook, Hook_Normal_Tag = Hook_Normal_Tag{})
{
HookFunction(Module, Offset, (void*&)Original, (void*)Hook);
}
template<typename MODULE, typename T>
void HookFunction(MODULE Module, uintptr_t Offset, T*& Original, T* Hook, Hook_Debug_Tag Tag)
{
HookFunction(Module, Offset, (void*&)Original, (void*)Hook, Tag);
}
}
#define DEFINE_HOOK_TYPE(HookName, RetType, CallConv, ...) \
using HookName##FuncType = RetType(CallConv*)(__VA_ARGS__); \
HookName##FuncType Original##HookName = nullptr; \
RetType CallConv Hook##HookName(__VA_ARGS__)
FunctionHook.cpp
// ReSharper disable CppCStyleCast
#include "FunctionHook.h"
namespace FunctionHook
{
void HookFunction(HMODULE HModule, uintptr_t FunctionOffset, PVOID& OriginalFunction,PVOID InHookFunction,Hook_Normal_Tag )
{
if (!HModule) {
return ;
}
uintptr_t BaseAddress = reinterpret_cast<uintptr_t>(HModule);
void* FunctionAddress = reinterpret_cast<void*>(BaseAddress + FunctionOffset);
OriginalFunction = FunctionAddress;
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)OriginalFunction, InHookFunction);
DetourTransactionCommit();
}
void HookFunction(HMODULE HModule, uintptr_t FunctionOffset, PVOID& OriginalFunction,PVOID InHookFunction,Hook_Debug_Tag)
{
if (!HModule) {
UE_LOG(LogTemp, Warning, TEXT("Failed to get module handle"));
return ;
}
uintptr_t BaseAddress = reinterpret_cast<uintptr_t>(HModule);
void* FunctionAddress = reinterpret_cast<void*>(BaseAddress + FunctionOffset);
OriginalFunction = FunctionAddress;
LONG Error = DetourTransactionBegin();
if (Error != NO_ERROR) {
UE_LOG(LogTemp, Warning, TEXT("DetourTransactionBegin failed with error code: %d"), Error);
return ;
}
Error = DetourUpdateThread(GetCurrentThread());
if (Error != NO_ERROR) {
UE_LOG(LogTemp, Warning, TEXT("DetourUpdateThread failed with error code: %d"), Error);
DetourTransactionAbort();
return ;
}
Error = DetourAttach(&(PVOID&)OriginalFunction, InHookFunction);
if (Error != NO_ERROR) {
UE_LOG(LogTemp, Warning, TEXT("DetourAttach failed with error code: %d"), Error);
DetourTransactionAbort();
return ;
}
Error = DetourTransactionCommit();
if (Error != NO_ERROR) {
UE_LOG(LogTemp, Warning, TEXT("DetourTransactionCommit failed with error code: %d"), Error);
return ;
}
}
void HookFunction(const char* ModuleName, uintptr_t FunctionOffset, PVOID& OriginalFunction,PVOID InHookFunction,Hook_Normal_Tag Tag)
{
HMODULE HModule = GetModuleHandleA(ModuleName);
HookFunction(HModule,FunctionOffset,OriginalFunction,InHookFunction,Tag);
}
void HookFunction(const char* ModuleName, uintptr_t FunctionOffset, PVOID& OriginalFunction,PVOID InHookFunction,Hook_Debug_Tag Tag)
{
HMODULE HModule = GetModuleHandleA(ModuleName);
HookFunction(HModule,FunctionOffset,OriginalFunction,InHookFunction,Tag);
}
}
使用封装的Hook函数:
DEFINE_HOOK_TYPE(PasteNodesHere, void, __fastcall, FMaterialEditor* This, const FVector2D& Location, const UEdGraph* Graph)
{
OriginalPasteNodesHere(This, Location, Graph);
//Hook
auto NewNodes = This->GetSelectedNodes();
for (const auto Node : NewNodes)
{
UMaterialGraphNode* MatNode = Cast<UMaterialGraphNode>(Node);
if (Cast<UMaterialExpressionMaterialFunctionCall>(MatNode->MaterialExpression))
{
MatNode->RecreateAndLinkNode();
}
This->AddToSelection(MatNode->MaterialExpression);
}
}
namespace MatHelperHook
{
using namespace FunctionHook;
void HookMatEditor()
{
constexpr const char* MaterialDll = "UnrealEditor-MaterialEditor.dll";
HMODULE MatModule = GetModuleHandleA(MaterialDll);
const UMatHelperSettings* Settings = GetDefault<UMatHelperSettings>();
HookFunction(MatModule, Settings->PasteNodeHereOffset, OriginalPasteNodesHere, HookPasteNodesHere);
}
}

浙公网安备 33010602011771号