首页
社区
课程
招聘
[原创]新姿势绕过应用的ROOT检测
2021-11-15 12:46 44523

[原创]新姿势绕过应用的ROOT检测

2021-11-15 12:46
44523

前言

最近一直在研究国企,更多的是银行相关的应用,发现几乎所有的应用都引入了ROOT检测机制,通过数次逆向分析ROOT检测的实现和尝试绕过检测机制,也发现了不少的乐趣。除此之外呢,由于疫情昨日接到防疫办的电话,通知居家隔离14天,也正因为这一突发情况让我暂时无法静下心来工作。今天也趁着时间比较富裕,分享一篇小文来打发下时间。

关于ROOT

关于安卓手机的ROOT,相信大部分人都不会陌生。在手机没有ROOT之前,我们只能作为一个使用者来用这个手机,需要在系统的约束下进行程序的安装和部分功能的使用。而ROOT之后的设备,对于我们来说有大有可为了,相当于我们可以获取最高的用户权限,从而卸载系统应用、定制化UI等各种骚操作便有了实现的可能。当然,ROOT之后的手机对于应用和用户来说都是极其危险的。

ROOT检测的意义

在前面提到了,ROOT之后的手机对于应用和用户来说都非常危险。那么ROOT检测的意义便是保护应用和用户,以免因为手机被ROOT而受到危害。我们可以猜测一下ROOT手机的原因,总的来说可以分为三类:个性化、安全研究、攻击行为。设想一下,为了追求个性化而去ROOT手机万一操作不慎删除一些关键文件或者卸载了一些必要的应用,很可能会导致系统不可用,造成不可挽回的损失。而对于安全研究来说,由于该需求的特殊性暂不考虑其危害性,毕竟研究的目的就是为了保障安全嘛。至于攻击行为,这一类别的名字已经告诉了我们有多么的危险。由于ROOT之后的手机允许获取最高用户权限,那么攻击者可以将目标应用运行在ROOT之后的手机上,从而可以越权访问应用的私有数据;除此之外,目前主流的Hook框架如frida、xposed等在ROOT手机上更是如鱼得水。ROOT检测存在的意义便是及时的发现应用所运行的环境是一个被ROOT后的手机、是一个不安全的运行环境,为了保障应用的安全运行,保护数据不被窃取、篡改,从而采取一系列的防护措施。

目前主流的检测方法

通过对部分应用的ROOT检测机制进行逆向分析,以及查阅相关资料发现一些事情。各应用ROOT检测机制没有相对统一的标准,具体表现在检查内容、处置方式等方面,下面是我整理的一些常用的检测方法:
1、检查常用目录是否存在su
2、使用which命令查看是否存在su
3、主动申请root权限
4、执行busybox
5、访问私有目录,如/data目录,查看读写权限
6、读取build.prop中关键属性,如ro.build.tags和ro.build.type
7、检查市面主流的模拟器
8、检测frida、xposed等Hook框架的特征

上面便是我整理的各种应用主流的检测方法,目前看来绝大多数的应用都是对上面几个检测方法组合使用,甚至有很多只会实现其中的一项或者两项。另外,上面的方法也没有囊括所有的可能,比如有些应用还会检测“面具”或者Superuser.apk等是否存在来进行ROOT检测。总结来说,目前主流的ROOT检测就是对ROOT之后的手机独有的一些特征进行验证,如特征文件是否存在、是否存在越权、关键属性是否被修改等等。

对ROOT设备的处置

