首页
社区
课程
招聘
[原创]某手游完整性校验分析
2023-11-2 23:16 11369

[原创]某手游完整性校验分析

2023-11-2 23:16
11369

前言

只是普通的单机手游,广告比较多,所以分析处理了下,校验流程蛮有意思的,所以就分享出来了

1.重打包崩溃处理

样本进行了加固,对其dump出dex后重打包出现崩溃

ida分析地址发现为jni函数引起



利用Xposed直接替换该函数,崩溃问题解决

1
2
3
4
5
6
XposedHelpers.findAndHookMethod("com.unity3d.player.UnityPlayerActivity", classLoader, "IsHDR_DisplayBoot", java.lang.String.class, java.lang.String.class, new XC_MethodReplacement() {
    @Override
    protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable {
        return null;
    }
});

2.卡加载界面处理

但是出现了新的问题,游戏卡在了加载页面



对该类的其他jni函数进行分析,发现supportVulkan很明显进行了签名读取,对其去除签名校验。

此处参考项目:ApkSignatureKillerEx

3.地图无法进入问题分析(基于原包)

去除完签名校验后,游戏能正常进入主页面,但是点击游戏地图没有任何反应。

在对原包进行多次测试发现,在首次启动游戏时,如果断网也会出现无法正常进入地图的问题,怀疑是游戏首次启动进行了网络请求进行数据校验

经过抓包对比定位到可疑数据包

利用Frida的算法通杀脚本没有定位到相关内容,怀疑是so层进行了请求

3.1 hook send函数进行调用栈分析

通过调用栈可以很清晰看到请求是从unity引擎相关的so中发出的。

3.2 il2cppTrace对请求调用栈分析

查阅资料,在unity中,网络请求主要通过UnityWebRequest 类来执行网络请求,利用frida-il2cpp-bridge对UnityWebRequest进行trace,打印调用栈得

1
2
3
4
5
6
7
8
9
10
0x0247db9c ┌─UnityEngine.Networking.UnityWebRequest::.ctor(this = UnityEngine.Networking.UnityWebRequest, url = "http://106.54.194.167:8077/CheckUpdate", method = "POST")
0x0247db00 │ ┌─UnityEngine.Networking.UnityWebRequest::set_url(this = UnityEngine.Networking.UnityWebRequest, value = "http://106.54.194.167:8077/CheckUpdate")
0x0247ef38 │ │ ┌─UnityEngine.Networking.UnityWebRequest::InternalSetUrl(this = UnityEngine.Networking.UnityWebRequest, url = "http://106.54.194.167:8077/CheckUpdate")
0x0247ef38 │ │ └─UnityEngine.Networking.UnityWebRequest::InternalSetUrl
0x0247db00 │ └─UnityEngine.Networking.UnityWebRequest::set_url
0x0247dc1c │ ┌─UnityEngine.Networking.UnityWebRequest::set_method(this = UnityEngine.Networking.UnityWebRequest, value = "POST")
0x0247e4e0 │ │ ┌─UnityEngine.Networking.UnityWebRequest::InternalSetMethod(this = UnityEngine.Networking.UnityWebRequest, methodType = Post)
0x0247e4e0 │ │ └─UnityEngine.Networking.UnityWebRequest::InternalSetMethod
0x0247dc1c │ └─UnityEngine.Networking.UnityWebRequest::set_method
0x0247db9c └─UnityEngine.Networking.UnityWebRequest::.ctor

在IDA中进行交叉引用分析定位到UnitySDKManager类

1
2
3
追踪调用定位到
class UnitySDK.UnitySDKManager.<PostData>d__25 : System.Object, System.Collections.Generic.IEnumerator<System.Object>, System.Collections.IEnumerator, System.IDisposable
    private Boolean MoveNext() { }

