Frida 框架学习

一、环境搭建

frida框架分为两部分:
  1. 一部分是运行在系统上的交互工具frida CLI。
  2. 另一部分是运行在目标机器上的代码注入工具 frida-serve。
 
环境安装
#windows
pip install frida
pip install frida-tools
getprop ro.product.cpu.abi  #查看安卓CPU型号,然后去 https://github.com/frida/frida/releases 下载对应型号

之后,把文件上传到手机

adb push frida-server-12.2.25-android-arm64 /data/local/tmp/
chmod 755 frida-server-12.2.25-android-arm64
./frida-server-12.2.25-android-arm64

下面我们尝试一些常见的Hook手段

二、Hook 普通函数

比如这样一个APK文件
 
0
我们Hook my_activity 这个类中的 fun 函数
Frida python代码
import time

import frida

device = frida.get_usb_device()
pid = device.spawn(["com.example.a11x256.frida_test"])
device.resume(pid)
time.sleep(1)  # Without it Java.perform silently fails
session = device.attach(pid)
with open("s1.js") as f:
    script = session.create_script(f.read())
script.load()

# prevent the python script from terminating
raw_input()
js代码
console.log("Script loaded successfully ");
Java.perform(function x() { //Silently fails without the sleep from the python code
    console.log("Inside java perform function");
    //get a wrapper for our class
    var my_class = Java.use("com.example.a11x256.frida_test.my_activity");
    //replace the original implmenetation of the function `fun` with our custom function
    my_class.fun.implementation = function (x, y) {
        //print the original arguments
        console.log("original call: fun(" + x + ", " + y + ")");
        //call the original implementation of `fun` with args (2,5)
        var ret_value = this.fun(2, 5);
        return ret_value;
    }
});
这里是要Hook一个Java方法,首先使用
Java.use找到Java的类,然后调用方法的
implementation来实现Hook,提供一个回调函数,这个函数将覆盖原有的函数。
运行后可以看到消息被打印到了控制台上
 
0
我们也可以在命令行上通过 adb logcat | findstr Sum 命令查看系统消息
0
 
 

三、Hook 重载函数

jadx载入后代码如下:
0
可以看出,主函数调用了两个fun函数,一个是void fun(int,int) ,另一个是string fun (string x)
我们Hook的时候需要考虑到重载的问题,不然程序会报错
 
frida Python代码
import time
import sys
import frida


def my_message_handler(message, payload):
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)


device = frida.get_usb_device()
pid = device.spawn(["com.example.a11x256.frida_test"])
device.resume(pid)
time.sleep(1)  # Without it Java.perform silently fails
session = device.attach(pid)
with open(r"D:\Java\Android\Frida\frida-android-examples-master\examples\2\s2.js") as f:
    script = session.create_script(f.read())
script.on("message", my_message_handler)
script.load()

# prevent the python script from terminating
sys.stdin.read()
python代码没有太大变化,主要是js代码
js代码
console.log("Script loaded successfully ");
Java.perform(function x() {
    console.log("Inside java perform function");
    var my_class = Java.use("com.example.a11x256.frida_test.my_activity");
    my_class.fun.overload("int", "int").implementation = function (x, y) { //hooking the old function
        console.log("original call: fun(" + x + ", " + y + ")");
        var ret_value = this.fun(2, 5);
        return ret_value;
    };
    var string_class = Java.use("java.lang.String");

    my_class.fun.overload("java.lang.String").implementation = function (x) { //hooking the new function
        console.log("*************************************")
        var my_string = string_class.$new("My TeSt String#####");
        console.log("Original arg: " + x);
        var ret = this.fun(my_string);
        console.log("Return value: " + ret);
        console.log("*************************************")
        return ret;
    };
});
可以看到 js代码的Hook函数多了 overload 这个函数...

四、主动调用类中的函数

