吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4855|回复: 17
收起左侧

[Android 原创] 使用Frida Il2Cpp Bridge 方便地进行Il2Cpp实例Hook

  [复制链接]
hitachimako 发表于 2024-2-21 12:48

前言

近日在逆向某安卓手游的数据时,发现其AssetBundle、网络请求均使用AES加密。
使用Frida Il2Cpp Bridge工具可在仅使用已知名称的情况下,方便地实现对某个可实例化类型的实例钩取,查看其内部函数的调用参数和结果。

工具

将il2cpp lib和metadata.dat dump为dll结构的工具:il2cpp-dumper
Frida以及Frida server:Github
Frida Il2Cpp Bridge:Github
dnSpy:Github
IDA Pro 7.7:百度网盘
NodeJS:官方网站
Python:官方网站

使用环境

PC:Windows 11
手机:Mi 8
Root:Magisk 27.0

实战

对APK的预操作

  1. 使用解压软件解压APK,并找到/lib/arm64-v8a/libil2cpp.so/assets/bin/Data/Managed/Metadata/metadata.dat
  2. 启动il2cpp-dumper,依次选中libil2cpp.sometadata.dat,等候其生成dump出的文件。我们在此仅关注DummyDll文件夹中的Dll结构。
  3. 使用IDA(在此选用7.7版本,因为最新的8.3版本并不支持对arm64的反汇编)导入libil2cpp.so,并使用工具栏中的File/Script file...选取il2cpp-dumper目录下的ida_with_struct_py3.py,依次选择对应的文件即可将名称信息导入IDA。

使用IDA进行初步分析

经过一段时间的分析,发现目标程序对Unity.ResourceManager进行魔改,并加入了SecurityStreams类进行加密:
图片1.jpg
接着,打开IDA定位到此函数,并寻找其函数的调用,发现如下调用:
图片2.jpg
由此可知,IV通过文件名计算而来,Key通过ToArray函数而来,但此函数有5000多行,很难分析参数v307的结构和其偏移对应的值。
调用Decrypt函数的时候需要传入Key和IV,因此钩取该函数的调用参数值成为了最佳选择。

使用Frida和Frida Il2Cpp Bridge进行Hook

配置Frida环境

  1. 使用带有Root手机开启adb,使用adb push frida-server-xx.x.x-android-arm64 /data/local/tmp将其置入手机
  2. 使用adb shell进入命令行, su命令获取root权限,进入路径cd /data/local/tmp,为可执行文件添加权限chmod +x ./frida-server-xx.x.x-android-arm64然后启动可执行程序./frida-server-xx.x.x-android-arm64
  3. 在电脑上安装Fridapip install frida frida-tools

配置Il2Cpp Bridge环境

创建文件夹并创建文件index.ts编写package.json,使其识别index.ts为源代码,编译出frida可用的js。

{
  "main": "index.ts",
  "scripts": {
    "prepare": "npm run build",
    "loop": "frida-compile index.ts -w -o frida.js"
  },
  "dependencies": {
    "frida-il2cpp-bridge": "^0.8.8"
  }
}

在此目录下打开命令行并运行npm run loop会自动安装所需的库,该命令行会不断检测index.ts是否被修改,然后重新编译。
接着编辑index.ts,在首行加入import "frida-il2cpp-bridge",可成功编译,环境配置完毕。

编写Hook代码

编写一个Hook代码,使其定位Unity.ResourceManager,并Hook默认命名空间中的SecurityStreams类,对其进行Attach。

Il2Cpp.perform(() => {
        const SecurityStreams = Il2Cpp.domain.assembly("Unity.ResourceManager").image.class("SecurityStreams");

        Il2Cpp.trace(true).classes(SecurityStreams).and().attach();
});

进行Attach后,在该类被初始化、实例方法被调用时,能够自动在控制台输出详细参数。

进行Hook

index.ts被自动编译为frida.js完成后,使用frida注入程序。

frida -U -f app.package.name -l .\frida.js

进入游戏,函数被自动Hook,在调用Decrypted时,参数被打印于控制台:
图片3.jpg
由于读取多个bundle时的key一致,因此得出key,由于Unity asset bundle文件的前16字节是基本相同的,因此无需考虑iv造成的前16字节乱码,直接将其定义为可识别的头部即可通过AssetStudio等程序拆出资源文件。

举一反三

