2016年10月10日星期一

Android 逆向学习笔记 (四)- 签名校验突破

有的时候在篡改 apk 并且重新打包之后不能直接运行,此时部分情况是因为存在软件的签名校验机制。在前面几篇的学习中可以知道,软件打包之后需要重新签名才可以安装到安卓机器上,此时重新签名一般用的都是公开的  platform.x509.pem 和 platform.pk8 , 有的软件为达到防篡改的目的会对新老签名进行比较,如果存在差异则确定被篡改,程序终止。

除了签名校验之外,还有一种情况是 classes.dex 的 MD5 检验,也可以发现被篡改的 classes.dex。怎么区分这两种不同的校验呢?拿到 apk 之后对其重新签名,如果仍然可以正常安装运行的即说明是 classes.dex 的 MD5 校验,反之则是签名校验。

一般在程序内进行的校验有两种途径,在线比对和离线比对,逆向者在意的是比对的这个过程及其参数。在线比对和离线比对的区别是:在断网情况下仍旧闪退的是离线比对,断网后先提示没有网络连接再闪退的是在线比对。

既然校验的过程是在本地程序进行的,那么在反编译得到代码之后,只要修改一下比较时的对应条件就可以简单的绕过校验了,整个校验过程有点掩耳盗铃的意思。

Signature · smali

获取当前 apk 签名的 Java 代码如下:

PackageInfo packageInfo = getPackageManager().getPackageInfo(
                    "xx.xxx.xx", PackageManager.GET_SIGNATURES);
Signature[] signs = packageInfo.signatures;

对应的 smali 代码为:

Landroid/content/pm/PackageInfo;->signatures:[Landroid/content/pm/Signature

所以对付签名校验,只要通过这段关键代码找到对应的调用位置,不断跟进,确定最终判断的位置然后修改比较条件即可。

另外有的时候会进入 native 层判断,这个时候搜索关键字 signature 然后判断跟进即可。

MD5 · smali

这类首先会在 smali 层获取当前 apk 的目录,然后再传入 native 层面进行比较,典型代码如下所示:

iget-object v1, v0,Landroid/content/pm/ApplicationInfo;->sourceDir:Ljava/lang/String;
const-string v1, "/sdcard/buyudaren.apk" # 本行目的是 bypass 校验,路径为原版 apk 路径,覆盖 v1
invoke-static {v1}, Lorg/cocos2dx/lib/Cocos2dxHelper;->nativeSetApkPath(Ljava/lang/String;)V

简单添加中间一行即可绕过 MD5 的判断,因为此时进入 native 层的是原版 apk 的路径,所以校验的也是原版 apk,此时当然没有问题。可以看到,看似复杂的进入 native 层面有的时候只是掩耳盗铃的一种方式。

如果要进入 native 层面去 Bypass 检查,在把 .so 文件拖入 IDA Pro 后搜索 “classes.dex” 即可,跟进可以发现 MD5 字符串或是进行比对的特征值。

另外有时存在 .so 和 classes.dex 文件相互验证的情况,这个时候就对应的搜索 “classes.dex” 和 “xxx.so” 两个特征字符串,找到程序运行中校验的文件路径,然后对应地修改成原版文件的路径即可。

REFERENCE



* 另外说一下,有的时候两个程序的 AndroidManifest.xml 中定义了 SharedUserId 并进行交互,这时必须两个程序的签名相同才可以正常安装。