首页
社区
课程
招聘
[原创]战双帕弥什分析(一) 资源解包方法
发表于: 2022-8-24 09:08 14539

[原创]战双帕弥什分析(一) 资源解包方法

2022-8-24 09:08
14539

之前因为某些原因需要获得战双帕弥什的一些资源,所以尝试对其进行解包,本人的解包技巧其实也不高,但由于之前战双安全做得有点糟糕,所以还是解出来了,当然最近发现战双换了一种加密方式,这种方式已经失效了,但还是有参考意义,趁着秋招结束了有点时间,因此打算发一些工作供别人参考。

 

将 apk 解包,显然,由其中的 libil2cpp.so 可以发现其使用的 unity 的 li2cpp 打包,使用该技术打包的游戏会将原 C# 代码转为 C++ 来增大破解难度。

 

为什么要转为 C++ 呢,因为 C# 编译器会将其源码编译为中间语言(il)来解析执行,这样提高了 C# 的跨平台性,同时 il 语言也有着不少优点,但 il 有一个安全隐患,会保存大量的元数据,如果 C# 等.NET 框架下的语言没有采用任何安全措施打包,利用这些元数据可以几乎无损地获得打包程序的源代码,常用的安全措施一般是混淆,高强度的混淆可以有效地提高对方的破解成本,而将 C# 转为 C++ 则可以使游戏等程序更加安全。

 

而对于 il2cpp 的应对方法,由于 C# 一定会产生元数据,而 il2cpp 本质上还是像 il 利用元数据的那一套,所以我们依然可以利用其元数据 (metadata) 来获取一些信息。

 

先尝试将用 IDA 打开 libil2cpp.so,可以发现无法打开,进行了加密,但对 so 文件加密作用是不大的,Linux 中的 so 文件相当与 Windows 里面的 dll 文件,是放入内存由 cpu 执行的,因此它必须要解密后放入内存,因此我们可以跳过加密之前从内存 dump,这里需要使用的是安卓模拟器(逍遥模拟器),Gameguardian(从内存 dump 数据),具体的使用方法网上有的。
图片描述
导出 libil2cpp.so,然后使用 Il2CppDumper 得到原 C# 的 il 代码中的元数据,具体网上也有教程的。

 

然后用 dnspy 什么的打开 Assembly-CSharp.dll 什么的,查找加密解密的关键字,可以发现下面这个东西:
图片描述
疑是密钥,用 IDA 打开导出的 libil2cpp.so 并定位到改地址:
图片描述
疑是解密算法,其是用 unity 自带的解密算法解密的,打开 unity 编写以下代码:

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEditor;
using System.IO;
using System;
 
public class MainBehaviourScript : MonoBehaviour
{
    Text listText;
    Text matrixPath;
    Text configPath;
    private Texture2D duplicateTexture(Texture2D source)
    {
        RenderTexture renderTex = RenderTexture.GetTemporary(
                    source.width,
                    source.height,
                    0,
                    RenderTextureFormat.Default,
                    RenderTextureReadWrite.Linear);
 
        Graphics.Blit(source, renderTex);
        RenderTexture previous = RenderTexture.active;
        RenderTexture.active = renderTex;
        Texture2D readableText = new Texture2D(source.width, source.height);
        readableText.ReadPixels(new Rect(0, 0, renderTex.width, renderTex.height), 0, 0);
        readableText.Apply();
        RenderTexture.active = previous;
        RenderTexture.ReleaseTemporary(renderTex);
        return readableText;
    }
    // Start is called before the first frame update
    void Start()
    {
        //listText = GameObject.Find("Canvas/Panel/Text").GetComponent<Text>();
        matrixPath = GameObject.Find("Canvas/matrixPath").GetComponent<Text>();
        configPath = GameObject.Find("Canvas/configPath").GetComponent<Text>();
    }
 
    public void OnClick1()
    {
        configPath.text = MyLocalDialog.GetDir().Trim('\0');
    }
 
    public void OnClick2()
    {
        matrixPath.text = MyLocalDialog.GetDir().Trim('\0');
    }
 
