小破站老版本(入门级)SO层Native方法分析

# 准备工作

APK:哔哩哔哩 6.13.0
首先通过 charles 抓包(charles 抓包配置)发现加密参数是 sign(32 位)。
image.png

使用 ajdx 打开 apk 后搜索 "sign" 并没有想要的数据,接着试着搜索 "sign=" 经过分析发现是 com.bilibili.nativelibrary.LibBili.s 生成。

image.png

# 验证加密

使用 frida 来 hook 验证以下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function hookSign() {
Java.perform(function () {
let LibBili = Java.use("com.bilibili.nativelibrary.LibBili");
LibBili["s"].implementation = function (sortedMap) {
console.log(
`LibBili.s is called: sortedMap=${sortedMap.entrySet().toArray()}`
);
let result = this["s"](sortedMap);
console.log(`LibBili.s result=${result}`);
return result;
};
});
}
setImmediate(hookSign);

frida hook 日志,很明显 sign 就是在这里面生成的。

1
2
[MI 8 Pro::tv.danmaku.bili ]-> LibBili.s is called: sortedMap=access_key=null,appkey=1d8b6e7d45233436,build=6130400,c_locale=zh-Hans_CN,channel=xxl_wyyd_001,mVersion=71,mallVersion=6130400,mobi_app=android,platform=android,s_locale=zh-Hans_CN,statistics={"appId":1,"platform":3,"version":"6.13.0","abtest":""}
LibBili.s result=access_key=&appkey=1d8b6e7d45233436&build=6130400&c_locale=zh-Hans_CN&channel=xxl_wyyd_001&mVersion=71&mallVersion=6130400&mobi_app=android&platform=android&s_locale=zh-Hans_CN&statistics=%7B%22appId%22%3A1%2C%22platform%22%3A3%2C%22version%22%3A%226.13.0%22%2C%22abtest%22%3A%22%22%7D&ts=1702547190&sign=83c0620895711a111a4fc8f046b76d9d

# 定位 SO

使用大佬的 hook 脚本并作了点修改

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
function hook_RegisterNatives() {
var symbols = Module.enumerateSymbolsSync("libart.so");
var addrRegisterNatives = null;
for (var i = 0; i < symbols.length; i++) {
var symbol = symbols[i];

//_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi
if (
symbol.name.indexOf("art") >= 0 &&
symbol.name.indexOf("JNI") >= 0 &&
symbol.name.indexOf("RegisterNatives") >= 0 &&
symbol.name.indexOf("CheckJNI") < 0
) {
addrRegisterNatives = symbol.address;
console.log("RegisterNatives is at ", symbol.address, symbol.name);

if (addrRegisterNatives != null) {
Interceptor.attach(addrRegisterNatives, {
onEnter: function (args) {
var env = args[0];
var java_class = args[1];
var class_name = Java.vm.tryGetEnv().getClassName(java_class);
// console.log(class_name);
// 筛选类
var taget_class = "com.bilibili.nativelibrary.LibBili";
if (class_name === taget_class) {
console.log("\n[RegisterNatives] method_count:", args[3]);
var methods_ptr = ptr(args[2]);

var method_count = parseInt(args[3]);
for (var i = 0; i < method_count; i++) {
var name_ptr = Memory.readPointer(
methods_ptr.add(i * Process.pointerSize * 3)
);
var sig_ptr = Memory.readPointer(
methods_ptr.add(
i * Process.pointerSize * 3 + Process.pointerSize
)
);
var fnPtr_ptr = Memory.readPointer(
methods_ptr.add(
i * Process.pointerSize * 3 + Process.pointerSize * 2
)
);

var name = Memory.readCString(name_ptr);
var sig = Memory.readCString(sig_ptr);
var find_module = Process.findModuleByAddress(fnPtr_ptr);
console.log(
"[RegisterNatives] java_class:",
class_name,
"name:",
name,
"sig:",
sig,
"fnPtr:",
fnPtr_ptr,
"module_name:",
find_module.name,
"module_base:",
find_module.base,
"offset:",
ptr(fnPtr_ptr).sub(find_module.base)
);
}
}
},
});
}
}
}
}

setImmediate(hook_RegisterNatives);

添加筛选条件后输出的结果不多

1
2
3
4
5
6
7
8
9
10
11
12
13
Spawning `tv.danmaku.bili`...
RegisterNatives is at 0xf0b3cbd9 _ZN3art3JNIILb0EE15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi
RegisterNatives is at 0xf0b910d5 _ZN3art3JNIILb1EE15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi
Spawned `tv.danmaku.bili`. Resuming main thread!
[MI 8 Pro::tv.danmaku.bili ]->
[RegisterNatives] method_count: 0x7
[RegisterNatives] java_class: com.bilibili.nativelibrary.LibBili name: a sig: (Ljava/lang/String;)Ljava/lang/String; fnPtr: 0xb8369c35 module_name: libbili.so module_base: 0xb8368000 offset: 0x1c35
[RegisterNatives] java_class: com.bilibili.nativelibrary.LibBili name: ao sig: (Ljava/lang/String;II)Ljava/lang/String; fnPtr: 0xb8369c3b module_name: libbili.so module_base: 0xb8368000 offset: 0x1c3b
[RegisterNatives] java_class: com.bilibili.nativelibrary.LibBili name: b sig: (Ljava/lang/String;)Ljavax/crypto/spec/IvParameterSpec; fnPtr: 0xb8369c49 module_name: libbili.so module_base: 0xb8368000 offset: 0x1c49
[RegisterNatives] java_class: com.bilibili.nativelibrary.LibBili name: s sig: (Ljava/util/SortedMap;)Lcom/bilibili/nativelibrary/SignedQuery; fnPtr: 0xb8369c4f module_name: libbili.so module_base: 0xb8368000 offset: 0x1c4f
[RegisterNatives] java_class: com.bilibili.nativelibrary.LibBili name: so sig: (Ljava/util/SortedMap;II)Lcom/bilibili/nativelibrary/SignedQuery; fnPtr: 0xb8369c55 module_name: libbili.so module_base: 0xb8368000 offset: 0x1c55
[RegisterNatives] java_class: com.bilibili.nativelibrary.LibBili name: getCpuCount sig: ()I fnPtr: 0xb8369c63 module_name: libbili.so module_base: 0xb8368000 offset: 0x1c63
[RegisterNatives] java_class: com.bilibili.nativelibrary.LibBili name: getCpuId sig: ()I fnPtr: 0xb8369c67 module_name: libbili.so module_base: 0xb8368000 offset: 0x1c67

这就是我们需要的结果,module_name: libbili.so 表示 s 函数在 libbili.so 中。
offset: 0x1c4f 偏移量为 0x1c4f 可以理解为函数起始地址。

1
[RegisterNatives] java_class: com.bilibili.nativelibrary.LibBili name: s sig: (Ljava/util/SortedMap;)Lcom/bilibili/nativelibrary/SignedQuery; fnPtr: 0xb8369c4f module_name: libbili.so module_base: 0xb8368000 offset: 0x1c4f

# 分析 SO

使用 file 命令查看 so 的位数是多少位,可以看到是 32 位的,那么使用 32 位的 ida 打开。

image.png

# 偏移量地址定位函数

PixPin_2023-12-14_18-36-22.gif

# JNI_OnLoad 定位 s 函数

PixPin_2023-12-14_18-32-47.gif