当前位置: 首页 > news >正文

洛克王国:世界 — ACE 绕过与自定义 ReShade Addon 实现

洛克王国:世界 — ACE 绕过与自定义 ReShade Addon 实现

接上篇解包分析,本文记录如何绕过 ACE 反作弊系统,编写自定义 ReShade addon 拦截绘制调用,实现隐藏角色鞋子的效果。


一、前情提要

在上一篇中,记录了 AEAD 加密 PAK 的解包过程,以及尝试了 8 种运行时注入方法,全部被 ACE 拦截。结论是:

ACE 是全内核级保护(ACE-BASE + ACE-ADVT + ACE-CORE),有自修复能力,常规手段无法绕过。

但是后来发现 B 站上有人成功实现了 ReShade 画质增强,这说明存在绕过 ACE 的方法


二、突破点:d3d12.dll

2.1 发现

网友分享的 ReShade 画质增强包可以直接放在游戏 exe 目录下使用,核心文件是:

Win64/NRC/Binaries/Win64/
├── d3d12.dll                  ← ReShade 6.7.1 (伪装)
├── ReShade.ini
├── ReShade64.dll
├── reshade-shaders/
└── *.addon64                  ← 各类 ReShade 插件

关键发现:ACE 的文件扫描不拦截 d3d12.dll

✅ d3d12.dll  → ACE 白名单外?不扫
❌ d3d11.dll  → ACE 扫描拦截
❌ dxgi.dll   → ACE 扫描拦截

游戏使用的是 DirectX 12 渲染管线,d3d12.dll 是系统合法 DLL。ReShade 利用这个特点,将自身编译为 d3d12.dll 放在游戏目录下,游戏启动时自动加载它,而 ACE 不会拦截。

2.2 ReShade 6.7.1 版

文件: d3d12.dll
实际: ReShade64.dll
版本: 6.7.1.2132
API:  v18

这个版本的 ReShade 支持 addon 系统,可以编写 C++ 插件在游戏进程中运行。


三、编写自定义 Addon:ShoeHide

3.1 技术原理

ReShade API v18 提供了 draw_indexed 事件,可以在每个索引绘制调用前拦截:

// 事件签名
bool on_draw_indexed(api::command_list *cmd_list,uint32_t index_count,      // 索引数量uint32_t instance_count,   // 实例数量uint32_t first_index,      // 起始索引int32_t  vertex_offset,    // 顶点偏移uint32_t first_instance    // 起始实例
);
// 返回 true → 跳过该绘制调用
// 返回 false → 正常绘制

核心思路:每个 3D 模型在渲染时会产生固定的 (index_count, first_index) 组合,可以将其哈希化作为该模型的"指纹"。然后拦截匹配的 draw call。

3.2 哈希函数

static uint64_t make_hash(uint32_t index_count, uint32_t first_index) {return ((uint64_t)index_count << 20) | (first_index & 0xFFFFF);
}

这个简单的哈希将索引数和偏移打包为一个 64 位整数。对于不同的模型部件,只要它们的索引数或偏移不同,就会产生不同的哈希值。

3.3 完整代码 (src/main.cpp)

