绕过libmsaoaidsec.so的Frida检测
zsk Lv4

已知使用libmsaoaidsec.so frida反调试的应用有:知乎,爱奇艺,携程,bilibili

以知乎10.37.0为例

Frida反检测

image

分析

Frida检测一般都是在Native层实现的,那么我们首先需要定位检测机制是在哪个so中实现的,这里我们就需要先hook Andoid的动态链接库加载函数,观察它加载到哪个so的时候会崩溃。

android_dlopen_ext 是专门为 Android 系统设计的,直接支持 Android 系统的优化和特性。因此,hook 这个函数可以避免可能的兼容性问题,确保你能够捕获到真正的库加载事件。

hook android_dlopen_ext

image

hook*android_dlopen_ext查看so的加载流程,观察它加载到哪个so的时候会崩溃

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function hook_dlopen() {
Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"),
{
onEnter: function (args) {
this.fileName = args[0].readCString()
console.log(`android_dlopen_ext onEnter: ${this.fileName}`)
}, onLeave: function(retval){
console.log(`android_dlopen_ext onLeave: ${this.fileName}\n`)
if(this.fileName != null && this.fileName.indexOf("libmsaoaidsec.so") >= 0){
let JNI_OnLoad = Module.getExportByName(this.fileName, 'JNI_OnLoad')
console.log(`android_dlopen_ext onLeave JNI_OnLoad: ${JNI_OnLoad}`)
}
}
}
);
}

image

从打印的结果看最是加载的so是libmsaoaidsec.so,没有调用onLeave,由此可知崩溃点就在libmsaoaidsec.so中,并且是在JNI_OnLoad之前检测的,so在加载之后会先调用.init_proc函数或类似的函数 ,接着调用.init_array中的函数,最后才是JNI_OnLoad函数,所以根据log可以确定检测点在JNI_OnLoad之前,接下来选择的注入时机可以选择在dlopen加载libmsaoaidsec.so之后

需要注意的一点是在dlopen函数调用完成之后.init_xxx已经执行完成了

xref: /bionic/linker/linker.cpp

image

那么我们就hook这里的call_constructors函数,在onEnter里注入代码
在设备中找到call_constructors的offset
readelf -sW /apex/com.android.runtime/bin/linker64 | grep call_constructors

1
2
3
4
5
λ adb shell
blueline:/ $ su
blueline:/ # readelf -sW /apex/com.android.runtime/bin/linker64 | grep call_constructors
754: 0000000000051120 896 FUNC LOCAL HIDDEN 11 __dl__ZN6soinfo17call_constructorsEv
blueline:/ #
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function hook_linker_call_constructors() {
let linker64_base_addr = Module.getBaseAddress('linker64')
let offset = 0x51120 // __dl__ZN6soinfo17call_constructorsEv
let call_constructors = linker64_base_addr.add(offset)
let listener = Interceptor.attach(call_constructors,{
onEnter:function(args){
console.log('hook_linker_call_constructors onEnter')
let secmodule = Process.findModuleByName("libmsaoaidsec.so")
if (secmodule != null){
// do something
}
}
})
}

确定hook点了之后,接下来定位具体的Frida检测点
对Frida的检测通常会使用openat、open、strstr、pthread_create、snprintf、sprintf、readlinkat等一系列函数

hook pthread_create 定位检测点

在加载libmsaoaidsec.so时,call_constructors之前对pthread_create进行hook,打印新线程要执行的函数地址

