首页
社区
课程
招聘
[原创]绕过爱奇艺新版libmsaoaidsec.so Frida检测
发表于: 2024-3-3 22:20 36353

[原创]绕过爱奇艺新版libmsaoaidsec.so Frida检测

2024-3-3 22:20
36353

手机:Google Pixel 6,Aosp android13
cpu架构:Arm64
Frida:16.1.10
爱奇艺:15.2.5

这段时间在研究爱奇艺的一些功能,当用Frida调试时发现有反Frida检测,现象是执行frida -l index.js -U -f com.qiyi.video时进程重启,下面是对Frida检测的一些分析,并通过hook绕过Frida检测。

用Frida hook do_dlopen函数看加载哪个so时崩溃的,hook之前先获取do_dlopen在linker64中的相对偏移


从结果可以看出加载的最后一个so是:libmsaoaidsec.so,并且没有调用onLeave,由此可断定崩溃点在libmsaoaidsec.so中,并且在JNI_OnLoad之前检测的,检测点应该是在init_array初始化函数中。
<br/>
既然挂上Frida后进程就退出,那么我们就来分析是调用哪个系统调用退出的,可以通过strace查看系统调用,但在执行strace时需要在dlopen加载libmsaoaidsec.so之前让线程sleep 10秒,以便留出strace执行时机。


