pip install frida-tools
pip install frida
PS: frida安装时需要编译所以会卡在Running setup.py bdist_wheel for frida ...\
,需要耐心等待
首先运行以下命令确保frida可以监控到非自己的子进程
$ sudo sysctl kernel.yama.ptrace_scope=0
基本例子
import frida
import sys
# hook逻辑脚本
jscode = """
Interceptor.attach(ptr("%s"), {
onEnter: function(args) {
send(args[0].toInt32());
}
});
"""
# 注入进程,attach传入进程名称(字符串)或者进程号(整数)
session = frida.attach("hello")
script = session.create_script(jscode % int(sys.argv[1], 16))
#int()函数把字符串表示的16进制数转换成整数
#上面的jscode % int(sys.argv[1], 16)是python格式化字符串的语法
# 接收脚本信息的回调函数
# message是一个对象,type属性为send则表示send函数发送的信息,其内容在payload里
# 下面这个on_message函数可以做固定用法,一般无需改动,当然也可直接打印message看看里边的内容
def on_message(message, data):
if message['type'] == 'send':
print(message['payload'])
elif message['type'] == 'error':
print(message['stack'])
# 应该是设置message事件的回调函数
script.on('message', on_message)
# 加载hook脚本
script.load()
# 保持主线程不结束(也可以使用time.sleep循环)
sys.stdin.read()
frida.attach
attach进程创建session,使用进程名(字符串)或者进程号(整数)指定进程session.create_script
session创建hook脚本script.on
注册用于通信的回调函数script.load
加载脚本sys.stdin.read
保持主线程运行Interceptor.attach(ptr(''%s'), {
onEnter: function(args) {
send(args[0].toInt32());//这里也可以直接修改args的值来改变传入的参数
onLeave: function onLeave(retval) {
}
});
Interceptor
类用于hook操作,其attach方法有两个参数
要hook的函数在内存中加载的地址
这个地址可以通过Module.findExportByName(链接库名,函数名)
来获取链接库中的导出函数地址
ptr()
接收一个字符串,用于构造一个指针
包含回调函数的对象
onEnter
在进入该函数时调用,args
为传入该函数的参数;onLeave
在函数返回时被调用,retval
该函数的返回值
send
函数用于hook脚本向外部发送信息,通过在外部python脚本中用script.on
设置回调函数来接收信息,回调函数有两个参数,第一个message
就包含send
发送的信息,第二个参数暂时没有在官方文档中找到用处。
详细描述参考官方文档Interceptor,ptr
var f = new NativeFunction(ptr("%s"), 'void', ['int']);
f(1911);
NativeFunction
构造函数接受三个参数——函数地址,返回值,参数类型(数组表示), 然后就可以调用该函数
详细描述参考官方文档NativeFunction
var st = Memory.allocUtf8String("TESTMEPLZ!");
Memory对象下的方法可以操作被attach的进程的内存空间 ,该方法分配的空间似乎会在函数结束后被销毁,比如在onEnter中分配的内存空间在onEnter结束后会被释放,使用时需注意。
详细描述参考官方文档Memory
编写hook脚本
var write_address = Module.findExportByName('libc.so.6', 'write');
//js字符串中的换行符必须要写成\\n
//给我们注入的字符串分配空间
var st = Memory.allocUtf8String('你的write函数已经被劫持了\\n');
if(write_address != null){
send('write is at' + write_address)
Interceptor.attach(write_address, {
onEnter: function(args) {
//输出write的原始参数write(int fd, char* buff, int size)
send(args[0].toString()+ ' ' + args[1].toString() + ' ' + args[2].toString());
//把第二个参数替换成我们的字符串
args[1] = st;
//第三个参数改为我们字符串的长度,utf8一个汉字为3个字节,我们的字符串共计36字节
args[2] = ptr('36');
},
onLeave: function(retval) {
}
});
}else{
send('no such function');
}
运行(加载js脚本的过程同上文所述)
python通过write
输出的所有值都变成我们的字符串,下边是python在write
stdout(0x1
)和stderr(0x2)
下载frida-server,一般为frida-server-12.3.5-android-arm64.xz ,根据Android系统的架构来选择(arm,arm64,x86,x86_64)
用电脑连接Android手机,把frida-server放到手机里,再通过adb启动手机shell,运行frida-server(需要root权限)
$ adb root
$ adb push frida-server /data/local/tmp/
$ adb shell "chmod 755 /data/local/tmp/frida-server"
$ adb shell "/data/local/tmp/frida-server &"
将hook脚本中的frida.attach()
替换成frida.get_usb_devicce().attach()
就可以hook手机上的进程了,可以hook java方法,也可用hook Android系统程序的native api
hook代码加载流程同上文hook native api相同,只是用于hook逻辑的js代码不同
Java.perform(function () {
// Function to hook is defined here
var MainActivity = Java.use('com.example.seccon2015.rock_paper_scissors.MainActivity');
// hook MainActivity类的onClick方法
// java方法的hook采用了替换的方式,提供了调用原方法的途径
MainActivity.onClick.implementation = function (v) {
//进入方法时的操作(操作参数)
send('onClick');
// 调用原方法
this.onClick(v);
// 退出原方法时的操作(操作返回值等)
this.m.value = 0;
this.n.value = 1;
this.cnt.value = 999;
// Log to the console that it's done, and we should have the flag!
console.log('Done:' + JSON.stringify(this.cnt));
};
});
Java.perform()
应该是对hook java层进行包装,其参数为一个函数,该函数为具体hook逻辑
Java.use()
参数为需要hook的类名
classname.method.implementation
需要被赋值为一个函数,该函数会替换method(不同于hook本地api时有onEnter
和onLeave
),在函数内使用this
可以访问该类,调用其方法和属性。调用属性时使用this.property.value
, 而不是this.property
,遇到有属性和方法同名时属性前加下划线_
,如this._property.value
hook有重载的方法时,使用overload
指出参数具体类型
例如classname.method.overload('type1','type2').implementation
构造方法使用$init
表示,例如classname.$init.overload('type1','type2').implementation
ps: 调用某些原方法可能无法正常工作,据说是系统原因,只有更换系统版本