今天拿到了新玩具JEB 3.17.1,想测试一下,顺便学习学习Android逆向,于是找了一个apk准备试试水,看看能不能搞到VIP。
拖入jeb查看
嗯。。google一下
几维安全的壳,虚拟化很烦,也没什么好的脱壳工具,于是切换思路
从本质上来说,虚拟化就是自己实现了一套基本的cpu指令,通过自己的解释器解释给机器。因此想要在虚拟化之后仍然能让机器运行,当然还得还原代码给机器,只不过可能在内存中释放,加大了静态分析的难度。
那么直接让apk运行起来,我们去dump虚拟机自解密出来的dex不就好了么。
frida 是一款基于 python+javascript 的 hook 框架,可运行在 android、ios、linux、win等各个平台,主要使用的动态二进制插桩技术。
总之frida是非常强大的,可以实现很多奇妙操作。
https://github.com/frida/frida/releases
到上面链接里找到对应手机架构,与frida版本相同的frida-server
移动frida-server到安卓的/data/local/tmp目录
./adb push frida-server /data/local/tmp ./adb kill-server && ./adb server && ./adb shell # 这里我用了mumu模拟器,其他虚拟机连接adb方法请自行查询 cd /data/local/tmp chmod 777 frida-server ./frida-server #开始你的炸弹秀
这里使用了FRIDA-DEXDump https://github.com/hluwa/FRIDA-DEXDump
其原理就是在内存中检索dex035头并获取dex长度,然后提取出来。当然,这样做会将内存中所有的dex包都提取出来,具体哪个才是我们需要的得根据包名判断并静态分析查看。
这里我成功提取到了无壳dex,包名都比较清晰
既然壳不好脱而且脱下来的又不容易修复,就考虑先看看封包里有没有VIP数据
使用HttpCanary对apk进行抓包
抓到了一个登陆验证包
但响应里没有我们想要的信息,只是一个随时变动的值而已
看来抓包这条路走不通了,我们接下来只能分析拿到的dex。
我们使用JEB来分析我们拿到的dex,查看包的层级数,定位到目标apk的包名,这里的命名都很清晰,直接按单词分析即可。
我这次用来当例子的是一个类似抖音快手的app,有视频和直播两个Activity,视频每天只能看十个(最坑的是重复点十次也算),然后就要求充值VIP,用户进入后可以不登录,会自动生成一个ID,且读取系统信息作为认证,所以重复的卸载安装当然是绕不过次数限制的。
所以我们的目标就是通过frida hook函数使我们绕过限制,成为VIP。
查看原apk文件的Manifest
这个权限它就离谱。。。
我们来看Activity的启动顺序
先来了一个开屏广告SplashActivity,然后加载Main和Live顺便走一遍登录注册。
一般判断用户是否是VIP,可能存在多处判断,这样的话我们hook的点会量很大,而且可能存在漏掉的情况,因此去找变量最初赋值的位置来hook是从源头上控制了数据,而且前面封包分析发现用户数据请求发送后给我们的响应不包含VIP确认数据,因此可以说是万无一失。
回到层级目录分析,我们发现video/bean这个包里有我们感兴趣的东西,应该是包含了短视频相关数据。
查看TouseinfoBean这个类
我们发现这里应该包含了用户数据,修改此处就可以了。
但事情没有那么简单,通过交叉引用我们发现这个类根本没有被实例化(可能是写着写着忘了又重新写了一个用户管理模块???)
搜索字符串后并没有什么有用的发现,分析良久才想到从包层级列表搜索方法,豁然开朗(还是太菜)。
嗯。。原来把直播和视频的vip管理合并到一个包了。
com.xxx.bean下有二十多个类,只能一个一个分析了,最后发现了几个有用的类
其中UserBean用来管理用户信息,评论,VIP相关等等,UserLevelBean管理用户经验之类的信息。
UserBean类中以下三个方法是我们的Hook点
@JSONField(name = "vvLevel") public String getVVLevel() { return this.vvLevel; } @JSONField(name = "isVV") public boolean isVV() { return this.isVV; } public int getLevel() { if(this.level == 0) { this.level = 1; } return this.level; }
没有加固的apk,一般来说直接改smali重新打包就行,而像我们现在遇到的壳,修复dex打包是一件很费力的事情,在这里frida的强大就体现出来了。
使用frida来hook加固的Android应用的java层 的原理可以看这里,简单来说就是拿到加载应用本身dex的classloader,通过这个classloader去找到被加固的类,通过这个类去Hook需要Hook的方法。
因此要穿透加固 Hook Java层函数的jscode模版如下:
if(Java.available) { Java.perform(function(){ var application = Java.use("android.app.Application"); application.attach.overload('android.content.Context').implementation = function(context) { var result = this.attach(context); // 先执行原来的attach方法 var classloader = context.getClassLoader(); // 获取classloader Java.classFactory.loader = classloader; var Hook_class = Java.classFactory.use("com.xxx.类名"); console.log("Hook_class: " + Hook_class); // 下面代码和写正常的hook一样 Hook_class.方法名.implementation = function() // 有参数填参数 { //要执行的操作 } return result; } }); }
根据上面对Hook点的分析,我们可以写出以下jscode
if(Java.available) { console.log('ok111'); Java.perform(function(){ var application = Java.use("android.app.Application"); console.log('ok222'); application.attach.overload('android.content.Context').implementation = function(context) { console.log('ok333'); var result = this.attach(context); // 先执行原来的attach方法 var classloader = context.getClassLoader(); // 获取classloader Java.classFactory.loader = classloader; console.log('ok444'); var userinfo = Java.classFactory.use("com.xxx.bean.UserBean"); console.log("user: " + userinfo); userinfo.getLevel.implementation = function(){ return 10; } userinfo.isVV.implementation = function(){ return true; } userinfo.getVVLevel.implementation = function(){ return "9"; } return result; } }); }
frida注入脚本有两种方式,使用python作为载体,直接运行脚本即可。
import frida, sys # hook代码,采用javascript编写 jscode = """ //javascript代码 """ # 自定义回调函数 def on_message(message, data): if message['type'] == 'send': print("[*] {0}".format(message['payload'])) else: print(message) process = frida.get_usb_device().attach('应用完整包名') script = process.create_script(jscode) script.on('message', on_message) script.load() sys.stdin.read()
或者使用命令行方式,当然了,还需要安装frida-tools
这里我使用命令行方式(这里有个坑,必须按我给出的顺序,不然会报错无法连接)
先连接adb
./adb kill-server && ./adb server && ./adb shell // mumu模拟器连接方式,其他模拟器自行查询
新开一个shell,打开tcp转发,默认为27042端口,但是有些app会检测这个端口,因此我们来自定义一个
./adb forward tcp:1234 tcp:1234
回到第一个shell,我们已经连接到了adb,直接输入
./data/local/tmp/frida-server -l 0.0.0.0:1234
在第二个shell中执行注入命令
frida -H 127.0.0.1:1234 -f com.xxx -l hook.js
成功连接后命令行有如下回显
输入 %resume 对应的apk自启,没有报错则成功注入
我这里还加了一些流程和类名的输出
测试apk,已经可以了,其他功能思路差不多,Hook就行。