diff --git a/content/generated/metas.json b/content/generated/metas.json index b24c406..60c93a3 100644 --- a/content/generated/metas.json +++ b/content/generated/metas.json @@ -3,8 +3,8 @@ "categories": [["置顶", 1], ["影评", 2], ["技术", 1], ["日记", 2], ["梦", 1], ["想想想", 1]], "pins": [14], "posts": [ - {"id": 14, "title": "部落格索引", "subtitle": "按分类、标签检索", "category": "置顶", "pinned": 1, "file": "content/posts/Index.md", "tags": [], "date": "2025-04-04T06:01:51.646173", "url_name": "Index", "content": "按分类检索:\n\n\n\n按主题检索:\n\n"}, - {"id": 13, "title": "《A Minecraft Movie》影评", "subtitle": "桂桂评分:★★☆☆☆ 2/5", "tags": ["影评"], "category": "影评", "file": "content/posts/2025-04-04 Minecraft Movie.md", "date": "2025-04-04T00:00:00", "url_name": "2025-04-04-Minecraft-Movie", "content": "Minecraft 电影刚上映,虽然已经知道了评价不太好,但是毕竟是陪伴了我 12 年的游戏还是第一天去看了,结果不出意料让我有些失望。\n\n首先说优点,我觉得对游戏机制还原得还不错——水桶速降、鞘翅烟花加速、末影人的视线仇恨、铁傀儡仇恨、喂两个动物吃食物会迷之冒出小动物(?)把原本游戏里这些不合逻辑的奇妙机制都展现出来了。\n\n缺点的话,可能就是「演出感」太浓了,感觉主角团智商完全没上线。很多困境都是只要留心就不会遇到的,也有很多次绕了大圈去解决很琐碎的事情。比起「遇到困境之后想出了解决方案」更像是「为了展示一个特定的解决方案而创造了困境」... 很多解决方案都是比起实用性更在意演出效果和 meme 程度。\n\n作为一个 Minecraft 电影我可以说它既没有 Mine 也没有 Craft... 就比如,整个电影结束没有一个人造出工作台或者把摆在那里的工作台带走就好过分啊?离开村子起飞的时候还把武器掉下了,真的没有一个人想到之后可能会用到?之后果然就遇到了需要打架但是没有武器的场面,然后大家还完全没对武器丢了感到惊讶,就像从来就没有造过武器一样?然后主角 Henry 去翻宝箱找 Earth Crystal 的时候翻到了两把武器,却只拿了自己的,多余的也没给朋友拿上...\n\n感觉就像是一个解谜地图的作者在直播玩自己的解谜,如果已经知道了接下来要怎么解为什么要动脑子?但是不动脑子的话,预先设想好的解法在观众眼里就变成了最繁琐最莫名其妙的。\n\n还有就是剧情实在太平淡了,超耿直毫无转折完全可以预测接下来会发生什么,也几乎没有人物塑造。感觉导演的优先级是 玩梗 > 展示游戏机制 >>> 角色成长 > 冒险解谜。\n\n不过看到结尾还是有些感动。结尾主角团拯救了 Minecraft 世界之后把从这里获得的成长和知识带到了现实中,解决了现实中各自的困境... 我觉得这部电影最真实的一幕可能就在这里。\n\n我也是从搭建建筑,到设计起红石电路,再到写起自己的插件和模组,最后像电影里一样走出 Minecraft 把这些技能带到现实,给我了有想法就自己去创造的勇气和能力——这才是我最喜欢 Minecraft 的原因,也是这个电影试图表达的,即使表达得不是很好。\n\n总之我给 2/5 星,也许适合关掉脑子看"}, + {"id": 14, "title": "部落格索引", "subtitle": "按分类、标签检索", "category": "置顶", "pinned": 1, "file": "content/posts/Index.md", "tags": [], "date": "2025-04-04T06:10:54.107988", "url_name": "Index", "content": "按分类检索:\n\n\n\n按主题检索:\n\n"}, + {"id": 13, "title": "《A Minecraft Movie》影评", "subtitle": "桂桂评分:★★☆☆☆ 2/5", "title_image": "https://profile-content.hydev.org/content/posts/Assets/2025-04-04%20Minecraft%20Movie/title.png", "tags": ["影评"], "category": "影评", "file": "content/posts/2025-04-04 Minecraft Movie.md", "date": "2025-04-04T00:00:00", "url_name": "2025-04-04-Minecraft-Movie", "content": "Minecraft 电影刚上映,虽然已经知道了评价不太好,但是毕竟是陪伴了我 12 年的游戏还是第一天去看了,结果不出意料让我有些失望。\n\n首先说优点,我觉得对游戏机制还原得还不错——水桶速降、鞘翅烟花加速、末影人的视线仇恨、铁傀儡仇恨、喂两个动物吃食物会迷之冒出小动物(?)把原本游戏里这些不合逻辑的奇妙机制都展现出来了。\n\n缺点的话,可能就是「演出感」太浓了,感觉主角团智商完全没上线。很多困境都是只要留心就不会遇到的,也有很多次绕了大圈去解决很琐碎的事情。比起「遇到困境之后想出了解决方案」更像是「为了展示一个特定的解决方案而创造了困境」... 很多解决方案都是比起实用性更在意演出效果和 meme 程度。\n\n作为一个 Minecraft 电影我可以说它既没有 Mine 也没有 Craft... 就比如,整个电影结束没有一个人造出工作台或者把摆在那里的工作台带走就好过分啊?离开村子起飞的时候还把武器掉下了,真的没有一个人想到之后可能会用到?之后果然就遇到了需要打架但是没有武器的场面,然后大家还完全没对武器丢了感到惊讶,就像从来就没有造过武器一样?然后主角 Henry 去翻宝箱找 Earth Crystal 的时候翻到了两把武器,却只拿了自己的,多余的也没给朋友拿上...\n\n感觉就像是一个解谜地图的作者在直播玩自己的解谜,如果已经知道了接下来要怎么解为什么要动脑子?但是不动脑子的话,预先设想好的解法在观众眼里就变成了最繁琐最莫名其妙的。\n\n还有就是剧情实在太平淡了,超耿直毫无转折完全可以预测接下来会发生什么,也几乎没有人物塑造。感觉导演的优先级是 玩梗 > 展示游戏机制 >>> 角色成长 > 冒险解谜。\n\n不过看到结尾还是有些感动。结尾主角团拯救了 Minecraft 世界之后把从这里获得的成长和知识带到了现实中,解决了现实中各自的困境... 我觉得这部电影最真实的一幕可能就在这里。\n\n我也是从搭建建筑,到设计起红石电路,再到写起自己的插件和模组,最后像电影里一样走出 Minecraft 把这些技能带到现实,给我了有想法就自己去创造的勇气和能力——这才是我最喜欢 Minecraft 的原因,也是这个电影试图表达的,即使表达得不是很好。\n\n总之我给 2/5 星,也许适合关掉脑子看"}, {"id": 12, "title": "世界计划 缤纷舞台!feat. IL2Cpp", "title_image": "https://profile-content.hydev.org/content/posts/Assets/2024-10-24%20PJSK%20Reversing/crop.png", "subtitle": "从零开始解密 pjsk 的网络请求", "tags": ["ctf", "教程"], "file": "content/posts/2024-10-24 PJSK Reversing.md", "date": "2024-10-24T00:00:00", "url_name": "2024-10-24-PJSK-Reversing", "content": "玩了这么久プロセカ,有点想分析一下游戏的排行数据,做一些自动化,比如让自己一直停留在整两百名之类的。这种手游抓抓包模仿请求一定不会很难吧——装上 Reqable 连上模拟器抓抓包偷吃几块饼干之后发现,请求负载和返回内容都依然是乱码...\n\n
只偷吃到了饼干渣
\n\n看到乱码的第一反应是编码不对,或者过了某种基础混淆,总之先丢给 CyberChef 看看魔法糊糊器能不能把答案糊出来,发现不行。去群里问问之后 Menci 和我说是 AES 加密过的,还告诉我了加密密钥在 APIManager 里面,那就试着找找吧\n\n## 1 从拆开 APK 开始\n\n因为プロセカ的客户端能够解密,那么解密密钥一定就在客户端里。首先下载了プロセカ 4.0.0 的 XAPK 包。所谓的 XAPK 其实就是把多个 APK 压缩在一起啦,其中一个包含了所有架构共享的文件,另一个包里放了 arm64 平台专属的二进制(虽然这个游戏好像还没有编译过除了 arm64 以外的平台,不知道为什么还要这样分包)\n\n解压缩之后单独用 apktool 把两个安装包的文件拆出来看一看吧。目录结构看上去就是一个一般的 IL2Cpp Unity 项目,没有什么特别的。编译过的游戏二进制在 lib/arm64-v8a/libil2cpp.so 里面,然后运行它、让它能够和 C# 那边的程序对接需要的元信息在 assets/bin/Data/Managed/Metadata/global-metadata.dat 里面。\n\n
两个重要的文件
\n\n如果想要快速找到代码的话,只看 IL2Cpp 编译好的的二进制肯定不行。这个文件里一共有 44 万个没有名字的函数指针,IDA 把这些分析出来都花了快一个小时,从这里一个一个找肯定是不行的。所以我搜索了一下 IL2Cpp 反编译,找到了 Perfare/IL2CppDumper 这个工具,试了一下发现报错说 \"Metadata file supplied is not valid metadata file\"\n\n
正常的元文件的文件头
\n\n为什么呢?看了一下,一般的 global-metadata 的文件头是 0xAF1BB1FA,而我拿到的这个不是,也就说明它很可能被加了壳。\n\n## 2. 脱壳元文件\n\n怎样解密呢?首先试了同一个作者的另一个工具 Zygisk-IL2CppDumper,是一个据说可以自动解密元文件的 Magisk 模块,但是构建好装上之后打开游戏发现什么都没有发生,说会解包到 /data/data 里面但是也没有,还是试试别的方法吧。\n\n既然上次用 Cheat Engine 在电脑上从内存中脱壳了 maimai 的 Assembly-CSharp,我觉得应该也可以用同样的方式脱壳プロセカ的元文件,所以就下载了一份 Game Guardian 试了一下。打开游戏,挂载上 GG,内存搜索 \"H AF1BB1FA\" 之后马上就找到了!\n\n
搜索到了内存
\n\n把地址复制下来,去第四个 Tab 把地址粘贴进去之后点旁边的选择器选中这个区域的头到尾,导出成文件之后重命名成新的 global-metadata.dat 就可以了。之后重新试了一下用 IL2CppDumper 反编译就没有报错了!\n\n
反编译成功了!
\n\n虽然还是有 \"This file may be protected\" 的报错,原本还以为 libil2cpp.so 也被套了壳,结果发现并没有,只是识别到了 JNI_OnLoad 预警性地报了一个错而已,也就是说已经成功啦。看了一眼 Dumper 输出的 `dump.cs`,里面确实有 APIManager,里面也确实用到了 AES 加密,但是因为实现的部分被转成二进制了,这里只能看到定义。\n\n## 3. 找钥匙!\n\n接下来就是找钥匙的环节,但是怎么找呢?如果要找到钥匙的话,大概需要看一下 APIManager 的代码实现,可是 `dump.cs` 里面只有空壳没有实现,可是只看 libil2cpp.so 又没办法知道 APIManager 在哪... 怎么办呢?看了一下 Dumper 仓库里面写的文档,看起来像是能和 IDA 联动让实现和空壳结合在一起的样子,那就下载一个 IDA 试试吧。\n\n下载了 IDA 9.0,其实不太会用,我只懂得基础操作比如用 F5 切换反编译代码和汇编之类的,但是总之先打开二进制 libil2cpp.so 看看吧。刚打开的时候什么都没有,只有不到一百个函数,我还在怀疑是不是打开错文件了,但是等了十分钟又出现了很多函数,发现原来它只是还没有分析完... 可能 150MB 大的二进制对于 IDA 来说也太大了吧,完整分析完花了差不多一个小时,存的数据库有 2 GB 大 (╯’ – ‘)╯︵ ┻━┻\n\n
好大文件
\n\n分析完之后依然没有函数名,全部都是 sub_1234567 这样的数字编号。看着 Dumper 的文档试着用 File > Script file 跑了一下那个 ida_py3 的脚本,选中了 script.json 之后函数名确实出现了!不过这只是第一步。\n\n看了 `dump.cs` 里面的函数定义之后,我觉得 CreateCrypt 这个函数比较像是我们要找的,因为是一个静态函数,调用了之后会凭空造出来一个 FastAES 的实例,而 FastAES 需要传入两个钥匙才能创建实例,所以无论怎样这里都会用到钥匙吧?\n\n
APIManager 的函数定义
\n\n看看反汇编的二进制代码发现确实用到了密钥。虽然变量全都被打乱了,但是从 FastAES 的构造器参数可以看出来 v3 v4 是 AES128 的两个钥匙 (key, iv),然后规格是 AES128,密文模式 1 (CBC),补格模式 2 (PKCS7)。那么这两个变量是从哪里来的呢?从代码里也能看到,v2 是从 v1 + 184 的指针地址读过来的,v3 是 v2 的值,v4 是 v2 的下一个值也就是 v1 + 192,而 v1 是 APIManager_TypeInfo 的地址。\n\n\n\n从 C# 的逻辑上想的话,TypeInfo 应该是一个类附带的常量或者变量类型的地方,从 `dump.cs` 里面可以看到它定义的变量指针偏差从 0 加到 8 加到 0x20, 0x28... 可是这里面没有 +184 = 0xb8 这个位置,为什么呢?\n\n
APIManager 类的变量偏差最高到了 0x38
\n\n我在这里卡了超级久,然后看了一下构造器发现 +184 这个偏差原来是在调用构造器的时候动态写进去的,是代码里面没有定义的,也许是某种编译器优化吧。打开 `APIManager$$.cctor` 构造器首先发现的是它写入了指向 +184 的地方 (L23 行),写入的是 v3 的值。向前读可以看到 v3 就是 v1,是一个空的 16 字节数组,然后把 v2 的数据复制到了 v1,而 v2 是 `Field__` 开头的这一长串。\n\n
cctor 构造器的反编译代码
\n\n回到 `dump.cs` 里面查找这个字段,发现它是一个编译器生成的只读项,上面下面也有类似的东西,看这个 \"Static Array Init Type Size\" 的话这些应该就是代码里用到的数组常量被转移到的位置了。但是这一长串的十六进制似乎只是某种指针,数值似乎不在这里——因为如果是数值的话,数组的长度对不上——我们要找的 AES128 密钥是 128 位也就是 16 字节,但是这一长串每个都有 32 字节,和前面的 Array Size 也对不上。那么数值在哪呢?\n\n
十六进制 offset 在编辑器里五颜六色
\n\n看到后面五颜六色的 offset 那里写了注释 \"Metadata offset\",我就在想难道它会在 global-metadata 里面吗?用 hexed.it 打开了这个文件然后把 offset 复制到 Go to 栏里面转向,发现那里真的有一个 16 位的钥匙!接下来又按同样的方式找到了 16 位的 iv,整个 AES 加密就被破解啦(在这里打一下码,需要的话看完这篇肯定就能够自己找到了 quq)\n\n
找到了 16 位的钥匙
\n\n## 4. 可以解密了!\n\n接下来就是解密的过程了,把请求内容和返回内容都导出成文件,再随手用搓两行 python 脚本就可以解密啦:\n\n```python\nimport sys\nfrom Crypto.Cipher import AES\nfrom Crypto.Util.Padding import unpad\nfrom pathlib import Path\n\naes = AES.new(b\"- key here qwq -\", AES.MODE_CBC, b\"- iv here meow -\")\ndec = lambda x: unpad(aes.decrypt(x), AES.block_size, style='pkcs7')\n\n[f.with_suffix(\".dec\").write_bytes(dec(f.read_bytes())) \n for f in Path(sys.argv[1]).glob(\"*\")]\n```\n\n解密之后似乎是一个 pjsk 自己的二进制序列化格式,看起来还需要一些时间研究怎样把它转换成 json 呢,不过今天就到这里好啦,累累睡睡(x)\n\n
解密之后的明文
"}, {"id": 11, "title": "示波器破解移动光光猫", "title_image": "https://profile-content.hydev.org/content/posts/Assets/2024-07-30%20%E5%85%89%E5%85%89%E7%8C%AB/IMG_20240730_100300.avif", "subtitle": "跑不掉了喵~", "tags": ["ctf"], "file": "content/posts/2024-07-30 光光猫.md", "date": "2024-07-30T00:00:00", "url_name": "2024-07-30-光光猫", "content": "今天去 syama 家帮忙破解了移动光光猫 🐱\n\n因为笔记本太重了,syama 想要在外面用 iPad 远程桌面直连到家里的笔记本上工作,但是移动光猫有防火墙 ipv6 没办法直连,而且用户配置界面并没有办法关掉防火墙,所以想要找到 CMCCAdmin 超管密码。\n\n通常来说超管密码直接找宽带师傅要或者找移动内鬼查就能查到,但是这台是公寓提供的集客宽带所以没办法联系师傅,内鬼也查不到密码。\n\n去网络论坛上看了其他人的破解方法,一月份的时候似乎还可以通过 192.168.1.1/hidden_version_switch.html 之类的网页后门直接开启 telnet,但是试了很多全都被堵住了。看来软件是没办法了,拆机看看\n\n
拆机后的完整布局
\n\n光光猫型号是 SK-D747,拆机之后看到了这个 PCB 的完整布局,中间是铠侠的 128MB E2PROM,旁边有八个测试孔,左侧是光纤接入口(系统里是一个 PCIe 设备...??)右边应该是 CPU 和 DDR3 DRAM,上边是两个 LDO 分别转 3.3V 和 5V,右上方还有一些空的焊盘应该是高配版多出来的网口。\n\n
发现了 E2PROM 芯片旁边的八个测试引脚
\n\n在 E2PROM 旁边的这八个测试孔,四个焊了排针,四个没焊。通常光猫上都会留用来测试的 UART 串口,但是串口只能够用到 TX RX GND 三条线,TX 用来向对方发送,RX 用来接收,GND 作为判断传输电压的参照物。排针上显然并没有文档,没有标记每个引脚代表什么,还可能是 RS232,I2C,或者 SPI 也说不定,那要怎样判断每个测试孔代表着什么呢?\n\n先断电用电表测试线路接通的模式戳一戳好了,因为一条线路的所有焊点都连在一起,所以只要确认了一个点就可以找到所有了。首先 GND(地线)是最容易找到的,因为元件的外壳通常就会接到 GND。把电表的一端点到 USB 口的金属外壳上,另一端轮流点一下每个测试孔就能看出从上面数的口 0、4、7 都是接地。\n\n接下来还剩五个不确定的孔,通电测一下电压好了。孔 1 是 5V,孔 5 是 3V,6 是 0.074V。只看电压其实不是很有头绪呢,把照片发到嵌入式群里,群友猜 5 6 分别是 TX 和 RX,但是插上串口工具发现并没有输出,反过来也没有。\n\n
电表针好长感觉不注意就会把什么戳短路
\n\n突然想到,既然之前买了示波器就试试用示波器看看这些孔的波形好了!虽然之前没有用过但是也不会太难理解吧?打开包装有一个主机和两根测笔,像电表一样,那一定是在测量这两根针之间的波形吧?一根戳上 GND 另一根戳上测试点,点一下自动... 不对啊,看上去无论点哪里都是一个正弦 ~ 波?这看起来也不像是时钟啊?\n\n...然后把照片发给群友问问,发现我完全理解错了(悲)测量到的波形也只是无线电噪音 \n\n实际上示波器的每根测笔是单独的波形,有两条线,侧边的夹子是电压的参照,而测针是相对的信号。读结果的时候也理解错了,虽然看上去是很大幅度的正弦波,但是右边显示着只有 5mV/格... 也就是说看到的这个波形上下差距一共不到 10mV,对于任何信号传输都太小了。居然这么小的电压区别也能测量出来哇\n\n学会使用示波器之后把示波器夹子接地,针点到每个引脚,调到 1V/格 的显示比例,发现脚 2 是稳定 3V,脚 3 有零到 3V 的 TTL 高低电平切换,由此推断 2 3 才是 TX 和 RX。这次接上 UART 终于看到日志输出了!\n\n
用示波器看到了上下切换的数据电平,高低电压分别代表 1 和 0
\n\n接上之后就交给 menci 了。重启路由器,终端打了很多日志,似乎是 uboot 在启动被运营商魔改的 OpenWRT。等开机结束之后导出日志,在里面搜索 \"pwd\" 找到了 root 密码 🌚 真安全。登录进 root 之后去读文件 /userconfig/cfg/db_user_cfg.xml 就拿到了超管密码,然后我累累睡睡,menci 念一串魔法配好了桥接和公网 v6 直通,好厉害\n\n
躺在床上听着魔法少女 menci 调网咒语 ASMR 睡着了
\n\n配网相关的事情可以右转去读读 [menci 的博客](https://blog.men.ci/),虽然我也想学学配 ipv6 但是这两天通宵爆肝实在没有力气啦,等加拿大 Bell 有了 ipv6 再学学好了。\n\n总之这次学会了用示波器很开心!~~会用之后就看到什么都想用示波器点一点了~~"}, {"id": 10, "title": "Maimai 街机音游逆向!", "subtitle": "从拆包到脱壳到魔改遇到的各种坑", "tags": ["ctf"], "file": "content/posts/2023-11-30 Maimai Reversing.md", "date": "2023-11-30T00:00:00", "url_name": "2023-11-30-Maimai-Reversing", "content": "不久之前某位朋友拿到了最新最热的 maimai DX 某版本的 .app 镜像,然后和ta一起折腾了几天拆包脱壳之后终于成功启动了!在这里简单写一下解包过程和学到的事情,作为记录也作为一个教程吧,希望之后尝试解包的人不需要踩同样的坑了。\n\n## 0x1 解压 .app 包\n\n首先,朋友拿到的文件是一个 .app,通常在官方渠道中通过 U 盘或者网络下发给官方的街机框体,这也是所有 Sega 游戏都在用的传统格式。解压起来也已经有工具和教程。首先 .app 是使用 AES128 加密的,是一个对称密钥加密算法,然而已经有人帮忙把真正街机上的密钥复制出来了,接下来就是按教程解密啦,工具和教程都在[这个仓库](https://gitdab.com/SEGA/sega/src/branch/master/tools/Filesystem)里面。\n\n解密之后是一个两层的 VHD 文件。第一层似乎文件元信息是坏的,大部分硬盘工具分区工具解压工具都打不开,不过试了各种方案发现在 Linux 上用 qemu-nbd 可以挂载,虽然会报错。挂载用了下面那一串指令,之后把 /mnt 里面的第二层 VHD 拷出来就可以直接用 7zip 解压了。\n\n```sh\nsudo modprobe nbd\nsudo qemu-nbd -c /dev/nbd0 \"outer.vhd\"\nsudo mount /dev/nbd0 /mnt\n# /mnt 里面应该有 internal_{N}.vhd\n```\n\n下面分别是两层 VHD 的文件头和 file 信息,`conectix` 开头的是第二层,`eb5290 NTFS` 开头的是第一层(以下用旧版中二节奏举例):\n\n\n\nqemu-nbd 加载了第一层之后连 fdisk 都是懵的,即使是这样乱来也能挂载上也真是好神奇:\n\n\n\n## 0x2 Crackproof 脱壳\n\n解开 .app 包之后脱壳反而是最难的步骤,因为没有找到任何针对 SEGA 街机的脱壳教程(所以我才在写这篇教程!)解压好文件之后发现直接运行会闪退,为什么呢?用文件识别工具 [DIE (detect it easy)](https://github.com/horsicq/Detect-It-Easy) 检测一下发现主程序 Sinmai.exe 被一个叫 HyperTech Crackproof 的工具加密过 (aka. “Htpac”)。\n\n\n\n啊但是 Sinmai.exe 明明就只是一个 Unity 启动器,Unity 的话所有游戏的启动器都是一样的,让每个游戏不同的是 {Game}\\_Data 里面的 dll 文件,所以我直接去下了一个[开源 GTA 圣安地列斯](https://github.com/GTA-ASM/SanAndreasUnity)然后把启动器拷出来改名成 Sinmai.exe 就可以了!\n\n\n\n...然后还是没法启动,怎么回事呢?用 `diec` 扫描一下目录里面的所有 exe 和 dll 发现还有四个文件是加密过的:\n\n* amdaemon.exe\n* Sinmai\\_Data/Managed/Assembly-CSharp.dll\n* Sinmai\\_Data/Plugins/Cake.dll\n* Sinmai\\_Data/Plugins/amdaemon_api.dll\n\n查了一下,首先我看到 SEGA 工具库里面有一个叫 [DecryptCrackproof](https://gitdab.com/SEGA/sega/src/branch/master/tools/Crackproof/DecryptCrackproofExe64) 的工具,感觉这个尝试起来是最简单的,所以就先用它试了一下。这个工具确实自动解出来了 amdaemon.exe,但是剩下三个 DLL 都失败了,报错说 CRC32 算法的长度 out of range of valid values。\n\n\n\n看着这个报错,第一眼觉得应该是这个算法实现的问题,毕竟从 stack trace 可以看出传给 CalcChecksum 的参数是 UInt32,而 CRC32 函数接收的参数是 Int32... 会不会是长度超了 Int32 的最高值呢?所以就用 DotPeek 反编译、导出项目重新编译再 Debug 饶了一大圈,然后发现并不是长度超过 Int32 最高值,而是超过了整个文件的长度... 而请求长度又是一串非常不标准的计算算出来的,看来应该是 HyperTech 他们换了加密算法,并不是改一两行就能修好的。\n\n\n\n接下来有点没有头绪,到处查查也没有查到有用信息,有一个 [iatrepair](https://github.com/rakisaionji/iatrepair) 仓库的脱壳思路是开一个 QEMU 有 2GB 内存的 VM 里面跑游戏,等游戏加载完之后把整个系统的内存 dump 出来再解包分析... 感觉这个方法好灵车,而且这个加密也会检测系统是不是 VM。\n\n接下来去群里问了问,群友给我了一份街机系统上会有的 odd.sys 驱动,用 OSRLoader 加载进系统就可以启动游戏了,虽然是黑屏而且不能正常启动 amdaemon。原本以为解密代码在驱动里面,所以先用 IDA Pro 打开驱动看看能不能提取出来,但是发现似乎只有一些 hashing 的 PID 验证和字符串处理,返回一个处理过的字符串,这样看来这个驱动应该是让用户进程上传识别信息来生成真正解密密钥的工具。\n\n\n\n但是感觉要反汇编找到真正解密的代码再重新实现出来有点太复杂了... 还是试试直接从内存里面找吧。\n\n下载一个 CheatEngine 用来查看内存,然后启动游戏。刚开始发现 CheatEngine 的调试器挂到 Sinmai 进程上会报错,上网查了一下说可以试试在设置的调试器选项里开 DBVM 内核驱动模式,再把 Extras 里面的用内核模式浏览内存选上,然后果然就可以挂了!\n\n\n\n接下来打开内存视图,先看了一遍 Memory Regions 里面标注的 DLL 名,但是似乎没有我想要找的 DLL 的样子。那怎么办呢?看看有没有只属于这个 DLL 的可识别信息吧,用 hexed.it 打开上一代别人解包好的 Assembly-CSharp.dll 发现文件末尾有一串元信息,写着 \"InternalName Assembly-CSharp.dll\",每个字符中间间隔着一个 NULL \\\\0 字符。\n\n\n\n所以我在 CE 内存视图里面搜这段文字然后就找到了!正好只有一个匹配结果。然后记录一下最左边的地址,打开内存区域视图找到这个地址所属的区域,右键把整个区域存下来,然后用 hexed.it 打开把 CE 的文件头去掉(截到 DLL 文件头那里)再把文件尾补到整 32 位,保存改名成 DLL 就可以了!\n\n\n\n之后就只差 Cake.dll 和 amdaemon_api.dll 了,可惜这两个我用同样的方法没有解出来。amdaemon_api 在游戏里并没有加载,而 Cake 是一个包含了依赖的 dll,被拆成了很多不同的内存区域导致让它完整结合回去比较麻烦,但是这两个文件在不同版本中几乎没有改动,所以就直接拿上一个版本别人脱好壳的用了(一般会放在 segatools 文件夹里一起发出来)\n\n## 0x3 魔改\n\n可惜脱完 dll 壳并不是直接就能玩,还要去掉一些街机验证。因为 maimai 是一个 Unity 游戏,是用 C# 写的,而众所周知 .NET 运行时的结构和 Java 差不多都是反编译出来几乎直接是源码的,所以魔改思路就是把 Assembly-CSharp 反编译成源码,改好所有的代码报错,改掉验证然后再编译回去啦。\n\n\n\n...结果还在修报错的时候发现朋友已经魔改完了,所以就先用别人的啦,具体需要做哪些魔改等下次拿到最新最热数据再研究好了 qwq\n\n## 0xF 总结\n\n总之这是我第一次做 Windows 二进制程序脱壳,感觉有别人写好的文档和群友帮忙也没有想象的那么难(以前打 CTF 从来都不敢碰 binary 题的呜呜呜)虽然是朋友先解出来魔改完了有点扫兴,但是学到了很多脱壳技巧还是很开心的!现在就差给 [AquaDX](https://github.com/hykilpikonna/AquaDX) 加服务器支持了...(下次再写 qwq"},