Frida框架简单使用

1.安装

pip install frida-tools
pip install frida      

PS: frida安装时需要编译所以会卡在Running setup.py bdist_wheel for frida ...\,需要耐心等待

2. Hook native api

首先运行以下命令确保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()

hook加载的基本流程

  1. frida.attach attach进程创建session,使用进程名(字符串)或者进程号(整数)指定进程
  2. session.create_script session创建hook脚本
  3. script.on 注册用于通信的回调函数
  4. script.load加载脚本
  5. sys.stdin.read保持主线程运行

hook代码(js)

1. hook函数

Interceptor.attach(ptr(''%s'), {
    onEnter: function(args) {
        send(args[0].toInt32());//这里也可以直接修改args的值来改变传入的参数
    onLeave: function onLeave(retval) {
    }
});

Interceptor类用于hook操作,其attach方法有两个参数

  1. 要hook的函数在内存中加载的地址

    这个地址可以通过Module.findExportByName(链接库名,函数名)来获取链接库中的导出函数地址

    ptr()接收一个字符串,用于构造一个指针

  2. 包含回调函数的对象

    onEnter在进入该函数时调用,args为传入该函数的参数;onLeave在函数返回时被调用,retval该函数的返回值

send函数用于hook脚本向外部发送信息,通过在外部python脚本中用script.on设置回调函数来接收信息,回调函数有两个参数,第一个message就包含send发送的信息,第二个参数暂时没有在官方文档中找到用处。

详细描述参考官方文档Interceptor,ptr

2. 调用函数

var f = new NativeFunction(ptr("%s"), 'void', ['int']);
f(1911);

NativeFunction构造函数接受三个参数——函数地址返回值参数类型(数组表示), 然后就可以调用该函数

详细描述参考官方文档NativeFunction

3. 分配空间

var st = Memory.allocUtf8String("TESTMEPLZ!");

Memory对象下的方法可以操作被attach的进程的内存空间 ,该方法分配的空间似乎会在函数结束后被销毁,比如在onEnter中分配的内存空间在onEnter结束后会被释放,使用时需注意。

详细描述参考官方文档Memory

4. hook printf函数的一个测试

  1. 编写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');
    }
    
  2. 运行(加载js脚本的过程同上文所述)

python通过write输出的所有值都变成我们的字符串,下边是python在write stdout(0x1)和stderr(0x2)
Frida框架简单使用_第1张图片

3. 在Android系统中运行

  1. 下载frida-server,一般为frida-server-12.3.5-android-arm64.xz ,根据Android系统的架构来选择(arm,arm64,x86,x86_64)

  2. 用电脑连接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 &"
    
  3. 将hook脚本中的frida.attach()替换成frida.get_usb_devicce().attach()就可以hook手机上的进程了,可以hook java方法,也可用hook Android系统程序的native api

4.hook java方法

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));
    };
});
  1. Java.perform()应该是对hook java层进行包装,其参数为一个函数,该函数为具体hook逻辑

  2. Java.use()参数为需要hook的类名

  3. classname.method.implementation需要被赋值为一个函数,该函数会替换method(不同于hook本地api时有onEnteronLeave),在函数内使用this可以访问该类,调用其方法和属性。调用属性时使用this.property.value, 而不是this.property,遇到有属性和方法同名时属性前加下划线_,如this._property.value

  4. hook有重载的方法时,使用overload指出参数具体类型

    例如classname.method.overload('type1','type2').implementation

  5. 构造方法使用$init表示,例如classname.$init.overload('type1','type2').implementation

ps: 调用某些原方法可能无法正常工作,据说是系统原因,只有更换系统版本

你可能感兴趣的:(逆向)