这里有多种思路,比如我们可以创建一个新对象完成调用,或者我们也可以搜索内存中已有的对象进行调用,Java.choose函数就是查找对象的功能
(推荐使用内存中原有的对象,因为内存中的对象才是应用真实的应用使用的对象,自己创建对象的数据可能与应用当时实际使用的数据不一致
python代码
import time

import frida


def my_message_handler(message, payload):
    print (message)
    print (payload)


device = frida.get_usb_device()
pid = device.spawn(["com.example.a11x256.frida_test"])
device.resume(pid)
time.sleep(1)  # Without it Java.perform silently fails
session = device.attach(pid)
with open(r"D:\Java\Android\Frida\frida-android-examples-master\examples\3\s3.js") as f:
    script = session.create_script(f.read())
script.on("message", my_message_handler)
script.load()

command = ""
while 1 == 1:
    command = input("Enter command:\n1: Exit\n2: Call secret function\nchoice:")
    if command == "1":
        break
    elif command == "2":
        script.exports.callsecretfunction()
js代码
console.log("Script loaded successfully ");
var instances_array = [];
function callSecretFun() {
    Java.perform(function () {
        if (instances_array.length == 0) { // if array is empty
            Java.choose("com.example.a11x256.frida_test.my_activity", {   //choose 在堆上查找实例化的对象  //onMatch 对应的函数在命中一个实例的时候被调用,传入函数中的参数 instance 就是被命中的实例
                onMatch: function (instance) {
                    console.log("Found instance: " + instance);
                    instances_array.push(instance)
                    console.log("Result of secret func: " + instance.secret());
                },
                onComplete: function () { }              //onComplete 函数会在所有实例遍历完毕之后被调用,可以做一些后续处理操作

            });
        }
        else {//else if the array has some values
            for (i = 0; i < instances_array.length; i++) {
                console.log("Result of secret func: " + instances_array[i].secret());
            }
        }

    });


}
rpc.exports = {
    callsecretfunction: callSecretFun   //用于和 python 代码打交互
};

 

Java.use(className),动态获取className的类定义
Java.perform方法:当 js 附加到目标的进程中时被执行,运行其中定义的函数
Java.choose方法:通过完整类名,获取它的实例,从而对实例中的数据进行修改
onMatch 对应的函数在命中一个实例的时候被调用,传入函数中的参数 instance 就是被命中的实例
onComplete 函数会在所有实例遍历完毕之后被调用,可以做一些后续处理操作
 
 
上面的frida 脚本中python脚本和js脚本通过rpc.exports 打了个交互
代码中我们直接搜索内存中的对象并调用了 secret 函数

五、Hook apk系统函数

python代码
import time

import frida


def my_message_handler(message, payload):
    print (message)
    print (payload)
    if message["type"] == "send":
        # print message["payload"]
        data = message["payload"].split(":")[1].strip()
        script.post({"my_data": data})  # send JSON object
        print ("Modified data sent")


device = frida.get_usb_device()
pid = device.spawn(["com.example.a11x256.frida_test"])
device.resume(pid)
time.sleep(1)
session = device.attach(pid)
with open(r"D:\Java\Android\Frida\frida-android-examples-master\examples\4\s4.js") as f:
    script = session.create_script(f.read())
script.on("message", my_message_handler)  # register the message handler
script.load()
sys.stdin.read()
js脚本
console.log("Script loaded successfully ");
Java.perform(function () {
    var tv_class = Java.use("android.widget.TextView");
    tv_class.setText.overload("java.lang.CharSequence").implementation = function (x) {
        var string_to_send = x.toString();
        var string_to_recv;
        send(string_to_send); // send data to python code
        recv(function (received_json_object) {
            string_to_recv = received_json_object.my_data
        }).wait(); //block execution till the message is received
        return this.setText(string_to_recv);
    }
});
这里通过Hook android.widget.TextView 类中的 java.lang.CharSequence方法读取了文本框中的值
每次点击button按钮就会触发
 
 
参考:
 
 

六、一些函数或关键词

1、spawn函数:  spawn模式,找到目标包名并重启,在进程创建时注入(js),与此同时,Frida还有一种操作APP的模式是attach,即应用运行过程中注入(js)到进程
2、overload函数,主要用于重载
3、Java.use(className),动态获取className的类定义

Java.perform方法:当 js 附加到目标的进程中时被执行,运行其中定义的函数
Java.choose方法:通过完整类名,获取它的实例,从而对实例中的数据进行修改
onMatch 对应的函数在命中一个实例的时候被调用,传入函数中的参数 instance 就是被命中的实例
onComplete 函数会在所有实例遍历完毕之后被调用,可以做一些后续处理操作

参考:
 
posted @ 2022-07-26 23:40  TLSN  阅读(380)  评论(0编辑  收藏  举报