Featured image of post Find UnityCN Assets Decrypt key

Find UnityCN Assets Decrypt key

获取UnityCN游戏资源加密密钥通用流程

前言

UnityCN

UnityCN是Unity游戏引擎中国区的官方加密,对于其加密实现本文暂且不表,因为已经有很多现成的开源工具用于UnityCN的解包,可以自行查看其解密源代码理解加密流程

本文主要解决如何拿到UnityCN加密Key

判断是否是UnityCN加密

需要用到Il2CppDumper来dump出libil2cpp.so文件相关内容(libil2cpp.so文件在 lib/{cpu架构} 目录下),如何dump libil2cpp.so项目文档中已经详细列出,此处不再赘述。 在dump出的文件中找到dump.cs,使用notepad打开,搜索SetAssetBundleDecryptKey,如果能搜到说明该游戏极大概率(99.99%)是使用UnityCN加密的

1
2
	// RVA: 0x269C2CC Offset: 0x269C2CC VA: 0x269C2CC
	public static void SetAssetBundleDecryptKey(string password) { }

SetAssetBundleDecryptKey方法其中的String类型的password参数就是我们的目标

当然也有大佬能一眼看出(反正我是不能),通过直接观察ab头,参考AXiX大佬的:UnityCN的加密与解密

工具准备(环境安装)

硬件环境:

  • Windows电脑一台
  • Root手机一台(我使用的小米6X)(不要用模拟器,不要用模拟器!!!)
  • USB数据线

软件环境:

  • Python
  • VSCode
  • QTScrcpy(用于在电脑上操纵手机,非必须)
  • Nodejs(方法1)

Python安装frida

CMD命令:

1
2
pip install frida
pip install frida-tools

安装完成后输入如下命令,获取frida版本

1
2
C:\Users\430>frida --version
16.4.10

Windows安装ADB

可以在官方网站下载压缩包:Android SDK Platform-Tools下载完毕后解压,配置环境变量:系统变量->Path->新建环境变量->adb.exe所在目录。环境变量配置完毕后再CMD中输入adb –version命令查看安装信息,如果有正常版本输出则安装成功:

1
2
3
4
C:\Users\430>adb --version
Android Debug Bridge version 1.0.41
Version 33.0.1-8253317
Installed as ......\adb.exe

在手机上启动frida-server

首先要确定你手机的CPU架构,在手机中 开发者选项 找到 USB调试 选项,可以把相关功能都打开,这样就能畅快使用啦

开发人员选项

然后在Windows中打开CMD:

1
2
3
4
5
6
C:\Users\430>adb devices
List of devices attached
485dda7 device(说明已经连接上了)

C:\Users\430>adb shell getprop ro.product.cpu.abi
arm64-v8a(手机CPU架构为arm64-v8a)

在GitHub中找到frida项目地址:https://github.com/frida/frida 在Release中找到2.1节frida相应的版本(这里是16.4.10)

Frida Android Server 根据我的手机CPU架构arm64-v8a,选择对应的 frida-server-16.4.10-android-arm64.xz,使用压缩包工具打开,解压其中的文件并重名为frida_arm64_new(非必要步骤,只是为了方便), 需要将该文件复制到手机中,需要使用adb push命令。打开CMD:

1
2
3
4
5
6
7
8
C:\Users\430>adb push ......\frida_arm64_new /data/local/tmp (命令解析:adb push local remote)
......\frida_arm64_new: 1 file pushed, 0 skipped. 28.6 MB/s (56475128 bytes in 1.886s)

C:\Users\430>adb shell
wayne:/ $ su (获取super user权限)
wayne:/ # cd /data/local/tmp (进入目录)
126|wayne:/data/local/tmp # chomd 777 frida_arm64_new (将frida-server的权限设置为777,这样才能运行,否则会出现"can't execute: Permission denied"这样的错误)
wayne:/data/local/tmp # ./frida_arm64_new

在VSCode搭建frida-il2cpp-bridge

frida-il2cpp-bridge是一个 Frida 模块,用于在运行时转储、跟踪或劫持任何 Il2Cpp 应用程序,而无需 global-metadata.dat 文件。

搭建frida-il2cpp-bridge前,需要安装好Nodejs环境并配置好环境变量,然后下载frida-agent-example 并在VSCode中创建一个空项目,将frida-agent-example中内容放到该项目目录下