#include <Windows.h>
#include <cstdio>
#include <io.h>
#include <fcntl.h>#include "../reshade/reshade.hpp"#define MAX_SKIP 256
#define MAX_TRACKED 1024static uint64_t g_skip_hashes[MAX_SKIP];
static int g_skip_num = 0;static uint64_t g_tracked_hashes[MAX_TRACKED];
static uint32_t g_tracked_counts[MAX_TRACKED];
static int g_tracked_num = 0;static volatile int g_test_idx = -1;
static volatile bool g_tracking = false;
static WCHAR g_config_path[MAX_PATH] = {};static uint64_t make_hash(uint32_t index_count, uint32_t first_index) {return ((uint64_t)index_count << 20) | (first_index & 0xFFFFF);
}bool on_draw_indexed(reshade::api::command_list *, uint32_t index_count, uint32_t instance_count,uint32_t first_index, int32_t, uint32_t) {if (index_count == 0 || instance_count == 0)return false;uint64_t hash = make_hash(index_count, first_index);for (int i = 0; i < g_skip_num; i++) {if (g_skip_hashes[i] == hash)return true;}int ti = g_test_idx;if (ti >= 0 && ti < g_tracked_num && g_tracked_hashes[ti] == hash)return true;if (g_tracking) {for (int i = 0; i < g_tracked_num; i++) {if (g_tracked_hashes[i] == hash) {g_tracked_counts[i]++;return false;}}if (g_tracked_num < MAX_TRACKED) {int idx = g_tracked_num++;g_tracked_hashes[idx] = hash;g_tracked_counts[idx] = 1;}}return false;
}static void load_config() {int fd = _wopen(g_config_path, _O_RDONLY);if (fd < 0) return;FILE* f = _wfdopen(fd, L"r");if (!f) { _close(fd); return; }g_skip_num = 0;while (g_skip_num < MAX_SKIP && fscanf_s(f, "%llx", &g_skip_hashes[g_skip_num]) == 1)g_skip_num++;fclose(f);
}static void save_config() {int fd = _wopen(g_config_path, _O_WRONLY | _O_CREAT | _O_TRUNC, 0644);if (fd < 0) return;FILE* f = _wfdopen(fd, L"w");if (!f) { _close(fd); return; }for (int i = 0; i < g_skip_num; i++)fprintf(f, "%llx\n", g_skip_hashes[i]);fclose(f);
}DWORD WINAPI hotkey_thread(LPVOID) {bool f6p = false, f7p = false, f8p = false;int cyc = 0;Sleep(2000);while (true) {Sleep(80);bool f6 = (GetAsyncKeyState(VK_F6) & 0x8000) != 0;bool f7 = (GetAsyncKeyState(VK_F7) & 0x8000) != 0;bool f8 = (GetAsyncKeyState(VK_F8) & 0x8000) != 0;if (f6 && !f6p) {g_tracking = !g_tracking;g_tracked_num = 0;g_test_idx = -1;cyc = 0;char b[64];snprintf(b, sizeof(b), "[ShoeHide] Track: %s", g_tracking ? "ON" : "OFF");reshade::log::message(reshade::log::level::info, b);}if (f7 && !f7p && g_tracking && g_tracked_num > 0) {cyc = (cyc + 1) % g_tracked_num;g_test_idx = cyc;char b[128];snprintf(b, sizeof(b), "[ShoeHide] Test #%d/%d hash=0x%llX cnt=%u",cyc, g_tracked_num, g_tracked_hashes[cyc], g_tracked_counts[cyc]);reshade::log::message(reshade::log::level::info, b);}if (f8 && !f8p && g_test_idx >= 0 && g_test_idx < g_tracked_num) {uint64_t h = g_tracked_hashes[g_test_idx];bool dup = false;for (int i = 0; i < g_skip_num; i++) {if (g_skip_hashes[i] == h) dup = true;}if (!dup && g_skip_num < MAX_SKIP) {g_skip_hashes[g_skip_num++] = h;save_config();char b[128];snprintf(b, sizeof(b), "[ShoeHide] SAVED hash=0x%llX", h);reshade::log::message(reshade::log::level::info, b);}}f6p = f6; f7p = f7; f8p = f8;}
}extern "C" __declspec(dllexport) bool AddonInit(HMODULE addon_module, HMODULE reshade_module) {if (!reshade::register_addon(addon_module, reshade_module))return false;char base[MAX_PATH] = {}; size_t len = MAX_PATH;reshade::get_reshade_base_path(base, &len);MultiByteToWideChar(CP_UTF8, 0, base, -1, g_config_path, MAX_PATH);wcscat_s(g_config_path, L"\\ShoeHide.ini");load_config();char buf[64];snprintf(buf, sizeof(buf), "[ShoeHide] v6 loaded, skip=%d hashes", g_skip_num);reshade::log::message(reshade::log::level::info, buf);reshade::register_event<reshade::addon_event::draw_indexed>(on_draw_indexed);CreateThread(nullptr, 0, hotkey_thread, nullptr, 0, nullptr);return true;
}extern "C" __declspec(dllexport) void AddonUninit(HMODULE addon_module, HMODULE reshade_module) {reshade::unregister_event<reshade::addon_event::draw_indexed>(on_draw_indexed);reshade::unregister_addon(addon_module);
}BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD reason, LPVOID) {if (reason == DLL_PROCESS_ATTACH)DisableThreadLibraryCalls(hinstDLL);return TRUE;
}

3.4 热键操作

通过独立线程轮询 GetAsyncKeyState

热键 功能
F6 切换追踪模式(开始/停止收集 draw call 指纹)
F7 轮流隐藏已收集的 draw call(实时预览哪个是鞋子)
F8 将当前选中的 hash 永久保存到配置文件