检测到手机被ROOT之后如何处置也是一个重要的问题,目前看来主要有两种处置手段:其一是发现即摧毁,顾名思义就是检测到ROOT设备之后不允许应用正常运行;另外一种则是允许应用运行,但是切换与后台服务器的交互。接下来,咱们对这两种处置手段进行简单的比较:
1、第一种处置,检测到ROOT设备之后如何停止应用的运行?根据工信部的监管要求还不能直接让应用直接退出,因此现在一般的做法就是通过弹窗、Toast等以文字描述提示用户当前手机被root了存在风险,由用户决定是否退出或者延时几秒后退出。我们可以看出这种处置是将用户体验放在了一个相对重要的位置,但是这同时也给攻击者带来了诸多便利。友好的文案提示方面攻击者确认是触发了环境检测机制,从而专心绕过。不立即停止应用的运行,留给攻击者足够的时间去进行分析和注入,从而绕过应用的ROOT检测机制。
2、对第二种处置,对于攻击者来说无法直接定位到业务失败的原因,可能需要通过抓包分析,甚至自己猜测来定位,在一定程度上提高了攻击者的攻击成本和难度。但是这种处置也可能会误杀一部分为了个性化而将手机ROOT的正常用户,为了减少误杀率,现在的应用也会进行一些特殊的处置。

如何绕过ROOT检测

前面扯了一堆,后面的内容算是开始步入正题。无论我们是从事安全研究还是作为攻击者,也无论应用对检测到的ROOT设备是如何处置的,ROOT检测机制的存在都会对我们造成一些阻碍。因此,绕过应用的ROOT检测是一个必要的事情,也是我们工作中经常要遇到的。那么,如何绕过ROOT检测机制呢?这里提供两个思路,其一是对应用下手,干预应用的ROOT检测行为;另外一个思路则是对系统下手,隐藏系统自身ROOT相关的特征。我们可以借助jadx等逆向工具对应用源码进行分析,Hook相关的实现函数绕过;也可以获取AOSP源码,通过定制ROM来隐藏ROOT的特征。

Hook

目前主流的Hook框架有frida和xposed,本人比较倾向于前者,我们也可以用frida的可见框架RMS进行注入。相对来说,通过Hook的方式来绕过ROOT检测机制操作比较简单、方便,但Hook本身会受到很多的约束。一方面,受限于应用自身的加固手段,可能难以定位ROOT检测的实现函数;另一方面,Hook框架自身也会具备一些易于被检测到的特征,可能会受到这些特征的约束而难以完成工作。

定制ROM

定制ROM的手段有很多种,可以通过对官方包进行解包、修改后重打包。我推荐的方式是获取AOSP源码,自己编译后制作ROM包。这样可以实现更高程度的定制化,与基于现有包修改的方式相比该方式的操作空间更大,但是同样的它会带来更好的编译成本、修改难度也更大。

实战演练

借助这篇文章记录一下前段时间对某银行的APP进行ROOT检测绕过的一些实践,需要声明的一点是无论是本文章还是所做的一些实践都是出于学习和研究的目的,无意攻击对应用进行攻击。后面也会对可能透漏该行信息的内容进行打码,如有因疏忽泄露的将会及时进行修改。经过平时测试发现,该行的应用存在ROOT检测机制,在本人ROOT之后的手机上启动会弹出一个提示弹窗,且只能点击确定后退出应用。起初的时候,我想简单的绕过弹窗,但是后续的结果告诉我想的过于简单了,怀疑该行对于ROOT设备的处置是同时兼容了两种处置手段。所以,本次实践的目的便是绕过该行应用的ROOT检测机制。

技术选型

本次初步采用Hook的方式来绕过该行应用的ROOT检测,在这里解释下下为什么不采用定制ROM的方式。如果想用定制ROM的方式来绕过,一方面需要全面梳理ROOT的特征,既需要修改相关的系统api也要对一些系统属性进行篡改,相对来说成本较高。然而这并不是主要原因,主要是本人觉得定制ROM来绕过ROOT检测有点浪费且无赖(定制ROM绕过该行的环境检测是经过结果验证的)。作为一个有素质的人,我决定采用Hook的方式来进行绕过。

实践过程

一般来说,在拿到目标应用的时候什么都不要想,直接无脑上jadx等逆向工具。然而事与愿违该行应用做了加固,反馈给我如下图的结果:
图片描述
得,静态分析走不通那咱们就动态分析吧,我这里使用RMS来进行分析和注入,在开始之前需要先思考一下,我们想要哪些信息来进行分析。咱们的目的是绕过ROOT检测机制,最重要的便是定位到ROOT检测的实现函数,有了RMS我们怎么样来定位相关函数呢?
咱们静下心来梳理下思路和分析步骤:
1、确定目标类
由于本人是开发出身,想到的最直接的定位类的方法就是打印出当前应用所有已经加载的类,并根据关键词进行搜索。