增加UnitySDKManager类重新对其trace(因为出现报错,把参数输出关了)

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
0x00b15b44 ┌─UnitySDK.UnitySDKManager::ServerVerifyApk
0x00b04b3c │ ┌─UnitySDK.UnitySDKManager::GetFileInfoList
0x00b04b3c │ └─UnitySDK.UnitySDKManager::GetFileInfoList
0x00b06ec0 │ ┌─UnitySDK.UnitySDKManager::GetGamePackageName
0x00b06ec0 │ └─UnitySDK.UnitySDKManager::GetGamePackageName
0x00b06204 │ ┌─UnitySDK.UnitySDKManager::GetSDKVersion
0x00b06204 │ └─UnitySDK.UnitySDKManager::GetSDKVersion
0x00b06278 │ ┌─UnitySDK.UnitySDKManager::HexStringToHex
0x00b06278 │ └─UnitySDK.UnitySDKManager::HexStringToHex
0x00b10fcc │ ┌─UnitySDK.UnitySDKManager::EncryptString
0x00b10fcc │ └─UnitySDK.UnitySDKManager::EncryptString
0x00b100a4 │ ┌─UnitySDK.UnitySDKManager::PostData
0x00a341fc │ │ ┌─UnitySDK.UnitySDKManager.<PostData>d__25::.ctor
0x00a341fc │ │ └─UnitySDK.UnitySDKManager.<PostData>d__25::.ctor
0x00b100a4 │ └─UnitySDK.UnitySDKManager::PostData
0x00a3422c │ ┌─UnitySDK.UnitySDKManager.<PostData>d__25::MoveNext
0x00b12be4 │ │ ┌─UnitySDK.UnitySDKManager::GetUrl
0x00b12be4 │ │ └─UnitySDK.UnitySDKManager::GetUrl
0x0247db9c │ │ ┌─UnityEngine.Networking.UnityWebRequest::.ctor
0x0247db00 │ │ │ ┌─UnityEngine.Networking.UnityWebRequest::set_url
0x0247ef38 │ │ │ │ ┌─UnityEngine.Networking.UnityWebRequest::InternalSetUrl
0x0247ef38 │ │ │ │ └─UnityEngine.Networking.UnityWebRequest::InternalSetUrl
0x0247db00 │ │ │ └─UnityEngine.Networking.UnityWebRequest::set_url
0x0247dc1c │ │ │ ┌─UnityEngine.Networking.UnityWebRequest::set_method
0x0247e4e0 │ │ │ │ ┌─UnityEngine.Networking.UnityWebRequest::InternalSetMethod
0x0247e4e0 │ │ │ │ └─UnityEngine.Networking.UnityWebRequest::InternalSetMethod
0x0247dc1c │ │ │ └─UnityEngine.Networking.UnityWebRequest::set_method
0x0247db9c │ │ └─UnityEngine.Networking.UnityWebRequest::.ctor
0x0247e0f4 │ │ ┌─UnityEngine.Networking.UnityWebRequest::set_uploadHandler
0x0247e0f4 │ │ └─UnityEngine.Networking.UnityWebRequest::set_uploadHandler
0x0247dfdc │ │ ┌─UnityEngine.Networking.UnityWebRequest::set_downloadHandler
0x0247dfdc │ │ └─UnityEngine.Networking.UnityWebRequest::set_downloadHandler
0x0247f66c │ │ ┌─UnityEngine.Networking.UnityWebRequest::SetRequestHeader
0x0247f66c │ │ └─UnityEngine.Networking.UnityWebRequest::SetRequestHeader
0x0247e42c │ │ ┌─UnityEngine.Networking.UnityWebRequest::Send
0x0247e430 │ │ │ ┌─UnityEngine.Networking.UnityWebRequest::SendWebRequest
0x0247e42c │ │ │ └─UnityEngine.Networking.UnityWebRequest::Send
0x0247e430 │ │ └─UnityEngine.Networking.UnityWebRequest::SendWebRequest
0x00a3422c │ └─UnitySDK.UnitySDKManager.<PostData>d__25::MoveNext
0x00a34cd4 │ ┌─UnitySDK.UnitySDKManager.<PostData>d__25::System.Collections.IEnumerator.get_Current
0x00a34cd4 │ └─UnitySDK.UnitySDKManager.<PostData>d__25::System.Collections.IEnumerator.get_Current
0x00b15b44 └─UnitySDK.UnitySDKManager::ServerVerifyApk

通过调用栈可以初步对校验流程进行了解,主要通过ServerVerifyApk函数进行校验,经过一系列字符串加解密,最后进行网络请求

3.3 加解密函数hook分析

对关键函数进行hook分析