四、编译与部署

4.1 项目结构

ShoeHide/
├── CMakeLists.txt
├── reshade/                    ← ReShade SDK headers (v6.7.1)
│   ├── reshade.hpp
│   ├── reshade_events.hpp
│   ├── reshade_overlay.hpp
│   └── reshade_api*.hpp
└── src/└── main.cpp                ← 约 170 行 C++

4.2 CMakeLists.txt

cmake_minimum_required(VERSION 3.15)
project(ShoeHide VERSION 1.0.0 LANGUAGES CXX)set(CMAKE_CXX_STANDARD 17)
add_definitions(-DUNICODE -D_UNICODE)
include_directories(${CMAKE_SOURCE_DIR}/reshade)add_library(ShoeHide SHARED src/main.cpp)
set_target_properties(ShoeHide PROPERTIES OUTPUT_NAME "ShoeHide")
set_target_properties(ShoeHide PROPERTIES SUFFIX ".addon64")

4.3 构建

mkdir build && cd build
cmake .. -G "Visual Studio 17 2022" -A x64
cmake --build . --config Release

输出 ShoeHide.addon64,复制到游戏 exe 目录即可。

4.4 部署

将以下文件放在 Win64/NRC/Binaries/Win64/ 下:

d3d12.dll              ← ReShade 主体 (rocowg 画质包)
ReShade.ini            ← ReShade 配置
reshade-shaders/       ← 着色器文件
ShoeHide.addon64       ← 自定义鞋子隐藏插件

五、实际效果

5.1 衣柜/预览界面

在衣柜里,每个角色部件(头发、衣服、鞋子、挂件等)是独立的 draw call,可以精确隐藏单个部件。

F6 → 开启追踪 → 121 个唯一 draw call 被收集
F7 × 60 次 → 找到鞋子: hash=0x16EC00000
F8 → 保存
结果: 鞋子消失,其余部件完好 ✓

5.2 大世界

在大世界里,UE4 引擎为了性能优化,将角色所有部件合并为单个网格体进行渲染,因此所有部件共享同一个 draw call。

F6 → 开启追踪
F7 循环 → 找到 body hash=0x5B2900000
F8 → 保存
结果: 全身消失(衣服+鞋子+身体一起) 

原因:这是 UE4 的场景加载优化——在进入大世界时引擎会将角色部件预合并,减少 draw call 数量。这不是运行时动态合并,所以无法在 draw call 层面分离。

5.3 效果对比

场景 Draw Call 级别 隐藏精度 效果
衣柜/预览 独立 单部件 只遮鞋子 ✓
大世界 合并 全身 全身消失

六、开发过程中的踩坑记录

6.1 编译问题

  • CMakeLists.txt 设置 LANGUAGES C 但源文件是 .cpp,需要改为 LANGUAGES CXX
  • UNICODE 宏导致 _stricmp 类型不匹配,需使用 ANSI 版本 API
  • ReShade SDK 依赖链:reshade.hpp → reshade_events.hpp → reshade_api.hpp → reshade_api_device.hpp → ...,需要下载全部头文件

6.2 运行时崩溃

  • 第一次尝试:在 draw call 回调中使用 std::unordered_set + CRITICAL_SECTION → 闪退(渲染线程中初始化临界区导致死锁)
  • 第二次尝试:在回调中进行文件 I/O (save_config) → 闪退(渲染线程中写文件)
  • 第三次尝试:使用固定数组替代 STL 容器,去掉所有锁和文件 I/O → 成功

结论:draw call 回调在渲染线程中执行,必须极度轻量——不能分配内存、不能文件 I/O、不能用重型同步原语。

6.3 数据竞争

渲染回调(多线程)和热键线程共享数组,但没有使用互斥锁。这依赖于 ARM/x64 的对齐读写是原子的这一特性,且只在数组末尾追加数据(不修改已有元素)。

6.4 中文路径

Windows 的 ANSI fopen 无法处理中文路径。改用 _wopen + _wfdopen 的宽字符方案。同时 MultiByteToWideChar(CP_UTF8, ...) 将 ReShade 返回的 UTF-8 路径转换为宽字符。


七、技术总结

完整绕过路径

游戏启动→ 加载 d3d12.dll (ReShade 6.7.1)→ ACE 不拦截 d3d12.dll ✓→ ReShade 加载所有 .addon64 插件→ 加载 ShoeHide.addon64→ 注册 draw_indexed 事件钩子→ 按哈希值过滤 draw call→ 鞋子对应的 draw call 被跳过

