某试用版so保护调试器检测分析

发布时间 2023-08-27 21:26:18作者: 怎么可以吃突突

so加固分析

查看so文件加固前的program header table

so文件被加固后program header table主要有以下几点变化。

  1. 第一个PT_LOAD代码段大小增加
  2. 在原so文件最后一个PT_LOAD段后新增加一个段
  3. .dynamic.rel.dyn.rel.plt三个重定位相关的节区会被移动到新增加的PT_LOAD段中
  4. .dynamic大小增加,说明新增加了项

查看.dynamic节区的内容,发现新增加了INITFINI类型的项

其扩大的PT_LOAD代码段是想嵌入自己的代码,而扩大代码段的前提是不能改变原始代码部分(包含.plt表)与.got表在内存中的相对偏移,因为原始代码是通过相对偏移的到.got中的重定位后的数据的。代码段和.got表属于不同的PT_LOAD段,想要增加足够大的空间嵌入代码在两个PT_LOAD段中间寻找显然是不行的,只能在代码段最开头的代码之前去开辟空间。查看原始so最开始的代码,实际就是plt表起始位置,对应的偏移为0x20F2C

查看加固后so,原始.plt表之前(0x1A1F2C)出了包含原始so的数据外,其余部分都是加固后嵌入的代码。

查看嵌入的代码,发现其嵌入代码自身的.plt调用

根据代码找到对应嵌入代码的.got表位于新增加的PT_LOAD段的起始位置,刚好与原始so的.got表紧挨着。

既然嵌入的代码有.got表,所以一定需要重定位,那么就需要向.rel.dyn.rel.plt表中新增加项(.dynstr和.dyntab也同样要增加)。

so文件加固前后的变化大致如下

反调试分析

so加固后会增加INITFINI动态链接项,对应的就是.init.proc.term.proc导出函数。并且JNI_OnloadJNI_OnUnload函数都会被劫持,劫持函数中在调用原始的JNI_Onload函数。

查看.init_proc函数,也就是嵌入代码部分存在着大量花指令

frida hook一下fopen发现一直轮询打开/proc/self/maps/proc/self/status/proc/pid/cmdline,这些都是反调试经常会检测的地方。其中栈回溯后发现调用的地方都在嵌入代码位置,也就是0x1A1F2C偏移之前的代码,既然是轮询肯定创建了线程。

frida hook pthread_create函数并判断线程函数的偏移是否在嵌入代码范围内,如果是就直接返回。因为嵌入代码的执行时机肯定是在.init.proc或者是劫持的JNI_Onload函数中,所以可以hook call_constructors来在最早的时机进行hook,这里不能在hook call_array获取执行时机了,因为call_array调用.init_array中的函数时INIT(.init_proc)函数已经执行过了。最后看到有两个在嵌入代码创建的线程被阻止,之后就可以正常调试了。

最后看一下其反调试检测了哪些东西,因为代码被花了,用unicorn模拟跑一下.init.proc函数,发现再执行了大量垃圾指令后才看到关键代码,是.plt表调用对应的是malloc函数

所以这里直接在其嵌入代码的所有.plt表中下断,这样其调用什么函数就可以看到而不用一步一步跟进了,经过调试发现部分反调试项。

  1. 检测/proc/self/statusTracerPid标志
  2. 检测/proc/%d/cmdline父进程
  3. 通过map表读取linker映射并检测rtld_db_dlactivity函数
  4. time时间查检测
  5. 检测ida 的23946,并轮询连接并断开
  6. 检测/data/local/tmp中的frida-server等文件
  7. 一旦检测到调试器就调用abort函数触发got SIGABRT signal (Abort)