我们看这一行[pid 15576] [000000751926c008] exit_group(0 <unfinished ...>,显示15576线程是在0x751926c008地址处调用exit_group退出的,通过proc/15534/maps查看libc.so的地址范围是0x7509b9c000 - 0x7509c70000,很明显0x751926c008不是libc.so的地址,由此可以断定exit_group的代码是动态释放的。
<br/>
动态释放代码一定是要操作内存的,接下来我们用前面相同的逻辑,用strace查看调用了哪些和内存相关的系统调用
<br/>
strace -e trace=process,memory -i -f -p 25947

注意这行:[pid 25990] [0000007509c48ab8] mmap(NULL, 28, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x751926c000
<br/>
上面这行mmap申请的内存返回的地址是:0x751926c000,正好匹配后面exit_group前面的地址:000000751926c008
<br/>
由mmap的地址可以看出mmap是libc.so中的函数,于是我们可以hook mmap打印一下调用栈

解决方案就是replace掉这个无返回值的函数 void sub_1B924()
<br/>
由于这个检测逻辑在.init_函数中,所以得先找到hook时机,查看Aosp代码发现linker执行init_array类的函数是call_constructors:

所以直接hook call_constructors函数,在onEnter中replace掉sub_1b924
<br/>
查看call_constructors在linker64中的偏移

function hookDlopen() {
    let linker64_base_addr = Module.getBaseAddress('linker64')
    let offset = 0x3ba00 // __dl__Z9do_dlopenPKciPK17android_dlextinfoPKv
    let android_dlopen_ext = linker64_base_addr.add(offset)
    Interceptor.attach(android_dlopen_ext, {
      onEnter: function(args){
        this.name = args[0].readCString()
        Log.log(`dlopen onEnter ${this.name}`)
      }, onLeave: function(retval){
        Log.log(`dlopen onLeave name: ${this.name}`)
                if (this.name != null && this.name.indexOf('libmsaoaidsec.so') >= 0) {
          let JNI_OnLoad = Module.getExportByName(this.name, 'JNI_OnLoad')
          Log.log(`dlopen onLeave JNI_OnLoad: ${JNI_OnLoad}`)
        }
      }
    })
  }
function hookDlopen() {
    let linker64_base_addr = Module.getBaseAddress('linker64')
    let offset = 0x3ba00 // __dl__Z9do_dlopenPKciPK17android_dlextinfoPKv
    let android_dlopen_ext = linker64_base_addr.add(offset)
    Interceptor.attach(android_dlopen_ext, {
      onEnter: function(args){
        this.name = args[0].readCString()
        Log.log(`dlopen onEnter ${this.name}`)
      }, onLeave: function(retval){
        Log.log(`dlopen onLeave name: ${this.name}`)
                if (this.name != null && this.name.indexOf('libmsaoaidsec.so') >= 0) {
          let JNI_OnLoad = Module.getExportByName(this.name, 'JNI_OnLoad')
          Log.log(`dlopen onLeave JNI_OnLoad: ${JNI_OnLoad}`)
        }
      }
    })
  }
function hookDlopen() {
    // 获取libc中的sleep函数
    let sleep: Function = new NativeFunction(Module.getExportByName('libc.so', 'sleep'), 'uint', ['uint'])
    let linker64_base_addr = Module.getBaseAddress('linker64')
    let offset = 0x3ba00 // __dl__Z9do_dlopenPKciPK17android_dlextinfoPKv
    let android_dlopen_ext = linker64_base_addr.add(offset)
    Interceptor.attach(android_dlopen_ext, {
      onEnter: function(args){
        this.name = args[0].readCString()
        Log.log(`dlopen onEnter ${this.name}`)
        if (this.name != null && this.name.indexOf('libmsaoaidsec.so') >= 0) {
          sleep(10) // sleep10秒留出strace执行时机
        }
      }, onLeave: function(retval){
        Log.log(`dlopen onLeave name: ${this.name}`)
      }
    })
  }
function hookDlopen() {
    // 获取libc中的sleep函数
    let sleep: Function = new NativeFunction(Module.getExportByName('libc.so', 'sleep'), 'uint', ['uint'])
    let linker64_base_addr = Module.getBaseAddress('linker64')
    let offset = 0x3ba00 // __dl__Z9do_dlopenPKciPK17android_dlextinfoPKv
    let android_dlopen_ext = linker64_base_addr.add(offset)
    Interceptor.attach(android_dlopen_ext, {
      onEnter: function(args){
        this.name = args[0].readCString()
        Log.log(`dlopen onEnter ${this.name}`)
        if (this.name != null && this.name.indexOf('libmsaoaidsec.so') >= 0) {
          sleep(10) // sleep10秒留出strace执行时机
        }
      }, onLeave: function(retval){
        Log.log(`dlopen onLeave name: ${this.name}`)
      }
    })
  }
function hook_mmap() {
  const mmap = Module.getExportByName("libc.so", "mmap");
  Interceptor.attach(mmap, {
    onEnter: function (args) {
      let length = args[1].toString(16)
      if (parseInt(length, 16) == 28) {
        Log.log('backtrace:\n' + Thread.backtrace(this.context, Backtracer.ACCURATE)
                                                  .map(DebugSymbol.fromAddress).join('\n') + '\n');
      }
    }
  })
}
 
function hookDlopen() {
    let sleep: Function = new NativeFunction(Module.getExportByName('libc.so', 'sleep'), 'uint', ['uint'])
    let linker64_base_addr = Module.getBaseAddress('linker64')
    let offset = 0x3ba00 // __dl__Z9do_dlopenPKciPK17android_dlextinfoPKv
    let android_dlopen_ext = linker64_base_addr.add(offset)
    Interceptor.attach(android_dlopen_ext, {
      onEnter: function(args){
        this.name = args[0].readCString()
        Log.log(`dlopen onEnter ${this.name}`)
        if (this.name != null && this.name.indexOf('libmsaoaidsec.so') >= 0) {
          // sleep(10)
      hook_mmap()
        }
      }, onLeave: function(retval) {
        Log.log(`dlopen onLeave name: ${this.name}`)
      }
    })
  }
function hook_mmap() {
  const mmap = Module.getExportByName("libc.so", "mmap");
  Interceptor.attach(mmap, {
    onEnter: function (args) {
      let length = args[1].toString(16)
      if (parseInt(length, 16) == 28) {
        Log.log('backtrace:\n' + Thread.backtrace(this.context, Backtracer.ACCURATE)
                                                  .map(DebugSymbol.fromAddress).join('\n') + '\n');
      }
    }
  })
}
 
function hookDlopen() {
    let sleep: Function = new NativeFunction(Module.getExportByName('libc.so', 'sleep'), 'uint', ['uint'])
    let linker64_base_addr = Module.getBaseAddress('linker64')
    let offset = 0x3ba00 // __dl__Z9do_dlopenPKciPK17android_dlextinfoPKv
    let android_dlopen_ext = linker64_base_addr.add(offset)
    Interceptor.attach(android_dlopen_ext, {
      onEnter: function(args){
        this.name = args[0].readCString()
        Log.log(`dlopen onEnter ${this.name}`)
        if (this.name != null && this.name.indexOf('libmsaoaidsec.so') >= 0) {
          // sleep(10)
      hook_mmap()
        }
      }, onLeave: function(retval) {

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2024-3-4 13:01 被yyy123编辑 ,原因: 上传libmsaoaidsec.so文件
上传的附件:
收藏
免费 40
支持
分享
最新回复 (63)
雪    币: 3004
活跃值: (30866)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2024-3-3 23:00
1
雪    币: 189
活跃值: (1600)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
牛逼
2024-3-4 09:46
0
雪    币: 1671
活跃值: (215822)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
4
tql
2024-3-4 09:54
0
雪    币: 2334
活跃值: (10386)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
大佬能附带个样本,去下载一个同版本的app,里面是arm版本so,在ida中搜索函数1b924没有的
2024-3-4 10:06
0
雪    币: 2334
活跃值: (10386)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
疑问1【显示15576线程是在0x751926c008地址处调用exit_group退出的,通过proc/15534/maps查看libc.so的地址范围是0x7509b9c000 - 0x7509c70000,很明显0x751926c008不是libc.so的地址,由此可以断定exit_group的代码是动态释放的】

这个查看libc.so的地址范围的时机是什么时候呢,如果注入frida后app重启了,这个地址会变化?如果变化了这个时候exit的那个地址范围就和重启后的libc.so地址对不上了吧。这个查libc.so的地址是在sleep的时候做的吗?


strace是自己编译的,还是可以在网上下到呢,在Android12上面没有这个工具。
2024-3-4 10:13
0
雪    币: 4747
活跃值: (2276)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7

感谢分享精彩文章,附加是64位so


上传的附件:
2024-3-4 10:23
0
雪    币: 102
活跃值: (2045)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
8
mark
2024-3-4 11:22
0
雪    币: 632
活跃值: (734)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
你瞒我瞒 大佬能附带个样本,去下载一个同版本的app,里面是arm版本so,在ida中搜索函数1b924没有的
要64位的,应用宝下载即可
2024-3-4 11:34
0
雪    币: 632
活跃值: (734)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10

1、是调用的sleep获取maps文件,另外linux的fork机制原因,libc.so映射地址一般都和zygote进程一样 

2、通过Aoso编译的镜像是自带strace的

稍等我在文章后面附加指定版本的strace

2024-3-4 11:40
0
雪    币: 632
活跃值: (734)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
你瞒我瞒 疑问1【显示15576线程是在0x751926c008地址处调用exit_group退出的,通过proc/15534/maps查看libc.so的地址范围是0x7509b9c000 - 0x7509c ...
1、是调用的sleep获取maps文件,另外linux的fork机制原因,libc.so映射地址一般都和zygote进程一样 

2、通过Aoso编译的镜像是自带strace的

稍等我在文章后面附加指定版本的strace
2024-3-4 11:41
0
雪    币: 2334
活跃值: (10386)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
yyy123 1、是调用的sleep获取maps文件,另外linux的fork机制原因,libc.so映射地址一般都和zygote进程一样 2、通过Aoso编译的镜像是自带strace的 稍等我在文章 ...
感谢
2024-3-4 14:13
0
雪    币: 4055
活跃值: (4792)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
学习一下
2024-3-4 19:24
0
雪    币: 4778
活跃值: (2974)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
学习了
2024-3-5 11:29
0
雪    币: 199
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
15
学习了
2024-3-5 14:12
0
雪    币: 22
活跃值: (945)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
复现不了...下载的爱奇艺版本也会Process terminated,但是没有没有到libmsaoaidsec.so 这个文件...
2024-3-6 11:58
0
雪    币: 632
活跃值: (734)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
linkin5epk 复现不了...下载的爱奇艺版本也会Process terminated,但是没有没有到libmsaoaidsec.so 这个文件...
版本一致吗?我在应用宝下载的
2024-3-6 14:07
0
雪    币: 22
活跃值: (945)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
yyy123 版本一致吗?我在应用宝下载的
有可能是因为我使用的是模拟器,我没试过真机,我换真机试试
2024-3-6 19:54
0
雪    币: 632
活跃值: (734)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
linkin5epk 有可能是因为我使用的是模拟器,我没试过真机,我换真机试试[em_51]
现在有点规模的app都有反作弊检测的,首先检测的就是模拟器
2024-3-6 19:59
0
雪    币: 27
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
20
66666
2024-3-6 22:38
0
雪    币: 22
活跃值: (945)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
yyy123 现在有点规模的app都有反作弊检测的,首先检测的就是模拟器
谢嘞大哥,换真机成功嘞
2024-3-7 00:09
0
雪    币: 21
活跃值: (475)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22

学习了,谢谢分享

最后于 2024-3-7 17:00 被jueqingqiai编辑 ,原因:
2024-3-7 16:36
0
雪    币: 21
活跃值: (475)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
Log 如何打印的  id 和 ThreadId?
2024-3-7 21:52
0
雪    币: 22
活跃值: (945)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
jueqingqiai Log 如何打印的 id 和 ThreadId?

同问

最后于 2024-3-8 00:49 被linkin5epk编辑 ,原因:
2024-3-8 00:40
0
雪    币: 22
活跃值: (945)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
我们看这一行[pid 15576] [000000751926c008] exit_group(0 <unfinished ...>,显示15576线程是在0x751926c008地址处调用exit_group退出的,通过proc/15534/maps查看libc.so的地址范围是0x7509b9c000 - 0x7509c70000,很明显0x751926c008不是libc.so的地址,由此可以断定exit_group的代码是动态释放的。

我太菜了,能请问一下这个动态释放是什么意思?(我可以认为是在exit前会执行动态释放内存的操作吗?)

上面这行mmap申请的内存返回的地址是:0x751926c000,正好匹配后面exit_group前面的地址:000000751926c008(这段话有什么含义么?不是很理解...这和上面的动态释放连着的吗?)
2024-3-8 01:01
0
游客
登录 | 注册 方可回帖
返回
//