钩子指南
介绍
Hook 是一种在运行时修改函数行为的技术。通常用于在不修改源代码的情况下修改函数行为。
wikipedia 关于 Hook 的解释:Hooking
在 LeviLamina 中,我们提供了封装好的Hook API,使得你可以快速便捷地对 Minecraft 基岩专用服务器(下文称为BDS)中的游戏函数进行行为修改。
Hook的类型
在ll/api/memory/Hook.h 中,我们定义了以下几种Hook 宏:
| C++ | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
其中,AUTO 标注的 Hook 会自动注册,即运行时自动调用hook()函数进行注册。 TYPE 标注的 Hook 会给你的 DEF_TYPE 继承到你指定的类型。
STATIC_HOOK
针对静态函数的Hook。
INSTANCE_HOOK
针对成员函数的Hook。
Hook的参数解释
DEF_TYPE:你给你这个 Hook 的起的类型名。
PRIORITY:Hook的优先级,例如ll::memory::HookPriority::Normal
Note
一般不是特殊需求,我们不推荐过高的优先级,Normal 即可。
TYPE:你的定义的 DEF_TYPE 继承到的类型。
IDENTIFIER:Hook 的查询函数使用的标识符,可以为:函数修饰后名称,函数的字节码,函数定义。
RET_TYPE:Hook 的函数的返回值类型。
...:Hook 的函数的参数列表。
Hook的使用
你可以查阅 LeviLamina 提供的Fake Header来获取你想要 Hook 的函数的定义。
简单的Hook示例
| C++ | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
这段代码会 Hook DedicatedServer 的构造函数,并在构造函数被调用时打印一条日志。
解析:
根据查阅 LeviLamina 提供的DedicatedServer.h,我们可以得到 DedicatedServer 的构造函数的thunk函数。
又由于构造函数是类的成员函数,所以我们使用了INSTANCE_HOOK类型的Hook,这使我们不需要填写由编译器产生的第一个this指针参数。
又由于我们希望在模组被加载的时候自动注册,所以我们使用了 AUTO标注的 Hook。
最后由于方便,我们使用了 TYPE标注的 Hook,使得我们可以直接在函数体内调用 DedicatedServer 类型下的函数,虽然在这段代码中我们并没有使用到,但是这是一个好习惯。
Hook的注册和卸载
注册
针对非自动注册的 Hook,你需要在你模组需要注册 Hook 的时机调用hook()函数进行注册。
卸载
所有的 Hook 都会在 BDS 卸载时自动卸载,你也可以在你模组需要卸载 Hook 的时机调用unhook()函数进行卸载。
写在最后
Hook 是一种非常强大的技术,但是也是一把双刃剑,如果使用不当,可能会导致BDS本体崩溃,或者模组崩溃,严重的甚至会对存档产生影响。
所以请在使用 Hook 的时候一定要谨慎,仔细检查你的代码,避免出现不必要的错误。