0%

bilibiliv7.31.0 anti-frida

目的:过掉bilibili v7.31.0 的frida 检测

apk 源码

什么都不注入,使用frida ,应用直接挂了,那必然是有 frida 的检测了

定位 在哪个 so

先 借助 android_dlopen_ext 确定frida 的检测在哪个so 中

void* android_dlopen_ext(const char* filename, int flags, const android_dlextinfo* info);

在android 中除了 静态so 或者自定义的 elf 解析器,几乎所有的so 在加载时都会触发 android_dlopen_ext 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function hook_dlopen() {
//实时监控目标进程自己在运行过程中的 so 加载行为
//确定 frida 检测在哪个 so 中
var dlopen_addr = Module.findExportByName("libdl.so", "android_dlopen_ext");
console.log("dlopen_addr", dlopen_addr);
Interceptor.attach(dlopen_addr, {
onEnter: function (args) {

console.log("args1", args[0].readCString(), "\n");
},
onLeave: function (retval) {

}
})
}

setImmediate(hook_dlopen)

frida -U -l dlopen.js -f tv.danmaku.bili

可见其挂在 libmsaoaidsec.so ,那么frida 的检测点就在其中了

定位目标函数

定位到了目标 so, 进一步定位 目标函数

so 的加载到运行内存中的流程是:

先解析so 的元数据(elf头等)-> 把so 文件从硬盘移到内存中(mmap映射+申请内存)-> 如果该so 有依赖库,那就递归加载全部依赖 -> 执行重定位,至此,so算是准备好

然后 android_dlopen_ext 主动执行 .init 和 .init_array 里的代码,若该so 是通过java层的 system.load() / system.loadlibrary() 加载,ART 会主动调用 JNI_OnLoad

顺序大概是 .init() -> .init_array() - > JNI_OnLoad()

故借助 ‘android_dlopen_ext 主动执行 .init 和 .init_array 里的代码’ 这个点,判断检测点是否在.init中

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
//如果调用了该函数,就输出一行日志,如果没有hook上就退出了,说明检测点在 .init函数中
function hook_dlopen(){
var dlopen_addr = Module.findExportByName("libdl.so","android_dlopen_ext")
console.log("hook [android_dlopen_ext]:",dlopen_addr)
Interceptor.attach(dlopen_addr,{
onEnter(args){
console.log("arg1",args[0].readCString(),"\n")
var name = ptr(args[0]).readCString()
this.isTarget = false;
if(name.indexOf("libmsaoaidsec") >= 0){
this.isTarget = true;
console.log("frida 触发点在libmsaoaidsec.so")
console.log("\n========================================");
console.log("[*] 准备加载 libmsaoaidsec.so ...");
console.log("[*] 如果日志卡在这里不往下走,说明检测点在 .init / .init_array");
console.log("========================================\n");

}
else{
console.log("not this")
}
},
onLeave(retval){
if (this.isTarget) {
// 3. 只有当库加载完毕(即 .init_array 执行完了),才会走到这里
console.log("[+] libmsaoaidsec.so 加载完成 (通过了 .init 检测)");
// 4. 此时库已经在内存里了,可以安全地 Hook JNI_OnLoad
hook_JNI_OnLoad();
}

}
})
}

function hook_JNI_OnLoad(){
var moudle = Process.getModuleByName("libmsaoaidsec.so")
// 我需要知道哪里 触发了 JNI_OnLoad
console.log("found libmsaoaidsec.so")
var addr = moudle.base.add(0x13A94)
Interceptor.attach(addr,{
onEnter(args){
console.log("call JNI_OnLoad")
}
})
}
setImmediate(hook_dlopen)

frida -U -l function.js -f tv.danmaku.bili

很明显,检测点在 .init 中

借助 pthread_create() 找触发frida的线程

将 libmsaoaidsec.so 拖入 ida中分析,搜索 .init 函数

在ida中 .init_proc 就表示初始化函数

进入 函数发现,调用了_system_property_get,来查看 android 版本

使用frida 缩小一下范围,看能不能hook到 _system_property_get 函数,若能,代表监测点在它之后,若不能说明检测点在它之前

