无限续杯——从app破解角度学习安卓保护手段

移动安全 2年前 (2023) admin
312 0 0

无限续杯——从app破解角度学习安卓保护手段

本文为看雪论坛优秀文章

看雪论坛作者ID:乐子人


最近tz不太好用,之前用的破解版的某洞已经彻底挂了。于是在网上找了一个某豹加速器。但是下载后发现只有两小时的会员时间,用完后还要花重金续费。作为社会主义爱国青年,面对这种违法app还在肆无忌惮的收费的现象,当然是不能忍了,于是决心破解之,以达到维护中华人民共和国宪法与法律尊严的目的!


违法app截图:

无限续杯——从app破解角度学习安卓保护手段





root检测


将app装在手机上打开,结果有提示:

无限续杯——从app破解角度学习安卓保护手段
应该是检测到root了,因为我在用一台root过的手机进行实验。


使用jadx加载apk,全局搜索关键字:检测到当前设备。
无限续杯——从app破解角度学习安卓保护手段

确实有这个字符串,他的id名叫 toast_app_fail,再次搜索这个id名看看哪里引用了这个:
无限续杯——从app破解角度学习安卓保护手段

发现了很多处调用,主要在MainActivity与StartActivity里。出现在这两个文件里也正常,因为这通常是android程序最早启动的两个类,很多检测都会放在启动阶段做。注意到每次调用有不同的后缀,逗号,叹号等等。我们的是逗号,所以选择后缀为逗号的进入。

无限续杯——从app破解角度学习安卓保护手段

看到调用了DeviceUtils.a()函数,如果返回true就会弹出 检测设备…字符串,然后退出。看看.a()函数是什么。

无限续杯——从app破解角度学习安卓保护手段

好家伙,果然是在检查root。检查设备是否root的常规方式就是尝试打开这些/system目录下的su文件,如果能打开,则说明设备有root权限。


使用MT管理器打开包,找到a函数,通过修改smali代码,让a函数不检测而直接返回false即可:
无限续杯——从app破解角度学习安卓保护手段
无限续杯——从app破解角度学习安卓保护手段

直接让他返回v0即可。


重新打包,安装。又弹出了那串文字,不过后缀变成了感叹号:

无限续杯——从app破解角度学习安卓保护手段

查看这个感叹号出现的位置:

无限续杯——从app破解角度学习安卓保护手段

好家伙,看到那个DeviceUtils.AntiRoot()了。如此欲盖弥彰的安全保护,真令破解者狂喜。本着学习的态度,我们还是看一下这个函数怎么antiroot的:


点进去发现是一个native函数,而加载的so叫myapplication:

无限续杯——从app破解角度学习安卓保护手段

无限续杯——从app破解角度学习安卓保护手段

使用ida加载libmyapplication.so这个文件,全局搜索AntiRoot:

无限续杯——从app破解角度学习安卓保护手段

发现还是在检查 su 文件,如果发现有就返回“yes”,没有就返回“no”,这和java代码中检测对上了。

无限续杯——从app破解角度学习安卓保护手段

而check_su_files()还是在本地尝试打开各种su文件:

无限续杯——从app破解角度学习安卓保护手段

既然如此,可以直接用MT管理器修改java代码,将比对的“yes”改成“f*ck”,这样就算检测到root,java层的比对也会失败。
无限续杯——从app破解角度学习安卓保护手段
无限续杯——从app破解角度学习安卓保护手段

至此,root检测就全部绕过了。可以打开app了。但是打开后一片空白:
无限续杯——从app破解角度学习安卓保护手段 无限续杯——从app破解角度学习安卓保护手段

这种情况通常是因为没有拿到正确的返回数据导致的。app打开后肯定会向后端请求各种数据,如果数据异常,则无法正常显示。这时候就要抓包看看了。





代理检测


打开手机代理:

无限续杯——从app破解角度学习安卓保护手段

启动fiddler,结果app又无法正常启动,这次弹出了这个:

无限续杯——从app破解角度学习安卓保护手段

app生怕你不知道他在检测代理,给出了非常温馨的提示。那我们就按图索骥。

无限续杯——从app破解角度学习安卓保护手段

看到字符串id是 toast_api_proxy_fail,查找引用:

无限续杯——从app破解角度学习安卓保护手段

和root检测的流程基本相同。不过是调用了一个b函数来检测是否有代理的,看看b函数:

无限续杯——从app破解角度学习安卓保护手段

可以看到,代理检测的逻辑是要确保 http.proxyHost(代理地址) 为空字符串,或者http.proxyPort(代理端口)为-1。


我们直接修改java代码让他始终返回false就好。
无限续杯——从app破解角度学习安卓保护手段

这样之后,就可以成功打开app,并且抓到了数据包:

无限续杯——从app破解角度学习安卓保护手段

但是,app内部依然是一片白,查看返回的数据,发现是加密的:

无限续杯——从app破解角度学习安卓保护手段

看到后面的== 以为是base64,但是base64解码失败,于是猜测是aes或者des加密。





数据解密


去哪里找解密的地方呢?这需要研究andorid系统网络请求的数据流了。


通常,客户端会使用okhttp作为http客户端进行收发请求。(目前还有使用cronet做客户端的,但仅限于字节这样的大厂,而且大多是在okhttp上嫁接的。)


okhttp工作方式是责任链,或者说pipline,每一环节处理一些事情,比如在发送阶段,第一个pipline是添加基本信息,如设备id,时间戳,等等。第二个pipline是计算签名值,第三个pipline是把发送数据加密,然后发出去。


收包时候也有pipline,解密操作一般就放在某个pipline中。所以我们的目的是找到okhttp客户端创建的地方,然后找到他添加pipline的地方。


查看okhttp,好家伙,被混淆了:

无限续杯——从app破解角度学习安卓保护手段
通常,okhttp的创建是在okhttpclient这个类里做的。混淆通常只能混淆函数名,类名,但是无法混淆函数的实现,包括一些特殊的字符串,我们可以从字符串入手,找到okhttpclient。


下载一份okhttp的源码,查看okhttpclient这个类有何特征:
无限续杯——从app破解角度学习安卓保护手段
发现在内部的builder里,有三个连续的字符串“timeout”,而这是无法被混淆的。我们在jadx里全局搜索“timeout”:

无限续杯——从app破解角度学习安卓保护手段

果然有。


点进去看看,发现结构和okhttpclient的结构如出一辙,我们断定这就是okhttpclient类。

无限续杯——从app破解角度学习安卓保护手段

查看引用,我们也因此找到了app创建okhttpclient的位置:

无限续杯——从app破解角度学习安卓保护手段

okhttp客户端另一个特征是对读写超时的设置,从TIMEUnit也可以看出这是在设置http请求的读写超时。


同时,看到后面一连串的.a函数,这是在添加一个一个pipline。(okhttp中叫拦截器,不过我个人感觉本质就是pipline)


分别查看这些pipline。


一个是添加请求头的:

无限续杯——从app破解角度学习安卓保护手段

还有一个是和数据处理有关的:

无限续杯——从app破解角度学习安卓保护手段

看到里面的response,bodystring字符串,好有d_key_three,不由的让人浮想联翩,这是在做什么,为什么出现了秘钥和返回体?


点进去看f5865a.a这个函数:

无限续杯——从app破解角度学习安卓保护手段

实锤!看到了password,iv,SecretKeySpec,Cipher,doFinal这些特征。显然是在做aes解密。


为了更准确,看看SecretKetSpec 中的f5866b与Cipher.getInstance(f)中的f分别是什么?

无限续杯——从app破解角度学习安卓保护手段

无限续杯——从app破解角度学习安卓保护手段

f5766b与f都反编译崩了,转到smali看看:

无限续杯——从app破解角度学习安卓保护手段

确信了,在用AES解密,模式是 CBC,填充方式是PKCS5padding。


我们hook这个a函数,看看解密后的值是什么。


写一个简单的frida脚本:

无限续杯——从app破解角度学习安卓保护手段

hook后发现,打印出了奇怪的东西:

无限续杯——从app破解角度学习安卓保护手段

传入的参数确实是加密的返回数据,但是解密后没有东西。而且秘钥很怪,以error结尾?


查看系统日志:

无限续杯——从app破解角度学习安卓保护手段

发现解密失败,报错Unsupported key。