关键突破点

  1. d3d12.dll 不被 ACE 扫描:这是整个方案的基础
  2. ReShade addon API:提供了合法的进程内代码执行渠道
  3. draw_indexed 事件:精确到每个 3D 模型的绘制拦截
  4. 固定数组 + 无锁设计:确保渲染线程安全

局限性

  • 大世界中 UE4 将角色部件合并渲染,无法在 draw call 层面分离单个部件
  • 该方法仅适用于衣柜/预览场景的精确隐藏
  • 需要在 UI 界面手动操作 F6/F7/F8 标记目标

八、文件清单

文件 说明
ShoeHide.addon64 最终编译产物,放入游戏目录即可
src/main.cpp 约 170 行核心代码
CMakeLists.txt CMake 构建配置
reshade/*.hpp ReShade API v18 头文件
ShoeHide.ini 自动生成的配置文件,存储跳过的 hash 列表

九、参考资源

  • ReShade: https://reshade.me/
  • ReShade API 源码: https://github.com/crosire/reshade
  • 洛克王国 画质包: B站视频 【洛克王国:世界】画质增强+帧生成 作者分享
  • 3DMigoto: https://github.com/bo3b/3Dmigoto
  • QuickBMS: https://aluigi.altervista.org/quickbms.htm
  • UE4 AES 密钥收集: https://cs.rin.ru/forum/viewtopic.php?t=100672

Disclaimer: 本文为技术研究记录,所有操作均在本地单机环境完成。修改游戏客户端可能违反用户协议。本文内容仅用于学习和研究目的。

http://www.zskr.cn/news/1363461.html

相关文章:

  • RTX51实时系统任务抢占与邮箱机制深度解析
  • 歌词滚动姬:免费网页版LRC歌词制作终极指南
  • 2026年评价高的德州管件深孔珩磨机/强力深孔珩磨机厂家选择推荐 - 品牌宣传支持者
  • AR Foundation工程落地难点:空间锚定与跨平台一致性实战解析
  • 6G通信中LDPC与Polar码的技术演进与统一编码方案
  • C51中断机制解析与调试实战指南
  • UnityXFramework:面向商业手游的可扩展热更新框架设计
  • C#中Activator的具体使用
  • XZ62C,0.7uA静态电流,CMOS输出电压检测芯片
  • 别只盯着oops!Linux内核‘防崩溃’工具箱:lockdep、KASAN等高级调试器实战配置指南
  • XL-MIMO近场定位:攻克PC-HAD相位模糊与球面波挑战
  • Claude API文档从零到上线:手把手教你3小时产出符合Anthropic官方规范的生产级文档
  • AutoM3L:基于大语言模型的全自动多模态机器学习框架解析与实践
  • 2026年4月国产化计算机公司推荐,定制计算机/加固下翻机/三防电脑/加固笔记本/特种计算机,国产化计算机公司选哪家 - 品牌推荐师
  • meent开源库实战:RCWA/TMM原理、实现与超表面优化避坑指南
  • Windows11下Detectron2安装避坑指南:从CUDA版本匹配到源码修改(附常见错误解决方案)
  • Android高版本HTTPS抓包解决方案:Magisk+MoveCert绕过证书限制
  • 再不部署AI Agent,你的核保团队将在2025Q3面临37%产能缺口:来自精算与IT双视角的倒计时预警
  • Appium Settings:Android自动化中的免Root系统参数控制工具
  • 2026 十大镁合金企业盘点:谁在定义高强镁合金的未来
  • 多任务学习如何提升文档级机器翻译的上下文感知能力
  • Armv8-R AArch64无硬件浮点支持开发实战指南
  • 2026年口碑好的温州加厚拉链袋/拉链袋免费打样推荐品牌厂家 - 品牌宣传支持者
  • PyTorch:主要模块简介
  • 量子机器学习梯度估计新突破:SPSB方法实现常数开销训练加速
  • 系统架构师2026年5月
  • 播客主必看的AI语音合成合规红线,版权/声纹/数据跨境三重雷区全解析,错过即违规
  • Ubuntu 20.04插上网线没反应?手把手教你搞定RTL8111/8168/8411网卡驱动(附自动加载服务配置)
  • 分布式机器学习中的精度与效率权衡:从近似计算到自动驾驶实践
  • Juno平台TF-A安全调试功能恢复与配置指南