从上图中我们可以很直观的看到咱们的目标类“RootUtil”,得来全不费工夫,从名字来看基本可以确定该类就是ROOT检测的封装工具类,咱们可以开始下一步了。
2、确定实现函数
在第一步我们基本确定了目标类,但是咱们无法明确该类的内容和各函数的具体实现,因此这一步需要将该类的一些关键信息进行打印,如变量、方法等。至于如何获取这些关键信息,反射可以帮助咱们实现这一需求。打印的方法内容如下:

根据上图展示,我们看到该类定义了6个函数,从这6个函数几乎可以得到了我们需要的一些信息:
首先,从几个函数的修饰词可以看到出了isDeviceRooted是公共方法,其余的全是私有方法,结合经验基本确定检测ROOT是调用方式为“RootUtil.isDeviceRooted”。当然也不排除应用会调用私有方法的可能,如果不放心后面实施绕过的时候也可以同时Hook所有的方法。
其次,几个函数的返回类型全部是boolean,结合方法的名字我们可以确定返回的结果即当前设备是否已经被ROOT,所以想要绕过应用的ROOT检测机制,我们只需将返回值改为false。
最后,分析所有函数的名字,我们基本可以确定应用ROOT检测机制的检测方法,在我前面整理的一些ROOT检测方法中基本包括了该行的几种检测方法,除了checkDeviceDebuggable,顾名思义该方法的作用就是检测是否可调试。无伤大雅,咱们先按照原来的思路进行绕过,后续会对这一方法进行补充说明。
3、Hook关键函数
经过前面两步,我们已经确定了要Hook的目标函数,最后便剩下Hook工作了,我们劫持RootUtil类的isDeviceRooted函数,将返回值改为false。理论上,我们便完成了ROOT检测机制的绕过,可结果真的如人意嘛?

结果分析

事与愿违,ROOT弹窗确实不再弹出,但是弹出一个新的弹窗“检测到攻击”。这样的结果可以证明两个事情,一个是我们确实成功绕过了应用的ROOT检测机制,第二个事情则是我们的绕过方案并不完美,虽然我们绕过了ROOT检测机制,但是这个绕过行为被应用识别成了攻击行为,显然这不是我们想要的最终结果。难道我们只能通过隐藏ROOT特征来绕过检测机制嘛?有没有新的思路可以完美绕过检测?

新姿势

带着对前面结果的疑问,我们继续追寻一种新姿势来绕过该行应用的ROOT检测机制。比如我们继续绕过Hook检测?其实这个想法一出现就被我自己否定了,冷静思考继续跟进定位Hook检测的实现再绕过的工作量甚至不小于定制ROM。在这里也感谢看雪提供了一个方面我们记录、分享的平台,很幸运也很偶然的翻到了自己之前一篇关于热修复的文章。啪,灵感来了。

关于热修复

热修复是什么?说白了就是咱们日常工作中常听到的“打补丁”。在热修复技术出现之前,如果线上的应用存在bug,需要紧急修复,重打包后发布。而一般来说,应用不可以随时进行发布,这就需要等到下一次版本发布的时候才可以修复bug。热修复的出现则解决了这一难题,热修复支持只替换出现bug部分的代码,支持随时修复bug,并对用户体验有了较大的提升。
在这里对热修复的原理不展开详细描述,之前分享过一篇《热修复》的文章,如果想了解的可以去看一下。今天还是分享如果利用热修复的技术来绕过ROOT检测机制,换种说法就是如何把该行应用自身的ROOT检测变成一个bug。
了解类加载机制的朋友会知道,在安卓中会通过DexPathList类来维护一个数组存储dex信息,热修复实现的本质就是将修复bug后的代码生成的dex放置在该数组的头部,基于这些咱们开始用新姿势绕过ROOT检测机制。

