安卓逆向-去某猫小说广告、过frida校验、修改so层源码过签名校验

104 阅读8分钟

1、前言

相信大家都有看小说的经历,小说大部分是免费的,但是看着看着容易出广告且摇晃一下就跳到广告详情页面,这十分恶心,无意间也听到某个同事抱怨,怀着学习的想法,试着逆向某猫小说,并去广告,最后成功修改,以下用于记录逆向过程

2、前置知识和准备

  • 一部root的安卓机,模拟器不行(后续会解释到)
  • 安卓基础、MT管理器
  • lsposed+frida
  • IDA(交互式反汇编器专业版)、010 Edit、jadx-gui等
  • 一些汇编的基础知识和部分ARM指令集(我也是模棱两可)
  • 了解二进制文件的大小端模式

3、开始逆向

3.1、导入jadx查看源码

第一步毫无疑问是下载apk然后导入jadx-gui,导入解析结果如下

image.png

打开发现没有进行加壳,这可太好了,省去了脱壳的步骤

3.2、尝试搜索关键字

一般带有会员机制的都会有vip,vip_level,is_vip等字眼

image.png

果不其然 搜出很多,可以做一下尝试,看到方式是判断和1是不是相等,那直接改为true就好了

3.3、MT文件管理器修改smali代码

通过MT管理器找到对应的dex文件,然后在返回值之前添加一个true的代码

image.png

保存之后转为java代码查看效果

image.png 已经生效了

image.png

参考以下链接(七猫小说破解思路)

同理需要改一下所有方法 isVipExpired
isVipState
isShowYearVip
在baseinfo类名底下赋值1

在做了一系列操作之后,重新打包安装打开一看

image.png

image.png

好家伙,一看就是做了签名校验,并且提示了两个弹窗,接下来就是做去签名校验的逻辑了,

3.4、frida分析签名校验位置

根据弹窗结果,搜索关键字参数错误/验签失败,都无果,那么接下来上frida神器,找一下弹窗的位置,看看是在哪做了签名校验,也能通过MT、NP一些工具的去签名校验做尝试

function printStacks() {
    console.log(
        Java.use("android.util.Log")
        .getStackTraceString(
            Java.use("java.lang.Throwable").$new()
        )
    );
}
function main() {
    Java.perform(function () {
        var toast = Java.use("android.widget.Toast");
        toast.show.implementation = function () {
        console.log("toast.show: ");
        printStacks();
        return this.show();
       }
    })
}
setImmediate(main);

好家伙,当我执行frida的时候,给我直接Process terminated了,这应该是做了frida的检测

image.png

4、过frida进程检测

4.1、定位过检测的so

过frida检测有很多方案

  1. 检测frida-server文件名
  2. 监听frida端口
  3. 检测/proc/pid/maps映射文件等

方案1和方案2都可以通过简单的操作进行测试,但是经过测试任然退出了,那么只能霸王硬上弓了,硬分析,根据so的加载流程,会走dlopen函数,那么先hook一下dlopen函数看看是哪个so文件检测到了 然后退出了

function hook_dlopen() {
    Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"), {
        onEnter: function (args) {
            var pathptr = args[0];
            if (pathptr !== undefined && pathptr != null) {
                var path = ptr(pathptr).readCString();
                console.log("load " + path);
            }
        }
    });
}
function main() {
    Java.perform(function () {
        hook_dlopen()
    })
}
setImmediate(main);

image.png

如图清晰明了看出来是加载了libsaoaidesc.so文件,看起来像是某家加固厂商的so文件,网上有很多分析该文件的检测流程,这里不做分析,直接暴力不让他加载这个so文件就好了,直接就能跳过检测

这里有个坑就是必须用真机才能找到加载的so文件,因为模拟器的架构一般是x86的而app中没有x86的so文件,只有armv8a和v7a架构的,用frida是扫描不到的,可以参考该链接

function hook_dlopen() {
    Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"), {
        onEnter: function (args) {
            var pathptr = args[0];
            if (pathptr !== undefined && pathptr != null) {
                var path = ptr(pathptr).readCString();
                if (path.indexOf('libmsaoaidsec') >= 0) {
                    //加载一个空的字符串
                    ptr(pathptr).writeUtf8String("");
                    console.log("rewrite libmsaoaidsec.so ptr ");
                }
            }
        }
    });
}

image.png

可以看到已经跳过了检测

5、分析签名校验

5.1、调用链路hook