1
2
3
4
5
6
7
8
9
function hook_pthred_create(){
console.log("libmsaoaidsec.so --- " + Process.findModuleByName("libmsaoaidsec.so").base)
Interceptor.attach(Module.findExportByName('libc.so','pthread_create'),{
onEnter(args){
let func_addr = args[2]
console.log(`The thread Called function address is: ${func_addr}`)
}
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
hook_linker_call_constructors onEnter
libmsaoaidsec.so --- 0x77c5402000
The thread Called function address is: 0x7898678d00
The thread Called function address is: 0x7898678d00
The thread Called function address is: 0x7898678d00
The thread Called function address is: 0x7898678d00
The thread Called function address is: 0x7898678d00
The thread Called function address is: 0x7898678d00
The thread Called function address is: 0x7898678d00
The thread Called function address is: 0x7898678d00
The thread Called function address is: 0x7898678d00
hook_linker_call_constructors onEnter
libmsaoaidsec.so --- 0x77c5402000
The thread Called function address is: 0x7898678d00
The thread Called function address is: 0x7898678d00
...
The thread Called function address is: 0x77c541cee4
The thread Called function address is: 0x77c541cee4
The thread Called function address is: 0x77c541cee4
...
The thread Called function address is: 0x7898678d00
The thread Called function address is: 0x7898678d00
...

根据打印的结果可以看到有个线程是libmsaoaidsec.so创建的
计算对应的偏移:

libmsaoaidsec.so — 0x77c5402000

使用0x7898678d00去计算偏移 -> D327 6D00 太大在so中找不到,换成
The thread Called function address is: 0x77c541cee4 偏移-> 1AEE4

既然libmsaoaidsec.so创建了个线程,猜测这其中是和Frida检测有关的
用IDA打开libmsaoaidsec.so,查找偏移

查看0x1AEE4地址处对应的函数

sub_1AEE4函数的代码看不懂根据引用找到调用其的地方

1
2
3
4
5
6
7
__int64 sub_1B88C()
{
pthread_t v1[2]; // [xsp+0h] [xbp-20h] BYREF

v1[1] = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
return pthread_create(v1, 0LL, (void *(*)(void *))
}

sub_1B88C的引用是sub_1A5B0,可以对sub_1A5B0进行hook,在调用sub_1B88C的时候可以选择直接nop掉pthread_create或者替换检测函数的代码逻辑都可以,我们这里选择replace掉sub_1A5B0

最终代码

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
function hook_dlopen() {
Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"),
{
onEnter: function (args) {
this.fileName = args[0].readCString()
console.log(`android_dlopen_ext onEnter: ${this.fileName}`)
if (this.fileName != undefined && this.fileName.indexOf("libmsaoaidsec.so") >= 0) {
hook_linker_call_constructors()
}
}, onLeave: function(retval){
console.log(`android_dlopen_ext onLeave: ${this.fileName}\n`)
if(this.fileName != null && this.fileName.indexOf("libmsaoaidsec.so") >= 0){
let JNI_OnLoad = Module.getExportByName(this.fileName, 'JNI_OnLoad')
console.log(`android_dlopen_ext onLeave JNI_OnLoad: ${JNI_OnLoad}`)
}
}
}
);
}

function hook_linker_call_constructors() {
let linker64_base_addr = Module.getBaseAddress('linker64')
let offset = 0x51120 // __dl__ZN6soinfo17call_constructorsEv
let call_constructors = linker64_base_addr.add(offset)
let listener = Interceptor.attach(call_constructors,{
onEnter:function(args){
console.log('hook_linker_call_constructors onEnter')
let secmodule = Process.findModuleByName("libmsaoaidsec.so")
if (secmodule != null){
// do something
// hook_pthred_create()
hook_sub_1A5B0()
listener.detach()
}
}
})
}

function hook_pthred_create(){
console.log("libmsaoaidsec.so --- " + Process.findModuleByName("libmsaoaidsec.so").base)
Interceptor.attach(Module.findExportByName('libc.so','pthread_create'),{
onEnter(args){
let func_addr = args[2]
console.log(`The thread Called function address is: ${func_addr}`)
}
})
}

function hook_sub_1A5B0(){
let secmodule = Process.findModuleByName("libmsaoaidsec.so")
Interceptor.replace(secmodule.base.add(0x1a5b0), new NativeCallback(function () {
console.log(`hook_sub_1b924 >>>>>>>>>>>>>>>>> replace`)
}, 'void', []));
}

setImmediate(hook_dlopen)

另一种定位检测点方式

检测的位置在JNI_OnLoad函数之前,所以我需要hook .init_xxx的函数,但这里有一个问题,dlopen函数调用完成之后.init_xxx函数已经执行完成了,这个时候不容易使用frida进行hook

因为想要hook linker的call_function并不容易,这里面涉及到linker的自举,可以在.init_proc函数中找一个调用了外部函数的位置,时机越早越好
ida中搜索init_proc函数,
image

期间调用了 sub_118FC()

1
2
3
4
5
6
7
8
9
__int64 sub_118FC()
{
_QWORD v1[2]; // [xsp+0h] [xbp-20h] BYREF

v1[1] = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
v1[0] = 0LL;
_system_property_get("ro.build.version.sdk", v1);
return atoi((const char *)v1);
}

选择_system_property_get函数,接下来使用frida hook dlopen函数,当加载libmsaoaidsec.so时,在onEnter回调方法中hook _system_property_get函数,以”ro.build.version.sdk”字符串作为过滤器。

如果_system_property_get函数被调用了,那么这个时候也就是.init_proc函数刚刚调用的时候,在这个时机点可以注入想要的代码,具体实现如下:

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
function hook_dlopen(soName = '') {
Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"),
{
onEnter: function (args) {
var pathptr = args[0];
if (pathptr !== undefined && pathptr != null) {
var path = ptr(pathptr).readCString();
if (path.indexOf(soName) >= 0) {
locate_init()
}
}
}
}
);
}

function locate_init() {
let secmodule = null
Interceptor.attach(Module.findExportByName(null, "__system_property_get"),
{
// _system_property_get("ro.build.version.sdk", v1);
onEnter: function (args) {
secmodule = Process.findModuleByName("libmsaoaidsec.so")
var name = args[0];
if (name !== undefined && name != null) {
name = ptr(name).readCString();
if (name.indexOf("ro.build.version.sdk") >= 0) {
// 这是.init_proc刚开始执行的地方,是一个比较早的时机点
// do something
}
}
}
}
);
}

setImmediate(hook_dlopen, "libmsaoaidsec.so")

在获取了一个非常早的注入时机之后,就可以定位具体的frida检测点了。网上对frida的检测通常会使用openat、open、strstr、pthread_create、snprintf、sprintf、readlinkat等一系列函数,从这里下手是一个不错的选择。

对pthread_create函数进行hook,打印一下新线程要执行的函数地址

1
2
3
4
5
6
7
8
9
function hook_pthread_create(){
console.log("libmsaoaidsec.so --- " + Process.findModuleByName("libmsaoaidsec.so").base)
Interceptor.attach(Module.findExportByName("libc.so", "pthread_create"),{
onEnter(args){
let func_addr = args[2]
console.log("The thread function address is " + func_addr)
}
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[Remote::com.zhihu.android ]-> libmsaoaidsec.so --- 0x780070c000
The thread function address is 0x7898678d00
The thread function address is 0x7800726ee4
The thread function address is 0x7800726574
The thread function address is 0x7898678d00
The thread function address is 0x7898678d00
The thread function address is 0x7898678d00
The thread function address is 0x7898678d00
The thread function address is 0x7898678d00
The thread function address is 0x7898678d00
The thread function address is 0x7898678d00
The thread function address is 0x7898678d00
The thread function address is 0x7898678d00
The thread function address is 0x7898678d00
The thread function address is 0x7898678d00
The thread function address is 0x7898678d00
The thread function address is 0x7898678d00

这里面有两个线程是libmsaoaidsec.so创建的,对应的函数偏移分别是0x97F6CD00和0x1AEE4,前者明显不对,

绕过的方法直接nop掉pthread_create或者替换检测函数的代码逻辑都可以,下面是完整代码。

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
function hook_dlopen(soName = '') {
Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"),
{
onEnter: function (args) {
var pathptr = args[0];
if (pathptr !== undefined && pathptr != null) {
var path = ptr(pathptr).readCString();
if (path.indexOf(soName) >= 0) {
locate_init()
}
}
}
}
);
}

function locate_init() {
let secmodule = null
Interceptor.attach(Module.findExportByName(null, "__system_property_get"), {
// _system_property_get("ro.build.version.sdk", v1);
onEnter: function (args) {
secmodule = Process.findModuleByName("libmsaoaidsec.so")
var name = args[0];
if (name !== undefined && name != null) {
name = ptr(name).readCString();
if (name.indexOf("ro.build.version.sdk") >= 0) {
// 这是.init_proc刚开始执行的地方,是一个比较早的时机点
// do something
// hook_pthread_create();
bypass();
}
}
}
});
}

function hook_pthread_create(){
console.log("libmsaoaidsec.so --- " + Process.findModuleByName("libmsaoaidsec.so").base)
Interceptor.attach(Module.findExportByName("libc.so", "pthread_create"),{
onEnter(args){
let func_addr = args[2]
console.log("The thread function address is " + func_addr)
}
})
}

function nop(addr) {
Memory.patchCode(ptr(addr), 4, code => {
const cw = new Arm64Writer(code, { pc: ptr(addr) });
cw.putNop();
cw.putNop();
cw.putNop();
cw.putNop();
cw.flush();
});
}

function bypass(){
let module = Process.findModuleByName("libmsaoaidsec.so")
// nop(module.base.add(0x1AEE4))
Interceptor.replace(module.base.add(0x1B88C), new NativeCallback(function () {
console.log(`hook_sub_1B88C >>>>>>>>>>>>>>>>> replace`)
}, 'void', []));
}


setImmediate(hook_dlopen, "libmsaoaidsec.so")

没走pthread_create另一种定位方式

携程也使用了libmsaoaidsec.so,但是不是通过pthread_create线程方式去检测的,面对这种情况如何定位

通过栈回溯的方式来定位Frida检测点

这里我们的思路是:

既然挂上Frida就会退出,那么我们就来分析最终是调用了哪个系统调用导致的退出,找到之后再通过栈回溯定位到Frida检测点

  1. 使用sleep让进程暂停在加载了libmsaoaidsec.so
  2. 使用工具strace监控系统调用
  3. 找到系统调用后,通过栈回溯定位检测点

定位退出进程前调用

1
2
3
blueline:/system/lib64 #
readelf -sW /apex/com.android.runtime/bin/linker64 | grep __dl__Z9do_dlopenPKciPK17android_dlextinfoPKv
219: 000000000003b508 2408 FUNC LOCAL HIDDEN 11 __dl__Z9do_dlopenPKciPK17android_dlextinfoPKv
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function hookDlopen() {
let Func_sleep = new NativeFunction(Module.getExportByName('libc.so', 'sleep'), 'uint', ['uint'])
let linker64_base_addr = Module.getBaseAddress('linker64')
let offset = 0x3b508 // __dl__Z9do_dlopenPKciPK17android_dlextinfoPKv
let android_dlopen_ext = linker64_base_addr.add(offset)
Interceptor.attach(android_dlopen_ext, {
onEnter: function (args) {
this.name = args[0].readCString()
console.log(`Current PID: ${Process.id}`)
console.log(`dlopen onEnter ${this.name}`)
if (this.name != null && this.name.indexOf('libmsaoaidsec.so') >= 0) {
Func_sleep(10)
// hook_mmap()
}
}, onLeave: function (retval) {
console.log(`dlopen onLeave name: ${this.name}`)
}
})
}
1
2
3
4
5
6
7
8
9
10
11
Current PID: 21203
dlopen onEnter libandroid.so
dlopen onLeave name: libandroid.so
Current PID: 21203
dlopen onEnter /data/app/~~QB20iVpvVrCqCfx6DKaduA==/ctrip.android.view-e4dkBKlAkLS9PH0omGuXkw==/lib/arm64/libtrace-canary.so
dlopen onLeave name: /data/app/~~QB20iVpvVrCqCfx6DKaduA==/ctrip.android.view-e4dkBKlAkLS9PH0omGuXkw==/lib/arm64/libtrace-canary.so
Current PID: 21203
dlopen onEnter /data/app/~~QB20iVpvVrCqCfx6DKaduA==/ctrip.android.view-e4dkBKlAkLS9PH0omGuXkw==/lib/arm64/libmsaoaidsec.so
Current PID: 21203
dlopen onEnter libc.so
dlopen onLeave name: libc.so

趁着进程暂停在Func_sleep(10)立即去执行:

strace -e trace=process -i -f -p 21203

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
blueline:/ # strace -e trace=process -i -f -p 21203
strace: Process 21203 attached with 116 threads
[pid 21311] [0000007878d4d7d8] wait4(21435, <unfinished ...>
[pid 21407] [0000007878cfafb4] exit(0) = ?
[pid 21407] [????????????????] +++ exited with 0 +++
[pid 21537] [0000007878cfafb4] exit(0) = ?
[pid 21537] [????????????????] +++ exited with 0 +++
[pid 21235] [0000007878cfafb4] exit(0) = ?
[pid 21235] [????????????????] +++ exited with 0 +++
[pid 21286] [0000007878cfafb4] exit(0) = ?
[pid 21286] [????????????????] +++ exited with 0 +++
[pid 21285] [0000007878cfaf7c] clone(strace: Process 21566 attached
child_stack=0x75b8bedca0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x75b8bedcc0, tls=0x75b8bee000, child_tidptr=0x75b8bedcc0) = 21566
[pid 21285] [0000007878cfaf7c] clone(strace: Process 21567 attached
child_stack=0x75b57bbca0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x75b57bbcc0, tls=0x75b57bc000, child_tidptr=0x75b57bbcc0) = 21567
[pid 21285] [0000007878cfafb4] exit(0) = ?
[pid 21285] [????????????????] +++ exited with 0 +++
[pid 21362] [0000007878cfaf7c] clone(strace: Process 21568 attached
child_stack=0x75b4fb5ca0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x75b4fb5cc0, tls=0x75b4fb6000, child_tidptr=0x75b4fb5cc0) = 21568
[pid 21297] [0000007878cfaf7c] clone(child_stack=0x75b4eabca0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x75b4eabcc0, tls=0x75b4eac000, child_tidptr=0x75b4eabcc0) = 21569
strace: Process 21569 attached
[pid 21419] [0000007878cfaf7c] clone(child_stack=0x75b4da1ca0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x75b4da1cc0, tls=0x75b4da2000, child_tidptr=0x75b4da1cc0) = 21570
strace: Process 21570 attached
[pid 21272] [0000007878cfb35c] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=21435, si_uid=10132, si_status=2, si_utime=0, si_stime=0} ---
[pid 21311] [0000007878d4d7d8] <... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 2}], 0, NULL) = 21435
[pid 21272] [0000007878cfb3a0] clone(strace: Process 21571 attached
<unfinished ...>
[pid 21345] [0000007878cfaf7c] clone(child_stack=0x753f027ca0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x753f027cc0, tls=0x753f028000, child_tidptr=0x753f027cc0) = 21572
strace: Process 21572 attached
[pid 21444] [0000007878cfaf7c] clone(strace: Process 21573 attached
child_stack=0x753a678ca0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x753a678cc0, tls=0x753a679000, child_tidptr=0x753a678cc0) = 21573
[pid 21571] [0000007878d4be98] execve("/product/bin/sh", ["sh"], 0x7ff5f809c0 /* 17 vars */) = -1 ENOENT (No such file or directory)
[pid 21571] [0000007878d4be98] execve("/apex/com.android.runtime/bin/sh", ["sh"], 0x7ff5f809c0 /* 17 vars */) = -1 ENOENT (No such file or directory)
[pid 21571] [0000007878d4be98] execve("/apex/com.android.art/bin/sh", ["sh"], 0x7ff5f809c0 /* 17 vars */) = -1 ENOENT (No such file or directory)
[pid 21571] [0000007878d4be98] execve("/system_ext/bin/sh", ["sh"], 0x7ff5f809c0 /* 17 vars */) = -1 ENOENT (No such file or directory)
[pid 21571] [0000007878d4be98] execve("/system/bin/sh", ["sh"], 0x7ff5f809c0 /* 17 vars */) = 0
[pid 21272] [0000007878cfb3a0] <... clone resumed> child_stack=NULL, flags=CLONE_VM|CLONE_VFORK|SIGCHLD) = 21571
[pid 21311] [0000007878d4d7d8] wait4(21571, <unfinished ...>
[pid 21243] [0000007889dc4008] exit_group(0 <unfinished ...>
[pid 21573] [????????????????] +++ exited with 0 +++
[pid 21572] [????????????????] +++ exited with 0 +++
[pid 21570] [????????????????] +++ exited with 0 +++
[pid 21568] [????????????????] +++ exited with 0 +++
[pid 21567] [????????????????] +++ exited with 0 +++
[pid 21566] [????????????????] +++ exited with 0 +++
[pid 21547] [????????????????] +++ exited with 0 +++
[pid 21569] [????????????????] +++ exited with 0 +++

执行到 [pid 21243] [0000007889dc4008] exit_group(0 <unfinished ...>就退出了

通过proc/pid/maps libc查看libc.so的地址范围

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
blueline:/ # cat /proc/21898/maps |grep libc.so
7878caf000-7878ceb000 r--p 00000000 07:58 38 /apex/com.android.runtime/lib64/bionic/libc.so
7878ceb000-7878cfb000 r-xp 0003c000 07:58 38 /apex/com.android.runtime/lib64/bionic/libc.so
7878cfb000-7878cfc000 rwxp 0004c000 07:58 38 /apex/com.android.runtime/lib64/bionic/libc.so
7878cfc000-7878cfe000 r-xp 0004d000 07:58 38 /apex/com.android.runtime/lib64/bionic/libc.so
7878cfe000-7878cff000 rwxp 0004f000 07:58 38 /apex/com.android.runtime/lib64/bionic/libc.so
7878cff000-7878d00000 r-xp 00050000 07:58 38 /apex/com.android.runtime/lib64/bionic/libc.so
7878d00000-7878d02000 rwxp 00051000 07:58 38 /apex/com.android.runtime/lib64/bionic/libc.so
7878d02000-7878d03000 r-xp 00053000 07:58 38 /apex/com.android.runtime/lib64/bionic/libc.so
7878d03000-7878d04000 rwxp 00054000 07:58 38 /apex/com.android.runtime/lib64/bionic/libc.so
7878d04000-7878d0c000 r-xp 00055000 07:58 38 /apex/com.android.runtime/lib64/bionic/libc.so
7878d0c000-7878d0e000 rwxp 0005d000 07:58 38 /apex/com.android.runtime/lib64/bionic/libc.so
7878d0e000-7878d4b000 r-xp 0005f000 07:58 38 /apex/com.android.runtime/lib64/bionic/libc.so
7878d4b000-7878d4e000 rwxp 0009c000 07:58 38 /apex/com.android.runtime/lib64/bionic/libc.so
7878d4e000-7878d55000 r-xp 0009f000 07:58 38 /apex/com.android.runtime/lib64/bionic/libc.so
7878d55000-7878d56000 rwxp 000a6000 07:58 38 /apex/com.android.runtime/lib64/bionic/libc.so
7878d56000-7878d64000 r-xp 000a7000 07:58 38 /apex/com.android.runtime/lib64/bionic/libc.so
7878d64000-7878d65000 rwxp 000b5000 07:58 38 /apex/com.android.runtime/lib64/bionic/libc.so
7878d65000-7878d69000 r-xp 000b6000 07:58 38 /apex/com.android.runtime/lib64/bionic/libc.so
7878d69000-7878d6a000 rwxp 000ba000 07:58 38 /apex/com.android.runtime/lib64/bionic/libc.so
7878d6a000-7878d6c000 r-xp 000bb000 07:58 38 /apex/com.android.runtime/lib64/bionic/libc.so
7878d6c000-7878d70000 r--p 000bd000 07:58 38 /apex/com.android.runtime/lib64/bionic/libc.so
7878d70000-7878d72000 rw-p 000c0000 07:58 38 /apex/com.android.runtime/lib64/bionic/libc.so

exit_group 是一个 Linux 系统调用,用于终止一个进程及其所有线程。如果使用 C 库(libc.so)中的相关函数(如 exit() 或 _exit()),它们会在内部调用 exit_group 来终结进程。而这里调用exit_group的地址范围不在libc中,说明exit_group没有通过标准库来调用。那么大概率是使用了动态分配的内存区域或者自定义加载的库

获取调用栈

使用动态调用的话就涉及到内存的动态分配

接下来使用与刚才相同的方法让进程sleep,使用strace来监控与内存相关的系统调用

strace -e trace=process,memory -i -f -p pid

1
2
[pid 22870] [0000007878d4ca78] mmap(NULL, 28, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7889dc4000
[pid 22870] [0000007889dc4008] exit_group(0 <unfinished ...>

根据这两行可以看出来exit_group的地址来自于mmap申请的28字节空间

mmap的地址0x0000007878d4ca78可以看出来它是libc.so中的函数,那么接下来可以hook mmap方法,打印其调用栈

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
function hookDlopen() {
let Func_sleep = new NativeFunction(Module.getExportByName('libc.so', 'sleep'), 'uint', ['uint'])
let linker64_base_addr = Module.getBaseAddress('linker64')
let offset = 0x3b508 // __dl__Z9do_dlopenPKciPK17android_dlextinfoPKv
let android_dlopen_ext = linker64_base_addr.add(offset)
Interceptor.attach(android_dlopen_ext, {
onEnter: function (args) {
this.name = args[0].readCString()
console.log(`Current PID: ${Process.id}`)
console.log(`dlopen onEnter ${this.name}`)
if (this.name != null && this.name.indexOf('libmsaoaidsec.so') >= 0) {
// Func_sleep(10)
hook_mmap()
}
}, onLeave: function (retval) {
console.log(`dlopen onLeave name: ${this.name}`)
}
})
}

function hook_mmap() {
var mmap_addr = Module.findExportByName("libc.so", "mmap")
console.log("mmap_addr ==>", mmap_addr)
Interceptor.attach(mmap_addr, {
onEnter: function (args) {
// console.log("args ==> ",)
var length = args[1].toInt32()
if (length === 28) {
console.log(`mmap length: ${length}`)
console.log('backtrace:\n' + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n") + '\n');
console.log("libmsaoaidsec.so --- " + Process.findModuleByName("libmsaoaidsec.so").base)
}
}
})
}

setImmediate(hookDlopen)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
android_dlopen_ext onEnter: /data/app/~~QB20iVpvVrCqCfx6DKaduA==/ctrip.android.view-e4dkBKlAkLS9PH0omGuXkw==/lib/arm64/libmsaoaidsec.so
mmap_addr ==> 0x7878d4ca70
mmap length: 28
backtrace:
0x7538e3158c
0x7538e3158c
0x7538e2b2f0
0x7538e29dcc
0x7538e29f7c
0x7538e22624
0x788b291470
0x788b27bd18
0x788b2770e8
0x78755470cc libdl.so!android_dlopen_ext+0x14
0x787c362dd0 libnativeloader.so!_ZNK7android21NativeLoaderNamespace4LoadEPKc+0xc0
0x787c362dd0 libnativeloader.so!_ZNK7android21NativeLoaderNamespace4LoadEPKc+0xc0

libmsaoaidsec.so --- 0x7538e0e000

成功的打印出了调用栈
0x7538e3158c - 0x7538e0e000 得到偏移 0x2358C

定位检测函数

用IDA打开libmsaoaidsec.so并跳转到栈回溯位置
image

image

根据交叉引用继续往上回溯找到调用的位置 sub_1CEF8

image

代码

那么接下来我们还是通过hook call_constructors方法,在加载到libmsaoaidsec.so时替换掉其中偏移sub_1CEF8处的函数

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
function hook_dlopen() {
Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"),
{
onEnter: function (args) {
this.fileName = args[0].readCString()
console.log(`android_dlopen_ext onEnter: ${this.fileName}`)
if (this.fileName != undefined && this.fileName.indexOf("libmsaoaidsec.so") >= 0) {
hook_linker_call_constructors()
// hook_mmap()
}
}, onLeave: function(retval){
console.log(`android_dlopen_ext onLeave: ${this.fileName}\n`)
if(this.fileName != null && this.fileName.indexOf("libmsaoaidsec.so") >= 0){
let JNI_OnLoad = Module.getExportByName(this.fileName, 'JNI_OnLoad')
console.log(`android_dlopen_ext onLeave JNI_OnLoad: ${JNI_OnLoad}`)
}
}
}
);
}

function hook_linker_call_constructors() {
let linker64_base_addr = Module.getBaseAddress('linker64')
let offset = 0x51120 // __dl__ZN6soinfo17call_constructorsEv
let call_constructors = linker64_base_addr.add(offset)
let listener = Interceptor.attach(call_constructors,{
onEnter:function(args){
console.log('hook_linker_call_constructors onEnter')
let secmodule = Process.findModuleByName("libmsaoaidsec.so")
if (secmodule != null){
// do something
hook_sub_1CEF8(secmodule)
listener.detach()
}
}
})
}

function hook_mmap() {
var mmap_addr = Module.findExportByName("libc.so", "mmap")
console.log("mmap_addr ==>", mmap_addr)
Interceptor.attach(mmap_addr, {
onEnter: function (args) {
// console.log("args ==> ",)
var length = args[1].toInt32()
if (length === 28) {
console.log(`mmap length: ${length}`)
console.log('backtrace:\n' + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n") + '\n');
console.log("libmsaoaidsec.so --- " + Process.findModuleByName("libmsaoaidsec.so").base)
}
}
})
}

function hook_sub_1CEF8(secmodule){
Interceptor.replace(secmodule.base.add(0x1cef8), new NativeCallback(function () {
console.log(`hook_sub_1CEF8 >>>>>>>>>>>>>>>>> replace`)
}, 'void', []));
}

setImmediate(hook_dlopen)
 评论