绕过检测机制

具体绕过流程如下:
1、生成用于替换dex
根据前面获取的类信息和方法信息,我们需要创建一个类并实现相应的函数,来生成替换的dex。
类的定义如下图所示
图片描述
后面就是编译生成class,再使用class2dex.jar创建dex文件,具体操作步骤在前面提到的文章中有描述。
2、热修复的核心逻辑实现
核心逻辑其实就是通过反射将新的dex文件放在DexPathList维护的数组的头部,详细的实现逻辑在前面提到文章中也有描述,这里只做一下补充。由于本次实践目标是某银行的应用,其自身有自己的热修复实现,并且我们也没有其源码。因此热修复的核心逻辑是通过定制ROM实现的,对Application.java进行修改,在Application创建的时候进行判断当前应用是否为目标应用以及判断要替换的dex文件是否存在,存在则进行热修复。
图片描述
3、结果展示
为了更好的展示效果,自己根据该行应用的ROOT检测逻辑编写一个demo,文案展示当前设备是否已经ROOT,如下图所示:
图片描述
然后,我们将第一步生成的dex文件放到/sdcard/Pictures目录下,重新启动应用,结果如下:
图片描述
Okay,大功告成!


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

收藏
点赞9
打赏
分享
最新回复 (21)
雪    币: 4707
活跃值: (5095)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
mb_aoooaosd 2021-11-15 15:03
2
0
大佬 银行app一般都加壳,怎么热修复自己的代码
雪    币: 3268
活跃值: (3675)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
镜中人24 2021-11-15 15:16
3
0
mb_aoooaosd 大佬 银行app一般都加壳,怎么热修复自己的代码
hi,这就是一个简单的实例哈。文中提到的应用也是动态加载的RootUtil,无非就是找到合适的替换位置就好
雪    币: 181
活跃值: (2863)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
huaerxiela 2021-11-16 11:04
4
0
你这个思路,有点东西的。。。
雪    币: 3268
活跃值: (3675)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
镜中人24 2021-11-16 11:09
5
0
huaerxiela 你这个思路,有点东西的。。。[em_85]
哈哈,就是玩儿嘛。图省事,自己刷个包一劳永逸
雪    币: 27
活跃值: (5005)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
不吃早饭 2021-11-16 11:56
6
0
虽然但是,这只会让检测更简单,本来还要解析method的修饰符或ArtMethod的flag的,你这getClassLoader判断一下dex路径就直接无了,历史倒退了属于是
雪    币: 3268
活跃值: (3675)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
镜中人24 2021-11-16 13:17
7
0
不吃早饭 虽然但是,这只会让检测更简单,本来还要解析method的修饰符或ArtMethod的flag的,你这getClassLoader判断一下dex路径就直接无了,历史倒退了属于是
哈哈,这就是搞着玩的。估计也不会有人去检测这玩意儿
雪    币: 32
活跃值: (28)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
三点水的湫 2021-11-22 10:08
8
0
这个不高级
雪    币: 3268
活跃值: (3675)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
镜中人24 2021-11-22 11:21
9
0
三点水的湫 这个不高级
不至于不至于,搞些玩的谈不上
雪    币: 5348
活跃值: (5343)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
GitRoy 3 2021-11-22 12:06
10
0
没见过风控检测在Java层的。。。
雪    币: 3268
活跃值: (3675)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
镜中人24 2021-11-22 12:11
11
0
GitRoy 没见过风控检测在Java层的。。。
大量APP的root检测都是在Java层...
雪    币: 50
活跃值: (60)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
fast鸟 2021-11-23 10:58
12
0
大佬,能把这个 ROOT检测逻辑demo给个链接。让我动手实践下。
雪    币: 348
活跃值: (1313)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
lipengzhu 2021-11-23 12:35
13
0
直接改内核,留个后门,神仙来了也检测不到
雪    币: 3262
活跃值: (2669)
能力值: ( LV3,RANK:25 )
在线值:
发帖
回帖
粉丝
xhyeax 2021-12-7 22:23
14
0

