跳到主要内容

hook函数【入门】

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方法向设备发送数据

参考