借助 pthread_create() 函数,查看执行过_system_property_get 判断后so 执行的线程

int pthread_create(
pthread_t *thread, // args[0]
const pthread_attr_t *attr, // args[1]
void *(*start_routine)(void *), // args[2] 表示线程真正执行的函数地址
void *arg // args[3]
);

pthread_create(&tid, NULL, sub_123456, arg);

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

function hook_dlopen() {
var dlopen_addr = Module.findExportByName("libdl.so", "android_dlopen_ext");
console.log("dlopen_addr", dlopen_addr);
Interceptor.attach(dlopen_addr, {
onEnter: function (args) {

//console.log("args1", args[0].readCString(), "\n");
var name = ptr(args[0]).readCString();
if (name.indexOf("libmsaoaidsec") >= 0) {
console.log("this");

//hook_pthread_creat()
//进入该so 就放钩子
hook_init();
}
}
})


}

function hook_init() {
var system_addr = Module.findExportByName("libc.so", "__system_property_get");
Interceptor.attach(system_addr, {
onEnter: function (args) {
var name = ptr(args[0]).readCString()
if (name.indexOf("ro.build.version.sdk") >= 0) {
console.log("name", name);
//这是.init_proc刚开始执行的地方,是一个比较早的时机点
var addr = Process.getModuleByName("libmsaoaidsec.so").base;
console.log("libmsaoaidsec.so----", addr);
hook_pthread_creat()

//bypass()
//注入代码

}

}


})
}

function hook_pthread_creat() {

var pthread_creat = Module.findExportByName("libc.so", "pthread_create")
Interceptor.attach(pthread_creat, {
onEnter: function (args) {

var func_addr = args[2];
console.log("The thread function address is " + func_addr)
}
})
}

hook_dlopen()

可见创建了两个线程偏移分别是 : 0x1AEE4   0x1A574

转到ida中搜索 pthread_create() 函数,并交叉引用,得知调用 pthread_create() 的地址

一个一个点进去看

得知frida 检测函数所在之处,分别是 0x1A858 0x1B8B4

用inline-frida nop掉检测函数

直接将这两个监测点nop掉

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

function hook_dlopen() {
var dlopen_addr = Module.findExportByName("libdl.so", "android_dlopen_ext");
console.log("dlopen_addr", dlopen_addr);
Interceptor.attach(dlopen_addr, {
onEnter: function (args) {

//console.log("args1", args[0].readCString(), "\n");
var name = ptr(args[0]).readCString();
if (name.indexOf("libmsaoaidsec") >= 0) {
console.log("this");
//hook_pthread_creat()
hook_init();
}
}
})


}

function hook_init() {
var system_addr = Module.findExportByName("libc.so", "__system_property_get");
Interceptor.attach(system_addr, {
onEnter: function (args) {
var name = ptr(args[0]).readCString()
//console.log(name);
if (name.indexOf("ro.build.version.sdk") >= 0) {
console.log("name", name);
//这是.init_proc刚开始执行的地方,是一个比较早的时机点
//在这bypassfrida检测
bypass()
}

}


})
}


function hook_pthread_creat() {

var pthread_creat = Module.findExportByName("libc.so", "pthread_create")
Interceptor.attach(pthread_creat, {
onEnter: function (args) {

var func_addr = args[2];
console.log("The thread function address is " + func_addr)
}
})

}

function nop(addr) {
// 运行时打补丁,inline patch
Memory.patchCode(ptr(addr), 4,
code => {
// pc寄存器表示 cou 正在/将要执行的指令地址,cpu每执行一条指令,都靠pc找下一条

const cw = new Arm64Writer(code, { pc: ptr(addr) });
// const cw = new ThumbWriter(code, { pc: ptr(addr) });
cw.putNop();
cw.putNop();
cw.putNop();
cw.putNop();
cw.flush();
});
}

function bypass() {

var addr = Process.getModuleByName("libmsaoaidsec.so").base;
console.log("libmsaoaidsec.so----", addr);
nop(addr.add(0x1A858));
nop(addr.add(0x1B8B4));

}

hook_dlopen()

成功绕过 frida 检测!