ARM场景下的frida检测对抗

0x00 前言

​ 很少的时候会遇到ARM架构的APP,存在frida检测的情况,这种情况下呢,没有办法直接使用ARM64的ret指令来直接让检测函数给返回。于是需要自己动手来分析so文件。

0x01 dumpSo

​ 两个APP均是梆梆加固的,众所周知梆梆企业壳也是有加固过的,所以要dump一下so文件。使用elf-dump-fix工具来dump so文件。正常运行APP,找到3873,这个PID。

image-20241017223355648

因为是arm指令的,所以要使用armeabi-v7a版本的来dump,首先通过maps文件来查找到so文件在内存中的地址分布。

1
2
3
4
oriole:/data/local/tmp/build/local/armeabi-v7a # cat /proc/3873/maps | grep libDexHelper.so
e0702000-e078b000 rwxp 00000000 fe:25 111083 /data/app/~~Vy2tt8bIwdc35DW_otNHMg==/com.cmiotmoiblecrm-3lHr_5L10U9hFhn-z4F2Xg==/lib/arm/libDexHelper.so
e078c000-e0798000 rw-p 00089000 fe:25 111083 /data/app/~~Vy2tt8bIwdc35DW_otNHMg==/com.cmiotmoiblecrm-3lHr_5L10U9hFhn-z4F2Xg==/lib/arm/libDexHelper.so
e07b0000-e07b2000 r-xp 00094000 fe:25 111083 /data/app/~~Vy2tt8bIwdc35DW_otNHMg==/com.cmiotmoiblecrm-3lHr_5L10U9hFhn-z4F2Xg==/lib/arm/libDexHelper.so

看到是e0702000-e07b2000。所以直接

1
2
3
4
5
6
7
8
9
oriole:/data/local/tmp/build/local/armeabi-v7a # ./dump 3873 e0702000 e07b2000 ./libDexHelp_dump.so 0 1
try dump 3873 from 00000000e0702000 to 00000000e07b2000
try to read /proc/3873/mem fp:3, off=00000000e0702000, sz=720896
read return 720896
720896 writed
try fix ./libDexHelp_dump.so.tmp
warning .init exist at 0x00000000000ae480
fixed so has write to ./libDexHelp_dump.so
end fix ./libDexHelp_dump.so.tmp output to ./libDexHelp_dump.so

将libDexHelp_dump.so文件pull出来就是dump下来并且修复好的so文件了。

0x02 寻找函数

​ 众所周知国内的检测函数基本都是通过pthread_create来创建检测线程的,所以要bypass检测的话就是patch掉检测的线程就好了。但是怎么找到函数的调用地址是困难的,壳会检测是否有hook pthread_create函数,所有没有办法直接通过该方法来获取到偏移地址,求助之后,师傅说可以自己修改一份ROM,在pthread_create函数的源代码中,增加一个logcat的打印,这样不需要Hook也可以每次运行的时候获取到偏移地址。于是找师傅直接要到偏移地址(后续有精力了再编译自己的ROM吧)。

1
2
3
4
normal find thread func offset libDexHelper.so 0xdc1b9645 226885 37645
normal find thread func offset libDexHelper.so 0xdc1b979d 227229 3779d
normal find thread func offset libDexHelper.so 0xdc1a8a79 158329 26a79
normal find thread func offset libDexHelper.so 0xdc1bc229 238121 3a229

那么就直接开始针对第一个地址37645的分析,找到该函数。

image-20241017225018310

直接交叉引用,找到是谁调用了它。可以看到在0x2AE34处,调用了一次sub_37644,但是这里IDA针对这一段都是红色的,说明并没有识别成函数,需要我们手动的来处理一下。

image-20241017225056713

大概找到0x2AC88的位置,一路选中所有的代码,选中到0x2AF70处。

image-20241017225235819

image-20241017225425720

按下键盘的P键,将这一段代码标记成函数。在IDA中就不显示成红色的了。

image-20241017225504034

然后找到调用sub_37644函数的汇编代码的位置,F5对这段函数进行反编译。

image-20241017225708497

即可看到这里存在函数sub_21F94(v445, 0, sub_37644, v8);看这个代码结构也能感觉到说sub_21F94其实就是pthread_create函数,但是可能是通过了dlsym函数的获取到函数指针从而直接调用的。

image-20241017225740687

0x03 处理函数

这里存在两种手法一种是直接nop掉sub_21F94,但是这个手法非常的不优雅,并且这里其实是存在v10的返回值的,后续的函数执行还需要用到v10,那么就存在程序运行崩溃的风险,那么我们选择的是处理sub_37644函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void __fastcall __noreturn sub_37644(unsigned __int8 *a1)
{
int v1; // r8
int i; // r6

v1 = *a1;
sub_21EBC(a1);
for ( i = 2; i != 8; ++i )
(*(void (__fastcall **)(int))(4 * ((1 << i) & v1) - 528911276))(-528910252); //真正的检测代码
sub_21D84(2); //实际上是sleep(2)函数
while ( 1 )
{
(*(void (__fastcall **)(int))(4 * (v1 & 2) - 528911276))(-528910252); //真正的检测代码
sub_21D84(3);
}
}

在0x37698还有0x3769C这两个位置存在两个DCD的字符串,我们需要手工的处理一下。

image-20241017231103079

选中一个使用快捷键U先设置为无意义字符串

image-20241017231151566

然后选中了之后快捷键C,设置为代码。0x3769C也是这么操作。

image-20241017231323323

最终这两个会被设置成如下样式。

image-20241017231400941

这里来仔细分析一下sub_37644函数,首先执行了PUSH {R4-R7,LR}来保存堆栈信息,所以一定会存在对应的POP {R4-R7,PC}汇编指令来恢复现场,那么这个汇编也就是该函数的结尾了其实。不过可以看到IDA的汇编中并没有对应的汇编,不知道是不是反编译的问题,这里使用鸡爪来一起分析一下。跳转到0x47644的地址,因为鸡爪开始的地址是`0x10000所以对比IDA的地址要大对应的数值。和IDA一样的是最后这里一样有两个位置的无意义字符,需要手动处理。

image-20241017232716587

image-20241017232734329

最终会被处理成以下内容

image-20241017232747132

于是处理完了之后就可以看到右边的伪代码中存在一个return了。对应到0x47782这个地址的汇编代码就是POP {R4-R7,PC}也就是函数的结尾。

image-20241017232847044

那么我们现在确定了THUMB指令就是F0BD

image-20241017233342720

如果我们将0x37646的位置修改成POP {R4-R7,PC}的话,之后的代码就根本不执行了,就相当于掏空了该函数。

image-20241017233525455

0x04 frida脚本

1
2
3
4
5
6
7
8
9
10
onLeave: function (retval) {
// console.log("this.soname=", this.soname);
if (this.soname.indexOf("libDexHelper.so") != -1) {
var base = Module.findBaseAddress("libDexHelper.so")
Memory.patchCode(base.add(0x37646), 2, function (code) {
code.writeByteArray([0x9C, 0xE0]);
console.log("patch code at ");
});
}
}

这里编写的脚本是将这个位置的汇编替换成了9C E0,对应的汇编就是b #0x13c,具体的意思就是跳转以当前地址以下的0x13C的地址去。

image-20241017233815452

那么现在的地址是0x37646 + 0x13C = 0x37782。也就是直接执行POPEQ {R4-R7,PC}

image-20241017234037066

从而这样就可以绕过检测函数。

下载链接:

https://github.com/strazzere/elf-dump-fix

https://armconverter.com/