仔细看了一下,所使用的的秘钥和iv 确实是14字节。而aes通常有128,192,256三种秘钥,所以秘钥只能是16,24,32字节,那么显然秘钥错了。


找找秘钥生成的地方,发现秘钥是由三部分相加得来:

无限续杯——从app破解角度学习安卓保护手段

第一部分是从资源文件中获取的:

无限续杯——从app破解角度学习安卓保护手段

第二部分是通过app内置数据库获取的,不过采用的是默认值:

无限续杯——从app破解角度学习安卓保护手段

第三部分是从native获取的:
无限续杯——从app破解角度学习安卓保护手段

查看这个native函数:

无限续杯——从app破解角度学习安卓保护手段

原来,在native获取秘钥的时候又做了一起签名检查!如果签名不对则返回error,只有签名正确才会返回正确的后缀。


修改frida脚本:

无限续杯——从app破解角度学习安卓保护手段

直接将正确的秘钥强行塞给解密函数,这次终于解密了,不过:

无限续杯——从app破解角度学习安卓保护手段

返回的数据显示“签名验证失败”。





签名校验


因为签名验证失败,所以没有拿到正确的返回数据。我们看看签名是怎么算的。


在http请求头里看到了sign字段:

无限续杯——从app破解角度学习安卓保护手段

这个就是签名。


因为通常,签名的命名方式为sign,sig,authcode,sec之类。特殊的例如某音,使用了希腊神话中的神的名字来命名。


jadx全局搜 “sign”。(注意用带双引号的sign来搜索,这样会提高搜索效率。因为通常生成sign时会把sign作为key,put进一个map里。而key是一个String,所以代码中生成sign的地方一定有字符串”sign”)


果然有:

无限续杯——从app破解角度学习安卓保护手段

看看a7的生成:

无限续杯——从app破解角度学习安卓保护手段

阅读a函数,可以知道:


先加入了ts参数作为时间戳。然后把所有参数首尾相连。然后添加了类似于aes秘钥的后缀。然后通过另一个a函数计算a2,再将a2全部小写。这就是签名的生成。


看看另一个a函数:

无限续杯——从app破解角度学习安卓保护手段

是在算MD5摘要。


那么问题就出在了那个类似于秘钥的后缀上。


确实,因为签名错误了所以也返回了错误的后缀,不过使用的是aaxx函数,不同于aes时候使用的ddmm函数。

无限续杯——从app破解角度学习安卓保护手段

无限续杯——从app破解角度学习安卓保护手段

同样的套路,签名不正确就返回error,正确才返回真正的后缀。

无限续杯——从app破解角度学习安卓保护手段

看看本地是怎么做签名校验的?

无限续杯——从app破解角度学习安卓保护手段
通过反射获取signature,然后计算MD5,与硬编码的正确SIGN_MD5进行比对。


既然如此,我们hook aaxx函数让他返回正确的值:

无限续杯——从app破解角度学习安卓保护手段

再次运行后,成功得到了正确的数据:

无限续杯——从app破解角度学习安卓保护手段

同时,app界面也正常了,不过显示已到期:

无限续杯——从app破解角度学习安卓保护手段





无限续杯


我们成功绕过了root检测,代理检测,签名校验。但是每一台设备只有2小时的免费机会,我们想要白嫖,怎么办?


通常app会通过唯一设备标识来跟踪设备,即deviceID。


deviceId可以有很多种计算方式。可以直接获取安卓设备本身的唯一标志(高版本android禁用了),可以获取设备的MAC地址,也可以生成一个UUID,或者随机字符串,藏在设备的某个角落。当apk重新安装时,先去看看之前这里有没有藏过相关的文件,如果有,就直接读出来当做设备ID,如果没有,则创建一个。这样做可以保证apk删除前后依然能跟踪设备。当然,如果你能发现这个文件,并且把他删掉,那么下次安装的时候app就会认为是一个新的设备了。


我们先找找deviceid在哪,全局搜索deviceid:

无限续杯——从app破解角度学习安卓保护手段

发现了跟多地方,有from app,有from sdcard。


跟入一个sdcard相关的函数:

无限续杯——从app破解角度学习安卓保护手段

看到了是从f文件读取的设备id。我们看看f文件的路径:

无限续杯——从app破解角度学习安卓保护手段

