资讯详情

资讯详情

行业资讯

YimMenuV2 如何在 GTA 5: Enhanced 中实现实时菜单:特征码定位、D3D12 渲染与 Lua 脚本

YimMenuV2 如何在 GTA 5: Enhanced 中实现实时菜单特征码定位、D3D12 渲染与 Lua 脚本【免费下载链接】YimMenuV2Experimental menu for GTA 5: Enhanced项目地址: https://gitcode.com/GitHub_Trending/yi/YimMenuV2给 GTA 5: Enhanced 写菜单类工具最先撞上的墙是版本更新游戏一换版本内存布局就跟着变任何写死的偏移量在下一次启动时都会指向错误的位置菜单当场失效。YimMenuV2 的应对方式是不记地址、只记指令特征配合一套钩子、渲染和脚本层把定位游戏内部这件事做成可维护的结构。方案地图五层各管一件事整套方案拆成五个相对独立的层从内存到界面到扩展各司其职层职责位置特征码扫描用指令特征定位指针与函数地址src/core/memory/PatternScanner.cpp钩子层基于 MinHook 拦截游戏函数src/core/hooking/DetourHook.hpp渲染层挂钩 D3D12 Present绘制 ImGuisrc/core/renderer/D3D12Hook.cpp脚本协程层用 Fiber 在脚本线程里跑菜单逻辑src/core/backend/ScriptMgr.cppLua 扩展层沙箱加载用户脚本src/core/scripting/LuaManager.cpp启动顺序在 src/main.cpp 里写得很直白ModuleMgr加载模块 →Pointers::Init()扫特征 →AnticheatBypass::RunOnStartup()→Hooking::Init()→ScriptMgr::Init()→D3D12Hook::Init()→Renderer::Init()→GUI::Init()→FiberPool::Init(16)最后注册一组脚本。任何一步失败就走EARLY_UNLOAD退出。下面按层拆开。特征码扫描用指令序列而不是地址找东西问题在于游戏更新后地址会变硬编码偏移必然腐烂。做法是把每条特征写成十六进制字节序列用?做通配在目标模块的字节流里滑动匹配。src/core/memory/Pattern.hpp 里的Pattern模板在编译期就把特征字符串解析成字节数组同时算出特征哈希constexpr auto scriptThreadsPtrn Pattern48 8B 05 ? ? ? ? 48 89 34 F8 48 FF C7 48 39 FB 75 97(ScriptThreads); scanner.Add(scriptThreadsPtrn, this { ScriptThreads ptr.Add(3).Rip().Asrage::atArrayrage::scrThread**(); });src/game/pointers/Pointers.cpp 的Pointers::Init()按这条模式把几十条特征一一登记匹配成功后回调里用PointerCalculator加偏移、取 RIP 相对地址得到指针或函数地址统一存进全局的Pointers结构。PatternScanner::Scan()里有个容易漏掉的细节非手动映射注入时每条特征的匹配任务会丢进std::async并行跑一条扫描能省出几百毫秒命中的偏移会以特征哈希 模块大小为键写进PatternCache二次注入时直接走缓存不再全文扫描。另外整条链路是一票否决任何一条特征没命中Init()整体失败并直接卸载而不是带着残缺的指针表继续跑。函数拦截两跳定位与延迟启用地址找到之后要真正插进游戏的执行流靠的是拦截。src/core/hooking/DetourHook.hpp 的DetourHook是一个基于 MinHook 的模板类构造时接收目标地址、菜单自己的回调内部调MH_CreateHook并保留原函数指针供拦截之后透传回原逻辑DetourHook(const std::string_view name, void* target, T detour) : BaseHook(name), m_TargetFunc(target), m_DetourFunc(reinterpret_castvoid*(detour)), m_OriginalFunc(nullptr) { OptimizeHook(); if (const auto result MH_CreateHook(m_TargetFunc, m_DetourFunc, m_OriginalFunc); result ! MH_OK) { LOGF(FATAL, Failed to create hook {}, name); throw std::runtime_error(Failed to create hook!); } }所有钩子在 src/core/hooking/Hooking.cpp 的构造函数里登记按功能分成 Anticheat、Script、Network、Matchmaking、Spoofing 等一组静态成员。两个容易漏掉的点一是OptimizeHook()会沿着0xE9jmp跳板一路跳到真正的函数体再挂钩否则挂到的只是跳板指令行为会不对二是Enable()/Disable()只是把钩子排进 MinHook 的队列Hooking::InitImpl()最后统一m_MinHook.ApplyQueued()一次性生效避免逐个启用时对游戏线程制造中间状态。D3D12 渲染先造假设备再钓真交换链ImGui 要画在游戏窗口上前提是先拿到游戏正在用的 D3D12 设备、交换链和命令队列而注入时机往往赶在游戏创建这些对象之前。src/core/renderer/D3D12Hook.cpp 的InitImpl()先自建一套临时对象探路auto d3d12Module LoadLibraryA(d3d12.dll); auto d3d12CreateDevice reinterpret_castdecltype(D3D12CreateDevice)*( GetProcAddress(d3d12Module, D3D12CreateDevice)); // 创建 dummy 设备、factory、command queue、swap chain // 然后在自己交换链的内存里搜 commandQueue 指针 for (uint32_t i 0; i 512 * sizeof(void*); i sizeof(void*)) if (*reinterpret_castID3D12CommandQueue**(base) commandQueue) m_CommandQueueOffset i;这段代码决定了一件事只要从临时交换链上量出命令队列成员离对象头的偏移之后拿到游戏的真实交换链就能靠同一偏移直接读出它的命令队列不需要任何额外钩子。Present 钩装分两阶段。第一阶段直接在交换链虚表第 8 槽位上打补丁RawVtablePatch捕获到真实交换链后卸载临时补丁改用VMTHook按实例克隆一份虚表来钩Present和ResizeBuffers——代码注释里写明这是故意的克隆虚表保证钩一个对象不影响别的对象。装上之后注册回调链Renderer在OnPresent里创建描述堆和渲染目标视图之后每帧走NewFrame→ 业务绘制 →RenderDrawDataOnResizeBuffers释放过期的 backbufferOnDeviceReset整体销毁重建。脚本协程让菜单逻辑跑在游戏的线程上游戏原语函数要求在 RAGE 脚本线程的上下文里调用普通工作线程直接调会破坏同步状态。YimMenuV2 的解法是把每块需要持续执行的逻辑封装成一个Script对象内部用 Windows Fiber 承载回调src/core/backend/ScriptMgr.cpp 负责调度void ScriptMgr::TickImpl() { std::lock_guard lock(m_Mutex); static bool ensure_main_fiber (ConvertThreadToFiber(nullptr), true); for (const auto script : m_Scripts) script-Tick(); }Script::Tick()只做一件事在到期的情况下SwitchToFiber切进自己的子纤程跑回调回调里调ScriptMgr::Yield()就切回主纤程。main.cpp里一次性注册了十来个脚本NativeHooks、Tunables、Self、GUI、LuaManager、HotkeySystem、Commands等另加FiberPool::Init(16)开出 16 个空闲纤程专门用来异步执行FiberPool::Push()丢进来的回调。这里最容易看走眼的是调度权归属Tick()只在游戏脚本线程的帧循环里被调一次意味着菜单的全部帧逻辑都运行在那一条线程上游戏原语调用才安全。YieldImpl()会区分当前处于 Lua 协程还是普通脚本纤程分别转发到LuaScript::Yield或Script::Yield。Lua 脚本层不改 C 也能加功能不想重新编译加功能时Lua 层就是入口。脚本放在%appdata%/YimMenuV2/scriptssrc/core/scripting/LuaManager.cpp 在启动时递归加载.lua文件每个脚本跑在独立的 LuaJIT 状态里API 面在 docs/lua-api.md 里列全了notify、menu、memory、natives、entities这些全局表都可以用但io、os、ffi以及load、loadfile这类直接加载函数全部不可用package.cpath保持为空防止脚本越出沙箱。一个值得学的磁盘约定禁用一个脚本时文件会被移动到scripts/disabled/下并保留相对路径重新启用时再移回去。脚本的开关状态直接反映在目录结构上重启菜单后状态不丢也不需要在设置文件里再存一份。设置持久化一个 JSON 管所有状态最后一条线是配置。FileMgr以%appdata%/YimMenuV2为根管理文件和目录Settings::Initialize(FileMgr::GetProjectFile(./settings.json))让所有实现IStateSerializer的组件命令、热键、子菜单状态把状态序列化进同一个 JSON。main.cpp的主循环里定期Settings::Tick()做防抖落盘避免每帧写盘。日志则走LogHelper输出到appdata/YimMenuV2/cout.log。最小验证路径注入后看三件事构建cmake -B build -DCMAKE_BUILD_TYPERelease cmake --build build --config Release。在 Windows 上直接构建在 Linux/macOS 上 cmake/cross-compile.cmake 默认开启交叉编译会自动找 Zig 或 Clang(MinGW) 工具链并静态链接。注入用注入器如 Xenos在 GTA 5: Enhanced 主菜单阶段注入构建出的 DLL然后看cout.log应当出现Welcome to YimMenuV2!、ScriptMgr initialized这类行以及每条特征码的Found pattern [...]记录。打开界面游戏内按INSERT或Ctrl\ImGui 菜单应该盖在画面上右上角弹出 Loaded succesfully 通知。加一个 Lua 脚本往scripts目录丢一个.lua文件刷新菜单它应出现在 Settings Lua Scripts 的 Disabled 列表里点击即加载运行。避坑记录现象钩子全装上、日志无报错但界面始终不出现。原因Present 钩子没捕获到真实交换链日志里没有D3D12 Hook initialized.这一行。改法优先排查交换链创建路径和CreateSwapchain补丁是否生效不要急着去改 ImGui 初始化代码。现象游戏更新后注入直接退出。原因某条特征码没命中日志里有Failed to find pattern [...]和Some patterns could not be found, unloading.。改法逐条更新特征码不要靠放宽通配符硬凑——通配符一多命中位置就不可信了。现象日志出现Some socialclub patterns could not be found但菜单照常工作。原因socialclub.dll的特征由Pointers::LateInit()单独扫失败只降级 SocialClub 相关功能不是致命错误。改法需要对应功能时再补特征码否则可以忽略这条告警。五层结构里特征码定位、MinHook 拦截、D3D12 两阶段钩子、Fiber 脚本模型、Lua 沙箱各自独立改任何一层时只动该层的代码不要跨层打补丁。这个每层可单独失败、单独修复的划分是这套代码最值得借鉴的地方。 【免费下载链接】YimMenuV2Experimental menu for GTA 5: Enhanced项目地址: https://gitcode.com/GitHub_Trending/yi/YimMenuV2创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

准备好开启您的数字化征程了吗?

专业团队为您量身定制,让您的企业网站脱颖而出

立即咨询