Hook最低级函数

还是以goatdroid.apk为例,在登录时,存在验证输入框是否都输入内容的代码如下:

image-20230315091656283

我们就尝试Hook此函数,编写JS代码如下,劫持输入的内容,并将输入改为adminpassword

console.log("Script loaded successfully ");
// 在目标进程中执行 Java 代码,允许以动态方式操作目标应用程序中的Java类和对象,从而实现各种功能,例如Hooking方法、修改类的属性等等
Java.perform(function () {
    console.log("Inside java perform function");
    //定位类
    var testClass = Java.use("org.owasp.goatdroid.fourgoats.activities.Login");
    console.log("Java.Use.Successfully!");
    //更改类的方法的实现(implementation)
    testClass.allFieldsCompleted.implementation = function(userName,password){
        console.log("userName: " + userName + " password: " + password)
        // 调用原来的函数获取结果并返回
        var ret_value = this.allFieldsCompleted("admin", "password");
        return ret_value;
    }
});

设备上启动app后,PC控制端执行

frida -U -l test.js FourGoats

客户端中输入内容,点击登录,会执行对应的函数

客户端输入

在frida中,可以看到以及劫持到了对应的函数

frida结果

Hook重载函数

重载函数指的是在同一个类中,有多个方法的方法名相同但参数列表不同的情况。

public int add(int a, int b) {
    return a + b;
}

// 重载函数,计算三个整数的和
public int add(int a, int b, int c) {
    return a + b + c;
}

还是以上面的函数为例,假如它是一个重载函数,那么hook的代码修改如下:

console.log("Script loaded successfully ");
// 在目标进程中执行 Java 代码
Java.perform(function () {
    console.log("Inside java perform function");
    // 定位类
    var testClass = Java.use("org.owasp.goatdroid.fourgoats.activities.Login");
    console.log("Java.Use.Successfully!");
    //更改类的方法的实现(implementation)
    testClass.allFieldsCompleted.overload("java.lang.String", "java.lang.String").implementation = function(userName,password){
        console.log("userName: " + userName + " password: " + password)
        // 调用原来的函数获取结果并返回
        var ret_value = this.allFieldsCompleted("admin", "password");
        return ret_value;
    }
});

核心就是使用overload方法指定传入的参数类型。

测试类.修改方法.overload("参数类型", "java.lang.String").implementation

主动调用函数

生成类实例后,直接调用方法即可。

Java.perform(function() {
    var instance = Java.use('com.xxx.lib.util.encrypt.AES128Helper');
    console.log(instance.encrypt("123"));
    console.log(instance.decrypt("da8bb0dd02cab00b299b2cb1142d0c1c"));
});

主动调用隐藏函数

有一些函数写在系统中,但是没有被调用过,这类就称为隐藏函数。我们可以通过Hook尝试主动调用。

假如之前的allFieldsCompleted函数是隐藏函数,那么我们尝试主动调用它。

编写代码如下:

// 使用 Java.perform() 函数来执行一些需要在 Java 虚拟机上下文中运行的代码
Java.perform(function () {
  // 使用 Java.choose() 函数来查找目标应用程序中的 Login 类的对象
  Java.choose("org.owasp.goatdroid.fourgoats.activities.Login", {
    // 当找到一个匹配的 Login 类对象时,会执行 onMatch 回调函数
    onMatch: function (instance) {
      console.log("Found instance: " + instance); // 打印找到的 Login 类对象
      // 调用 Login 类的 allFieldsCompleted() 方法,并打印返回结果
      console.log("Result is: " + instance.allFieldsCompleted("admin", "password"));
    },
    // 当搜索完成后,会执行 onComplete 回调函数
    onComplete: function () {
      console.log("Search completed");
    }
  });
});

Java.choose() 函数是 Frida JavaScript API 中一个重要的函数,用于查找并选择目标应用程序中符合指定类名的 Java 对象,并返回一个实例,供用户对其进行进一步的操作。

instance其他一些用法和java中反射类似

