这次继续上次的案例,同样是hook方法的参数和返回值,只不过这次我们换个更牛逼的框架————frida
四哥的Android逆向之旅之frida
看雪两篇很好的教程 初识Frida–Android逆向之Java层hook
官网示例
推荐一篇国外的精华总结文章(里面包括好几个系列frida案例)链接
由于frida 12.0版本开始把frida和CLI工具frida-tools分开了,所以要在命令行使用frida命令的话还要装tools 见官方介绍,看雪有个一键运行的脚本,这里精简了一下贴在了评论里,第一次配置好了之后直接运行bat文件即可,省去了繁琐的操作命令
代码解释
import frida, sys
def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
jscode = """
Java.perform(function () {
var HttpRequestEntity = Java.use('cn.thecover.www.covermedia.data.entity.HttpRequestEntity');//要hook的类名完整路径
HttpRequestEntity.getSign.implementation = function (arg1,arg2,arg3) { // 重写要hook的方法getSign,当有多个重名函数时需要重载,function括号为函数的参数个数
var Sign=this.getSign(arg1,arg2,arg3); //调用原始的函数实现并且获得返回值,如果不写的话我们下面的代码会全部替换原函数
send("arg1:"+arg1); //打印参数值
send("arg2:"+arg2);
send("arg3:"+arg3);
send(this.timestamp); //{u'fieldReturnType': {u'className': u'java.lang.String', u'type': u'pointer', u'name': u'Ljava/lang/String;', u'size': 1}, u'fieldType': 2, u'value': u'1529071177437'}
send(this.timestamp.value); //1529071177437
send(Sign); //打印返回值
return Sign; //函数有返回值时需要返回
};
});
"""
process = frida.get_usb_device().attach('cn.thecover.www.covermedia')
script = process.create_script(jscode)
script.on('message', on_message)
script.load()
sys.stdin.read()
有几个需要注意的点:
1:代码里面不能有中文注释,因为Python和Unicode编码有冲突,可以改但是有点麻烦没弄。
2:当hook方法有多个重名函数时,需要调用overload进行重载(参数签名可从error中获取信息),见hook 重载函数
3:可以一次hook多个函数,见下面
下面来看frida直接hook一个经过360加固的APP的案例
触发APP里一个刷新的事件,我们使用Tracevie来跟踪他
我们脱壳后找到这个方法com.peopledailychina.activity.network.GetParamsUtil.getParams
接着我们使用frida进行hook上面关键的那个getMD5Str函数
直接attach包名会报这个错误,这是因为程序中存在了两个进程,直接用报错信息里面的pid进行attach即可
因为最后输出的结果是有中文的,而frida本身不支持Unicode编码,会报UnicodeEncodeError: ‘ascii’ codec can’t encode characters这个错误,所以使用js里面的encodeURI函数进行编码,然后再进行URL解码即可
而使用xposed来hook提示连类都找不到,必须要先拿到classloader才行(因为xposed在应用启动前就做好了hook准备,而这些类被壳隐藏了导致hook失败,而frida是等应用启动了以后才注入到进程hook所以能hook成功)
只能说明frida真是强大啊!!!另外亲测除了360加固,腾讯,阿里,爱加密也是可以的,但是梆梆就不行了,因为壳有防hook,注入保护,下面我们来看如何绕过梆梆的双进程保护,还是拿上面那个xx新闻开始动手,4.2.0加了梆梆的壳,运行应用之后存在两个进程,直接用frida hook其中的一个进程报错
这应该就是梆梆壳搞的鬼,外壳创建了两个父子进程互相调试,由于一个进程只能被附加一次,再使用frida的话我们就附加不上了,见防止应用被调试——先占坑,自己附加,所以我们的解决方法就是在启动这个应用时用frida提前占坑,防止子进程ptrace父进程,然后就可以愉快的注入父进程了,frida的spawn和resume给我们提供了这样的功能。
import frida, sys
def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
jscode = """
Java.perform(function () {
// Function to hook is defined here
//hook的类和方法仍然和上面一致,此处省略...
};
});
"""
device = frida.get_device_manager().enumerate_devices()[-1]
pid = device.spawn(["cn.thecover.www.covermedia"])
session = device.attach(pid)
print("[*] Attach Application id:",pid)
device.resume(pid)
print("[*] Application onResume")
script = session.create_script(jscode)
'''
process = frida.get_usb_device().attach("cn.thecover.www.covermedia")
script = process.create_script(jscode)
'''
script.on('message', on_message)
print('[*] Running CTF')
script.load()
sys.stdin.read()
而且发现attach的23703进程的确就是附加报错的第一个进程。这样就成功绕过了梆梆的双进程防注入。对此有一个更简单的命令行参数 -f ,参考 hacking-android-apps-with-frida-3,使用frida -U -f com.example.pkgname -l 1.js –no-pause,但是我在实际测试的时候会提示找不到类的情况,对此要在脚本外加一个setTimeout的嵌套,防止超时,而且由于是js的脚本这样就不用再担心Python和中文注释的冲突问题。
最后来看如何利用frida打印堆栈,参考frida社区CodeShare get-a-stack-trace-in-your-hook进行了下修改
Java.perform(function () {
var HttpRequestEntity = Java.use('cn.thecover.www.covermedia.data.entity.HttpRequestEntity');
var threadef = Java.use('java.lang.Thread');
var threadinstance = threadef.$new();
function Where(stack){
for(var i = 0; i < stack.length; ++i){
send(stack[i].toString());
}
}
HttpRequestEntity.getSign.implementation = function (arg1,arg2,arg3) {
var ret = this.getSign(arg1,arg2,arg3);
var stack = threadinstance.currentThread().getStackTrace();
send("Full call stack:" + Where(stack));
//send("Deobfuscated " + ret + " @ " + stack[3].toString());
return ret;
};
});
以后有新的姿势再慢慢研究吧!