这篇文章上次修改于 304 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

前言

本文将演示如何利用eBPF手段快速定位frida反调试(简单版)

2024/01/23: 基础使用教学请看README,或者这篇文章

环境

目标

  • Y29tLndvZGkud2hv 64位版

步骤

首先打开APP,啥也不动,就让它正常运行着就行

总所周知,frida反调试中最高频的是libc.sostrstr函数

我们选择对该函数进行堆栈追踪,命令如下:

./stackplz --name com.wodi.who stack --symbol strstr --regs --unwindstack

注,stackplz 2.x起的版本使用如下命令:

./stackplz --name com.wodi.who -w strstr[str,str] --regs --stack

PS,stackplz默认设定了hook的库是/apex/com.android.runtime/lib64/bionic/libc.so,查看帮助信息请执行./stackplz stack --help

很快啊,我们就得到了如下的日志(简化):

stack_2022/11/28 03:10:06 StackMod      hook info:libc.so + strstr
stack_2022/11/28 03:10:06 start 1 modules
stack_2022/11/28 03:10:10 [libc.so + strstr] PID:28875, Comm:com.wodi.who, TID:28925, Regs:
{"lr":"0x7c2ecada7c","pc":"0x7f4c793200","sp":"0x7c2ec8e7c0","x0":"0x7c2ec8e7d8","x1":"0x7c2ecd804a","x10":"0x1ea","x11":"0x1","x12":"0x7c2ec8ddda","x13":"0xe1137761","x14":"0x10","x15":"0x0","x16":"0x7c2ecd6ac0","x17":"0x7f4c793200","x18":"0x64","x19":"0xb400007e088de7a0","x2":"0x1","x20":"0x7c2ecbf933","x21":"0x7c2ecbf932","x22":"0x7c2ecd8030","x23":"0x7c2ecd804a","x24":"0x7c2ecd8056","x25":"0xa9","x26":"0x7c2ec8f000","x27":"0x7c2ec8e7c8","x28":"0x7c2ec8e7d8","x29":"0x12","x3":"0x1","x4":"0x1","x5":"0x4","x6":"0x6174732f","x7":"0x73757461","x8":"0xa","x9":"0xc8349a5f5e975c78"}
Stackinfo:
  #00 pc 0000000000095200  /apex/com.android.runtime/lib64/bionic/libc.so (strstr)
  #01 pc 000000000001aa78  /data/app/~~edmZjLa9WVogYX0ek9xfLw==/com.wodi.who-zBzvEwdTiPkB-l2ix5dW2w==/lib/arm64/libmsaoaidsec.so    
  #02 pc 000000000001b870  /data/app/~~edmZjLa9WVogYX0ek9xfLw==/com.wodi.who-zBzvEwdTiPkB-l2ix5dW2w==/lib/arm64/libmsaoaidsec.so    
  #03 pc 00000000000b1690  /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+204)
  #04 pc 00000000000510ac  /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64)

...

stack_2022/11/28 03:10:12 [libc.so + strstr] PID:28875, Comm:com.wodi.who, TID:28926, Regs:
{"lr":"0x7c2ecacfa0","pc":"0x7f4c793200","sp":"0x7c2eb903e0","x0":"0x7c2eb903f0","x1":"0x7c2eb903e0","x10":"0x70fe","x11":"0x70fe","x12":"0x9","x13":"0x1","x14":"0x1","x15":"0xa","x16":"0x7c2ecd6ac0","x17":"0x7f4c793200","x18":"0x64","x19":"0xb400007de88780c0","x2":"0xd","x20":"0x7f4c7c0000","x21":"0x7c2eb91000","x22":"0x70cb","x23":"0x70cb","x24":"0x7c2eb90cb0","x25":"0x7c2eb90cb0","x26":"0x7c2eb90ff8","x27":"0xfc000","x28":"0xfe000","x29":"0x7c2eb90c50","x3":"0xb400007da8923e20","x4":"0xb400007da8923e29","x5":"0x7c2eb903fd","x6":"0x6950726563617254","x7":"0xa30093a64695072","x8":"0x0","x9":"0x1"}
Stackinfo:
  #00 pc 0000000000095200  /apex/com.android.runtime/lib64/bionic/libc.so (strstr)
  #01 pc 0000000000019f9c  /data/app/~~edmZjLa9WVogYX0ek9xfLw==/com.wodi.who-zBzvEwdTiPkB-l2ix5dW2w==/lib/arm64/libmsaoaidsec.so    
  #02 pc 000000000001a584  /data/app/~~edmZjLa9WVogYX0ek9xfLw==/com.wodi.who-zBzvEwdTiPkB-l2ix5dW2w==/lib/arm64/libmsaoaidsec.so    
  #03 pc 00000000000b1690  /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+204)
  #04 pc 00000000000510ac  /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64)

可以发现调用来源主要是两个地方:

  • libmsaoaidsec.so + 0x1aa78

  • libmsaoaidsec.so + 0x19f9c

这两个地方很可疑,谁没事开个线程不停调用strstr,这两个地方大概率就是反调试了

这个时候再用estrace看下都在打开什么文件

libmsaoaidsec.so + 0x1aa78前面是用的fopenlibmsaoaidsec.so + 0x19f9c则是openat

fopen最后也是要走到openat的,所以使用estrace执行如下命令看看参数:

./estrace --name com.wodi.who --syscall openat

得到如下日志(简化):

syscall_03:33:47 start 1 modules
syscall_03:33:49 [com.wodi.who] type:2 pid:28875 tid:28925 nr:openat arg_index:1 arg_str:/proc/self/task
syscall_03:33:49 [com.wodi.who] type:1 pid:28875 tid:28925 nr:openat {"lr":"0x7f4c758c34","pc":"0x7f4c79b658","sp":"0x7c2ec8e740","x0":"0x0","x1":"0x7c2ecd8020","x2":"0x0","x3":"0x0"}
syscall_03:33:49 [com.wodi.who] type:3 pid:28875 tid:28925 nr:openat ret:0x5
syscall_03:33:49 [com.wodi.who] type:2 pid:28875 tid:28925 nr:openat arg_index:1 arg_str:/proc/self/task/28875/status
syscall_03:33:49 [com.wodi.who] type:1 pid:28875 tid:28925 nr:openat {"lr":"0x7f4c758dc8","pc":"0x7f4c79b658","sp":"0x7c2ec8e690","x0":"0x0","x1":"0x7c2ec8e9d8","x2":"0x0","x3":"0x0"}
syscall_03:33:49 [com.wodi.who] type:3 pid:28875 tid:28925 nr:openat ret:0xa9
syscall_03:33:49 [com.wodi.who] type:2 pid:28875 tid:28925 nr:openat arg_index:1 arg_str:/proc/self/task/28887/status
syscall_03:33:49 [com.wodi.who] type:1 pid:28875 tid:28925 nr:openat {"lr":"0x7f4c758dc8","pc":"0x7f4c79b658","sp":"0x7c2ec8e690","x0":"0x0","x1":"0x7c2ec8e9d8","x2":"0x0","x3":"0x0"}
syscall_03:33:49 [com.wodi.who] type:3 pid:28875 tid:28925 nr:openat ret:0xa9

...

syscall_03:33:49 [com.wodi.who] type:2 pid:28875 tid:28925 nr:openat arg_index:1 arg_str:/proc/self/fd
syscall_03:33:49 [com.wodi.who] type:1 pid:28875 tid:28925 nr:openat {"lr":"0x7f4c758c34","pc":"0x7f4c79b658","sp":"0x7c2ec8e6f0","x0":"0x0","x1":"0x7c2ecd805c","x2":"0x0","x3":"0x0"}
syscall_03:33:49 [com.wodi.who] type:3 pid:28875 tid:28925 nr:openat ret:0x5
syscall_03:33:49 [com.wodi.who] type:2 pid:28875 tid:28925 nr:openat arg_index:1 arg_str:/proc/self/maps
syscall_03:33:49 [com.wodi.who] type:1 pid:28875 tid:28925 nr:openat {"lr":"0x7f4c758b20","pc":"0x7f4c79b658","sp":"0x7c2ec8c9d0","x0":"0x0","x1":"0x7c2ecd8074","x2":"0x0","x3":"0x0"}
syscall_03:33:49 [com.wodi.who] type:3 pid:28875 tid:28925 nr:openat ret:0x5
syscall_03:33:50 [uploadChecker t] type:2 pid:28875 tid:29012 nr:openat arg_index:1 arg_str:
syscall_03:33:50 [uploadChecker t] type:1 pid:28875 tid:29012 nr:openat {"lr":"0x7f4c758c34","pc":"0x7f4c79b658","sp":"0x7beb4d6e60","x0":"0x0","x1":"0xb400007cd88b9d10","x2":"0x0","x3":"0x0"}
syscall_03:33:50 [uploadChecker t] type:3 pid:28875 tid:29012 nr:openat ret:0x5

果然是在打开/proc/self/task/proc/self/maps,没跑了

再用IDA对上面两处调用strstr进行交叉引用,发现有创建线程,将对应位置nop,patch得到新so,替换手机内的

经过测试frida attach上去再也不崩了!

定位到的创建线程位置如图所示(不止一处,这里就不完整贴出来了):

总结

通过eBPF hook用户态的库关键函数来定位frida反调试当然是可行的,不过首先得知道frida反调试的基础知识

结合一些经验也是能定位出来反调试点的,当然本文只是演示了一个很简单的例子

本文主要以演示stackplzestrace效果为主,如果要更加灵活,建议还是上bcc环境

在bcc下直接读取strstr参数也会比较方便,可以参考eBPF on Android之hook libc.so open


Q: 如果是更复杂的反调试呢?

A: 那可以对所有的syscall调用进行追踪(estrace不会被检查到),找到调用位置,深入分析逻辑,一步步逆推也是能定位的;但这样很耗时,更多的时候还是建议结合反调试的手段,针对性hook和分析