bingyue 发表于 2025-1-8 20:47

某痛单词过frida检测

**小白第一次发帖,可能写的不太好,见谅**
本文已同步到本人博客,本人博客为 `5Yaw5pyI5Y2a5a6i`

今天来给大家分享一个案例,某某单词`app`,由于该`app`有反调试,本文就介绍一下如何绕过他的反调试

---

## 参考资料

[绕过最新版bilibili app反frida机制 ](https://bbs.kanxue.com/thread-281584.htm)

[绕过bilibili frida反调试](https://bbs.kanxue.com/thread-277034.htm)

---

## 开始

检测Frida的机制一般在Native层实现,通常会创建几个线程轮询检测。首先要知道检测机制是由哪个so实现的,通过hook `android_dlopen_ext`函数,观察加载到哪个so的时候,触发反调试进程终止即可。下面我们来试一下

```javascript
Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"),
{
    onEnter: function (args) {
      var pathptr = args;
      if (pathptr !== undefined && pathptr != null) {
            var path = ptr(pathptr).readCString();
            console.log("load " + path);
      }
    }
}
);
```

![](https://pic1.imgdb.cn/item/677e0d31d0e0a243d4f1c50e.png)

可以看到,在加载`libmsaoaidsec.so`后,`frida`挂掉了

现在直接注入我们的线程替换脚本,看看这个so加载的线程

```javascript
function check_pthread_create(name = null) {
    var pthread_create_addr = Module.findExportByName(null, 'pthread_create');

    var pthread_create = new NativeFunction(pthread_create_addr, "int", ["pointer", "pointer", "pointer", "pointer"]);
    Interceptor.replace(pthread_create_addr, new NativeCallback(function (parg0, parg1, parg2, parg3) {
      var module = Process.findModuleByAddress(parg2)
      var so_base = module.base;
      var off = "0x" + parg2.sub(so_base).toString(16)
      var so_name = module.name;
      console.log(so_name, off, parg3)

      return pthread_create(parg0, parg1, parg2, parg3);
      
   
    }, "int", ["pointer", "pointer", "pointer", "pointer"]))
}
setImmediate(check_pthread_create)
```

![](https://pic1.imgdb.cn/item/677e0e0cd0e0a243d4f1c523.png)

这里创建了三个线程,内存偏移量为`0x1c544`,`0x1b8d4`,`0x26e5c`,**注:画线的地方不是检测线程,请看清楚so名字**

现在回去改`dlopen`,如果这个`so`一加载就`nop`线程

![](https://pic1.imgdb.cn/item/677e0fe8d0e0a243d4f1c56c.png)

结果不尽人意,这里获取不了`libmsaoaidsec.so`的基址

因为安卓加载so整个流程如下

```bash
linker->init_proc ->JNI_OnLoad
```

从`dlopen`获取基址,是要等`jni`加载后的,但是现在`jni`还没加载我们的`frida`就被杀掉了,说明检测在`init_proc`里,我们需要寻找一个合适的hook时机

现在我们打开`ida`,把so拖进去,静态分析

![](https://pic1.imgdb.cn/item/677e11a8d0e0a243d4f1c5dd.png)

这里我尝试过寻找发起线程的地方,但从导入函数并没有看到,`pthread_create`相关字样,他应该是间接调用的,但是我们已经知道了线程的偏移量,所以这里不太重要了

![](https://pic1.imgdb.cn/item/677e17fdd0e0a243d4f1c73f.png)

接下来我们继续看`init_proc` ,这里被`ollvm`混淆了,转成伪`c`代码,通过交叉引用,可以得到他是先执行`sub_123f0`

![](https://pic1.imgdb.cn/item/677e13fdd0e0a243d4f1c62a.png)

可以看到`sub_123f0`,`_system_property_get`调用了一个`ro.build.version.sdk`,这里我们从注入`system_property_get`是个不错的选择,因为他是在`init.proc`阶段执行的

![](https://pic1.imgdb.cn/item/677e1473d0e0a243d4f1c63d.png)

现在我们去hook`_system_property_get`

```javascript
function locate_init() {
    let r = null
    Interceptor.attach(Module.findExportByName(null, "__system_property_get"),
      {
            onEnter: function (args) {
                var name = args;
                if (name !== undefined && name != null) {
                  name = ptr(name).readCString();
                  console.log(name)
                  if (name.indexOf("ro.build.version.sdk") >= 0) {
                     console.log(Process.findModuleByName("libmsaoaidsec.so").base)
                  }
                }
            }
      }
    );
}

Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"),
{
    onEnter: function (args) {
      var pathptr = args;
      if (pathptr !== undefined && pathptr != null) {
            var path = ptr(pathptr).readCString();
            if(path.search("libmsaoaidsec.so") != -1){
                this.hook = true
                locate_init()
            }
      }
    }
}
);
```

输出如下,我们成功获取到了基址

![](https://pic1.imgdb.cn/item/677e16ffd0e0a243d4f1c710.png)

那我们就`nop`上面提到的偏移量吧

![](https://pic1.imgdb.cn/item/677e178bd0e0a243d4f1c724.png)

完美绕过`frida`检测

---

## 完整代码

```javascript
function locate_init() {
    let r = null
    Interceptor.attach(Module.findExportByName(null, "__system_property_get"),
      {
            onEnter: function (args) {
                var name = args;
                if (name !== undefined && name != null) {
                  name = ptr(name).readCString();
                  //console.log(name)
                  if (name.indexOf("ro.build.version.sdk") >= 0) {
                      varr = Process.findModuleByName("libmsaoaidsec.so")
                      nop_64(r.base.add("0x1c544"))
                      nop_64(r.base.add("0x1b8d4"))
                      nop_64(r.base.add("0x26e5c"))
                  }
                }
            }
      }
    );
}

Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"),
{
    onEnter: function (args) {
      var pathptr = args;
      if (pathptr !== undefined && pathptr != null) {
            var path = ptr(pathptr).readCString();
            if(path.search("libmsaoaidsec.so") != -1){
                this.hook = true
                locate_init()
            }
      }
    }
}
);

function nop_64(addr) {
    Memory.protect(addr, 4 , 'rwx');
    var w = new Arm64Writer(addr);
    w.putRet();
    w.flush();
    w.dispose();
}
```

SNSD99 发表于 2025-2-14 09:35

我分析过不止一个App,确实有好多用到`libmsaoaidsec.so`来反Frida调试的。除了屏蔽它的检测线程,还可以直接把该so文件删掉。如果删掉之后App闪退的话,那就自己写一个假的so文件给它替换一下。(我当时试了可以,目前还没发现影响App的功能啥的)。

当然了,也能用IDA把`libmsaoaidsec.so` 的检测线程RET掉(打补丁),再替换回去试试看。这样就不用每次加载frida脚本去绕反调试了,稍微方便一点。
{:301_997:}

clabobo 发表于 2025-1-9 11:49

这么说 其实所有对 frida的检测和 xposed的检测都可以被 绕过

huagequan 发表于 2025-1-9 12:48

大佬,牛逼

stw8899 发表于 2025-1-9 14:05

厉害,学习了

laoser 发表于 2025-1-9 14:17

清晰值得推荐

Owenpojie 发表于 2025-1-9 16:15

这个是真的厉害!

llfly 发表于 2025-1-9 16:38

思路清晰明了 学习了!

linix 发表于 2025-1-10 08:04

感谢大佬分享,很好的思路

xiaobaixuepj 发表于 2025-1-10 08:49

感谢分享,学习

Musiclab 发表于 2025-1-10 09:04

思路清晰明了 学习了!
页: [1] 2 3
查看完整版本: 某痛单词过frida检测