抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

学习Frida脚本的编写

Frida脚本的概念

FRIDA脚本就是利用FRIDA动态插桩框架,使用FRIDA导出的API和方法,对内存空间里的对象方法进行监视、修改或者替换的一段代码。FRIDAAPI是使用JavaScript实现的,所以我们可以充分利用JS的匿名函数的优势、以及大量的hook和回调函数的API。如果不了解匿名函数,可以先简单了解一下,相信对你编写Frida脚本会有帮助。

举一个最简单的例子

1
2
3
4
5
6
function main(){
Java.perform(function(){
console.log("hello world");
})
}
setImmediate(main)

这段代码的作用是在 Android 应用程序的 Java 虚拟机中执行一个函数,该函数将在 Frida 控制台输出 "hello world"。

控制台运行

1
frida -U -l hello-world.js android.process.media

“-U”表示使用USB连接到设备,而不是通过网络连接;”-l”表示指定要加载的JavaScript脚本文件;”android.process.media”是目标进程的名称

image-20240416220253422

解释一下这段代码,

  • 首先是function main() { ... }是javascript中定义函数的语法,函数名为main。
  • Java.perform(function() { ... });是Frida中的一个重要函数,它接受一个函数作为参数,在被Frida Hook的Java虚拟机中执行。在这个函数内部,可以使用Frida提供的API接口来进行动态分析、修改、拦截等操作。
  • console.log("hello world");: 这是 JavaScript 中的输出语句,用于在 Frida 控制台输出信息。在这里,它将输出字符串 “hello world
  • setImmediate(main): 这是 JavaScript 中的一个函数,用于在当前 JavaScript 事件循环的下一个循环迭代中执行指定的函数。在这里,它用于在当前 JavaScript 执行栈结束后立即调用 main 函数。

进阶操作

了解了Frida如何进行基本的使用,接下来就可以尝试使用一些API函数进行操作。

FRIDA的API手册

枚举所有加载的类

1
2
3
4
5
6
7
8
9
10
11
12
function main(){
Java.perform(function(){
console.log("\n[*] enumerating classes...");
Java.enumerateLoadedClasses({
onMatch: function(_className){
console.log("[*] found instance of '"+_className+"'");
},
onComplete: function(){
console.log("[*] class enuemration complete");
})
}
setImmediate(main)

保存并执行

可以得到如下结果

image.png

太多了只截取了最后一段

解释一下这段代码:

  • Java.enumerateLoadedClasses({ ... }): 这是 Frida 提供的一个方法,用于枚举当前已加载的所有类。它接受一个 JavaScript 对象作为参数,该对象包含两个属性:onMatchonComplete
  • onMatch: function(_className) { ... }: 这是一个回调函数,用于处理每个匹配到的类名。在每次找到一个类名时,这个函数会被调用。在这个函数中,类名存储在 _className 参数中,并通过 console.log 输出到 Frida 控制台
  • onComplete: function() { ... }: 这也是一个回调函数,当所有类都被枚举完毕时调用。在这个函数中,通过 console.log 输出一条消息表示类枚举过程完成

定位目标类

我们既然可以获取到加载的所有类,也可以获取指定类。假如我们需要获取有关蓝牙的某个类,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function main(){
Java.perform(function(){
Java.enumerateLoadedClasses({
onMatch: function(instance){
if (instance.split(".")[1] == "bluetooth"){
console.log("[->]\t"+instance);
}
},
onComplete: function() {
console.log("[*] class enuemration complete");
}
});
})
}
setImmediate(main)

image.png

这里不再赘述代码的其他部分,只说onMatch函数的判断

instance是通过Java.enumerateLoadedClasses获取到的类,代表一个完整类名

split(".")[1]是JavaScript的字符串方法,用于按照分隔符将字符串进行分割,因为类名多是以"."进行命名,所以将"."作为分割符使用,[1]则是JavaScript 数组索引操作符,用于获取数组中指定索引位置的元素。在这里,我们获取分割后的字符串数组的第二个元素,即索引为1的元素。

打印类的实例

在我们定位到目标类后,就可以打印想要研究的类的实例了

代码:

1
2
3
4
5
6
7
8
9
10
11
function main(){
Java.perform(function(){
Java.choose("android.bluetooth.BluetoothDevice",{
onMatch: function (instance){
console.log("[*] "+" android.bluetooth.BluetoothDevice instance found"+" :=> '"+instance+"'");
bluetoothDeviceInfo(instance);
},
onComplete: function() { console.log("[*] -----");}
})
}
setImmediate(main)

运行

1
frida -U -l hello.js com.android.bluetooth

枚举所有方法

使用java.use()获取所有方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function main() {
Java.perform(function () {
// 获取 android.bluetooth.IBluetoothHeadset 接口的类对象
var BluetoothHeadset = Java.use('android.bluetooth.IBluetoothHeadset');

// 获取该接口的所有方法
var methods = BluetoothHeadset.class.getDeclaredMethods();

// 遍历所有方法
for (var i = 0; i < methods.length; i++) {
var method = methods[i];
// 输出方法名
console.log(method.toString());
}
});
}

// 立即执行枚举方法
setImmediate(main)

控制台命令

1
frida -U -l hello.js com.android.bluetooth

image.png

获取了方法,接下来就是对方法的各种修改拦截了。