APP使用frida反调试检测绕过
一、环境
- bilibili 7.26.1
- arm
- frida 15.2.2(去除特征版本)
- pixel 6 android 12
二、正文
-
使用
frida
以spawn
模式启动应用,frida
进程直接被杀掉了 -
我需要知道是那个
so
在检测frida
,可以hook dlopen
看一下so
的加载流程-
function hook_dlopen() { 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(); console.log("load " + path); } } } ); }
-
-
由so的加载流程可知,当
libmsaoaidsec.so
被加载之后,frida进程就被杀掉了- 因此监测点在
libmsaoaidsec.so
中 - 如果有了解过so的加载流程,那么就会知道linker会先对so进行加载与链接
- 然后调用so的
.init_proc
函数 - 接着调用
.init_array
中的函数 - 最后才是
JNI_OnLoad
函数
- 然后调用so的
- 所以我需要先确定检测点大概在哪个函数中
- 因此监测点在
-
使用
frida hook JNI_OnLoad
函数-
如果调用了该函数就输出一行日志
-
如果没有日志输出,那么就说明检测点在
.init_xxx
函数中- 注入的时机可以选择
dlopen
加载libmsaoaidsec.so
完成之后。
- 注入的时机可以选择
-
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) { this.is_can_hook = true; } } }, onLeave: function (retval) { if (this.is_can_hook) { hook_JNI_OnLoad() } } } ); } function hook_JNI_OnLoad(){ let module = Process.findModuleByName("libmsaoaidsec.so") Interceptor.attach(module.base.add(0xC6DC + 1), { onEnter(args){ console.log("call JNI_OnLoad") } }) } setImmediate(hook_dlopen, "libmsaoaidsec.so")
-
-
并没有输出日志,那么说明检测的位置在
JNI_OnLoad
函数之前- 所以我需要
hook .init_xxx
的函数 - 但这里有一个问题
dlopen
函数调用完成之后.init_xxx
函数已经执行完成了- 这个时候不容易使用
frida
进行hook
- 这个问题其实很麻烦的,因为你想要
hook linker
的call_function
并不容易 - 这里面涉及到
linker
的自举,我想到了一个取巧的办法,请看接下来的操作。
- 所以我需要
-
首先在
.init_proc
函数中找一个调用了外部函数的位置,时机越早越好 -
我选择了
_system_property_get
函数,接下来使用frida hook dlopen
函数-
当加载
libmsaoaidsec.so
时- 在
onEnter
回调方法中hook _system_property_ge
t函数,以"ro.build.version.sdk
"字符串作为过滤器。
- 在
-
如果
_system_property_get
函数被调用了 -
那么这个时候也就是
.init_proc
函数刚刚调用的时候 -
在这个时机点可以注入我想要的代码,具体实现如下:
-
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
,打印一下新线程要执行的函数地址-
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) } }) }
-
-
这里面有两个线程是
libmsaoaidsec.so
创建的,对应的函数偏移分别是0x11129
和0x10975
-
这两个函数都检测了
frida
,想要了解具体检测方法的可以自己看看,这里不再深入 -
绕过的方法很简单,直接
nop
掉pthread_create
或者替换检测函数的代码逻辑都可以 -
我是直接把
pthread_create
函数nop
掉了,下面是完整代码。-
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 ThumbWriter(code, { pc: ptr(addr) }); cw.putNop(); cw.putNop(); cw.flush(); }); } function bypass(){ let module = Process.findModuleByName("libmsaoaidsec.so") nop(module.base.add(0x10AE4)) nop(module.base.add(0x113F8)) } setImmediate(hook_dlopen, "libmsaoaidsec.so")
-
-