1
2
3
4
5
6
0x00b10fcc │ UnitySDK.UnitySDKManager::EncryptString
0x00b0b13c │ UnitySDK.UnitySDKManager::DecryptString
il2cpp: EncryptString:"{"appid":"com.meta.peopleground.dream","appName":"com.meta.peopleground.dream","appVer":"1.0.1","sdkVer":"2","appFileList":"981lDpbOb+vjRn/3yI74fI+1S5cr08qKLGq0xg7TcfFOA7hRePAQjXPCgIzEC+p58RThRtl4PIsPEgiJZVYn1e2oZl06jeKGD6+C0Zy6lQaDiAVaOtO5szilZxfmN2j8XtlBmBaOzOVBBi6ctJamGsfM2XbkGqYtf/TlNyvOZ8p5YDdOgnhG+WonkjG1sGEbuqc4sm+hkCxOC7dX1rEM4/5S6wT1erPL5+iWJoRpiTuHok15zCQjdNFCXn97Vwg+/h8eXrtxrDvkkLqqCff2D0WrAISmIOi4fY97ulhpz5Pey2vJlkLK5MdSApENx4exv3t4Q1sVfoPojwDueXAW9T1RTOc1LeK0cYMrBjsE1W8="}"
il2cpp: EncryptString result:"xoJRLdGOAd21Q0JEIDiJqlciJFWy0jtiSLU7lNue9ZLAOxsuEkWvJwP8o3ub4CfcIjulssJC+ocK0ai3lkr1XfNXlLi2QJ0iTtYzbCDQqYmqXr7uTTdOrAK3Y6NKkd+mYgFF+J2w7tY/rl5NBPPlwRWWbvw45CRp3JX34BkKTA2KEXiWywj90T3Qw72AnpWgBOo1rD3USuAVQeN8EOhxVc1jTjsBn2qSfZX38tNC73wvceexa4w5wOea9rzaRshViPWjFkNko/BDuaAyWDJbB656FNatES6/WA3+f7qMlQmEU4BGrX72+StLzsNQYc+QZ4KDFnWwqfhuH1zCudxxDoNpi53fqv+DavLwmRRE19e1clgW5ANilhelSIGi9U9nq2BcNr3GMJ8eI2FAV76U1FHONbyMUggUYr6cDc9Vo9l5Wc4UVD4+tqDmulUW1L1s8NTkieyVDEJxb1o6166fVVWdUKNBlrIXDfbWjE6gLheTHfp1/ywUy4EcVSklERsEkdufZrdKM57XVkXcKFnA0hutpuKqCbbkthULifwkAtwyQ/qSdcZHIh6pNYGuLOFF5NiCsHhJ6D/O03/uUgsR/V5WwRGuOeZFsM2NK+NcyYjHDkoKh6SjoEnZZ/yfg1M4kOUJy5qG/V1jR8IqOmZ84w=="
il2cpp: DecryptString:"+CVWCEji2Lgf4nUTWb7J/Q=="
il2cpp: DecryptString result:"100"

发现之前抓包获得的请求体相对应,其中,appFileList还是密文,继续分析

3.4 追EncryptString函数

与dump.cs中的函数进行对照发现v14 由字符串转hex转base64获得

1
System.Byte[] HexStringToHex(System.String inputHex); // 0x00b06278

hook HexStringToHex 函数获得参数

1
il2cpp: inputHex:"f7cd650e96ce6febe3467ff7c88ef87c8fb54b972bd3ca8a2c6ab4c60ed371f14e03b85178f0108d73c2808cc40bea79f114e146d9783c8b0f120889655627d5eda8665d3a8de2860faf82d19cba95068388055a3ad3b9b338a56717e63768fc5ed94198168ecce541062e9cb496a61ac7ccd976e41aa62d7ff4e5372bce67ca7960374e827846f96a279231b5b0611bbaa738b26fa1902c4e0bb757d6b10ce3fe52eb04f57ab3cbe7e896268469893b87a24d79cc242374d1425e7f7b57083efe1f1e5ebb71ac3be490baaa09f7f60f45ab0084a620e8b87d8f7bba5869cf93decb6bc99642cae4c75202910dc787b1bf7b78435b157e83e88f00ee797016f53d514ce7352de2b471832b063b04d56f"

IDA继续查找调用发现其中字符串由GetFileInfoList函数获得

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// static System.IntPtr GetFileInfoList(); // 0x00b04b3c
__int64 sub_B04B3C()
{
  __int64 (*v0)(void); // x8
  __int64 v2[5]; // [xsp+0h] [xbp-40h] BYREF
  int v3; // [xsp+28h] [xbp-18h]
  char v4; // [xsp+2Ch] [xbp-14h]
 
  v0 = (__int64 (*)(void))qword_30365A0;
  if ( !qword_30365A0 )
  {
    v3 = 0;
    v2[0] = (__int64)"UnitySDK";
    v2[1] = 8LL;
    v2[2] = (__int64)"GetFileInfoList";
    v2[3] = 15LL;
    v2[4] = 0x200000000LL;
    v4 = 0;
    v0 = (__int64 (*)(void))sub_812FB4(v2);
    qword_30365A0 = (__int64)v0;
  }
  return v0();
}

很明显,调用了libUnitySDK.so中的GetFileInfoList函数获得

3.5 libUnitySDK.so分析

1
2
3
4
__int64 GetFileInfoList()
{
  return FileInfoListStr;
}

