某直装外挂卡密校验逆向分析

前言

最近分析了一款外挂软件的卡密校验,过程挺有趣的,故记录下来。

正文

该软件的界面如下图:

在这里插入图片描述

登录卡密是该界面的一个按钮,首先是获取该界面的类名,如下:

在这里插入图片描述

该界面所在的类是com.app.batman.MainActivity,获取到该界面所在的类之后,下一步的目标是定位到发送请求的方法,这一次我的定位方法不同于以往,以前我的定位方法是搜索字符串或者hook关键函数打印调用栈,这一次我所采用的方法是trace,那么什么是trace呢?hook了大量的函数就是trace,使用的脚本是r0trace(项目地址)。我trace了MainActivity类下的所有的函数,从而定位到SignUp这个关键函数,如下图:

在这里插入图片描述

在jadx中反编译的代码如下:

在这里插入图片描述

该方法是一个native方法,所以要找到注册该函数的so文件,首先我们假设它是动态注册的,因此可以使用frida hook RegistNatives,这里使用yang神的脚本(项目地址),运行结果如下:

在这里插入图片描述

发现找不到SignUp函数,那么该函数大概率是静态注册的,那么静态注册的方法该如何定位so文件呢?可以使用frida-trace,还需要注意,静态注册的函数命名是有规则的:Java_ + 包名 + 类名 + 方法名,并且".“都替换成”_",运行结果如下:

在这里插入图片描述

可以看到,静态注册该函数的so文件是ban,命名很奇怪,在jadx中找到该so文件的加载位置,如下:

在这里插入图片描述

ban文件是经过System.load进行动态加载的,一般都是把该文件进行加密,然后在动态加载是进行解密,先把cache目录下的ban文件放在IDA中看一眼,如下:

在这里插入图片描述

我采取的对抗手段是dump出内存中的ban文件,因为程序已经运行起来了,这时候内存中的ban文件是已经解密了的,frida dump脚本如下:

function dumpso(so_name){
    var libso = Process.findModuleByName(so_name);
    if (libso == null) {
        return -1;
    }
    Memory.protect(ptr(libso.base), libso.size, 'rwx');
    var libso_buffer = ptr(libso.base).readByteArray(libso.size);
    libso.buffer = libso_buffer;
    var f = new File("/data/data/com.tencent.tmgp.pubgmhd/cache/ban.so","wb")
    f.write(libso.buffer)
    f.flush()
    f.close()
    console.log("success dump ban.so")
    console.log("ban.so base address --> " + libso.base)
}
function main(){
    dumpso("ban")
}
setImmediate(main)

在这里插入图片描述

但这个时候的so是不能分析的,还需要修复一下,修复工具使用的是elf-dump-fix(项目地址),把修复后的so文件放进IDA中进行反编译,SignUp的伪代码如下:

在这里插入图片描述

发现已经能够正常识别了,下面就是找到发送请求的逻辑,关键代码如下:

在这里插入图片描述

上面图中标注的很清楚了,核心逻辑就是判断响应体中的内容的长度是否为32,如果长度为32位,则卡密校验成功,否则卡密校验失败。卡密校验成功之后调用了MainActivity类下面的TOAC方法,下面看看该函数的逻辑:

在这里插入图片描述

代码很简单,就是开启了一个服务,那么有没有什么办法看到服务开启后的效果呢?很简单,使用frida进行主动调用就行了,脚本代码及运行效果如下:

在这里插入图片描述

在这里插入图片描述

在主动调用了TOAC方法之后,外挂的功能模板就弹出来了。那么我们只要修改登录卡密之后的逻辑,让其不论校验成功还是校验失败都调用TOAC方法,这样也就绕过了卡密的校验,具体的过程不再展示。

修改并重打包之后,功能一切正常,但在点开绘制初始化开关之后软件直接闪退了,因此可以判断该软件有签名校验。那么我判断的依据是什么呢?很简单,就是之前我使用frida进行主动调用过卡密校验的时候软件并没有闪退,开关也能正常打开,但是在重打包之后打开开关就闪退。

下面开始绕过签名校验,首先尝试在jadx中搜索signatures,如下:

在这里插入图片描述

直接根据包名进行过滤,在java层中并没有我们想要的结果,那么在ban这个so文件里面进行搜索,如下:

在这里插入图片描述

可以看到,程序调用了android.content.pm.Signature类下的hashcode方法获取了签名的hash值,并且拿该值与一个特定的值进行比较,如果不相等则直接退出程序,因此这里就是签名校验的关键点。

既然找到了程序校验签名的逻辑,那么我们该如何绕过呢?起初我想的是直接修改so文件,把exit的调用直接nop掉,但是别忘了这个so文件是从内存中dump出来的,其原本的状态是加密的,所以是没办法直接进行修改。那么就可以写xposed模块,去hook hashcode这个方法,让其返回那个正确的hash值,这样也能绕过检验。但是考虑到使用Xposed的环境要求比较苛刻,需要手机进行root,因此我也抛弃了这个方案。

其实即使不root也是能够去hook的,这个方案也被许多知名的软件应用了,比如太极、VirtualXposed、应用转生等等,这也就是我采用的方案,我使用的AOP框架是epic(项目地址),核心代码如下:

在这里插入图片描述

然后把编译好的dex文件添加进软件中,并主动调用这段代码也就实现了非root下的hook。

重打包后程序正常运行,至此逆向分析结束。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>