某加速器APP分析
前言:
我之前在逍遥模拟器安装Xposed框架后,提示需要在线下载文件,为了下载这个文件,便把加速器APP安装到模拟器,然后发现它有Root检测,在模拟器中运行不起来。
为了能在逍遥模拟器上使用这个加速器APP,便有了以下研究。
本次实验所应用到的工具
1.jadx
2.MT管理器
3.frida
4.objeciton
5.JeB
6.IDA
分析过程如下:
1.打开APP,发现APP存在环境检测。
2.利用jadx查看AndroidManifest.xml文件,找到并进入APP第一个启动的Activity。
在StartActivity发现检测代码,从代码中可以看出这里有非常多的if()语句用来检测环境,判断为真后就弹框,并return.
3.看看这些检测函数的代码逻辑,看它都做了什么。
DeviceUtils.a(),通过代码可以知道,这是一个通过特定文件来检测Root的函数。
分析下EmulatorDetectUtil.a(startActivity),从函数名上看,它的作用应该是检测模拟器,它返回了a.a(context)||Native detect()
看看a.a(context),它return c(context) || b(context) || b() || a(),
a()代码逻辑是,如果/proc/cpuinfo文件中含有”intel”或”AMD”字符串,就return true,否则return false.(这样检测是因为正常手机不会是intel或AMD型号的CPU。)
c(context) , b(context),b() 这三个函数调用Native中的PropertiesGet.a
(String),该函数使用_system_property_get()来获取系统属性值来判断是否为模拟器。
函数通过以下特性判断是否为模拟器:
ro.kernel.qemu该值在模拟器中为1,通常在正常手机中没有该属性。
ro.product.model:该值在模拟器中为sdk,通常在正常手机中它的值为手机的型号。
4.此外APP还进行了Xposed,代理检测等,如何绕过这些检测呢?
解决上面的检测我有两个方案
一,用frida Hook对应的函数,统一返回false。
二, 修改对应的smali语句,将eqz修改成nez。
这里我采用第二个方案,因为可持久化。
5.以DeviceUtils.a()为例,实现第二个方案,过Root检测。
找到检测函数的smali,定位到关键地方if-eqz p1,:cond_30
意为如果p1寄存器的值为0,则跳转到cond_30,而cond_30后的代码就是弹框及retun语句。
这里将eqz改成nez即可绕过检测。
如遇到其他检测,如法炮制即可。
6.完成以上的环境检测绕过后,
APP发生了闪退,估计是在某个地方把APP Kill了。
这个Kill函数在哪呢?简单的看了下代码,没发现Kill函数。那咋办?
用神器objection Hook下常见的Kill函数,打印出调用栈不就知道它在哪了吗
常见的APP退出函数有
1.KillProcess(),android.os.Process.killProcess(android.os.Process.myPid());
2.System.exit(),Java.lang.System.exit()
Hook亿下
很明显com.speed.speed_library.module.main.MainActivity.n 调用了exit()
7.定位到n()看看,哇塞这么多exit()。
从代码中可以看出这个n()也是用来检测环境的,环境不对就会执行exit(),退出APP.
解决它也很简单,删除com.speed.speed_library.module.main.MainActivity.onCreate中调用n()语句就行。(PS:jadx反编译出来的onCreate()中并没有调用n()的语句,而对应的smali中有invoke n(),smali和objection的结果都能说明jadx静态反编译出来的结果并不准确)
8.删除Oncreate中的n()
9.再次打开APP,又显示”未知错误”。
10.在源码中搜索不到”未知错误”,猜测它可能来自服务端,抓包看看。
好像是base64?解码后结果是乱码。估计是对称加密,hook下系统的加解密函数,看下哪里有调用。
11.一顿搜索,Hook后定位到这个可疑的函数,它非常有可能是用来解密的。
12.Hook这个函数看看,它的入参str是Respone Body,str2,str3估计是Key和IV,
返回值是none?!怎么回事?估计是key,iv的原因,因为它们的结尾是很明显的”error”,分析下它们是怎么来的吧
13.定位到调用解密函数的地方,结合之前的分析可以得出
a4=str1=Respone Body
st2=str3=g.f5211b.a().getResources().getString(R.string.d_key_three) + g.f5211b.W() + DeviceUtils.ddmm(g.f5211b.a()).
str2,str3都是由三个字符串拼接而成,前两个字符串是常量,分别为”MNEI“
和“pTzYn”,第三个字符串则由Native函数ddmm()返回
14.用IDA看看ddmm(),通过流程图和伪代码可以看出,函数逻辑是,如果getSignature()为true就返回vAsvTho,否则返回error.
15.ddmm()返回值的问题可以这样解决:在smali中定义一个const-string v6替换ddmm()的返回值即可。
16.在Hook这个解密函数看看。数据已经解密出来了,可以确定解密函数的IV和KEY就是
”MNEIpTzYnvAsvTho”
17.终于能正常打开应用了!废了不少功夫。
18.如何能一直使用呢?
1)购买VPN?下策。这个梯子经常连不上Google。
2)每日签到可以获取15分钟的使用时间,分析签到流程,然后一直签到?这太麻烦了。
3)新用户可以免费使用两小时,一直当新用户?这个可以,两个小时可以满足我的需求了。
那方案三该如何实现呢
19.通过分析”设备登陆”的数据包,可以得知APP是通过DeviceId来识别用户
20.分析了DeviceId是如何生成的,生成过程中调用了很多函数,这里省略了一些函数的图,。
33DFDFER21.B75VGF5XIY.EB2FBCB874=
com.speed.speed_library.b.e.f5202a.a(g.f5211b.a()).a()=
e.a.a(v2).a()=kotlin.h.g.a(kotlin.h.g.a(v0, "\r", "", false, 4, null), "\n", "", false, 4, null)=
g.a(arg0, arg1, arg2, ((boolean)(((int)arg3))))=
c.a(g.a(((CharSequence)arg19), new String[]{arg20}, arg22, 0, 4, null), ((CharSequence)arg21), null, null, 0, null, null, 62, null)=
c.a(arg4, arg5, v12, v0, (arg11 & 8) == 0 ? arg8 : -1, v2, arg10)=
((StringBuilder)c.a(arg9, ((Appendable)new StringBuilder()), arg10, arg11, arg12, arg13, arg14, arg15)).toString() =
arg3
21.DeviceId的生成过程比较复杂,其实不用深究DeviceId的生成,可以写一个返回String的函数生成对应的smali语句,再去替换原来的smali即可,如下图。
22.重新登陆APP,发现已经是一个新的ID,VIP时间也增加了两个小时。
分析总结
个人感觉 JEB比jadx准确,建议使用JEB进行分析。
APP 对JAVA代码进行了混淆,并在Java和Native层,分别对Root,模拟器,Xposed框架,网络代理等进行了常规检测,一定程度提高了APP的安全性。
APP可以进行加固或字符串加密,动态注册,ollvm等技术手段来进一步提高APP的安全性,防止重打包,破解等。
出于业务安全考虑,最好将识别用户的关键数据DeviceId的生成算法写入SO中,并使用自定义加密算法,将关键数据进行加密后传输,这样能有效提高安全性,并减少损失。
仅提供技术交流,不对任何成员利用技术文章或者检测工具造成任何理论上的或实际上的损失承担责任。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课