// 使用 Java.perform() 函数来执行一些需要在 Java 虚拟机上下文中运行的代码
Java.perform(function () {
  // 使用 Java.choose() 函数来查找目标应用程序中的 Login 类的对象
  Java.choose("org.owasp.goatdroid.fourgoats.activities.Login", {
    // 当找到一个匹配的 Login 类对象时,会执行 onMatch 回调函数
    onMatch: function (instance) {
      console.log("Found instance: " + instance); // 打印找到的 Login 类对象
      // 获取对象的类引用
      var clazz = instance.getClass();
      console.log("Class: " + clazz);

      // 获取类的所有方法
      var methods = clazz.getDeclaredMethods();
      console.log("Methods: " + methods);

      // 获取类的所有属性
      var fields = clazz.getDeclaredFields();
      console.log("Fields: " + fields);

      // 访问对象的属性
      var field = clazz.getDeclaredField("rememberMeCheckBox");
      field.setAccessible(true);
      console.log("Example field value: " + field.get(instance));
    },
    // 当搜索完成后,会执行 onComplete 回调函数
    onComplete: function () {
      console.log("Search completed");
    }
  });
});

远程调用Hook函数【py】

在上面主动调用函数时,是在APP设备中调用的,如果我们想要通过本机PC去调用该函数,可以使用frida的RPC,通过 rpc.exports 函数将需要远程调用的函数暴露出去。

编写python脚本如下

#!/usr/bin/env python

import time
import frida

# 找到对应的设备,如果默认usb是对的,可以直接用 device = frida.get_usb_device()
device_manager = frida.get_device_manager()
devices = device_manager.enumerate_devices()
device = next((d for d in devices if d.name == 'Android Emulator 5554'), None)
# 启动指定应用程序,拿到PID
pid = device.spawn(["org.owasp.goatdroid.fourgoats"])
# 将进程重新启动,让frida注入agent
device.resume(pid)
time.sleep(1)
# 连接到对应进程
session = device.attach(pid)

# hook脚本
hooksc = """
function test(){
  Java.perform(function () {
    Java.choose("org.owasp.goatdroid.fourgoats.activities.Login", {
      onMatch: function (instance) {
        console.log("Found instance: " + instance);
        console.log("Result is: " + instance.allFieldsCompleted("admin", "password"));
      },
      onComplete: function () {
        console.log("Search completed");
      }
    });
  });
}

rpc.exports = {
    testfunc: test //把test函数导出为testfunc符号,导出名不可以有大写字母或者下划线
};
"""
script = session.create_script(hooksc)
script.load()


while True:
    try:
        command = input("回车")
        # 异步
        script.exports_sync.testfunc()
    except:
        break

每次回车,都将会调用一次allFieldsCompleted函数。

Hook函数并手动传参【py】

还是以hook org.owasp.goatdroid.fourgoats.activities.LoginallFieldsCompleted方法为例

#!/usr/bin/env python

import time
import frida

# 消息处理函数
def my_message_handler(message, payload):
    print (message) # {'type': 'send', 'payload': '222:123'}
    print (payload)
    if message.get("type") == "send":
        password = input("password: ")
        script.post({"password": password})



# 找到对应的设备,如果默认usb是对的,可以直接用 device = frida.get_usb_device()
device_manager = frida.get_device_manager()
devices = device_manager.enumerate_devices()
device = next((d for d in devices if d.name == 'Android Emulator 5554'), None)
# 启动指定应用程序,拿到PID
pid = device.spawn(["org.owasp.goatdroid.fourgoats"])
# 将进程重新启动,让frida注入agent
device.resume(pid)
time.sleep(1)
# 连接到对应进程
session = device.attach(pid)

# hook脚本
hooksc = """
Java.perform(function () {
    var testClass = Java.use("org.owasp.goatdroid.fourgoats.activities.Login");
    testClass.allFieldsCompleted.implementation = function(userName,password){

        send(userName + ":" + password) // 将数据发送给PC
        recv(function (received_json_object) {
            // 更新password
            password = received_json_object.password
            console.log("Receive data: " + password);
        }).wait(); //收到数据之后,再执行下去

        console.log("userName: " + userName + " password: " + password)
        var ret_value = this.allFieldsCompleted(userName, password);
        return ret_value;
    }
});
"""
script = session.create_script(hooksc)
script.on("message", my_message_handler)  # 注册消息处理函数
script.load()

while True:
    input()

核心如下:

  • 在设备中主要是通过sendrecv方法来发送和接受数据
  • 在PC中主要是通过script.port方法向设备发送数据

参考

Copyright © d4m1ts 2023 all right reserved,powered by Gitbook该文章修订时间: 2023-03-16 14:37:49

results matching ""

    No results matching ""