安卓 day4
想吃蛋挞

@source: https://github.com/r0ysue/AndroidSecurityStudy/tree/master/FRIDA/B01

获取蓝牙设备

enumerateLoadedClasses() 枚举已加载的类

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

加个判断可以定位目标类。蓝牙相关类属于系统框架,被 zygote 预加载进进程内存,几乎所有 app 进程都能看到。

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

配合 Java.choose 可以拿到目标类实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
setTimeout(function() {
Java.perform(function() {
const BluetoothDevice = Java.use("android.bluetooth.BluetoothDevice");

function bluetoothDeviceInfo(anyObj) {
try {
// 如果不是 BluetoothDevice,先尝试 cast
let dev = anyObj;
if (anyObj.$className !== "android.bluetooth.BluetoothDevice") {
dev = Java.cast(anyObj, BluetoothDevice); // cast 失败会抛异常
}

console.log(" name=" + dev.getName() +
", address=" + dev.getAddress() +
", type=" + dev.getType());
} catch (e) {
console.log(" [!] not a BluetoothDevice / cast failed: " + anyObj.$className + " => " + e);
}
}

console.log("\n[*] enumerating classes...");
Java.enumerateLoadedClasses({
onMatch: function(instance) {
if (instance.split(".")[1] == "bluetooth") {
console.log("[->]\t" + instance);
}
},
onComplete: function() {
console.log("[*] class enumeration complete");
}
});

console.log("[*] finding android.bluetooth.BluetoothDevice instance...");
Java.choose("android.bluetooth.BluetoothDevice", {
onMatch: function(instance) {
console.log("[*] android.bluetooth,BluetoothDevice" + idx + " instance found" + " :=> '" + instance + "'");
console.log("class=" + instance.$className);

bluetoothDeviceInfo(instance);
},
onComplete: function() {
console.log("[*] -----")
}
})
});
});

对原文代码做了一些修改,但是本质差不多。以上脚本在安卓12下扫不到蓝牙设备,可能和注入时机有关(实例已经被gc),那么我们可以通过调用 BluetoothAdapter.getBondedDevices() 在进程中创建 BluetoothDevice 实例后马上开始扫。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
setTimeout(function () {
Java.perform(function () {
const BluetoothDevice = Java.use("android.bluetooth.BluetoothDevice");
const BluetoothAdapter = Java.use("android.bluetooth.BluetoothAdapter");

function jtype(o) {
try { return o.$className || (typeof o); } catch (_) { return typeof o; }
}
function bluetoothDeviceInfo(anyObj) {
try {
// 如果不是 BluetoothDevice,先尝试 cast
let dev = anyObj;
if (anyObj.$className !== "android.bluetooth.BluetoothDevice") {
dev = Java.cast(anyObj, BluetoothDevice); // cast 失败会抛异常
}

console.log(" name=" + dev.getName() +
", address=" + dev.getAddress() +
", type=" + dev.getType());
} catch (e) {
console.log(" [!] not a BluetoothDevice / cast failed: " + anyObj.$className + " => " + e);
}
}

let adapter;
try {
adapter = BluetoothAdapter.getDefaultAdapter.overload().call(BluetoothAdapter);
console.log("[*] adapter = " + adapter + " type=" + jtype(adapter));
} catch (e) {
console.log("[!] getDefaultAdapter failed: " + e);
return;
}

if (adapter === null) {
console.log("[!] No Bluetooth adapter");
return;
}
let bonded;
try {
bonded = adapter.getBondedDevices.overload().call(adapter); // returns java.util.Set
// console.log("[*] bonded = " + bonded + " type=" + jtype(bonded));
} catch (e) {
// console.log("[!] getBondedDevices failed: " + e);
return;
}

console.log("[*] finding android.bluetooth.BluetoothDevice" + idx + " instance...");
Java.choose("android.bluetooth.BluetoothDevice", {
onMatch: function (instance) {
console.log("[*] android.bluetooth,BluetoothDevice instance found" + " :=> '" + instance + "'");
console.log("class=" + instance.$className);

bluetoothDeviceInfo(instance);
},
onComplete: function () {
console.log("[*] -----")
}
})
});
});

直接 Java.choose 扫比较暴力,其实可以直接把拿到的 bonded 迭代出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
Java.perform(function () {
function jtype(o) {
try { return o.$className || (typeof o); } catch (_) { return typeof o; }
}
function bluetoothDeviceInfo(anyObj) {
try {
// 如果不是 BluetoothDevice,先尝试 cast
let dev = anyObj;
if (anyObj.$className !== "android.bluetooth.BluetoothDevice") {
dev = Java.cast(anyObj, BluetoothDevice); // cast 失败会抛异常
}

console.log(" name=" + dev.getName() +
", address=" + dev.getAddress() +
", type=" + dev.getType());
} catch (e) {
console.log(" [!] not a BluetoothDevice / cast failed: " + anyObj.$className + " => " + e);
}
}

const BluetoothAdapter = Java.use("android.bluetooth.BluetoothAdapter");
const BluetoothDevice = Java.use("android.bluetooth.BluetoothDevice");


// 1) getDefaultAdapter (static)
let adapter;
try {
adapter = BluetoothAdapter.getDefaultAdapter.overload().call(BluetoothAdapter);
console.log("[*] adapter = " + adapter + " type=" + jtype(adapter));
} catch (e) {
console.log("[!] getDefaultAdapter failed: " + e);
return;
}

if (adapter === null) {
console.log("[!] No Bluetooth adapter");
return;
}

// 2) getBondedDevices (instance)
let bonded;
try {
bonded = adapter.getBondedDevices.overload().call(adapter); // returns java.util.Set
console.log("[*] bonded = " + bonded + " type=" + jtype(bonded));
} catch (e) {
console.log("[!] getBondedDevices failed: " + e);
return;
}

// 3) size()
try {
const n = bonded.size.overload().call(bonded);
console.log("[*] bonded size = " + n);
} catch (e) {
console.log("[!] bonded.size() failed: " + e);
}

// 4) iterator + loop
let it;
try {
it = bonded.iterator.overload().call(bonded);
console.log("[*] iterator type=" + jtype(it));
} catch (e) {
console.log("[!] bonded.iterator() failed: " + e);
return;
}

while (true) {
let has;
try {
has = it.hasNext.overload().call(it);
} catch (e) {
console.log("[!] it.hasNext() failed: " + e);
break;
}
if (!has) break;

let dev;
try {
dev = it.next.overload().call(it);
console.log(" dev=" + dev + " type=" + jtype(dev));
} catch (e) {
console.log("[!] it.next() failed: " + e);
break;
}

// 5) getName/getAddress(同样强制 overload 调用)
try {
const name = BluetoothDevice.getName.call(dev);
const addr = BluetoothDevice.getAddress.call(dev);
console.log(" name=" + name + ", addr=" + addr);
} catch (e) {
console.log(" [!] getName/getAddress failed: " + e);
}
console.log("[*] finding android.bluetooth.BluetoothDevice instance...");
}
});

非常多代码,使我的眼睛发炎。

利用反射 api 枚举类内所有方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Java.perform(function () {
function enumMethods(targetClass) {
var hook = Java.use(targetClass);
var ownMethods = hook.class.getDeclaredMethods();
hook.$dispose(); // 释放 wrapper 内部引用

return ownMethods;
}

var a = enumMethods("android.bluetooth.BluetoothDevice");
a.forEach(function (s) {
console.log(s);
});

})

.class 获取 Class 对象,.getDeclaredMthods() 获取所有声明的方法。