修复缺失的 AOB
如果 UE4SS 因缺失 AOB 而无法正常启动,可以使用 Lua 提供自己的 AOB 和回调。
阅读本指南前,需要了解 root directory(根目录)和 working directory(工作目录)的含义。根目录始终是包含 ue4ss.dll 的目录。工作目录可以是包含 ue4ss.dll 的目录,也可以是游戏专属目录,例如 <root directory>/SatisfactoryEarlyAccess。
如何查找 AOB
[!CAUTION] 为游戏查找 AOB 并非简单任务,需要研究逆向工程的基本原理。我们无法用一份包罗万象的指南覆盖所有情况。
由于过程较为复杂,这里仅介绍一般步骤。
- 使用与目标游戏相同的 UE 版本创建一个空白游戏,并以 Shipping 配置打包,保留 PDB。
- 使用 x64dbg 读取游戏内存。
- 查找需要的签名,相关函数签名见下文。
- 复制该函数的一段字节,有时函数开头就够了。如果不够,可以尝试找到对该函数的调用;若不是虚函数,可以在调用处取得 RIP 相对地址。
- 在 x64dbg 中打开目标游戏的内存,搜索同一段字节。
- 如果找到,可使用 Swiss Army Knife 工具提取其 AOB,再将其用于这里的示例一类简单脚本中。
更详细的说明请参阅进阶指南。
设置自定义 AOB 和回调
- 如果工作目录中还没有
UE4SS_Signatures,创建该目录。 - 确认哪些 AOB 已失效、需要修复。
- 根据失效的 AOB,在
UE4SS_Signatures中创建对应文件:- GUObjectArray.lua
- FName_ToString.lua
- FName_Constructor.lua
- StaticConstructObject.lua
- GMalloc.lua
- GUObjectHashTables.lua(可选)
- GNatives.lua(可选)
- ConsoleManager.lua(可选)
- ProcessLocalScriptFunction.lua
- ProcessInternal.lua
- CallFunctionByNameWithArguments.lua
- 在
.lua文件中,可以直接在文件作用域返回符号地址,也可以定义Register和OnMatchFound函数来执行 AOB 扫描。- 注意,
UE4SS_Signatures目录中 Lua 文件的函数名称区分大小写。 - 如果选择在文件作用域返回符号地址,
LoadExport可能有用,但通常仅适用于模块化构建的游戏。
- 注意,
Register函数必须返回希望 UE4SS 扫描的 AOB。- 格式是一连串半字节(nibble),每两个半字节组成一个字节。
- 为便于阅读,可以在每个字节之间加空格,但这不是必需的。
- AOB 示例:
8B 51 04 85。 - 另一个示例:
8B510485。 - AOB 扫描器支持对单个半字节或整个字节使用通配符。
- 接着创建全局
OnMatchFound函数。- 它只有一个参数
MatchAddress,即匹配位置的地址。 - 所有计算最终地址的逻辑都放在此函数中。
- 最简单的方法是让 AOB 直接对应目标地址的开头,这样直接返回
MatchAddress即可。 - 如果需要更复杂的操作(例如间接 AOB 扫描),UE4SS 提供两个全局函数:
DerefToInt32接收地址并将该地址的数据作为 32 位整数返回,无法解引用时返回nil;print可用于调试。
- 它只有一个参数
各 AOB 的 OnMatchFound 必须返回什么
- GUObjectArray
- 必须返回名为
GUObjectArray的全局变量的准确地址。
- 必须返回名为
- FName_ToString
- 必须返回
FName::ToString函数入口的准确地址。 - 函数签名:
public: void cdecl FName::ToString(class FString & ptr64)const __ptr64。
- 必须返回
- FName_Constructor
- 必须返回
FName::FName函数入口的准确地址。 - 此回调很可能被调用多次;UE4SS 会在内部检查是否找到正确的构造函数。
- AOB 同时匹配
char*和wchar_t*版本也没有关系。 - 函数签名:
public: cdecl FName::FName(wchar_t const * ptr64,enum EFindName) __ptr64。
- 必须返回
- StaticConstructObject
- 必须返回全局函数
StaticConstructObject_Internal入口的准确地址。 - UE4SS 会扫描
UUserWidget::InitializeInputComponent中间的一处调用,再解析该调用的目标位置。 - 函数签名:
class UObject * __ptr64 __cdecl StaticConstructObject_Internal(struct FStaticConstructObjectParameters const & __ptr64)。
- 必须返回全局函数
- GMalloc
- 必须返回名为
GMalloc的全局变量的准确地址。 - UE4SS 会扫描
FMemory::Free,再解析最靠近第一条 CALL 指令的 MOV 指令。
- 必须返回名为
- GNatives(可选)
- 必须返回名为
GNatives的全局变量的准确地址。
- 必须返回名为
- GUObjectHashTables(开发中,可选)
- ConsoleManager(开发中,可选)
- ProcessLocalScriptFunction
- 99% 的情况下不需要提供。
- 必须返回 ProcessLocalScriptFunction 的准确地址。
- ProcessInternal
- 99% 的情况下不需要提供。
- 必须返回 ProcessInternal 的准确地址。
- CallFunctionByNameWithArguments
- 99% 的情况下不需要提供。
- 必须返回 CallFunctionByNameWithArguments 的准确地址。
示例脚本(简单直接扫描)
function Register()
return "48 8B C4 57 48 83 EC 70 80 3D ?? ?? ?? ?? ?? 48 89"
end
function OnMatchFound(MatchAddress)
return MatchAddress
end
示例脚本(高级间接扫描)
function Register()
return "41 B8 01 00 00 00 48 8D 15 ?? ?? ?? ?? 48 8D 0D ?? ?? ?? ?? E9"
end
function OnMatchFound(MatchAddress)
local InstrSize = 0x05
local JmpInstr = MatchAddress + 0x14
local Offset = DerefToInt32(JmpInstr + 0x1)
local Destination = JmpInstr + Offset + InstrSize
return Destination
end
原文:docs/guides/fixing-compatibility-problems.md · 固定提交 560411f18512 · 来源与 MIT 许可