FileInfoListStr在writeFileJson函数中被赋值,而writeFileJson则是由java函数上文中的IsHDR_DisplayBoot调用(该函数首个参数为base.apk路径)

很明显,ll11l1l1ll函数 对之前的明文字符串进行了加密,frida hook打印参数

1
2
3
4
5
6
7
8
9
10
11
12
13
args: {
        "HashList" :
        {
                "AndroidManifest.xml" : " c72b1d2",
                "assets/bin/Data/Managed/Metadata/game.dat" : "a34a757d",
                "classes.dex" : "58b6cf21",
                "lib/arm64-v8a/libUnitySDK.so" : "b56d5af4",
                "lib/armeabi-v7a/libUnitySDK.so" : "3d548ba2"
        },
        "fileCount" : 1620
}
 
returnResult: f7cd650e96ce6febe3467ff7c88ef87c8fb54b972bd3ca8a2c6ab4c60ed371f14e03b85178f0108d73c2808cc40bea79f114e146d9783c8b0f120889655627d5eda8665d3a8de2860faf82d19cba95068388055a3ad3b9b338a56717e63768fc5ed94198168ecce541062e9cb496a61ac7ccd976e41aa62d7ff4e5372bce67ca7960374e827846f96a279231b5b0611bbaa738b26fa1902c4e0bb757d6b10ce3fe52eb04f57ab3cbe7e896268469893b87a24d79cc242374d1425e7f7b57083efe1f1e5ebb71ac3be490baaa09f7f60f45ab0084a620e8b87d8f7bba5869cf93decb6bc99642cae4c75202910dc787b1bf7b78435b157e83e88f00ee797016f53d514ce7352de2b471832b063b04d56f

其中 HashList 中文件对应的值为文件的 crc
至此,除了具体的字符串加密算法,游戏的校验流程已经很清晰

4.校验流程归纳

  • 签名校验
  • 读取base.apk进行关键文件crc读取
  • 进行服务器请求,对关键文件crc校验

5.后记

对该校验去除的思路:

  • 对读取安装包的函数 zip_open 或java层的 IsHDR_DisplayBoot 函数进行hook,进行参数替换,进行io重定向
  • 基于il2cpp中的ServerVerifyApk函数进行更详细的分析,直接对其检测去除

ps.仅限于学习交流,切勿用于商业用途,若造成侵权,请联系作者处理。
样本链接:https://pan.baidu.com/s/1awWYCF2nT50qPRj8_CRt-A?pwd=d8wn


[培训]《安卓高级研修班(网课)》月薪三万计划

最后于 2023-11-2 23:33 被wx_嗨编辑 ,原因: 编辑标题
收藏
点赞14
打赏
分享
最新回复 (15)
雪    币: 17901
活跃值: (25552)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
秋狝 2023-11-3 09:26
2
2
感谢分享
雪    币: 9
活跃值: (210697)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
shinratensei 1 2023-11-3 10:36
3
1
tql
雪    币: 187
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
Ruinv6 2023-11-3 11:25
4
1
感谢分享
雪    币: 2941
活跃值: (2324)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
黑屏 2023-11-3 13:25
5
1
tql
雪    币: 1556
活跃值: (7764)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
你瞒我瞒 2023-11-9 09:08
6
1
666
雪    币: 887
活跃值: (1974)
能力值: ( LV4,RANK:52 )
在线值:
发帖
回帖
粉丝
夏男人 2023-11-13 22:35
7
1
请问楼主样本还能分享一下吗
雪    币: 178
活跃值: (1573)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
wx_嗨 2023-11-14 11:48
8
2
夏男人 请问楼主样本还能分享一下吗
误删了,已补
雪    币: 37
活跃值: (238)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
万里星河 2023-11-17 16:19
9
1
支持一下
雪    币: 164
活跃值: (1875)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
FANGG3 2023-11-23 16:25
10
1
感谢分享
雪    币: 3464
活跃值: (3311)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
caolinkai 2023-12-2 11:07
11
1
感谢分享
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_ldbucrik 2024-1-4 09:42
12
1
wx_嗨 误删了,已补
楼主,样本链接失效了,请问可以传论坛不
雪    币: 27
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
zx_990497 2024-1-4 16:53
13
1
dddd
雪    币: 178
活跃值: (1573)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
wx_嗨 2024-1-15 10:00
14
1
mb_ldbucrik 楼主,样本链接失效了,请问可以传论坛不
补了
雪    币: 3486
活跃值: (3474)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
简单的简单 2024-1-15 11:41
15
1
这个案例很不错啊,值得学习
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_ldbucrik 2024-1-19 22:32
16
1
wx_嗨 补了
谢谢楼主
游客
登录 | 注册 方可回帖
返回