过了frida之后进行toast的hook,查看校验路径,发现是起了的线程去弹窗了

image.png 继续hook调用位置,看看是怎么触发的

1729580937334.png

这里有两个思路,因为文本是传入的,可以hook构造方法,看看这个“验签失败”是传入的,还是getString出来的,分别做尝试,做了构造方法尝试,果然是传进来的

image.png

image.png

hook一下这个异常类,看看在哪创建的

image.png

其实到这里就已经不用看了,BaseResponse意味着这是一个响应体,说明思路应该从HTTP请求入手了,“验签失败”是由某个响应体返回的,接下来就是爬虫抓取接口分析了

5.2 爬虫寻找验证接口

手机端的爬虫这里我用了Reqable,小黄鸟,有两种方式 1、手机和电脑是一个局域网,手机代理了电脑的IP和端口,请求打到电脑上查看请求 2、手机直接抓包,本机开启代理直接进行抓包

image.png

这里选择第二种,安装好证书之后开启中间人代理,直接全局搜索关键字

image.png

通过和不修改任何操作的安装包抓包最后发现是sign的问题,sign传入了一个FAIL的值,问题转为SIGN的值为什么是FAIL的问题了

image.png

5.3、Sign接口的分析

上一节提到因为sign的值为FAIL导致的请求失败,那么肯定在某个位置传入了sign的值,只要逆向出这个sign的值就能过签名校验了,思路还是老样子全局搜索sign关键字寻找,这里找出很多,有个技巧就是寻找跟APP包名的,其他的可能是第三方依赖的sign

image.png

最后定位到这里

image.png

继续跟踪,哦豁,发现是一个native方法了,接下来就要分析so文件了,但是发现没有System.loadLaibary这个方法,不确定是在哪个so文件中了

image.png

5.4、分析so层的sign获取

上节讲到native方法不知道是在哪个so文件中,要确定的是肯定是在libs中的所有so文件,所以有以下思路 1、native方法肯定会会被导出,所以通过Frida hook所有导出函数确定在哪个so中即可 2、如果是静态导出的java方法,可以通过JAVA_COM..(导出函数的命名规则)的关键字搜索所有so文件

最后再libcommon-encryption.so中找到,具体的方式可以参照这篇文章

进来之后发现这里有很明显的FAIL关键字,也就是sign

image.png

这里逐步分析,首先是 (v6 & 1) != 0,说明v6必须是0,不然就为fail了,然后  
v6 = AndroidUtils::isCheckFailed(v5),也就是说v5作为参数传进去返回的值必须是0
,发现v5是通过init创建一个线程返回,然后调用回调返回回来的,

image.png

点击进去thread_callback,大致看了一下发现有个未混淆的方法名:checkSignUseApplicationPackageManager,其实这里已经可以看出来大致的意思了。

通过PackageManager去获取签名,所以核心签名校验就在这里,点进去发现运用了大量的Java反射和加密算法,要看懂需要花点时间

image.png

image.png

5.5、修改so层源码跳过签名校验

上节分析到是通过这个方法的返回值来判断的,那么直接对这个方法,交叉引用,然后取反就能跳过签名校验了

image.png

简单分析一下这里,如果签名校验不通过那么v4就等于2和自然就报错了,那么只要把v4无论怎么样都改为1就行了

image.png 这里看一下地址,需要把#2改为#1,怎么改呢,要打开arm64的指令集手册

image.png

由于elf是小端模式所以要反过来 52 80 00 48,转为2进制为 01010010 10000000 00000000 01001000,查看一下指令集,这里刚好能对的上,也就说5-20位是立即数,我们取出原来的就是 0100000000000000 转为十进制就是2 那么我们要改为1 就是 1000000000000000 最后装回去就是 01010010 10000000 00000000 00101000 再改为十六进制就是 52 80 00 28,其实和上面的地址是一样的,这里只是解析一下怎么来的

image.png

那么接下来就简单了,打开IDA然后编辑HEX 改为28然后应用即可,查看结果

image.png

image.png 可以看到无论怎么样v1的值都会1即可,同理改掉其他引用

5.6、替换so文件然后重新打包

这里用010Editor查找48008052,都改为28008052,然后保存为新的so文件 image.png

image.png

然后再重新安装即可食用,再次打开就发现没有弹出验签失败的问题了

image.png

image.png

6、总结

写这篇文章的意义是在于记录如何从0开始完整的逆向到成功的一个流程,用于学习,仅供参考

有了思路才有技术,有了技术才有出路