范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

干货关于Androidroot绕过的知识,你知道多少?

  也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大
  少走了弯路,也就错过了风景,无论如何,感谢经历
  本篇文章遇到排版混乱的地方,可点击文末阅读原文或前往该地址:https://orangey.blog.csdn.net/article/details/126219953
  更多关于Android安全的知识,可前往:https://blog.csdn.net/ananasorangey/category11955914.html
  0x01 前言
  Android 是基于 Linux 多用户机制的访问控制。应用程序在默认的情况下不可以执行其他应用程序,包括读写用户的私有数据。一个应用程序的进程就是一个安全的沙盒(在受限的安全环境中运行应用程序,在沙盒中的所有改动对操作系统不会造成任何危害)
  每一个Android应用程序都会在安装时就分配一个独有的Linux用户ID,这就为它建立了一个沙盒,使其不能与其他应用程序进行接触。这个用户ID会在安装时分配给它,并在该设备上一直保持同一个数值
  所有的Android应用程序必须用证书进行签名认证,而这个证书的私钥是由开发者保有的。该证书可以用以识别应用程序的作者。签名影响安全性的最重要的方式是通过决定谁可以进入基于签名的permisssions,以及谁可以share 用户IDs。通过这样的机制,在不考虑root用户的情况下,每个应用都是相互隔离的,实现了一定的安全
  在Linux操作系统中,root的权限是最高的,也被称为超级权限的拥有者。在系统中,每个文件、目录和进程,都归属于某一个用户,没有用户许可其它普通用户是无法操作的,但对root除外
  在渗透测试 Android 应用程序时候,大多数技术都需要 root 权限才能安装各种工具,从而危及应用程序的安全性。就有了攻防双方对抗的现象产生,Android 开发人员又是使用了哪些技术来检测运行应用程序的设备是否已Root呢?接下来会介绍一些Android 开发人员在检查设备是否已经root的几种常用方法和一些绕过技术
  出于安全原因,多应用程序不允许在已经root的设备上运行,因为手机Root后给攻击者(用户)带来了非常大的自主操作权利,让攻击者(用户)可以删除系统应用、安全或删除应用程序、查看并修改程序的运行信息,但与此同时,也给恶意软件打开方便之门,给设备信息安全带来了极大的挑战。目前许多APP在启动时会进行Root环境监测,防止APP在已经Root的手机环境中运行,如果发现设备已被Root,会向用户弹窗提示运行环境存在安全风险不让APP继续运行下一步操作或禁止安装运行(出现APK闪退现象)
  上面说的检测到手机被Root,主要有两种处置手段: 第一种处置:发现即摧毁,顾名思义就是检测到ROOT设备之后不允许应用正常运行 第二种处置:允许应用运行,但是切换与后台服务器的交互。
  两种Root处置手段进行简单的比较: 第一种处置:检测到ROOT设备之后如何停止应用的运行?根据工信部的监管要求还不能直接让应用直接退出,因此现在一般的做法就是通过弹窗、Toast等以文字描述提示用户当前手机被root了存在风险,由用户决定是否退出或者延时几秒后退出。可以看出这种处置是将用户体验放在了一个相对重要的位置,但是这同时也给攻击者带来了诸多便利。友好的文案提示方面攻击者确认是触发了环境检测机制,从而专心绕过。不立即停止应用的运行,留给攻击者足够的时间去进行分析和注入,从而绕过应用的Root检测机制。 第二种处置:对于攻击者来说无法直接定位到业务失败的原因,可能需要通过抓包分析,甚至自己猜测来定位,在一定程度上提高了攻击者的攻击成本和难度。但是,这种处置也可能会误杀一部分为了个性化而将手机Root的正常用户,为了减少误杀率,现在的应用也会进行一些特殊的处置
  Root:获取手机超级管理员权限,android系统是基于linux内核,默认情况下并不提供超级管理员权限,所有获取su的权限就是所谓root
  Root用户的特权性:root可以超越任何用户和用户组来对文件或目录进行读取、修改或删除(在系统正常的许可范围内);对可执行程序的执行、终止;对硬件设备的添加、创建和移除等;也可以对文件和目录进行属主和权限进行修改,以适合系统管理的需要(因为root是系统中权限最高的特权用户);root是超越任何用户和用户组的,基于用户ID的权限机制的沙盒是隔离不了它的
  除了检测(test-keys(测试版)、release-keys(发布版))系统是否测试版、检测Root工具的安装路径,包名(特有刷root工具的包名称)是否带有su、activity、busybox、supersu或superuser等关键词外,还有如下检测方法: 检查su命令是否存在 检查常用目录是否存在su(或检测是否存在s权限的文件) 使用which命令查看是否存在su 主动申请root权限 执行busybox 检查Android属性(读取build.prop中关键属性,如ro.build.tags和ro.build.type) 检查特定路径是否有写权限(在Android系统中,有些目录是普通用户不能访问的,例如/data、/system、/etc 等) 检查市面主流的模拟器 检测frida、xposed等Hook框架的特征
  目前看来绝大多数的应用都是对上面几个检测方法组合使用,甚至有很多只会实现其中的一项或者两项。另外,上面的方法也没有囊括所有的可能,比如有些应用还会检测"Magisk"或者"Superuser"等是否存在来进行Root检测。总结来说,目前主流的Root检测就是对Root之后的手机独有的一些特征进行验证,如特征文件是否存在、是否存在越权、关键属性是否被修改等等
  补充知识 :  1)标准文件权限
  普通文件权限是通过十位进行表示的,如下图
  第一位代表的" 文件" 类型,其可能的值有:-(文件)、d(目录)、b(块设备)、l (link文件)、c (字符设备,如串口)、s(socket套接字)。 第2~4位表示文件属于的权限 第5~7位表示文件属组的权限 第8~10位表示其他用户和组具有的权限
  linux系统内有档案有三种身份 u:拥有者(user,文件的属主) g:群组 (group,文件的属主所在的组,属组) o:其他人(other,其它的用户) a:包括属主、属组、其它用户(all,以上所有人)
  2)rwx权限设置
  文件或目录每三位用rwx表示相应的权限值,其中r值为4、w值为2、x值为1 ,可以使用chmod 对文件或目录进行权限更改。对于文档常用的有下面权限: r:读权限,用户可以读取文档的内容,如用cat,more查看 w:写权限,用户可以编辑文档 x:该目录具有可以被系统执行的权限
  权限的设置有两种表示方式: 权限值表示法,如下: # chmod 777 file1  字母值表示法,如下: # chmod a+x file1
  命令
  结果
  含义
  chmod a-x myfile
  rw- rw- rw-
  收回所有用户执行权限
  chmod og-w myfile
  rw- r-- r--
  收回同组用户和其它用户的写权限
  chmod g+w myfile
  rw- rw- r--
  赋予同组用户写权限
  chmod u+x myfile
  rwx rw- r--
  赋予文件属主执行权限
  chmod go+x myfile
  rwx rwx r-x
  赋予同组用户和其它用户执行权限
  3)umask
  在/etc/profile文件有umask值的默认设置,默认值为022,该值对应的是默认文件和目录创建后的权限值: 目录的默认权限是:777-umask 文件的默认权限是:666-umask
  所以umask为022的情况下,默认创建的目录的权限是755 ,默认创建的文件权限为644
  4)特殊权限
  除了上面的提到的rwx权限外,除了读写执行权限外,系统还有三个特殊权限 s s t 权限(冒险位与粘滞位) 【强制位(s权限)和粘滞位(t权限)】,具体描述如下:
  权限
  对文件的影响
  对目录的影响
  suid
  以文件的所属目录身份执行,而非执行文件的用户
  无
  sgid
  以文件所属组身份执行
  该目录中创建的任意新文件的所属组与该目录的所属组相同
  sticky
  无
  对目录拥有写入权限的用户仅可以删除其拥有的文件,无法删除其它用户所拥有的文件
  这三个特殊权限也可以用字母和数字表示,具体如下: 设置suid:    chmodu+stestchmod  u+s  testchmodu+stest 设置sgid:设置sgid:设置sgid: chmod  g+s  test 设置sticky:  $ chmod  o+t  test suid:4 sgid:2 sticky:1
  s即(SUID,Set UID)设置使文件在执行阶段具有文件所有者的权限,相当于临时拥有文件所有者的身份. 典型的文件是passwd. 如果一般用户执行该文件, 则在执行过程中, 该文件可以获得root权限, 从而可以更改用户的密码
  在一些特殊情况下会用到特殊权限位,如passwd命令,如果没有s权限,其他用户会无法使用passwd命令修改自己的密码 # ls -l /usr/bin/passwd -rwsr-xr-x. 1 root root 27832 Jun 10  2014 /usr/bin/passwd
  给一个增加了s s t 权限的示例: # touch test # ll test -rw-r--r--. 1 root root   0 Aug  5 01:03 test # chmod 7777 test # ll test -rwsrwsrwt. 1 root root   0 Aug  5 01:03 test # su - usera
  上面由于增加了t权限,所以普通用户usera,可以通文件写入和更改,无法删除文件
  注 : 在设置s权限时文件属主、属组必须先设置相应的x权限,否则s权限并不能正真生效(c h m o d命令不进行必要的完整性检查,即使不设置x权限就设置s权限,chmod也不会报错,当我们ls -l时看到rwS,大写S说明s权限未生效) 需要注意的是特殊权限是把双刃剑,很多木马提供也会利用到s 权限位,所以经常在查找主机木马时,我们会用find查找所有4777 和 6777文件; 假如本来在该位上有x, 则这些特别标志 (suid, sgid, sticky) 显示为小写字母 (s, s, t) ,否则,显示为大写字母 (S, S, T) 还有一个大X权限,后面在ACL时也会提到 s或S(SUID,Set UID):可执行的文件搭配这个权限,便能得到特权,任意存取该文件的所有者能使用的全部系统资源。请注意具备SUID权限的文件,黑客经常利用这种权限,以SUID配上root帐号拥有者,无声无息地在系统中开扇后门,供日后进出使用。 T或T(Sticky):/tmp和 /var/tmp目录供所有用户暂时存取文件,亦即每位用户皆拥有完整的权限进入该目录,去浏览、删除和移动文件
  备注 :BusyBox是很多标准 Linux 工具的一个单个可执行实现。BusyBox 包含了一些简单的工具,例如 cat 和 echo,还包含了一些更大、更复杂的工具,例如 grep、find、moun)
  root方式分为两种: 不完全 root 完全 root
  目前获取Android root 权限常用方法是通过各种系统漏洞,替换或添加SU程序到设备,获取Root权限,而在获取root权限以后,会装一个程序用以提醒用户是否给予程序最高权限,可以一定程度上防止恶意软件,通常会使用Superuser或者 SuperSU ,这种方法通常叫做"不完全Root"。
  而 "完全Root"是指,替换设备原有的ROM,以实现取消secure设置
  如何绕过Root检测机制呢?这里提供两个思路,其一是对应用下手,干预应用的Root检测行为;另外一个思路则是对系统下手,隐藏系统自身Root相关的特征。我们可以借助jadx等逆向工具对应用源码进行分析,Hook相关的实现函数绕过;也可以获取AOSP源码,通过定制ROM来隐藏Root的特征 Hook
  目前主流的Hook框架有frida和xposed,可以用frida的可见框架RMS进行注入。相对来说,通过Hook的方式来绕过Root检测机制操作比较简单、方便,但Hook本身会受到很多的约束。一方面,受限于应用自身的加固手段,可能难以定位ROOT检测的实现函数;另一方面,Hook框架自身也会具备一些易于被检测到的特征,可能会受到这些特征的约束而难以完成工作。 定制ROM
  定制ROM的手段有很多种,可以通过对官方包进行解包、修改后重打包。我推荐的方式是获取AOSP源码,自己编译后制作ROM包。这样可以实现更高程度的定制化,与基于现有包修改的方式相比该方式的操作空间更大,但是同样的它会带来更好的编译成本、修改难度也更大 热修复
  热修复实现的本质就是将修复bug后的代码生成的dex放置在该数组的头部
  基于设备的作弊检测: 检测危险的APP包名主要检测hook框架,模拟点击工具,magisk,supersu等root工具 测是否存在root权限一般通过是否存在bin、sbin目录里的su文件,kingroot权限管理apk 检测是否有调试状态default.prop文件的ro.secure=1、ro.debuggable=1状态,/proc/self/status文件的TracerPid值 检测是否设备是否出厂ROM一般通过android.os.Build的各类参数值来判断,如Build.fingerprint 一些设备常规信息,比如电量状态,usb状态,屏幕亮度,地理位置,wifi或者sim卡信息,ip、mac等一般批量操作或者抹机操作的时候这些值更改难度和成本比较大,所以这些指标能伪造会大大降低被风控
  基于行为的作弊检测: Log日志搜集android开发时有Log.i、Log.e等运行日志,大部分APP用这个来调试或者检测APP的运行情况,并且release版本一般通过boolean值来开关,如果能hook掉这个开关就容易检测APP Exception栈信息搜集大部分APP的日志会搜集Exception,如果栈里有hook框架的包路径就会把自己给杀死,让hook难以长久执行,怕APP变成一个微型服务器抓包工具 统计sdk的信息,如talkingdata、umeng等一般阿里系的会用umeng来做统计工具,一般这些sdk都有自己的唯一编号,有些app引用这个编号来做一个指标去判断app的安装唯一性,有的会通过统计数据来判断批量作弊行为,因为这些sdk会搜集一些设备指纹并且技术和破解还原难度比较大的 异常上报sdk,比如buggly,友盟sdk等等异常上报sdk一般记录运行时所有的异常情况,并且会记录设备指纹,通过这些也能检测到作弊的一些设备
  APP的反作弊策略和思路: 设备指纹的手机如数美、易盾或一些大厂的app都会内嵌搜集设备指纹的模块,通过设备指纹来确定设备的唯一性,如imei、android_id、mac、其他设备信息结合起来就能变成一个唯一的标志 搜集IP、MAC地址、蓝牙MAC、WIFI等网络指纹大量的搜集后跟其他搜集的信息结合并且不断地完善代理ip或黑ip、mac库,就能不断地能识别作弊源头 地理位置、下载渠道、授权登录地理位置和ip是否对应、基站信息是否对应、或者设备型号跟下载渠道对应、一般小米手机的软件大部分应该小米商城下载的,比如微信授权登录,QQ授权登录等、如果模拟授权登录需要破解其他内嵌到APP的一些协议 参数签名、参数加密一般APP会把get或者post的参数通过某种算法去签名,并且这个签名难还原。比如抖音的x-gorgon,有的把post或者get参数直接加密,一般通过aes、rsa、des等 私有网络协议、protobuf等协议、私有网络证书、反代理抓包目前流行protobuf协议替代json,还有一些私有TCP/Socket协议,这些协议不可直接读,需要一个解析工具或者分析还原。私有网络证书需要证书密钥配合代理抓包工具才能抓包分析,这种其实难度在于找到证书密钥和判断是否用了私有证书,一般聊天APP的IM协议常用。还有反代理抓包,比如集成OkHttp框架等的时候用Proxy.NO_Proxy来防止抓包 dex代码混淆,native层ollvm编译、webview的js混淆对应dex代码混淆还原或者分析难度比较容易,并且各种反编译软件来分析难度不是那么大,主要难度在于rxjava等异步框架,接口实现类查找上。native层的话主要难度是还原基于ollvm的各种混淆的代码,其他还原或者理解难度其实并不大,并且大部分APP把一些加解密和签名等算法用native方式实现并用ollvm混淆,其他的native层的都是一些媒体或者网络等库。webview一般用在验证码上,并且用js的java层接口一起使用,把滑动或者图片顺序识别之类的路径日志通过贾母方式网络提交的,javascript代码一般混淆的比较厉害,不过AST反混淆等方法或者下载后nginx本地搭建并chrome自行调试归纳后通过脚本也能还原,如极验、易盾等验证码插件 APP加壳、安全sdk、私有安全插件加壳更多是为了过合规,安卓里dex文件总能运行内存中dump出来的,除非操作系统从底层设计上更改了,所有hook或者fart等修改的ROM都能搞定。360,腾讯,梆梆等等安全sdk也可以纸老虎这么说,不过安全sdk一般跟机器学习,大数据匹配等相关,越来越有难度去应付,不过一般在社交电商类的反虚拟登录时用,这些SDK搜集指纹设备,并保存到自己的服务器,会识别代理IP,打码平台的卡号,风险设备,不过把加解密算法还原后可以伪造设备和其他数据,能绕过还有一些大APP有自己开发的安全相关的模块,这些其实跟安全sdk类似,主要还是设备信息伪造和加解密算法破解后都能绕过。数美、易盾等
  APP的反作弊工具和策略: 逆向工具java:dex2jar、jadx、jeb、android-killer等等so文件:IDA、jeb、Gidrajs:其实nodejs加谷歌或者火狐就能搞定其他:unicorn、unidibug等基于qemu虚拟机的工具 抓包工具Charles、Fillder:这两个其实差不多,用于http、websocket等应用层抓包WireShark:各种协议都能支持,需要更深入的网络协议技术基础BurpSuite:可以开发一些插件,会开发就各种神操作把 hook框架xposed:用户比较多,尤其云控、群控、那些化妆品,教育类、保险等销售类的都在用,市场规模大frida:一般开发者使用的多,快,无需重启,会js就会玩吧cydia:hook Native层的时候多一点,老开发者用的多inlinehook,xhook:这两个类似,inlinehook多用于hook b跳转的,xhook多用于系统函数magisk:安卓8以上xposed或者其他一些证书安装等工具都基于这个,这个其实未来深入研究的东西 模拟点击uiautomator:很多模拟点击软件基于这个,跟xposed结合开发比较合适,理解安卓开发的入门比较简单还有按键精灵,节点精灵,触摸精灵等等 行业难度ollvm混淆:逆向行业目前应该最头疼的就这个吧,主要原理时if-else改成while(true){switch() case:}了,但是逆向成本变高了,并且各种延申的东西越来越多,需要经验和技术积累才能100%还原或破解机器学习风控策略:这个没有多年经验或者没有相当长的时间去摸索或者没跟班一个APP的成长的话很难对应,并且大部分都是通过养号,养设备等方式去对应,还有的是破解协议,并且伪造大量的设备信息,通过IP代理池,卡商和打码平台等第三方服务来维持。不过随着法律的完善和APP自身的风控体系健全对应成本越来越高,现在很难实现大批量账号登录注册 0x02 绕过root检测实验
  1)检查su命令是否存在
  通常要获取Root权限,是使用su命令来实现的,因此可以通过检查这个命令是否存在,来判断运行环境是否Root
  2)检查Android属性
  检查ro.debuggable、ro.secure 两个属性是否为true,为true的话APP所运行环境很可能是Root环境
  3)检查特定路径是否有写权限
  具体路径包括:/system、/system/bin、/system/sbin、/system/xbin、/vendor/bin、/sys、/sbin、/etc、/proc、/dev
  通过mount命令确认对应分区的权限是否为"rw" adb shell mount | grep -w /sysfs on /sys type sysfs (rw,seclabel,relatime)
  下面从几种被应用程序广泛使用的检测技术开始说起,如果设备已经root,会增加一些新的文件,所以可以通过检测这些文件是否存在来判断,还有一些开发者通过检查能否执行只有root权限下才能运行的命令来判断,当然还有一些其他的手段 之前几年最流行(现在已停止维护许久了)的root工具是Superuser.apk,是一个被广泛使用的用来root安卓设备的软件,所以可以检查这个app是否存在,但在检测之前,我们先解决安装包错的情况
  解决方案 :
  SuperSU 包含一个 su 可执行文件和一个 Superuser.apk,只需要把 SuperSU 提供的 su 可执行文件替换系统的 su 文件,并且给予权限 -rwsr-sr-x (6755) 即可。
  一般在实际设备上有两种方式替换文件: 手动将 SuperSU 的 su 文件替换系统文件,需要 Root 权限; 通过 Recovery 模式直接将 su 文件已补丁包的形式刷入。 对于模拟器来说,它没有 Recovery 模式, 是直接使用 img 镜像启动的,所以只能使用第一种方法
  官网下载:https://supersuroot.org/download/,选择 Recovery V2.82 Flashable.zip 进行下载,里面包含各个架构所需的 su 文件,以及 Superuser.apk 安装包 查看模拟器的 su 命令所在的路径 adb shell which su 打开一个cmd窗口,执行如下命令adb remount 是为了将 /system 挂载为可写,然后我们将su进行替换 adb root adb remount adb push su /system/bin/su
  备注 :对于 Android 5.0 版本及之上的设备来说,需要使用 su.pie 文件,它是使用 -fPIE 标记编译的位置无关的可执行文件,具有地址空间随机化特性 再次打开一个cmd窗口执行如下命令 adb shell chmod 6755 /system/bin/su ls -al /system/bin/su su --install su --daemon& setenforce 0
  上面命令解释如下: 设置权限,使 SuperSU 提供的 su 可执行文件能够被所有应用执行; 初始化安装 su; 设置 su 守护进程; 关闭 SELinux 安全策略,解除 Root 权限的限制
  点击重启按钮可能会卡住,直接关闭模拟器重启即可,再次打开不会再提示su被占用的情况 adb shell ls -l /system/app/ | grep "Super"
  SuperSU 工作原理: daemonsu 为 su 启动的守护进程 现在第三方应用开始调用 su 命令,请求申请 Root 权限 su 是一个可执行文件,内部与 daemonsu 进行通信,发送执行命令请求 daemonsu 创建 sush 子进程,sush 进程使用 am(Activity Manager)命令启动 Superuser 应用,请求授权,出现用户授权界面 授权通过,Superuser 应用通过 socket 返回给 sush 用户授权结果,通过授权,则 sush 选择是否执行请求的命令
  SuperSU 工作原理时序图如下:
  SuperSu,通过recovery将其文件刷入系统即可使用。然而现在安卓会对系统的完整性进行验证,这一方法就未必行得通了。更何况很多厂商会对bootloader进行封锁,这意味着安卓各分区无法被私自修改,不能通过fastboot来刷入Su文件,也无法使用第三方的recovery,su的刷入也就无从谈起,root权限成为了遥不可及的传说。
  大家可以尝试Magisk工具,跟SuperSu一样的,但比SuperSu要香多了,而且SuperSu在2021年时已经不在进行维护了。
  下载地址:https://github.com/topjohnwu/Magisk
  Magisk 是一套用于定制 Android 的开源软件,支持高于 Android 5.0 的设备。 一些突出特点: MagiskSU:为应用程序提供 root 访问权限 Magisk Modules:通过安装模块修改只读分区 MagiskBoot:最完整的安卓启动镜像解包和重新打包工具 Zygisk:在每个 Android 应用程序的进程中运行代码 还可以搜索一些特殊的package,比如下图所展示的 pm list packages | grep "shell"
  有一些应用程序只能在root的设备上运行,所以检查他们是否存在也是一个不错的方法。比如众所周知的Busybox: busybox pwd
  运行"su"和"id",然后查看uid来检查 su id
  正式绕过root检测
  安装了SuperSU的检测:
  没安装SuperSU的检测:
  测试代码如下 : CheckRoot.java代码
  package com.example.testpoc4;  import android.util.Log; import java.io.File;  public class CheckRoot {     // 定义TAG常量     private  static String TAG = CheckRoot.class.getName();       // 检查判断是否存在SuperSU.apk文件,存在的话就是root,并在日志里打印一行信息,若不是返回false     public  static boolean checkSuperuserApk() {         try{             File file = new File("/system/app/SuperSU/SuperSU.apk");             if (file.exists()){                 Log.w(TAG, "/system/app/SuperSU/SuperSU.apk exist");                 return  true;             }         }catch (Exception e){         }         return false;     }  }  MainActivity.java代码
  package com.example.testpoc4;  import androidx.appcompat.app.AppCompatActivity;  import android.os.Bundle; import android.widget.TextView;  public class MainActivity extends AppCompatActivity {      @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);          boolean root = CheckRoot.checkSuperuserApk();         ((TextView) findViewById(R.id.text)).setText("Device Root:"+root);     } } activity_main.xm代码
  <?xml version="1.0" encoding="utf-8"?>         
  为了绕过这个检查,让我们将应用程序"Superuser.apk"重命名为"Superuser0.apk",先将/system目录从可读变成可写先使用remout,如下图所示
  将其名称改变即可绕过,再次运行检测该软件的程序时已不再提示为true
  0x03 常用root检测方法
  常规检测方法:检测(test-keys(测试版)、release-keys(发布版))系统是否测试版、检测提权为root的应用包名、检测常用或非常用su存在的目录、检测是否使用which 查找su、检测Busybox是否存在、检测/data目录,是否有读写权限等,下面列出常用的一些检测方法的介绍: 查看系统是否测试版
  可以查看发布的系统版本,是test-keys(测试版),还是release-keys(发布版)。     public static boolean checkDeviceDebuggable() {         String buildTags = android.os.Build.TAGS;         if (buildTags != null && buildTags.contains("test-keys")) {             Log.i(TAG, "buildTags=" + buildTags);             return true;         }         return false;     }
  实际情况下,某些厂家的正式发布版本,也是test-keys,可能大家对这个标识也不是特别注意吧。所以具体是否使用,要多考虑考虑。 检查是否存在Superuser.apk
  Superuser.apk是一个被广泛使用的用来root安卓设备的软件,所以可以检查这个app是否存在。
  检测方法如下:     public  static boolean checkSuperuserApk() {         try{             File file = new File("/system/app/SuperSU/SuperSU.apk");             if (file.exists()){                 Log.w(TAG, "/system/app/SuperSU/SuperSU.apk exist");                 return  true;             }         }catch (Exception e){         }         return false;     } 检查su命令
  su是Linux下切换用户的命令,在使用时不带参数,就是切换到超级用户。通常我们获取root权限,就是使用su命令来实现的,所以可以检查这个命令是否存在。
  检测在常用目录下是否存在su:     public static boolean checkRootPathSU() {         File f = null;         final String kSuSearchPaths[] = {"/system/bin/", "/system/xbin/", "/system/sbin/", "/sbin/", "/vendor/bin/"};         try {             for (int i = 0; i < kSuSearchPaths.length; i++) {                 f = new File(kSuSearchPaths[i] + "su");                 if (f != null && f.exists()) {                     Log.i(TAG, "find su in : " + kSuSearchPaths[i]);                     return true;                 }             }         } catch (Exception e) {             e.printStackTrace();         }         return false;     } 执行su,看能否获取到root权限
  执行这个命令su。这样,系统就会在PATH路径中搜索su,如果找到,就会执行,执行成功后,就是获取到真正的超级权限了。     public static synchronized boolean checkGetRootAuth() {         Process process = null;         DataOutputStream os = null;         try {             Log.i(TAG, "to exec su");             process = Runtime.getRuntime().exec("su");             os = new DataOutputStream(process.getOutputStream());             os.writeBytes("exit ");             os.flush();             int exitValue = process.waitFor();             Log.i(TAG, "exitValue=" + exitValue);             if (exitValue == 0) {                 return true;             } else {                 return false;             }         } catch (Exception e) {             Log.i(TAG, "Unexpected error - Here is what I know: "                     + e.getMessage());             return false;         } finally {             try {                 if (os != null) {                     os.close();                 }                 process.destroy();             } catch (Exception e) {                 e.printStackTrace();             }         }     } 访问/data目录,查看读写权限
  在Android系统中,有些目录是普通用户不能访问的,例如 /data、/system、/etc 等。 我们就已/data为例,来进行读写访问。本着谨慎的态度,我是先写入一个文件,然后读出,查看内容是否匹配,若匹配,才认为系统已经root了。     public static synchronized boolean checkAccessRootData() {         try {             Log.i(TAG, "to write /data");             String fileContent = "test_ok";             Boolean writeFlag = writeFile("/data/su_test", fileContent);             if (writeFlag) {                 Log.i(TAG, "write ok");             } else {                 Log.i(TAG, "write failed");             }              Log.i(TAG, "to read /data");             String strRead = readFile("/data/su_test");             Log.i(TAG, "strRead=" + strRead);             if (fileContent.equals(strRead)) {                 return true;             } else {                 return false;             }         } catch (Exception e) {             Log.i(TAG, "Unexpected error - Here is what I know: "                     + e.getMessage());             return false;         }     }      //写文件     public static Boolean writeFile(String fileName, String message) {         try {             FileOutputStream fout = new FileOutputStream(fileName);             byte[] bytes = message.getBytes();             fout.write(bytes);             fout.close();             return true;         } catch (Exception e) {             e.printStackTrace();             return false;         }     }      //读文件     public static String readFile(String fileName) {         File file = new File(fileName);         try {             FileInputStream fis = new FileInputStream(file);             byte[] bytes = new byte[1024];             ByteArrayOutputStream bos = new ByteArrayOutputStream();             int len;             while ((len = fis.read(bytes)) > 0) {                 bos.write(bytes, 0, len);             }             String result = new String(bos.toByteArray());             Log.i(TAG, result);             return result;         } catch (Exception e) {             e.printStackTrace();             return null;         }     }
  将上述说的检测弄成代码做为检测,此处就不演示了,大家自行操作:
  CheckRoot.java代码: package com.example.testpoc4;  import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.util.ArrayList;  import android.util.Log;   public class CheckRoot {     // 定义TAG常量     private  static String TAG = CheckRoot.class.getName();      public static boolean isDeviceRooted() {         if (checkDeviceDebuggable()) {             return true;         }//check buildTags         if (checkSuperuserApk()) {             return true;         }//Superuser.apk         if (checkRootPathSU()) {             return true;         }//find su in some path         if (checkRootWhichSU()) {             return true;         }//find su use "which"         if (checkBusybox()) {             return true;         }//find su use "which"         if (checkAccessRootData()) {             return true;         }//find su use "which"         if (checkGetRootAuth()) {             return true;         }//exec su          return false;     }       // 检查判断是否存在SuperSU.apk文件,存在的话就是root     public  static boolean checkSuperuserApk() {         try{             File file = new File("/system/app/SuperSU/SuperSU.apk");             if (file.exists()){                 Log.w(TAG, "/system/app/SuperSU/SuperSU.apk exist");                 return  true;             }         }catch (Exception e){         }         return false;     }       public static boolean checkDeviceDebuggable() {         String buildTags = android.os.Build.TAGS;         if (buildTags != null && buildTags.contains("test-keys")) {             Log.i(TAG, "buildTags=" + buildTags);             return true;         }         return false;     }      public static boolean checkRootPathSU() {         File f = null;         final String kSuSearchPaths[] = {"/system/bin/", "/system/xbin/", "/system/sbin/", "/sbin/", "/vendor/bin/"};         try {             for (int i = 0; i < kSuSearchPaths.length; i++) {                 f = new File(kSuSearchPaths[i] + "su");                 if (f != null && f.exists()) {                     Log.i(TAG, "find su in : " + kSuSearchPaths[i]);                     return true;                 }             }         } catch (Exception e) {             e.printStackTrace();         }         return false;     }      public static boolean checkRootWhichSU() {         String[] strCmd = new String[]{"/system/xbin/which", "su"};         ArrayList execResult = executeCommand(strCmd);         if (execResult != null) {             Log.i(TAG, "execResult=" + execResult.toString());             return true;         } else {             Log.i(TAG, "execResult=null");             return false;         }     }      public static ArrayList executeCommand(String[] shellCmd) {         String line = null;         ArrayList fullResponse = new ArrayList();         Process localProcess = null;         try {             Log.i(TAG, "to shell exec which for find su :");             localProcess = Runtime.getRuntime().exec(shellCmd);         } catch (Exception e) {             return null;         }         BufferedWriter out = new BufferedWriter(new OutputStreamWriter(localProcess.getOutputStream()));         BufferedReader in = new BufferedReader(new InputStreamReader(localProcess.getInputStream()));         try {             while ((line = in.readLine()) != null) {                 Log.i(TAG, "–> Line received: " + line);                 fullResponse.add(line);             }         } catch (Exception e) {             e.printStackTrace();         }         Log.i(TAG, "–> Full response was: " + fullResponse);         return fullResponse;     }      public static synchronized boolean checkGetRootAuth() {         Process process = null;         DataOutputStream os = null;         try {             Log.i(TAG, "to exec su");             process = Runtime.getRuntime().exec("su");             os = new DataOutputStream(process.getOutputStream());             os.writeBytes("exit ");             os.flush();             int exitValue = process.waitFor();             Log.i(TAG, "exitValue=" + exitValue);             if (exitValue == 0) {                 return true;             } else {                 return false;             }         } catch (Exception e) {             Log.i(TAG, "Unexpected error - Here is what I know: "                     + e.getMessage());             return false;         } finally {             try {                 if (os != null) {                     os.close();                 }                 process.destroy();             } catch (Exception e) {                 e.printStackTrace();             }         }     }      public static synchronized boolean checkBusybox() {         try {             Log.i(TAG, "to exec busybox df");             String[] strCmd = new String[]{"busybox", "df"};             ArrayList execResult = executeCommand(strCmd);             if (execResult != null) {                 Log.i(TAG, "execResult=" + execResult.toString());                 return true;             } else {                 Log.i(TAG, "execResult=null");                 return false;             }         } catch (Exception e) {             Log.i(TAG, "Unexpected error - Here is what I know: "                     + e.getMessage());             return false;         }     }      public static synchronized boolean checkAccessRootData() {         try {             Log.i(TAG, "to write /data");             String fileContent = "test_ok";             Boolean writeFlag = writeFile("/data/su_test", fileContent);             if (writeFlag) {                 Log.i(TAG, "write ok");             } else {                 Log.i(TAG, "write failed");             }              Log.i(TAG, "to read /data");             String strRead = readFile("/data/su_test");             Log.i(TAG, "strRead=" + strRead);             if (fileContent.equals(strRead)) {                 return true;             } else {                 return false;             }         } catch (Exception e) {             Log.i(TAG, "Unexpected error - Here is what I know: "                     + e.getMessage());             return false;         }     }      //写文件     public static Boolean writeFile(String fileName, String message) {         try {             FileOutputStream fout = new FileOutputStream(fileName);             byte[] bytes = message.getBytes();             fout.write(bytes);             fout.close();             return true;         } catch (Exception e) {             e.printStackTrace();             return false;         }     }      //读文件     public static String readFile(String fileName) {         File file = new File(fileName);         try {             FileInputStream fis = new FileInputStream(file);             byte[] bytes = new byte[1024];             ByteArrayOutputStream bos = new ByteArrayOutputStream();             int len;             while ((len = fis.read(bytes)) > 0) {                 bos.write(bytes, 0, len);             }             String result = new String(bos.toByteArray());             Log.i(TAG, result);             return result;         } catch (Exception e) {             e.printStackTrace();             return null;         }     } }
  MainActivity.java代码 package com.example.testpoc4;  import androidx.appcompat.app.AppCompatActivity;  import android.os.Bundle; import android.widget.TextView;  public class MainActivity extends AppCompatActivity {      @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);           boolean deviceRoot = CheckRoot.isDeviceRooted();         ((TextView) findViewById(R.id.text)).setText("Device Root:" +deviceRoot);     } }
  参考链接 :
  https://blog.csdn.net/weixin_47883636/article/details/108687059
  https://www.jianshu.com/p/8a9b84df5018
  https://blog.chrxw.com/archives/2020/07/18/1301.html
  https://bbs.pediy.com/thread-263203.htm
  https://github.com/yunshuipiao/Potato/issues/53
  https://juejin.cn/post/6844903733248131079
  https://github.com/DeFuture/Superuser
  https://blog.csdn.net/quanshui540/article/details/48242459
  https://github.com/Labmem003/anti-counterfeit-android
  https://github.com/t0thkr1s/allsafe
  你以为你有很多路可以选择,其实你只有一条路可以走

