本文为银行类 App 某加密检测流程的绕过思路与详细分析,纯属个人研究分享,关键对抗细节模糊化 分析过程中也遇到不少底层原理盲区,欢迎各位大佬指点。内容偏向新手向 SO 层逆向实战流程,希望能给同样入门加固对抗的朋友提供一些可落地的分析思路。如有疏漏或误导之处,恳请大佬们批评指正,感谢!
如何拿到的脱壳的防护so文件libexec.so ,怎么修复 这里篇幅过长 所以大家可以参考东方玻璃大佬这个帖子[原创]某加固新版frida检测绕过-trace一把嗦-Android安全-看雪安全社区|专业技术交流与安全研究论坛
hook clone偏移的原理 看雪有不少优质帖,大家可以去了解了解,这里本人底层知识不够扎实 ,怕误导大家
追踪工具的使用 可以来私信作者,我不记得我怎么下载的了,嘿嘿
某加密 libexec.so 的进程终止并非来自 SO 加载阶段(init_array/JNI_OnLoad)的子线程检测,而是 Java 层早期调用 JNI 动态注册的 native 方法(sub_68060),该方法通过全局检测结构体(off_E3290)调用核心检测函数 ,检测异常后触发 sub_66998 杀进程;最终通过 Hook 核心检测函数 并强制返回 0 实现绕过。

frida注入应用被杀进程 , 发现dlopen了两个so ,这是某加密的so检测文件 经验来说 某加密的so检测主要在 libexec.so
下面分析是哪个层面被杀的 这里的层面指的是杀死进程的时机
先明确 SO 加载核心时机:call_constructors(SO 加载时最先执行,触发.init/.init_array 段)→ JNI_OnLoad → SO 业务函数 / 子线程阶段。
验证逻辑:
因此锁定后续阶段,Hook clone 函数排查:是否是 SO 通过 call_constructors 初始化后,创建子线程执行检测并杀进程。
这里如果想了解详细机制 ,大家可以去自行找帖子,b站勇敢的小佳有一个视频比较好的介绍了这个流程 ,这里先用脚本注入验证

发现成功跑出libexec.so 的jni函数,应该不是在iniarray 或者jni_onload里面做的进程终止
hook clone函数,查看so文件地址上有没有创建子线程的痕迹
clone函数是 Android 系统中创建线程的底层核心函数,监控它就能精准捕捉到 libexec.so 创建检测子线程的源头行为 详细参考前置说明

检测到so层创造了两个线程 ,这里打印的偏移是clone函数执行的入口函数 , 大家可以理解为产生了一个工人 ,这里偏移就是他的任务 我们跳转到ida的入口函数看看 ,看看是不是有什么检测
先看看0x42FE4


可以发现这里调用了TPIDR_EL0 后续又做了校验 ,有反调试嫌疑 ,继续跟进。发现末尾做了标志位校验,如果监测到异常,直接跳入死亡函数分支,如果没有,函数ret0 sub_dd770是一个跳板函数 , 跳入一个偏移 ,偏移是自杀函数


那么另外一个偏移呢 0x42F90 ,我们同样跳转看看

这里其实我已经确定是一个检测函数了 ,为什么呢,要归于我之前的判断 ,首先,开头if(*off_E3290)很重要,这个极有可能是一个检测防护结构体的地址,是一个二级指针 , 为什么这样说呢 ,我们对刚刚分析的偏移0x42FE4分析看看 ,我们一直对这个函数链交叉引用




最后追踪到了init_array段,我们先梳理一下,我们刚刚分析的链条 sub_432A4 -> off_E5BB0偏移 -> 431A0 -> 检测子线程入口 42FE4
开头的sub_432A4在init_array段和其他函数一起被早期执行 这里一般有大量的初始化和检测,是绕过的重点区域

我们再聚焦于sub_432A4干了什么,就可以知道,我为什么说偏移off_E3290是一个防护结构体了 ,后面也可以验证我的猜想

这里先是LDR 读取 off_E3290的所在内存页偏移 (就是拿到off_E3290)的地址到x8
然后读取off_E5BB0的地址保存到x9
最后把x9的地址放入x8的0x40偏移处
这里就类似于 *off_E3290 -> 0x40 = int * off_E5BB0 就是把这个偏移放入off_E3290结构体
所以我们现在来看看到底放了什么东西吧
先看off_E5BB0

这不正是我们向上追溯的431A0吗,也就是子线程入口函数的调用上层,所以说这里很明确了,这个so会把偏移函数放到函数表off_E5BB0,再最终放入off_E3290,肯定不止一个函数表off_E5BB0
,肯定会有其他的函数表被放入检测结构体,这里后面也有验证 但是需要注意的是,也有可能其实更大可能是一个全局结构体,只是放入了很多检测函数表

大量函数调用off_E3290 这里肯定是有很多检测函数调用off_E3290结构体 ,为什么要这样多此一举呢,因为可以妨碍我们追踪调用堆栈,我们定位死亡函数,向上交叉引用是引用不到真正调者的,因为可能是通过这个调用 off_E3290结构体进行调用,但是注意,这里这个结构体不仅仅有函数,还有检测状态数等
我们具体聚焦刚刚分析的第二个子线程入口函数偏移0x42F90,这里就很明确了

先判断检测体是不是为空, 执行计数操作最后进入函数sub_43364


这里先拿全局结构体off_E3290 + 18 和 + 88 ,调用函数sub_75F40做校验,检查源码发现就是一个逐字节对比函数,检查是否是相同字符 ,源码在上图 , 只要不匹配进入检测if分支 ,再调用sub_75F40 把一段字符串放入缓冲区v71 再调用sub_75DC4进行校验,看缓冲区包不包含":",如果失败 ,跳转LABEL_52

'
最后跳转死亡函数,和刚刚一样的stack_chk_fail
检测远不止这点,这里不逐个分析了,所以这里这个子线程的入口函数0x42F90也是检测函数,所以两者都为检测线程,直接试试把两个入口ret0
但是我注入脚本之后,发现进程还是死了

我们使用ida的外部插件 stalker trace so 来追踪一下函数的执行流,定位死亡函数
我们注入生成后的trace流脚本 ,同时注入我们的线程杀函数脚本,对比两者函数执行流有无区别
这里是同时注入的 frida frida -U -f 包名 -l 杀偏移脚本 -l trace脚本
用了杀函数脚本

没用杀函数脚本
用了杀函数脚本

可以看到,没有任何区别,这就奇怪了啊,为什么呢我们杀线程没有影响一点最后的终止函数线程流,这说明并不那两个线程最终触发的函数死亡
我们进一步分析最后死亡的函数sub_6AAC8

可以看到直接发送死亡信号 ,杀了进程,这肯定是死亡流末尾了,也就是已经在上层触发了死亡分支,我们要向上交叉引用

也就是执行流堆栈的66998 , 我们直接查看代码
66998函数是一个烈性的检测杀进程,包含大量自杀逻辑,没有任何业务分支

拿到自身pid 为后续kill进程做准备

轮询发送死亡信号,杀进程

末尾调用死亡函数6AAC8 ,没有任何活分支 ,这里只展现冰山一角,我们继续向上追溯,这里肯定远远不够

传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 20小时前
被reserve_zhou编辑
,原因: