已知使用libmsaoaidsec.so frida反调试的应用有:知乎,爱奇艺,携程,bilibili
以知乎10.37.0为例
Frida反检测
分析 Frida检测一般都是在Native层实现的,那么我们首先需要定位检测机制是在哪个so中实现的,这里我们就需要先hook Andoid的动态链接库加载函数,观察它加载到哪个so的时候会崩溃。
android_dlopen_ext
是专门为 Android 系统设计的,直接支持 Android 系统的优化和特性。因此,hook 这个函数可以避免可能的兼容性问题,确保你能够捕获到真正的库加载事件。
hook android_dlopen_ext
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} ` ) } } } ); }
从打印的结果看最是加载的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
那么我们就hook这里的call_constructors函数,在onEnter里注入代码 在设备中找到call_constructors的offsetreadelf -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 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 ){ } } }) }
确定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 ]; 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 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 ){ 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
函数,
期间调用了 sub_118FC()
1 2 3 4 5 6 7 8 9 __int64 sub_118FC () { _QWORD v1[2 ]; 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" ), { 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 ) { } } } } ); } 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" ), { 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 ) { 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" ) 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检测点
使用sleep让进程暂停在加载了libmsaoaidsec.so
处
使用工具strace监控系统调用
找到系统调用后,通过栈回溯定位检测点
定位退出进程前调用 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 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 ) } }, 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/~~QB20 iVpvVrCqCfx6DKaduA==/ctrip.android.view-e4dkBKlAkLS9PH0omGuXkw==/ lib/arm64/libtrace-canary.so dlopen onLeave name : /data/ app/~~QB20 iVpvVrCqCfx6DKaduA==/ctrip.android.view-e4dkBKlAkLS9PH0omGuXkw==/ lib/arm64/libtrace-canary.so Current PID : 21203 dlopen onEnter /data/app/~~QB20 iVpvVrCqCfx6DKaduA==/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 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 ) { 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 ) { 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/~~QB20 iVpvVrCqCfx6DKaduA==/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
并跳转到栈回溯位置
根据交叉引用继续往上回溯找到调用的位置 sub_1CEF8
代码 那么接下来我们还是通过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 () } }, 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 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 ){ 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 ) { 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)