frida 几个月前就关注了,无奈当时连环境都搭建不起来。
这次下定决心体验了一把 frida ,感觉像是发现了新大陆一样,不信你继续看~
frida跟xposed对比:
xposed:root + xposed环境 + 写xposed模块
frida:root + 写js脚本
frida只需要设备有root权限,无需刷xposed环境,就能实现hook功能,修改js脚本之后直接注入即可。本文将手把手带你上手frida。
frida github: github.com/frida/frida
frida分客户端环境和服务端环境。
在客户端我们可以编写js代码或者Python代码,连接远程设备,提交要注入的代码到远程,接受服务端的发来的消息等。
在服务端,我们需要用Javascript代码注入到目标进程,操作内存数据,给客户端发送消息等操作。我们也可以把客户端理解成控制端,服务端理解成被控端。
假如我们要用PC来对Android设备上的某个进程进行操作,那么PC就是客户端,而Android设备就是服务端。
根据自己的平台下载frida服务端并解压 github.com/frida/frida…
解压,重命名为 frida-server,( 不要下载错,不然启动服务失败,手机可能会崩溃重启 )
电脑连接手机,通过adb执行以下命令将服务端推到手机的 /data/local/tmp 目录
adb push frida-server /data/local/tmp/frida-server
执行以下命令修改frida-server文件权限
adb shell chmod 777 /data/local/tmp/frida-server
启动服务
adb shell cd /data/local/tmp su ./frida-server
我用的是mac,windows同理, 首先需要python环境,这个自己安装,然后通过pip安装frida
命令如下
pip install frida-tools pip install frida
等待一会儿,测试frida是否安装成功
查看进程
frida-ps -U
参数解释:
-U 指定对USB设备操作 -l 指定加载一个Javascript脚本 最后指定一个进程名,如果想指定进程pid,用-p选项。正在运行的进程可以用frida-ps -U命令查看
这个报错是由于服务端没启动,前面的启动服务是不是没成功,回去看 1.1节。 然后在另一个控制台执行 frida-ps -U
可以看到手机正在运行的进程pid都列出来了。
接下来将一个脚本 myhook.js 注入到Android目标进程
frida -U -l myhook.js com.lanshifu.demo_module
成功了,这个myhook.js内容如下
if(Java.available){ Java.perform(function(){ var MainActivity = Java.use("com.lanshifu.demo_module.ui.activity.DemoMainActivity"); MainActivity.testCrash.overload("int").implementation=function(chinese,math){ console.log("[javascript] testCrash method be called."); send("hook testCrash method success>>>"); return this.testCrash(12345678); } }); } 复制代码
拦截 DemoMainActivity
的 testCrash(int)
方法,调用的时候打印信息并且将入参改为12345678。日志打印出来了,界面上看也成功了,就不上图了,下面会详细介绍hook的各种操作。
frida运行过程中,执行**%resume 重新注入,执行 %reload 来重新加载脚本;执行 exit**结束脚本注入
一般情况下,修改js脚本,然后输入exit结束脚本注入,再重新输入注入命令即可
Java.use
方法用于声明一个Java类,在用一个Java类之前首先得声明。比如声明一个String类,要指定完整的类名: var StringClass=Java.use("java.lang.String");
修改一个函数的实现是逆向调试中相当有用的。修改一个函数的实现后,如果这个函数被调用,我们的Javascript代码里的函数实现也会被调用。
对于基本类型,直接用它在Java中的表示方法就可以了,不用改变,例如: int short char byte boolean float double long
基本类型数组,用左中括号接上基本类型的缩写
基本类型 缩写: boolean: Z byte: B char: C double: D float: F int: I long: J short: S
例如:int[]类型,在重载时要写成[I
例如: java.lang.String
例如: [java.lang.String;
//声明一个Java类 var MyClass = Java.use("com.lanshifu.demo_module.ui.activity.DemoHookTestActivity$MyClass"); //hook 无参构造 MyClass.$init.overload().implementation=function(){ //调用原来构造 this.$init(); send("hook 无参构造 "); } 复制代码
var MyClass = Java.use("com.lanshifu.demo_module.ui.activity.DemoHookTestActivity$MyClass"); //hook 有参构造,入参是 int[] MyClass.$init.overload('[I').implementation=function(param){ send("hook 有参构造 init(int[] i) method "); } 复制代码
ClassName.$init.overload('[B','int','int').implementation=function(param1,param2,param3){ //do something } 复制代码
注意:当构造函数(函数)有多种重载形式,比如一个类中有两个形式的func:void func()和void func(int),要加上overload来对函数进行重载,否则可以省略overload
//void函数 MyClass.method1.overload().implementation=function(){ send("hook method1 "); } //有入参的函数 MyClass.method2.overload("int","int").implementation=function(param1,param2){ send("hook method2 "); this.method2(100,100) } 复制代码
和Java一样,创建类实例就是调用构造函数,而在这里用$new表示一个构造函数。
//实例化一个类并调用它的method3方法 var ClassName=Java.use("com.lanshifu.demo_module.ui.activity.DemoHookTestActivity$MyClass"); var instance = ClassName.$new(); instance.method3(1,2,3); 复制代码
用Java.cast方法来对一个对象进行类型转换,如将variable转换成java.lang.String:
var StringClass=Java.use("java.lang.String"); var NewTypeClass=Java.cast(variable,StringClass); 复制代码
Java.perform(fn)在Javascript代码成功被附加到目标进程时调用,我们核心的代码要在里面写。格式:
Java.perform(function(){ //do something... }); 复制代码
我直接在Pycharm 里面创建了 FridaDemo.py,然后代码如下
# -*- coding: UTF-8 -*- import frida, sys jscode = """ if(Java.available){ Java.perform(function(){ var MainActivity = Java.use("com.lanshifu.demo_module.ui.activity.DemoMainActivity"); MainActivity.testCrash.overload("int").implementation=function(chinese,math){ console.log("[javascript] testCrash method be called."); send("hook testCrash method success>>>"); return this.testCrash(12345678); } //声明一个Java类 var MyClass = Java.use("com.lanshifu.demo_module.ui.activity.DemoHookTestActivity$MyClass"); //hook 无参构造 MyClass.$init.overload().implementation=function(){ //调用原来构造 this.$init(); send("hook 无参构造 "); } //hook 有参构造,入参是 int[] MyClass.$init.overload('[I').implementation=function(param){ send("hook 有参构造 init(int[] i) method "); } //hook 多个参数构造,入参是 int,int MyClass.$init.overload("int","int").implementation=function(param1,param2){ send("hook success "); send(param1); send(param2); //修改返回值 return 100; } //void函数 MyClass.method1.overload().implementation=function(){ send("hook method1 "); this.method1(); } //有入参的函数 MyClass.method2.overload("int","int").implementation=function(param1,param2){ send("hook method2 "); //实例化一个类并调用它的method3方法 var ClassName=Java.use("com.lanshifu.demo_module.ui.activity.DemoHookTestActivity$MyClass"); var instance = ClassName.$new(); instance.method3(1,2,3); this.method2(100,100) } // MyClass.method3.overload("int","int","int").implementation=function(param1,param2,param3){ send("hook method3 "); this.method3(100,100,100) } }); } """ def on_message(message, data): if message['type'] == 'send': print("[*] {0}".format(message['payload'])) else: print(message) pass # 查找USB设备并附加到目标进程 session = frida.get_usb_device().attach('com.lanshifu.demo_module') # 在目标进程里创建脚本 script = session.create_script(jscode) # 注册消息回调 script.on('message', on_message) print('[*] Start attach') # 加载创建好的javascript脚本 script.load() # 读取系统输入 sys.stdin.read() 复制代码
执行这个python脚本,手机打开app执行到该方法,看到打印了日志
ctrl + c
停止脚本,看起来方便一些。
接下来分析一下步骤:
frida.get_usb_device() attach create_script() on() load()
adb shell 'su -c /data/local/tmp/frida-server' frida -U -l myhook.js com.lanshifu.demo_module
经过体验一把frida之后, 发现不需要写xposed模块,直接一个js脚本,就能马上测试hook点。
手机frida服务端的配置踩了一个坑,我的6.0 手机是arm架构的,我下载了arm64的,导致注入js之后手机崩溃。
本文参考:
www.jianshu.com/p/f98aca8f3…
www.frida.re/docs/exampl…如有雷同,纯属copy。
下一篇逆向实战将用frida来寻找hook点。
首次在掘金发文,各位看官。
我的简书主页:[蓝师傅_Android]