Hook最低级函数
还是以goatdroid.apk为例,在登录时,存在验证输入框是否都输入内容的代码如下:
我们就尝试Hook此函数,编写JS代码如下,劫持输入的内容,并将输入改为admin
和password
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中,可以看到以及劫持到了对应的函数
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.Login
的allFieldsCompleted
方法为例
#!/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()
核心如下:
- 在设备中主要是通过
send
和recv
方法来发送和接受数据 - 在PC中主要是通过
script.port
方法向设备发送数据