捉虫:更好的编译成本 -> 更高的编译成本


文章建议:


可在我决定采用Hook的方式来进行绕过。之后加上一句(最后还是没素质了~)


这样读者的困惑可能会更少一些。

雪    币: 3262
活跃值: (2669)
能力值: ( LV3,RANK:25 )
在线值:
发帖
回帖
粉丝
xhyeax 2021-12-7 22:26
15
0

楼主一开始提到:


定制ROM来绕过ROOT检测有点浪费且无赖(定制ROM绕过该行的环境检测是经过结果验证的)。作为一个有素质的人,我决定采用Hook的方式来进行绕过。


但是之后却没有将这个思路进行下去,因为:


定位Hook检测的实现再绕过的工作量甚至不小于定制ROM


于是使用了定制ROM实现的热修复技术。


个人认为其实可以继续下去。因为定位Hook检测的工作量可能并不是很大,属于常规逆向流程。之后使用Frida或Xposed即可完成绕过。


如果实在要用热修复的话,定制ROM还是有点重了,按楼主的修改方式,可以考虑重打包framework


PS:既然都定制ROM了,为啥不直接重写系统和内核层的Root相关检测

最后于 2021-12-17 20:53 被xhyeax编辑 ,原因:
雪    币: 6
活跃值: (980)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
lookzo 2021-12-8 08:48
16
0
我只想知道在家隔离,工资照发吗
雪    币: 27
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
wx_山东好汉 2021-12-9 18:45
17
0
Hotfix是什么类
雪    币: 244
活跃值: (266)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
数字云信息 2021-12-11 12:06
18
0
建议直接修改rom 做定向root,内核级别的,只有自己的APP能 拿到shell和root权限 
雪    币: 1782
活跃值: (1039)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Itachi_ 2021-12-17 04:13
19
0
xhyeax 楼主一开始提到:定制ROM来绕过ROOT检测有点浪费且无赖(定制ROM绕过该行的环境检测是经过结果验证的)。作为一个有素质的人,我决定采用Hook的方式来进行绕过。但是之后却没有将这个思路进行下去,因 ...
定位hook检测的工作量可能很大哦,一般hook检测以及root检测都放在so层,但是有些so会对代码做ollvm混淆,分析起来还是很有难度的
雪    币: 3262
活跃值: (2669)
能力值: ( LV3,RANK:25 )
在线值:
发帖
回帖
粉丝
xhyeax 2021-12-17 20:30
20
1
Itachi_ 定位hook检测的工作量可能很大哦,一般hook检测以及root检测都放在so层,但是有些so会对代码做ollvm混淆,分析起来还是很有难度的

你也说了是“可能”嘛,还是需要具体情况具体分析。


我的意思是,楼主可以再跟一下,看看检测点到底是在哪个函数。然后再评估要不要继续分析。


况且,我们不需要分析整个so函数的流程,只需要找到它的检测点即可。


常规定位思路:

Hook常见检测点(比如通过libc.so或JNI),过滤字符串并回溯堆栈,定位到检测位置。

对于系统调用,可采用SVC Hook、定制内核、或模拟执行的方式,找到检测位置。


对于混淆严重的代码,不好分析逻辑进行Patch。可以通过上述方式,找到它的检测特征(通常是字符串),然后写脚本或定制Hook工具,绕过检测。


PS:这种有提示的检测都是可以定位并绕过的(只是工作量大小有区别),隐而不发、散布各处的才是最难搞的。

最后于 2021-12-17 21:09 被xhyeax编辑 ,原因:
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_kwfufuyv 2023-12-29 05:31
21
0
大神。求助。我想给你私信打不开、有偿、看到希望能联系我、希望有机会合作
雪    币: 221
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_yynjwyzd 2024-1-3 14:36
22
0
打印出当前应用所有已经加载的类
请问这个怎么操作的
游客
登录 | 注册 方可回帖
返回