回顾

《Android逆向小技巧③:批量注入日志,打印目标程序执行流程》

在上一篇2019年的文章中,我们使用python写了一个简单的文本处理工具:
https://github.com/encoderlee/android_tools

在使用apktool对目标应用的apk解包以后,用这个python写的小工具,分析反编译出来的smali代码,并在每个方法中注入日志,然后对apk重新打包,这样,但APP运行的时候,我们每做一个操作,都可以从日志中看出目标代码的执行流程。

不过这种方式有一个很大的缺点,就是现在很多APP都做了签名校验,重新打包后,APP启动时检查到自身签名不对,发现自己被重新打包了,就罢工不再工作了。

签名校验

以支付宝目前的最新版10.2.50 (2021-12-20) 为例,使用apktool重新打包后,你需要使用apksigner重新签名才能安装:

apksigner sign --v2-signing-enabled false --v3-signing-enabled false --ks encoderlee.jks alipay.apk

但是重新签名签的这个名,只能用自己生成的证书去签名,因为你不可能搞到支付宝的证书和私钥。

这个时候如果直接安装运行这个APK,会出现这样的问题:

在这里插入图片描述
抱歉,请求参数不合法。

其实这就是因为APP检测到自身签名不对,就拒绝服务的表现,有的APP会表现为闪退。
其实想想也很容易理解,apktool解包,修改,重新打包APP那么简单,没有哪个厂商愿意让自己的APP被随意的玩弄和修改,所以目前绝大多数的APP都做了签名校验。

常规对抗

常规的对抗思路是,既然你在代码中做了签名校验,那么我找到你校验签名的代码,通过修改smali文件或用Xposed干掉它,不久OK了。于是你在网上可以搜到很多如何校验签名的代码:

 public static int verifySignature(Context context) {
        boolean isValidated  = false;
        try {
            //得到签名
            PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(),PackageManager.GET_SIGNATURES);
            Signature[] signs = packageInfo.signatures;

            //将签名文件MD5编码一下
            String signStr = md5(signs[0].toCharsString());

            //将应用现在的签名MD5值和我们正确的MD5值对比
            return signStr.equals("这里写正确的签名的MD5加密后的字符串");
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return isValidated;
    }

本质上来说这些签名校验的代码,都是调用 PackageManager.getPackageInfo().signatures 去获取自身的签名信息,进行比较判断自己是否被重新打包。

实际上,你在微信和支付宝反编译出的代码中,也能找到这些调用,但当你尝试修改smali或用xposed干掉这些调用的,替换原版的 signatures 数据,就会发现:

并没有什么卵用

想想也不可能那么简单,微信支付宝可是和这帮打包者对抗多年了,实际上在微信支付宝中,还使用了多种手段来校验自身签名,其中就包括在so中使用本机代码来实现校验,这样使得我们解决起来非常麻烦。

当然理论上来说,你完全可以找到它在so中检验签名的代码,干掉它,但是时间成本未免太高,看看微信支付宝的那个JAVA代码量,以及so的数量,想在这些海量代码中分析出关键点来,非常麻烦,就不要在这个思路上浪费时间了。

剑走偏锋

那么,能不能找到一种通用的办法,干掉大多数APP的签名校验呢?

我们先来分析,apksigner对APK签名,本质上是做了什么事情?

我们可以比较apktool重打包后没有签名的APK文件和apksigner签名后的APK文件,发现在APK文件中多了一个【META-INF】目录,里面的文件保存的实际上就是签名和文件校验信息。

Android签名原理可以看这篇文章:《Android 端 V1/V2/V3 签名的原理》

而PackageManager.getPackageInfo().signatures获取的signatures是什么?实际上就来自【META-INF】目录中的【CERT.RSA】文件(有的APK里面的RSA文件不一定是这个名字,但都以.RSA结尾),里面有【包含公钥的开发者证书】,证明这个APK是谁签名的。