    public void OnClick3()
    {
        AssetBundle.SetAssetBundleDecryptKey("kurokurokurokuro");
        FileStream newfst = new FileStream(configPath.text + "\\newoutfile.txt", FileMode.Create, FileAccess.Write);
        FileStream allfst = new FileStream(configPath.text + "\\alloutfile.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite);
        StreamWriter newfWriter = new StreamWriter(newfst);
        StreamWriter allfWriter = new StreamWriter(allfst);
        StreamReader allfReader = new StreamReader(allfst);
        Dictionary<string, int> myDictionary = new Dictionary<string, int>();
        DirectoryInfo TheFolder = new DirectoryInfo(matrixPath.text);
        string outdir = MyLocalDialog.GetDir().Trim('\0') + "\\";
        if (outdir == "")
            return;
        //istText.text = "";
        string str;
        bool outtext = GameObject.Find("Canvas/TextToggle").GetComponent<Toggle>().isOn;
        bool outpic = GameObject.Find("Canvas/PicToggle").GetComponent<Toggle>().isOn;
        while ((str = allfReader.ReadLine()) != null)
        {
            myDictionary.Add(str, 1);
        }
        foreach (FileInfo NextFile in TheFolder.GetFiles())
        {
            MyLocalDialog.MessageBox((IntPtr)0, NextFile.ToString(), "提示", 0);
            var ab = AssetBundle.LoadFromFile(NextFile.ToString());
            Debug.LogError(ab);
            string[] patht = ab.GetAllAssetNames();
            MyLocalDialog.MessageBox((IntPtr)0, patht[0], "提示", 0);
            return;
            foreach (string s in patht)
            {
                if (!myDictionary.ContainsKey(s))
                {
                    var o = ab.LoadAsset(s);
                    if (o == null)
                        continue;
                    if (outpic && o.GetType() == typeof(Texture2D))
                    {
                        Texture2D tx = (Texture2D)o;
                        Texture2D readableText = duplicateTexture(tx);
                        string file_path = outdir + s;
                        int len = file_path.Length - 1;
                        while (len > 0 && file_path[len] != '/')
                            len--;
                        Directory.CreateDirectory(file_path.Substring(0, len));
 
                        byte[] filedata = readableText.EncodeToPNG();
                        using (var fs = new FileStream(file_path, FileMode.Create, FileAccess.Write))
                        {
                            fs.Write(filedata, 0, filedata.Length);
                        }
                        newfWriter.WriteLine(s);
                        allfWriter.WriteLine(s);
                    }
                    else if (outtext && o.GetType() == typeof(TextAsset))
                    {
                        TextAsset ta = (TextAsset)o;
                        string file_path = outdir + s;
                        int len = file_path.Length - 1;
                        while (len > 0 && file_path[len] != '/')
                            len--;
                        Directory.CreateDirectory(file_path.Substring(0, len));
                        using (var fs = new FileStream(file_path, FileMode.Create, FileAccess.Write))
                        {
                            fs.Write(ta.bytes, 0, ta.bytes.Length);
                        }
                        newfWriter.WriteLine(s);
                        allfWriter.WriteLine(s);
                    }
                }
            }
        }
        newfWriter.Close();
        newfst.Close();
        allfWriter.Close();
        allfReader.Close();
        allfst.Close();
        MyLocalDialog.MessageBox((IntPtr)0, "导出完成!", "提示", 0);
    }
 
    public void OnClick4()
    {
        AssetBundle.SetAssetBundleDecryptKey("kurokurokurokuro");
        var ab = AssetBundle.LoadFromFile("E:\\matrix2\\fff6ce1e25b48a043aa026247cbb231fbd2f225c");
        //string[] patht = ab.GetAllAssetNames();
        //Debug.LogError(patht[0]);
        var o = ab.LoadAsset("assets/product/scene/sceneres/common/scene020/textures/2003.png");
        MyLocalDialog.MessageBox((IntPtr)0, o.name, "提示", 0);
    }
    // Update is called once per frame
    void Update()
    {
 
    }
}

发现可以解密:
图片描述
上面是很早前写的了,有点乱,现在密钥也已经换了,我2022年七月份又尝试了一下发现没有更换解密算法, 而且用以下代码可以转成常规没有加密的 ab 包:

1
2
3
AssetBundle.SetAssetBundleDecryptKey(key);
// var ab = AssetBundle.LoadFromFile(infile);
AssetBundle.RecompressAssetBundleAsync(infile,outfile,BuildCompression.Uncompressed,0,0);

密钥就不提供了。


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 1
支持
分享
最新回复 (6)
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
大佬你好,请问解包功能只能在Unity Pro或者Unity Plus里面才能用?我测试的时候LoadFromFile和RecompressAssetBundleAsync都会失败。密钥是直接改UnityEngine接口获取的,没有错。
2022-10-9 12:26
0
雪    币: 4
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
mb_bbpgdtlc 大佬你好,请问解包功能只能在Unity Pro或者Unity Plus里面才能用?我测试的时候LoadFromFile和RecompressAssetBundleAsync都会失败。密钥是直接改Uni ...
大佬您好,我在用il2cppdumper时出现metadata超出索引字长的问题,搜索了一下可能是metadata加密了,然后换用了zygisk-il2cppdumper,但是一旦开启模块战双就会闪退,请问是怎么解决的.
2022-10-11 11:25
0
雪    币: 4
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
mb_ibjjjhqf 大佬您好,我在用il2cppdumper时出现metadata超出索引字长的问题,搜索了一下可能是metadata加密了,然后换用了zygisk-il2cppdumper,但是一旦开启模块战双就会闪退 ...
今天又继续看了一下,metadata似乎是没有加密的,所以又继续用il2cppdumper试了一下,但是发现自动搜索metadataRegistration是0,现在改为手动输入,找了一些教程发现不一样,所以这个metadataRegistration怎么找到呢?codeRegistration是正常的。
2022-10-11 16:14
0
雪    币: 4
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
mb_ibjjjhqf 今天又继续看了一下,metadata似乎是没有加密的,所以又继续用il2cppdumper试了一下,但是发现自动搜索metadataRegistration是0,现在改为手动输入,找了一些教程发现不一 ...
现在问题应该是找到了,gg导出的so文件缺失了一部分,导致metadataRegistration找不到,现在还没有找到有什么方法解决这个问题.大佬是用的什么版本模拟器和gg呢
2022-10-12 19:16
0
雪    币: 4715
活跃值: (4255)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
破解这种资源有什么用呢
2022-10-12 20:11
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
一开始用的Unity2021,RecompressAssetBundleAsync根本没反应,也不报错,后来换了2018.4.30f后就成功解密了(战双的资源包就是这个版本的)
2024-4-19 14:21
1
游客
登录 | 注册 方可回帖
返回
//