学习了frida检测原理后,打算找个样本练手。起初是仿照着东方玻璃师傅的文章复现,但是在找最后一个检测点遇到了一些阻碍,导致最后一个执行流没找出来所以暂且先放放。后来在误打误撞下发现了一个新的样本,该样本有不少检测,故借此样本分析一下。最终完成检测的绕过,以及其中各类检测方法实现的分析还原。阅读这篇文章,可能能让你掌握这些知识点:首先就是检测点定位,这里直接使用两种方式进行frida注入的测试。这里直接使用frida的纯交互模式进行测试:进入frida之后,发现frida马上就退出了,而程序正常运行: 说明可能在某些so,或者子线程当中会有检测,接下来在app成功运行的情况下,使用attach进行附加,发现等待几秒钟后app直接退出, 退出之后app又重新启动了: 那么已经可以推测出来,这个app在子线程当中一定有对frida的检测。接下来需要定位一下这个这个检测点到底在哪里,这里对dlopen进行hook,脚本如下:在onEnter和 onLeave的位置输出一下提示,由于这个frida检测是通过退出自己的进程来打断frida注入的话,那么这个过程中检测的so可能会有下面的行为特征: 经过hook发现,进行检测的地方在这个libmsaoaidsec.so的so当中。而且可以看到的是在dlopen onLeave之前,这个进程就已经退出了,可以初步判断这个检测的位置大致是在init_proc,init_array执行过程当中,因为JNI_Onload的调用时机是在dlopen之后,所以需要对这个so当中的init_proc和init_array进行主要分析确定了目标,就可以开始着手分析了。这里将so从apk当中取出来后,丢到IDA,接着想要通过段来找到init_array,这个结构,但是这个so有点不同,只有这三个段 这里直接在IDA View窗口搜索:能够搜到 DT_INIT,DT_INIT_ARRAY,对应的就是.init_proc,.init_array了。 这里为了快速定位到究竟是.init_proc还是.init_array当中的某个函数检测并退出进程的,就直接对call_function进行hook,将对应的函数偏移答应出来,如果遇到只进不出的就说明是在这个函数当中进行了检测行为了。对应的hook代码如下:运行之后,发现是在这个0x14400函数退出进程的,因为并没有从这个函数onLeave,也就是检测在这个.init_proc函数当中。 如果后续想要更进一步的了解这个so的检测的话就需要这个.init_proc进行深度分析了。这里先不深入进行分析,上面定位检测点的时候猜测是有通过子线程进行检测的,这里可以先尝试对pthread_create,进行hook,然后替换掉线程函数,看看能否绕过再说。结合上述的分析结果可以判断,也是有子线程检测的。还有一种可能是,在.init_proc当中启动的子线程检测。这里就先尝试一下,通过替换子线程的线程函数来看看能否绕过检测。而想要对子线程的线程函数进行替换,就需要先找到到底是使用了哪一个函数,而想要做到这一步,就需要对pthread_create这个函数进行hook。既然提及到了这个pthread_create,那么顺带的也来整理一下这个函数的调用链,以及是怎么调用线程函数的。Android分析有一点好处就是可以直接对照着源码,再和IDA的反编译进行比对分析。那么首先就先进行源码的分析。(源码可以使用在线网站,这里使用的是XRefAndroid - Support Android 16.0 & OpenHarmony 6.0 (AndroidXRef/AospXRef))pthread_internal_t在这个pthread_internal_t当中,对于找线程函数来说比较重要的就是这几个成员,他们的名称以及结构体偏移大致如下:有了上面的了解之后就能够来分析pthread_create的调用链了。这个函数可以在源码当中的/bionic/libc/bionic/pthread_create.cpp当中找到: pthread_create首先会调用__allocate_thread将新线程所需要的内存空间申请好,并且划分不同的功能区。然后返回tcb指针,后续会通过tcb来获取pthread_internal_t*指针。接着就是对thread进行初始化,包括线程回调函数,以及函数参数的初始化。完成上述准备之后,就进入到了最关键的一步,调用clone:以当前进程为模板,克隆出来一个新的执行流。这里需要注意的这几个参数:参数一(__pthread_start),参数四(thread)。 跟进函数,继续分析: 可以看到函数声明:fn就是__pthread_start,arg就是thread,接着往下分析:下面会判断fn是否为空,如果有值则调用__bionic_clone,来进行系统调用进行创建线程。 到这里会发现无法跟进__bionic_clone,这里需要配合ida来分析libc.so代码。首先在libc.so的clone当中找到调用__bionic_clone的位置,标记一下参数传递所使用的寄存器。这里标记了几个比较重要的参数: 这里是对照着源码的参数进行标记的。完成后进入__bionic_clone进行分析,汇编代码分析如下 这里系统调用之后如果返回的是子线程的空间的话,sp已经变成了子线程的栈帧。然后回进入__start_thread这个函数当中。此时x0是pthread_start的函数地址,x1是结构体地址。接着继续看看__start_thread这个函数作用是什么。在这个函数当中,我们主要关注的是thread和phread_start相关的寄存器即可。 最后会调用pthread_start这个函数。这个函数可以在源码当中看到是什么情况: 可以看到传入的arg参数就是thread,接着会在后面调用thread当中的回调函数,也就是pthread_create传入的回调函数地址。综上所述,pthread_create的调用链大致如下(在其他不同版本的设备中可能会不太一样):而且在这个分析过程中可以发现,有非常多的点可以通过pthread_internal_t结构体拿到对应的线程函数,例如clone,__bionic_clone,__start_thread等函数当中。有了上面的知识就可以来试试找出通过创建线程来检测frida的函数在哪里了。有了上面的知识就可以通过frida来写脚本,获取线程函数地址了:以Spawn方式注入,输出如下,可以发现能够找到对应so当中相关的线程函数了。 接着就来试一下将这三个函数给替换掉只不过替换线程函数之后就进程就直接崩溃了,命令行当中也打印了崩溃转储信息(这个可能需要多重复操作几次才会打印): 通过转储信息可以看到一个是fault addr = 0x000b0202000015b2,另一个是backtrace,当中的调用堆栈。现在就需要去IDA当中通过调用堆栈回溯一下发生这种问题的点可能在哪里。在IDA当中直接跳转到0x20d10的位置: 可以看到这里是将W8,这个寄存器当中的值存放到x20+0x188的位
...(已截断)
---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-290658.htm
[原创]libmsaoaidsec.so Frida检测、反调与绕过实战
329 浏览
14 回复
感谢分享
感谢分享
rbq
非常感谢
感谢分享,认真学习。
从 SO 里定位检测逻辑再到 Frida 绕过的过程写得很清楚,这类实战拆解比只看结论更有参考价值,感谢分享。
谢谢分享
谢谢分享
这个so被干透了
bluegatar
这个so被干透了
是的
这个so被干透了
是的
非常感谢
好东西 来看看
感谢分享
学习一下