那么,我们直接把原版APK中的【META-INF】目录下的【CERT.RSA】取出来,覆盖到我们打包后的APK中,不就可以了?这样APK运行后,获取到的signatures 仍然是支付宝自己原版的 signatures 。

理想是美好的,但是Android的这套签名机制当然不会那么傻,这样简单替换【CERT.RSA】文件后的APK,自然是无法正常安装的,提示【INSTALL_PARSE_FAILED_NO_CERTIFICATES】错误,这是因为【CERT.RSA】不仅包含【开发者证书】也包含【对 CERT.SF 文件的签名】,我们重新打包APK后,【MANIFEST.MF】和【CERT.SF】都改变了,自然支付宝原版的签名就和它对不上了,android系统在安装的时候检测到签名对不上,自然就不允许安装。

但要知道,Android系统是开源的,我们能不能修改Android系统源代码,让它允许安装签名错误的APK呢?当然是可以的。实际上也不需要这么麻烦,如果我们有root权限或有xposed,一样可以Hook Android系统本身的代码,让Android系统跳过签名校验,安装这个签名错误的APK。

这样的Xposed模块早已有人写好了,就是【幸运破解器「Lucky Patcher」】

https://www.luckypatchers.com/download/

幸运破解器有两种方式来运行,一种是手机已经Root,一种是手机已装好Xposed。

推荐Xposed的方式来运行,装好幸运破解器后,在Xposed中勾选启用该模块,打开幸运破解器APP。

【工具箱】-》【Android核心破解】-》勾选【签名验证始终真实】,勾选【禁用ZIP签名验证】
在这里插入图片描述
一番操作后,我们就可以把重新打包后签名不对的APK安装到这个手机上。即使APP运行起来后,动态检查签名,也不会有什么问题,因为无论在JAVA层还是so层获取signatures,最终都是从【CERT.RSA】读取的数据,而【CERT.RSA】已经被我们替换成了支付宝原版的【CERT.RSA】,货真价实,APP运行起来后动态检测也不会发现什么异常,于是我们就可以正常运行我们重新打包修改过的微信支付宝APP了,当然其它大多数APP都可以这样来搞。

注意!这种覆盖【CERT.RSA】文件的方法,仅仅适用于V1签名,所以我们上面使用

apksigner sign --v2-signing-enabled false --v3-signing-enabled false --ks encoderlee.jks alipay.apk

给APK前面的时候,禁用了V2 V3签名,只保留V1签名

(也许V2 V3也有对应的搞法?回头有空再研究)

神器【VirtualXposed】

有人说,这种方式也太苛刻了吧,这样一搞,虽然是个通用和万能的方法破解了签名校验,但是要求安装该APP的手机,必须是一个ROOT过的手机,或者装了Xposed的手机呀,现在最新的小米华为手里,想要ROOT和Xposed,实在太麻烦了。而且我修改过的APP,想发布到群里给大家伙使用,不可能大家伙都要去ROOT手机吧?

那就要借助大名鼎鼎的 【VirtualXposed】了!
https://github.com/android-hacker/VirtualXposed

这个神器,可以在没有Root权限的手机上创建一个虚拟环境,然后对该虚拟环境内的APP启用Xposed模块!
这个项目的作者真的是个天才,这个思路都被他想出来并且付诸实现了。

而且 【VirtualXposed】本身实际上已经集成了【幸运破解器「Lucky Patcher」】的【Android核心破解】功能,即允许在【VirtualXposed】中安装签名不对的APK。

在这里插入图片描述
我们只需要在【VirtualXposed】的【高级设置】中勾选【允许安装没有签名的应用】
这样就不需要【幸运破解器「Lucky Patcher」】了,绝大多数APK,我们使用apktool重新修改打包后,只需把原APK的签名文件【CERT.RSA】覆盖回去,然后通过这样的方式,安装签名不对的APK文件,就可以暴力绕过APK签名校验,愉快的重新打包和分发各种APP了。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