某手游完整性校验分析

前言

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

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.classnew 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函数进行更详细的分析,直接对其检测去除

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/136962.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java学习

Java的三大版本 Write Once、Run Anywhere JavaSE:标准版&#xff08;桌面程序,控制台开发…) JavaME:嵌入式开发(手机,小家电…) JavaEE: E企业级开发(web端&#xff0c;服务器开发…) JDK : Java Development Kit&#xff0c;Java开发者工具&#xff0c;在JRE之上扩充了一些…

算法进阶指南图论 通信线路

通信线路 思路&#xff1a;我们考虑需要升级的那条电缆的花费&#xff0c;若其花费为 w &#xff0c;那么从 1 到 n 的路径上&#xff0c;至多存在 k 条路径的价值大于 w &#xff0c;这具有一定的单调性&#xff0c;当花费 w 越大&#xff0c;我们路径上价值大于 w 的花费会越…

Unity 使用INI文件存储数据或配置参数预设

法1&#xff1a;调用外部Capi库 具体使用&#xff1a; public class Ini{//读取INI文件需要调用C的APP[System.Runtime.InteropServices.DllImport("kernel32")]private static extern long WritePrivateProfileString(string section, string key, string val, st…

STM32--系统滴答SysTick

一、SysTick是什么&#xff1f; Systick定时器是一个24bit的倒计时&#xff08;向下计数&#xff09;定时器&#xff0c;功能就是实现简单的延时。 SysTick 是一种系统定时器&#xff0c;通常在嵌入式系统中使用。它是 ARM Cortex-M 处理器的一个特殊定时器&#xff0c;用于提…

7.运算符

目录 一.算数运算符 1、算术运算符 2、比较运算符 1、等号()用来判断数字、字符串和表达式是否相等。 2、安全等于运算符(<>) 3、不等于运算符(<>或者!) 4、小于或等于运算符(<) 5、小于运算符(<) 6、IS NULL(IS NULL)&#xff0c;IS NOT NULL 运算…

[MySQL] MySQL表的基础操作

文章目录 一、创建表 1、1 SQL语法 1、2 实例演示 二、查询表 三、修改表 3、1 修改表名字 3、2 新增列&#xff08;字段&#xff09; 3、3 修改列类型 3、4 修改列名 3、5 删除表 四、总结 &#x1f64b;‍♂️ 作者&#xff1a;Ggggggtm &#x1f64b;‍♂️ &#x1f440; 专…

【MySQL日志与备份篇】数据库备份与恢复

数据库备份与恢复 文章目录 数据库备份与恢复1. 物理备份与逻辑备份2. mysqldump实现逻辑备份2.1 备份一个数据库2.2 备份全部数据库2.3 备份部分数据库2.4 备份部分表2.5 备份单表的部分数据2.6 排除某些表的备份2.7 只备份结构或只备份数据2.8 备份中包含存储过程、函数、事件…

微信聊天,收到二维码图片就自动帮你提取出来的方法

10-3 如果你是二维码收集的重度用户&#xff0c;那我非常推荐你好好阅读本文&#xff0c;也许可以帮你解决你的问题&#xff0c;比如做网推的人&#xff0c;需要常年混迹在各种微信群&#xff0c;那如何在各个微信群中收集到群友分享出来的二维码&#xff0c;并且要立即保存出…

吃透 Spring 系列—MVC部分

目录 ◆ SpringMVC简介 - SpringMVC概述 - SpringMVC快速入门 - Controller中访问容器中的Bean - SpringMVC关键组件浅析 ◆ SpringMVC的请求处理 - 请求映射路径的配置 - 请求数据的接收 - Javaweb常用对象获取 - 请求静态资源 - 注解驱动 标签 ◆ SpringMV…

推荐系统笔记--Swing模型的原理