好家伙,在alarms文件夹呢。打开看看:

无限续杯——从app破解角度学习安卓保护手段

果然,sd卡下的alarm文件下有一个文件,打开后的确是发送数据时使用的设备id。


删掉这个文件,重新启动,发现设备id还是没有变化,难道还有其他地方存着?


注意到上面的from app:

无限续杯——从app破解角度学习安卓保护手段

看到deviceid是从b函数获取的,看看b函数:

无限续杯——从app破解角度学习安卓保护手段

懂了,原来是从sharedpreferences里拿的。


SharedPreferences 是app内部存储数据的一种方式,而sd卡是一种外部存储方式。具体知识可以参考Android数据存储相关文章。


我们打开sharedPreferences

无限续杯——从app破解角度学习安卓保护手段

果然看到了customdeviceid:

无限续杯——从app破解角度学习安卓保护手段

看看他的设备id是怎么获取到的?

无限续杯——从app破解角度学习安卓保护手段

首先通过a(10)获取了一个10位的随机字符串a2:

无限续杯——从app破解角度学习安卓保护手段

然后计算了“33dfdfer21”+a2+”sddddsfe”的md5值 a3。


最后用“33dfdfer21”+a3+a3[0:10]为最终的设备id。


所以设备id是随机生成的。


每台新设备有2小时的免费时间,也就是7200秒。我们考虑每7000秒重新生成一次设备id,不就可以无限续杯了吗?


看看app在那里获取到这个deviceid并发送给服务器的,注意到之前的okhttp 的pipline中有一个就是添加http头的:
无限续杯——从app破解角度学习安卓保护手段

看到deviceid是通过a()方法获得的。查看a()方法:

无限续杯——从app破解角度学习安卓保护手段

最终是返回了一个字符串。其实不用继续跟下去了,无非是从app获得或者从sd卡获得。我们在这里hook就行。


理论上只要写一个字符串随机生成算法,保证2小时内保持一直就行了。我这里使用时间戳除以7000的方式获取:

无限续杯——从app破解角度学习安卓保护手段
字节写一个函数,然后使用android studio编译出smali代码。将对应的smalidaima插入到a()函数返回之前即可:


随机设备id的smali代码:

无限续杯——从app破解角度学习安卓保护手段

插入到a()函数返回之前:

无限续杯——从app破解角度学习安卓保护手段

这样,我们每隔两小时会使用全新的deviceid,对服务端来说好像是新的设备安装了他的app,然后就可以又白嫖两小时的免费时间了。

无限续杯——从app破解角度学习安卓保护手段
无限续杯——从app破解角度学习安卓保护手段





后记


这些不符合社会主义核心价值观的软件,都要统统破解了才好。为了维护社会正气,我辈当仁不让。


同时仅就安卓安全保护而言,这款app该做的都做了,但路数都比较常规。


唯一的亮点是将秘钥分段存储,在签名错误时返回错误的秘钥以对包体篡改提供更强的保护。


本次教程以学习研究为目的,请勿以此做违法犯罪的事情。遵纪守法的社会主义好公民,从我做起。



无限续杯——从app破解角度学习安卓保护手段


看雪ID:乐子人

https://bbs.kanxue.com/user-home-872365.htm

*本文由看雪论坛 乐子人 原创,转载请注明来自看雪社区

无限续杯——从app破解角度学习安卓保护手段

# 往期推荐

1、Android – 系统级源码调试

2、攻防世界easyjni精析

3、对1x06app网络请求时Sign简单的分析

4、地图浏览器-vip分析

5、车服务平台-ios版本分析

6、STL容器逆向与实战


无限续杯——从app破解角度学习安卓保护手段


无限续杯——从app破解角度学习安卓保护手段

球分享

无限续杯——从app破解角度学习安卓保护手段

球点赞

无限续杯——从app破解角度学习安卓保护手段

球在看


无限续杯——从app破解角度学习安卓保护手段

点击“阅读原文”,了解更多!

原文始发于微信公众号(看雪学苑):无限续杯——从app破解角度学习安卓保护手段

版权声明:admin 发表于 2023年2月13日 下午6:00。
转载请注明:无限续杯——从app破解角度学习安卓保护手段 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...