记一次Chunithm的逆向
前言
之前写过一篇关于中二的逆向,但是没什么成果,加上我确实没什么逆向的能力,所以删掉重新研究了一下
这次目标依然是实现AutoPlay,得益于Agent的发展,现在我们可以让Agent直接去访问IDA MCP来逆向,我们就不用啃反编译出来的狗屎了
注意:为了防止滥用,以下内容的函数偏移量/特征码被隐藏,仅提供思路给有能力的人去研究
所有操作都在私服进行,违规使用后果自负
逆向过程
首先的话我想起我挺久以前看到的这篇文章
https://cloud.tencent.com/developer/article/2216947
里面实现全自动理论值的方式是修改游戏判定,使所有判定都指向大P
这是一个不错的方案,但是会带来一点小问题
比如说,Note会在越过判定线后变成大J,这样观感不会太好
我想起之前maimai的一个项目
通过内部钩子启动游戏内置的Autoplay
我们可以合理推测,中二也有Autoplay
怎么确定呢?我们想一想在Tutorial里就有自动击打Note的场景
寻找Hook点
一开始想直接在String里搜索Autoplay,发现出来的并不是什么很有价值的东西
因此我打算从参考那篇文章,从判定的函数入手
我想起之前maimai的代码里面,与判定有关的函数一般都带有JudgeManger/JudgeTap之类的名字
因为中二是MSVC编译,因此大概率存在虚函数表,我们可以在IDA里搜索\\?\\?_7Judge.*
其中//?/_7是虚函数表在MSVC编译后的特征
Judge.*是通配符
很快就找到了 ??_7JudgeTapChecker@projView@@6B@ 这个函数虚表
我们对其交叉引用,发现在三个函数中被调用
第一个是这个

*this这个标志很明显,这是个构造器函数,因此我把他改名为了Constructor
顺着 xrefs 往下找,可以找到这个函数

很长,但是仔细看看可以发现这其实就是我们找的判定函数
里面有不少的if语句,最有意思的是截图中的这个if
这个 if 的判断在后面一大堆的 if 判定之前,后面的 if 是真正的判定逻辑,那这个 if 呢?
简单Hook一下发现这就是我们要的 Autoplay 的开关
直接用 Frida Hook 这个 if 判断的两个变量,使其恒成立,秒了
const engineInstance = safeReadPointer(ENGINE_INSTANCE_PTR); if (engineInstance.isNull()) return;
const autoKeyA = engineInstance.add(OFFSET_AUTO_KEY_A); const autoKeyB = engineInstance.add(OFFSET_AUTO_KEY_B);
safeWriteU8(autoKeyA, 1); safeWriteU32(autoKeyB, 1);说实话我不擅长读伪C 但MCP工具恰好弥补了这个缺点
虽然 LLM 做这方面并不聪明,但如果有思路提供给LLM的话他还是很强大的
别的实现思路
我们也给自己加点难度,假如Agent能力不足其实是Token用完了,我的能力也不足以纯静态去分析,那还有什么办法呢?
我依然觉得一开始的那篇文章有些思路很有意思也很好玩
通过Frida-trace追踪函数的调用,我们只需要追踪游戏中负责处理Note判定的函数,然后直接在IDA中找到对应的函数分析 再用Frida-trace
一开始我发现IDA/CE的Debbuger无法附加,因为segatools或者游戏本身的反调试
后来我发现Frida是可以Attach的,因为Frida并不是传统意义上的 Debbuger
官方的定义 它属于动态二进制插桩工具,不走传统 Windows Debugger API
但因为需要注入chusanhook.dll保证游戏能启动,所以我们还是没有办法直接让Frida去spawn
我们看到inject.exe有个用法
Usage: inject [options] program args...All options must precede the program name.
The following options are understood:
-h Print this message.
-d Attach to target as a debugger and print debug messages.
-p Pause the target until a debugger attaches to it. Cannot be used with -d.
-w Wait for target to terminate. Cannot be used with -d.
-k dll Inject the named DLL into the target process. Can be specified more than once.Segatools本身就提供了一个Inject的功能,我们可以让Frida-Gadget注入到游戏里,便于我们研究
之前只听说过Gadget,没真的实际用过,借着这个机会好好试试
我们去Frida的Release里下载Windows的Gadget Dll
放到游戏目录
inject_x86.exe -d -k chusanhook.dll -k frida-gadget.dll chusanApp.exe默认Gadget就是Listen模式,所以我们不需要改Gadget的配置,只需要确保27042端口是chusanApp在监听就是没问题的
那篇文章是通过frida-trace批量hook游戏的几乎所有函数
但其实现在不需要,我们可以用Stalker去做
我们可以用用Stalker.follow(mainThreadId)去追踪主线程的所有call行为,我们只在游戏里进行特定操作数次
最后会把所有可能的call输出出来,我们再全部hook,一边打Note一边看控制台输出,知道哪个函数是一定在我们打Note的时候调用的就可以
这样的好处是不用一下子Hook几万个函数让游戏直接闪退,也不用多次运行排除噪声了
题外话
实际上不用Gadget也是可以的,因为Frida的特性直接Attach就可以
动态寻找Hook点
现在的Hook代码依然是硬编码函数名和识别符,每次游戏大版本更新我们都要重新做这些,那有没有方法可以让Frida动态寻找函数并Hook呢
这里写不动了() 等我以后补上吧
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!