1--Swing模型的引入 在 Item CF 召回中&#xff0c;物品的相似度是基于其受众的交集来衡量的&#xff0c;但当受众的交集局限在一个小圈子时&#xff0c;就会误将两个不相似的物品定义为相似&#xff1b; Swing 模型引入用户的重合度来判断两个用户是否属于一个小圈子&#xff…

C++基础(2)——类和对象

目录 1. 类的引入&#xff1a; 2. 类的定义&#xff1a; 2.1类的定义以及基本结构&#xff1a; 2.2 类的访问限定符&#xff1a; 3. 类的声明与定义的分离&#xff1a; 4. 类的实例化&#xff1a; 5. 类的大小计算&#xff1a; 1. 类的引入&#xff1a; 在数据结构系列的…

使用openvc进行人脸检测:Haar级联分类器

1 人脸检测介绍 1.1 什么是人脸检测 人脸检测的目标是确定图像或视频中是否存在人脸。如果存在多个面&#xff0c;则每个面都被一个边界框包围&#xff0c;因此我们知道这些面的位置 人脸检测算法的主要目标是准确有效地确定图像或视频中人脸的存在和位置。这些算法分析数据…

[Android]修改应用包名、名称、版本号、Icon以及环境判断和打包

1.修改包名 在Android Studio中更改项目的包名涉及几个步骤&#xff1a; 打开项目结构: 在Android Studio中&#xff0c;确保您处于Android视图模式&#xff08;在左侧面板顶部有一个下拉菜单可以选择&#xff09;。 重命名包名: 在项目视图中&#xff0c;找到您的包名&…

结构型设计模式07-享元模式

结构型设计模式07-享元模式 1、享元模式介绍 享元模式是一种结构型设计模式&#xff0c;旨在通过共享对象来减少内存使用和提高性能。它主要用于处理大量细粒度对象的情况&#xff0c;其中许多对象具有相似的属性和行为。 在享元模式中&#xff0c;对象分为两种类型&#xf…

互联网大厂招兵买马开发鸿蒙应用,移动开发的春天又来了?

日前&#xff0c;美团拟开发鸿蒙系统APP的多个相关岗位正招聘开发人员引发业内关注。事实上&#xff0c;鸿蒙开发者已经成为京东、WPS、凤凰新闻、微博等互联网大厂争相招聘的人才&#xff0c;且招聘岗位众多。也就是说&#xff0c;这些公司正在加快鸿蒙化开发&#xff0c;为鸿…

计算机毕业设计选题推荐-校园交流平台微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

录制GIF图,动态图

软件下载链接&#xff1a; https://www.cockos.com/licecap/ 参考链接&#xff1a; https://chat.xutongbao.top/

Linux学习第40天:Linux SPI 驱动实验(一):乾坤大挪移

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 主从工作方式完成数据交换&#xff0c;形象的说就是武侠中的乾坤大挪移。 本章实验的最终目的就是驱动 I.MX6UALPHA 开发板上的 ICM-20608 这个 SPI 接口的六轴传…

2023.11.13 hive数据仓库之分区表与分桶表操作,与复杂类型的运用

目录 0.hadoop hive的文档 1.一级分区表 2.一级分区表练习2 3.创建多级分区表 4.分区表操作 5.分桶表 6. 分桶表进行排序 7.分桶的原理 8.hive的复杂类型 9.array类型: 又叫数组类型,存储同类型的单数据的集合 10.struct类型: 又叫结构类型,可以存储不同类型单数据的集合…

【函数讲解】pygmo中的函数 fast_non_dominated_sorting() + 利用支配关系,学习一个SVM分类器,将解分为两类

这个函数是用来执行非支配排序的&#xff0c;可以分层构建Pareto&#xff0c;并返回每一层的解以及每个解支配其他解的索引、解被其他解支配的次数、解所在的非支配层级。这个函数对这些解进行非支配排序&#xff0c;并返回四个数组&#xff1a;ndf, dl, dc, 和 ndr。 ndf (Non…