曝张隆被四川女篮开除球队集体活动李梦未亮相彻底淡出公众视线北京时间4月7日,日前四川女篮组织了一趟元谋研学行的集体活动,李梦依然没有亮相,而有球迷爆料,张隆已经被四川女篮开除了。四川女篮在夺冠之后,俱乐部也是好好犒劳了队员,组织全队去云南不服不行,小因扎吉两次选择用欧冠续命,杯赛教练名不虚传入口1高清免费观看全球体育赛事入口2头条搜索857体育欧冠14决赛首回合,赛前被普遍看衰的国际米兰20击败本菲卡,两回合比赛占据先机,基本锁定一个欧冠4强名额。小因扎吉强势续命!本浓眉最后一防我和詹姆斯搞混了老詹加时还能打回来直播吧4月12日讯今日NBA附加赛,湖人在主场以108102加时击败森林狼。赛后,詹姆斯和浓眉接受了记者采访。谈到最后对康利犯规,浓眉说道这应该是我和詹姆斯去防(但我们俩搞混了),哈姆康利展现出的篮球水平太了不起了我以为我在看灰熊打球呢直播吧4月12日讯今日NBA附加赛,湖人坐镇主场108102加时战胜森林狼,晋级季后赛。赛后,湖人主帅哈姆在接受采访时谈到森林狼老将康利,哈姆表示迈克康利,伙计,我以为我在观看孟菲湖南湘煤摔跤队庞倩玉亚锦赛摘金下一站世锦赛庞倩玉时隔7年再夺亚锦赛冠军。图中国摔跤队红网时刻新闻4月12日讯(记者周雨墨)4月11日,2023亚洲摔跤锦标赛在哈萨克斯坦进行了女子自由式摔跤首个比赛日的较量。中国队选手湖南湘前国脚赵明剑我现在月薪一万多,连房租都不够,继续踢是因为热爱前国脚赵明剑日前加盟中甲球队石家庄功夫,他在直播中透露自己目前在球队的月薪仅为一万多,连租房都不够。上个赛季赵明剑还被中超的上海申花租借到中甲的昆山FC效力。赵明剑和杨旭视频连线435胜!让我恶心!等等,5年2。5亿不是你给的?35胜47负,奇才又经历了一个失败的赛季。总经理谢泼德说道去年夏季联赛,有人预测奇才只能拿到35胜。现在我们真的是35胜,这让我非常恶心!我们内部本来有更高的目标,我们必须变得更好9中1,4中0!湖人晋级之夜揪出最大水货,詹姆斯差点被他拖垮了北京时间4月12日,湖人队在附加赛108102击败森林狼,昂首晋级季后赛首轮。本场比赛湖人队三军用命,詹姆斯全场打了45分钟,戴维斯打了43分钟,刚刚复出的施罗德也打了33分钟。湖国足爆猛料!下午2点,足协奥运会双线破局,财务问题再突出国足持续到现在已经进行了一个多天的一个反腐运动,目前也是没有一个暂停的一个态度,而这也是让大家看到了足协的未来,毕竟咱们的足协已经有十几年的时间没有重见天日了,准确的说也是被骂了十巴特勒希罗各取24分,热火狂胜76人在今日的常规赛中,热火客场129101大胜76人。双方开局不断交替领先,76人一度以2322领先1分。随后马丁三分命中巴特勒连得4分,马丁中投勒夫连中三分,热火突然一波162反超了主场迎战拜仁,曼城主帅瓜迪奥拉不玩花活就可看好文羊城晚报全媒体记者刘毅曼城是本赛季欧冠夺标第一热门,拜仁慕尼黑是第二热门,因为14决赛抽签不设种子队,两支豪门球队不幸抽到一起。4月12日(央视体育3时直播),曼城将在首回合主场
这届双十一为啥不行?年轻人不花钱对抗环境内卷11月财经新势力文徐梦豪编辑G3007刚过去的双十一,就像一面透光镜,反映出社会经济生活的方方面面。有人说,这届双十一没有GMV了,各家电商平台在流量焦虑中告别了对纯数据的追求和比中国游客去泰国游玩,体验毒奶服务后,直呼很爽,还想来一次中国的小伙伴喜欢到处旅游观光,他们在外出游玩时,可能会欣赏当地的特色。(此处已添加小程序,请到今日头条客户端查看)了解当地的风土人情,比如我国带游客在泰国体验毒奶服务之后,大声欢呼网红唐小鱼秀身材穿紧身旗袍,关门动作画面引争议,网友封杀一招鲜,吃遍天。视频直播带货的兴起,催生了不少网红。为了制造流量,为了吸引更多人的关注,有些人一直在不断地试探底线。网红唐小鱼就是其中之一。近日,唐小鱼在小号上更新的视频,就引起了50大胜!中国女足联赛霸主又赢了,16场轰47球,提前问鼎三冠王正文12日进行的女超联赛第16轮一场较量中,国内霸主武汉车谷江大女足又赢了,50横扫广东梅州客家。女超16场角逐下来,卫冕冠军武汉女足合计狂轰47球,并于第14轮的赛后实现提前成功沙特改用人民币结算,动摇美元霸权?人民币的份额会超过日元?自从俄乌冲突发生以来,全球能源供应秩序被打破重整,石油天然气价格飙升,欧美各国通货膨胀。在此背景下,美国和欧佩克(OPEC)的领头羊沙特阿拉伯频繁开会,要求欧佩克石油增产以缓和飙升中国助听器渗透率不足5,国外巨头争相涌入,背后的市场逻辑是什么?中国经济周刊记者郭志强湖南报道10月24日晚8点,双11预售正式启动,天猫数据显示预售首小时,助听器预售金额同比增长约1400,销售火爆可见一斑。助听器属于康复医疗器械中的认知言语最高降价2万元!谁把小鹏逼上绝路?前两天,有位知名汽车博主爆料,除了G9,其余三款小鹏车型,尾款会有最高2万元减免,同时部分长库龄车型将进行特价销售。针对消息,小鹏汽车相关负责人表示,调整了权益结构,具体等官方通知今年荣耀X30性价比怎样?头条创作挑战赛荣耀X30发布与去年12月,快接近一年了,目前价格直降至1499元,那这款手机性能如何?手机屏幕与处理器荣耀X30屏幕是6。81英寸LCD中置挖孔屏,屏幕分辨率是23拉美物流专线,助力卖家掘金跨境电商蓝海市场全球疫情常态化使许多消费者都改变了购物习惯,跨境电商乘势而起,拉美的电子商务也借势发展壮大,成为跨境电商追逐的新蓝海市场。拉美地区全球零售电商销售额增势迅猛,跻身全球电商销售额增长6个不为人知的黑科技网站,我可以玩一整天一台电脑一个浏览器,就可以探索超多的黑科技。今天给大家分享6个不为人知的黑科技网站,我可以玩一整天,看完你也可以去试试。01精准云工具httpsjingzhunyun。com这是一人死后,细胞竟然还能存活!它们会发生什么变化?精子细胞在人体死亡后的36小时还会有活力,也能传递基因,只要存取得当,还能孕育出一个健康的宝宝,但是,人死后,其他细胞就没这样的好命,它们有的忙着自相残杀,也有的忙于拯救一下你这个