然后在VSCode中打开Powershell输入:

1
2
3
4
5
PS ......\frida> npm i frida-il2cpp-bridge
added 52 packages in 2s

5 packages are looking for funding
  run `npm fund` for details

此时项目目录下会多出一个node_modules文件夹

实战Hook,以纯白和弦为例

使用frida-il2cpp-bridge hook

在1.2节中已经提到SetAssetBundleDecryptKey 函数是我们要hook的核心。首先要dump出libil2cpp.so相关内容编写dump.ts

1
2
3
4
5
6
import "frida-il2cpp-bridge";

Il2Cpp.perform(() => {
    
    Il2Cpp.dump()
})

在VSCode中的Powershell使用frida hook命令。注入模式与启动命令解析:

注入模式 描述 命令或参数
Spawn 将启动App的权利交由Frida来控制,即使目标App已经启动,在使用Frida注入程序时还是会重新启动App -f
Attach 在目标App已经启动的情况下,Frida通过ptrace注入程序从而执行Hook的操作

由于不清楚so的加载时机我们采用Spawn模式注入App,com.rastar.cbhx 是包名可以通过MT管理器之类的工具或者查看,自行搜索

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
PS ......\frida> frida -U -f com.rastar.cbhx -l dump.ts
     ____
    / _  |   Frida 16.4.10 - A world-class dynamic instrumentation toolkit
   | (_| |
    > _  |   Commands:
   /_/ |_|       help      -> Displays the help system
   . . . .       object?   -> Display information about 'object'
   . . . .       exit/quit -> Exit
   . . . .
   . . . .   More info at https://frida.re/docs/home/
   . . . .
   . . . .   Connected to MI 6X (id=485dda7)
Spawned `com.rastar.cbhx`. Resuming main thread!
[MI 6X::com.rastar.cbhx ]-> il2cpp: dumping mscorlib...
il2cpp: dumping Mono.Security...
il2cpp: dumping System.Xml...
[MI 6X::com.rastar.cbhx ]-> il2cpp: dumping System.Core...
il2cpp: dumping UnityEngine.SharedInternalsModule...
il2cpp: dumping UnityEngine.CoreModule...
il2cpp: dumping UnityEngine.AssetBundleModule...
il2cpp: dumping UnityEngine.PhysicsModule...
il2cpp: dumping UnityEngine.TextRenderingModule...
il2cpp: dumping UnityEngine.AudioModule...
il2cpp: dumping UnityEngine.GridModule...
il2cpp: dumping UnityEngine.IMGUIModule...
il2cpp: dumping UnityEngine.ImageConversionModule...
il2cpp: dumping UnityEngine.InputLegacyModule...
il2cpp: dumping UnityEngine.Physics2DModule...
il2cpp: dumping UnityEngine.TextCoreModule...
il2cpp: dumping UnityEngine.UnityWebRequestAssetBundleModule...
il2cpp: dumping UnityEngine.XRModule...
il2cpp: dumping System.Diagnostics.StackTrace...
il2cpp: dumping System.Globalization.Extensions...
il2cpp: dumping System.IO.Compression...
il2cpp: dumping UnityEngine.AIModule...
il2cpp: dumping UnityEngine.AndroidJNIModule...
il2cpp: dumping UnityEngine.AnimationModule...
il2cpp: dumping UnityEngine.DirectorModule...
il2cpp: dumping UnityEngine.GameCenterModule...
[MI 6X::com.rastar.cbhx ]-> il2cpp: dumping UnityEngine.InputModule...
il2cpp: dumping UnityEngine.JSONSerializeModule...
il2cpp: dumping UnityEngine.ParticleSystemModule...
il2cpp: dumping UnityEngine.TerrainModule...
il2cpp: dumping UnityEngine.TilemapModule...
il2cpp: dumping UnityEngine.UIElementsModule...
il2cpp: dumping UnityEngine.UIModule...
il2cpp: dumping UnityEngine.UIWidgetsModule...
il2cpp: dumping UnityEngine.UnityAnalyticsModule...
il2cpp: dumping UnityEngine.UnityWebRequestWWWModule...
il2cpp: dumping UnityEngine.VFXModule...
il2cpp: dumping UnityEngine.VRModule...
il2cpp: dumping UnityEngine.VideoModule...
il2cpp: dumping UnityEngine...
il2cpp: dumping netstandard...
il2cpp: dumping DOTween...
[MI 6X::com.rastar.cbhx ]-> il2cpp: dumping UnityEngine.UI...
il2cpp: dumping BetterStreamingAssets...
[MI 6X::com.rastar.cbhx ]-> il2cpp: dumping DOTweenPro...
il2cpp: dumping GameLog...
il2cpp: dumping Google.Protobuf...
il2cpp: dumping IFix.Core...
il2cpp: dumping Unity.TextMeshPro...
il2cpp: dumping spine-unity...
il2cpp: dumping Unity.Timeline...
il2cpp: dumping Assembly-CSharp-firstpass...
il2cpp: dumping Assembly-CSharp...
il2cpp: dump saved to /storage/emulated/0/Android/data/com.rastar.cbhx/files/com.rastar.cbhx_1.0.20.cs (该路径为dump出的文件保存位置)

关闭App,

1
2
3
4
5
6
Process terminated
[MI 6X::com.rastar.cbhx ]->

Thank you for using Frida!
PS ......\frida> adb pull /storage/emulated/0/Android/data/com.rastar.cbhx/files/com.rastar.cbhx_1.0.20.cs dump.cs (使用pull命令从手机下载到电脑)
/storage/emulated/0/Android/data/com.rastar.cbhx/files/com.rastar.cbhx_1.0.20.cs: 1 file pulled, 0 skipped. 34.1 MB/s (10253448 bytes in 0.287s)

dump.cs 搜索SetAssetBundleDecryptKey,搜索到如下内容

1
2
3
4
5
6
7
// UnityEngine.AssetBundleModule
class UnityEngine.AssetBundle : UnityEngine.Object
{
    ...
    static System.Void SetAssetBundleDecryptKey(System.String password); // 0x0269c2cc
    ...
}

拿取到重要信息并编写 hook.ts:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import "frida-il2cpp-bridge";

Il2Cpp.perform(() => {
    console.log("Unity version: " + Il2Cpp.unityVersion);

    const img = Il2Cpp.domain.assembly("UnityEngine.AssetBundleModule").image;

    const AssetBundle = img.classes.filter(klass => klass.name.includes("AssetBundle"))[0];
    if (!AssetBundle) {
        console.error("AssetBundle not found!");
        return;
    }

    Il2Cpp.trace(true)
    .classes(AssetBundle)
    .filterMethods(method => method.name.includes("SetAssetBundleDecryptKey"))
    .and()
    .attach();
});

运行hook.ts

1
PS ......\frida> frida -U -f com.rastar.cbhx -l hook.ts

如果没有信息输出,则点击提示下载资源包下载资源包,不用着急。

加密key就是yulong1868gnoluy ,把它转成16进制后就可以在解密软件中使用了:79756c6f6e6731383638676e6f6c7579

常规方法hook

hook.js:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Interceptor.attach(Module.findExportByName(null, "dlopen"), {
    onEnter: function(args) {
      this.path = Memory.readCString(args[0]);
    },
    onLeave: function(retval) {
      if (this.path.indexOf("libil2cpp.so") !== -1) {
        console.log("[+] libil2cpp.so loaded at: " + retval);
  
        var baseAddr = Module.findBaseAddress("libil2cpp.so");
  
        if (baseAddr) {
          console.log("[+] libil2cpp.so base address: " + baseAddr);
  
          var targetFunc = baseAddr.add(0x269C2CC);
          Interceptor.attach(targetFunc, {
            onEnter: function (args) {
              console.log("SetAssetBundleKey called");
  
             
              var strArgPtr = args[0];
              var strArg = Memory.readCString(strArgPtr);
              console.log(hexdump(strArgPtr));
            },
            onLeave: function (retval) {
              console.log("SetAssetBundleKey returned");
            }
          });
        } else {
          console.error("[-] Failed to find base address of libil2cpp.so");
        }
      }
    }
  });

Powershell:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
PS D:\Game\noname\TOOL\AAAAA_Reverse\frida\tutorial\frida> frida -U -f com.rastar.cbhx -l hook.js
     ____
    / _  |   Frida 16.4.10 - A world-class dynamic instrumentation toolkit
   | (_| |
    > _  |   Commands:
   /_/ |_|       help      -> Displays the help system
   . . . .       object?   -> Display information about 'object'
   . . . .       exit/quit -> Exit
   . . . .
   . . . .   More info at https://frida.re/docs/home/
   . . . .
   . . . .   Connected to MI 6X (id=485dda7)
Spawned `com.rastar.cbhx`. Resuming main thread!
[MI 6X::com.rastar.cbhx ]-> [+] libil2cpp.so loaded at: 0x2a0f8c1209669ad7
[+] libil2cpp.so base address: 0x7bc108c000
SetAssetBundleKey called
             0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
7b046fc7c0  00 38 f2 c0 7b 00 00 00 00 00 00 00 00 00 00 00  .8..{...........
7b046fc7d0  10 00 00 00 79 00 75 00 6c 00 6f 00 6e 00 67 00  ....y.u.l.o.n.g.
7b046fc7e0  31 00 38 00 36 00 38 00 67 00 6e 00 6f 00 6c 00  1.8.6.8.g.n.o.l.
7b046fc7f0  75 00 79 00 00 00 00 00 00 00 00 00 00 00 00 00  u.y.............
7b046fc800  50 41 e9 0b 7b 00 00 00 00 00 00 00 00 00 00 00  PA..{...........
7b046fc810  00 00 00 00 00 00 00 00 07 00 00 00 00 00 00 00  ................
7b046fc820  06 00 00 00 01 00 00 00 02 00 00 00 05 00 00 00  ................
7b046fc830  ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00  ................
7b046fc840  00 38 f2 c0 7b 00 00 00 00 00 00 00 00 00 00 00  .8..{...........
7b046fc850  0c 00 00 00 50 00 61 00 79 00 4c 00 61 00 73 00  ....P.a.y.L.a.s.
7b046fc860  74 00 4f 00 72 00 64 00 65 00 72 00 00 00 00 00  t.O.r.d.e.r.....
7b046fc870  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
7b046fc880  00 38 f2 c0 7b 00 00 00 00 00 00 00 00 00 00 00  .8..{...........
7b046fc890  0f 00 00 00 53 00 63 00 65 00 6e 00 65 00 4d 00  ....S.c.e.n.e.M.
7b046fc8a0  69 00 72 00 72 00 6f 00 72 00 46 00 61 00 6c 00  i.r.r.o.r.F.a.l.
7b046fc8b0  67 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  g...............

在地址7b046fc7d3-7b046fc7f2中可以解析出yulong1868gnoluy,(由于Unity引擎String类型使用的是UTF-16小端序2字节表示一个字符)

常见游戏UnityCN Key(网络收集) 持续更新中…

name key
PGR GLB/KR 6B75726F6B75726F6B75726F6B75726F
PGR CN/JP/TW 7935585076714C4F72436F6B57524961
Archeland/Kalpa of Universe 426C61636B4A61636B50726F6A656374
Archeland 1.1.14 50726F6A65637441726368654C616E64
Neural Cloud 31636162383436663532393031633965
Higan: Eruthyll 45317832633361346C35693662377572
White Chord 79756C6F6E6731383638676E6F6C7579
Mecharashi 33384338334631333245374637413041
Castlevania: Moon Night 31323334353637383132333435363738
Huā Yì Shān Xīn Zhī Yuè 494E484A6E68647970716B3534377864
Doula Continent 52346366773339474644326661785756
Bless Global 6C6F6E67747567616D652E796A66623F
Starside 41394A3542384D4A50554D3539464B57
Resonance Soltice 5265736F6E616E63655265626F726E52
Oblivion Override 7179666D6F6F6E323331323433343532
Dawnlands 636F6465737339353237636F64657373
BB 5F6C4E3F3A3F233F3F3F3F663F1A3F3F
Dynasty Legends 2 746169686567616D6573323032323032
SevenSphere 3753484446474356433836356B6B514C
Evernight CN 68687878747478736868787874747873
Xintianlong Babu 61323562623133346363326464333265
Frostpunk: Beyond the Ice 7368756978696E673838383838383838
Elpis 79756e67756968616f77616e31323334

后记

本人自学安卓逆向个人理解笔记,仅做学习用途

感谢 ChatGPT 提供技术支持
感谢 暗夜十三司 提供代码思路

参考文献: