APP使用frida反调试检测绕过

一、环境

二、正文

  • 使用fridaspawn模式启动应用,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函数
    • 所以我需要先确定检测点大概在哪个函数中
  • 使用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 linkercall_function并不容易
    • 这里面涉及到linker的自举,我想到了一个取巧的办法,请看接下来的操作。
  • 首先在.init_proc函数中找一个调用了外部函数的位置,时机越早越好

  • 我选择了_system_property_get函数,接下来使用frida hook dlopen函数

    • 当加载libmsaoaidsec.so

      • onEnter回调方法中hook _system_property_get函数,以"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创建的,对应的函数偏移分别是0x111290x10975

    • 这两个函数都检测了frida,想要了解具体检测方法的可以自己看看,这里不再深入

    • 绕过的方法很简单,直接noppthread_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")
        
posted @ 2023-09-04 22:58  树大招疯  阅读(4612)  评论(1编辑  收藏  举报