【Android安全-绕过爱奇艺新版libmsaoaidsec.so Frida检测】此文章归类为:Android安全。
手机:Google Pixel 6,Aosp android13
cpu架构:Arm64
Frida:16.1.10
爱奇艺:15.2.5
这段时间在研究爱奇艺的一些功能,当用Frida调试时发现有反Frida检测,现象是执行frida -l index.js -U -f com.qiyi.video
时进程重启,下面是对Frida检测的一些分析,并通过hook绕过Frida检测。
用Frida hook do_dlopen函数看加载哪个so时崩溃的,hook之前先获取do_dlopen在linker64中的相对偏移
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
function hookDlopen() {
let linker64_base_addr
=
Module.getBaseAddress(
'linker64'
)
let offset
=
0x3ba00
/
/
__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()
Log.log(`dlopen onEnter ${this.name}`)
}, onLeave: function(retval){
Log.log(`dlopen onLeave name: ${this.name}`)
if
(this.name !
=
null && this.name.indexOf(
'libmsaoaidsec.so'
) >
=
0
) {
let JNI_OnLoad
=
Module.getExportByName(this.name,
'JNI_OnLoad'
)
Log.log(`dlopen onLeave JNI_OnLoad: ${JNI_OnLoad}`)
}
}
})
}
|
frida -l index.js -U -f com.qiyi.video
从结果可以看出加载的最后一个so是:libmsaoaidsec.so,并且没有调用onLeave,由此可断定崩溃点在libmsaoaidsec.so中,并且在JNI_OnLoad之前检测的,检测点应该是在init_array初始化函数中。
<br/>
既然挂上Frida后进程就退出,那么我们就来分析是调用哪个系统调用退出的,可以通过strace查看系统调用,但在执行strace时需要在dlopen加载libmsaoaidsec.so之前让线程sleep 10秒,以便留出strace执行时机。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
function hookDlopen() {
/
/
获取libc中的sleep函数
let sleep: Function
=
new NativeFunction(Module.getExportByName(
'libc.so'
,
'sleep'
),
'uint'
, [
'uint'
])
let linker64_base_addr
=
Module.getBaseAddress(
'linker64'
)
let offset
=
0x3ba00
/
/
__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()
Log.log(`dlopen onEnter ${this.name}`)
if
(this.name !
=
null && this.name.indexOf(
'libmsaoaidsec.so'
) >
=
0
) {
sleep(
10
)
/
/
sleep10秒留出strace执行时机
}
}, onLeave: function(retval){
Log.log(`dlopen onLeave name: ${this.name}`)
}
})
}
|
frida -l index.js -U -f com.qiyi.video
我们看这一行[pid 15576] [000000751926c008] exit_group(0 <unfinished ...>
,显示15576线程是在0x751926c008地址处调用exit_group退出的,通过proc/15534/maps查看libc.so的地址范围是0x7509b9c000 - 0x7509c70000,很明显0x751926c008不是libc.so的地址,由此可以断定exit_group的代码是动态释放的。
<br/>
动态释放代码一定是要操作内存的,接下来我们用前面相同的逻辑,用strace查看调用了哪些和内存相关的系统调用
<br/>strace -e trace=process,memory -i -f -p 25947
注意这行:[pid 25990] [0000007509c48ab8] mmap(NULL, 28, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x751926c000
<br/>
上面这行mmap申请的内存返回的地址是:0x751926c000,正好匹配后面exit_group前面的地址:000000751926c008
<br/>
由mmap的地址可以看出mmap是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
|
function hook_mmap() {
const mmap
=
Module.getExportByName(
"libc.so"
,
"mmap"
);
Interceptor.attach(mmap, {
onEnter: function (args) {
let length
=
args[
1
].toString(
16
)
if
(parseInt(length,
16
)
=
=
28
) {
Log.log(
'backtrace:\n'
+
Thread.backtrace(this.context, Backtracer.ACCURATE)
.
map
(DebugSymbol.fromAddress).join(
'\n'
)
+
'\n'
);
}
}
})
}
function hookDlopen() {
let sleep: Function
=
new NativeFunction(Module.getExportByName(
'libc.so'
,
'sleep'
),
'uint'
, [
'uint'
])
let linker64_base_addr
=
Module.getBaseAddress(
'linker64'
)
let offset
=
0x3ba00
/
/
__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()
Log.log(`dlopen onEnter ${this.name}`)
if
(this.name !
=
null && this.name.indexOf(
'libmsaoaidsec.so'
) >
=
0
) {
/
/
sleep(
10
)
hook_mmap()
}
}, onLeave: function(retval) {
Log.log(`dlopen onLeave name: ${this.name}`)
}
})
}
|
void sub_1B924()
是检测逻辑所在,具体检测逻辑就不在这赘述了,大家有兴趣可以自行研究
解决方案就是replace掉这个无返回值的函数 void sub_1B924()
<br/>
由于这个检测逻辑在.init_函数中,所以得先找到hook时机,查看Aosp代码发现linker执行init_array类的函数是call_constructors:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
void soinfo::call_constructors() {
if
(constructors_called) {
return
;
}
constructors_called
=
true;
if
(!is_main_executable() && preinit_array_ !
=
nullptr) {
/
/
The GNU dynamic linker silently ignores these, but we warn the developer.
PRINT
(
"\"%s\": ignoring %zd-entry DT_PREINIT_ARRAY in shared library!"
,
get_realpath(), preinit_array_count_);
}
get_children().for_each([] (soinfo
*
si) {
si
-
>call_constructors();
});
TRACE(
"\"%s\": calling constructors"
, get_realpath());
/
/
DT_INIT should be called before DT_INIT_ARRAY
if
both are present.
call_function(
"DT_INIT"
, init_func_);
call_array(
"DT_INIT_ARRAY"
, init_array_, init_array_count_, false);
}
|
所以直接hook call_constructors函数,在onEnter中replace掉sub_1b924
<br/>
查看call_constructors在linker64中的偏移
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
|
function hookDlopen() {
let linker64_base_addr
=
Module.getBaseAddress(
'linker64'
)
let offset
=
0x3ba00
/
/
__dl__Z9do_dlopenPKciPK17android_dlextinfoPKv
let android_dlopen_ext
=
linker64_base_addr.add(offset)
if
(android_dlopen_ext !
=
null) {
Interceptor.attach(android_dlopen_ext, {
onEnter: function(args){
this.name
=
args[
0
].readCString()
if
(this.name !
=
null && this.name.indexOf(
'libmsaoaidsec.so'
) >
=
0
) {
hook_linker_call_constructors()
}
}, onLeave: function(retval){
Log.log(`dlopen onLeave name: ${this.name}`)
if
(this.name !
=
null && this.name.indexOf(
'libmsaoaidsec.so'
) >
=
0
) {
let JNI_OnLoad
=
Module.getExportByName(this.name,
'JNI_OnLoad'
)
Log.log(`dlopen onLeave JNI_OnLoad: ${JNI_OnLoad}`)
}
}
})
}
}
function hook_linker_call_constructors() {
let linker64_base_addr
=
Module.getBaseAddress(
'linker64'
)
let offset
=
0x521f0
/
/
__dl__ZN6soinfo17call_constructorsEv
let call_constructors
=
linker64_base_addr.add(offset)
let listener
=
Interceptor.attach(call_constructors, {
onEnter: function (args) {
Log.log(
'hook_linker_call_constructors onEnter'
)
let secmodule
=
Process.findModuleByName(
"libmsaoaidsec.so"
)
if
(secmodule !
=
null) {
hook_sub_1b924(secmodule)
listener.detach()
}
}
})
}
function hook_sub_1b924(secmodule) {
Interceptor.replace(secmodule.base.add(
0x1b924
), new NativeCallback(function () {
Log.log(`hook_sub_1b924 >>>>>>>>>>>>>>>>> replace`)
},
'void'
, []));
}
|
更多【Android安全-绕过爱奇艺新版libmsaoaidsec.so Frida检测】相关视频教程:www.yxfzedu.com