经过抓包,发现几乎所有网络请求都是被加密的。经过一段时间的搜寻,发现其网络请求函数SendTgServerEncrypted
图片4.jpg
但此函数并没有传入key等,由此可见key应该在函数内部进行计算。
此时打开IDA,对此函数进行分析:
图片5.jpg
可见NetworkUtils被初始化后直接获取该类的key和otp字段,然后进行密钥计算。由此可知key应该是固定值并在cctor中被赋值。
因此,对初始化函数进行分析:
图片6.jpg
由此可见,一些编译器生成的PrivateImplementationDetails偏移值被sub_EDC5C8初始化后才能够变成有效偏移值,因此通过静态分析得出真正的key值极为困难。
不过我们知道,想要通过AES加密必须初始化一个AES加密类,我们在此发现了游戏函数对该类型的引用:
图片7.jpg
mscorlib中的System.Security.Cryptography.Aes看起来并没有什么有用字段,通过翻阅一些文档,发现其实际上使用System.Core中的System.Security.Cryptography.AesCryptoServiceProvider.CreateDecryptor创建解密器,而该类型是这样的:
图片8.jpg
由此可见,我们仅需钩取System.Security.Cryptography.AesCryptoServiceProvider类即可得知CreateDecryptor函数被调用时的参数。
尝试钩取,特意发送几个网络请求,同时获得以下信息:
图片9.jpg
使用该密钥,空IV解密,发现前16byte为乱码,但16byte后明文完整。合理猜测response前16byte为IV,后面为密文,使用该逻辑成功解密出明文。

尾声

Il2Cpp Bridge还有很多其它实用功能,比如直接定位某函数并通过invoke()调用,获取返回值,若对其感兴趣可以阅读官方wiki

本人第一次在论坛发表文章,也是为了记录自己的逆向学习过程,若有不足请大佬多多指出!

免费评分

参与人数 3威望 +1 吾爱币 +22 热心值 +3 收起 理由
Raincccup + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
K0ongbai + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
正己 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

Smilience 发表于 2024-4-16 17:39
        public static string xxxx(string input)
                {
                        return null;
                } 对于这种该如何hook呢?这是我写的xxxxt.implementation = function (value: Il2Cpp.String): Il2Cpp.String
vs报错:Type '(this: Class | Object | ValueType, value: String) => String' is not assignable to type '(this: Class | Object | ValueType, ...parameters: Type[]) => ReturnType'.
  Types of parameters 'value' and 'parameters' are incompatible.
    Type 'Type' is not assignable to type 'String'.
      Type 'number' is not assignable to type 'String'.
 楼主| hitachimako 发表于 2024-2-23 11:14
zouzhiqiang 发表于 2024-2-23 11:09
frida-bridge就是根据ill2cpp源文件中一些特定的方法,计算方法加载时的偏移地址得到,如果ill2cpp.so文件 ...

符号表存在于metadata.dat,如果metadata加密,可以先不处理Apk,直接先注入il2cpp-bridge,然后会在默认目录生成无加密的metadata.dat。so混淆也可以通过类似Zygisk-Il2CppDumper这样的工具获取正常so文件。il2cpp-bridge默认通过从内存中获取符号表钩取,一般来说为了方便Bug寻找和维护,不会把原metadata直接进行obf
xixicoco 发表于 2024-2-22 23:05
yuqic987 发表于 2024-2-22 23:34
学习了,现在刚好在追个加密,头都追大了
Junlee 发表于 2024-2-23 08:24
这个厉害,很好的教程
zouzhiqiang 发表于 2024-2-23 11:09
frida-bridge就是根据ill2cpp源文件中一些特定的方法,计算方法加载时的偏移地址得到,如果ill2cpp.so文件中不存在符号表,就难搞咯
慵懒丶L先森 发表于 2024-2-23 13:11
Frida Il2Cpp Bridge工具之前就看到有了,但是找了很久都没有发现使用的教程,感谢分享
zouzhiqiang 发表于 2024-2-23 17:01
hitachimako 发表于 2024-2-23 11:14
符号表存在于metadata.dat,如果metadata加密,可以先不处理Apk,直接先注入il2cpp-bridge,然后会在默认 ...

我明白你的意思,metadata元数据文件确实是存放ill2cpp.so文件中的符号表和偏移,一般dump metadata.data文件,都是文件固定头部(AF 1B B1 FA)进行内存检索+ 偏移104(100或者108)个字节得到metadata文件大小,得到原始metadata.dat文件,但如果这些特征都没有呢??比如某神,它做的就比较好,你可以去试试
阿清 发表于 2024-2-23 17:35
多出一些这类教程 真不错
 楼主| hitachimako 发表于 2024-2-23 22:50
zouzhiqiang 发表于 2024-2-23 17:01
我明白你的意思,metadata元数据文件确实是存放ill2cpp.so文件中的符号表和偏移,一般dump metadata.data ...

这个我其实很早就研究过(老mhy玩家了),在il2cpp中搜索有关metadata.dat的字符串就会发现猫腻在MetadataCache::Initialize里,确实可以规避靠文件头搜索的dumper,还有相当多硬壳il2cpp和metadata同时加密,mhy对metadata的魔改不算是很强的加密,通用模式处理不了的加壳确实就只能对着ida推了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-14 00:46

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表