一、情况简述
随着 Android 在全球移动设备市场上不断地“攻城略地”,其应用也越来越广泛。它的身影不仅仅在手机上出现,很多机顶盒、POS机、对讲机等设备也搭载了 Android 。近日接触到了一款类似对讲机的设备就是这样:与数年前那种功能单一的设备不同的是,这款设备采用的并不是专用的SoC ,而是在类似手机的硬件平台上搭载了一个客制化的 Android 系统,而后在此基础上安装了运营商定制的对讲程序,以实现对讲功能。然而虽然搭载了 Android 系统,其系统却经过了高度精简,界面仅有拨号、对讲、设置等简单功能,而至于浏览器、输入法等常用功能却都没有,大大限制了其业务(娱乐)功能。
但它毕竟搭载的是 Android 系统,理论上来讲,是可以通过在系统上安装相应的 APK 来实现有关功能的。可是对讲机的界面上并没有文件管理器,怎么安装第三方 APK 呢?我通过 “设置” 功能,打开了“开发者模式”,想要通过 ADB 来解决这个问题。连接上设备之后,在CMD窗口中输入以下指令:
adb install browser.apk
然而以上命令却失败了,CMD窗口中显示:ADB_INSTALL_FAILED (安装失败) ,目标设备界面上toast提示一闪而过:“禁止安装第三方应用”,brower.apk 也没有出现在应用列表中。这说明,设备提供商在该设备中限制了第三方应用的安装。那么,就让我们从本文开始,踏上解除限制的“破解之旅”吧。
二、原理分析
在移动设备的 Android 系统开发中,限制用户安装第三方 APP 已经成为了常见的操作。其目的一是保证“专机专用”,确保本公司生产的产品只能用于运行本公司提供的业务;目的二也是为了安全考量,避免安装来路不明的APP 导致诸如信息泄露、远程控制等安全风险。
首先我们先简略了解下第三方的 Android 应用是如何安装的。在没有系统内置应用市场的情况下(Google Play ,华为应用商店 等),主要有以下两种安装方式:
1、通过下载或USB等方式,将应用安装包传输到手机中,然后点击安装
2、通过 ADB 方式,在命令行下进行安装(如前文所述)
其中 第一种方式需要调用系统内置的packageinstaller.apk来处理应用安装,移动设备上会显示安装界面。第二种方式则是通过命令行的方式(如adb install 或 pm install)处理,移动设备上不显示安装界面。
而深入到应用安卓的代码实现层面,对安卓系统有所了解的同学应该知道是Package Manager Service(简称PMS)负责应用的安装。得益于 Android 的开源特性,我们可以从其源代码文件“frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java” 里,一窥其安装流程 (关于 Package Manager Service 这里就不展开讲了,想要深入了解的同学可以阅读 Android Framework 相关的资料)。为了便于理解,我找到了一张图,清晰展示了 安卓应用的安装流程(图片来自sdkdlwk的博客
https://blog.csdn.net/sdkdlwk/article/details/102451279):
从上图我们可以得知,虽然安装第三方应用的方式有所不同,但最终却是殊途同归,归根结底都要走以下流程(如上图底部最后三个框所示):
PMS.scanPackageTracedLI ( ) => PMS.scanPackageLI ( )=> PMS.scanPackageDirtyLI ( )
上面我们了解了应用的安装流程,那么相应的,限制安装第三方 APP 的主要方式有以下几个方式:
1、在 系统内置的 packageinstaller.apk 上做手脚。有的开发者学艺不精,把 packageinstaller.apk 一删了之;或者对其进行魔改,不允许安装白名单以外的应用。很显然,这种方式只能阻止上文所说的“方法1”(点击安装包的安装方式),并无法阻挡 通过“方法2”(使用 adb install 的方式)。
2、修改系统框架,在 PackageManagerService 上做文章。如果在 Android 系统的定制化过程中修改了 Package Manager Service 的源码,那么就很难使用 adb install 的方式绕过。这样的话,只有通过逆向 Framework 的代码,找到系统作者留下的“暗桩”,然后将其拔除。(在实践中我也试过取得 Root 权限之后手动复制APK安装包到特地目录下 ,变相实现了应用的“安装”。但不够完美,有一定局限性。)
三、逆向破解
通过以上分析,笔者认为该设备就是修改了Android Framework 中的 Package Manager Service ,从而导致第三方应用程序安装失败。虽然能在取得设备的 Root 权限之后,手动复制APK安装包到 system/app 目录下 ,变相实现第三方应用的“安装”,但这种方式很有局限性,仅限于不依赖动态链接库的安装包。有动态链接库的安装包,就需要提取出其自带的 *.so 文件,放到 system/lib 目录下,以提供动态链接库 ,不然就会报错,十分麻烦。所以,最根本的解决方式,还是通过对系统的逆向分析,来找到问题的真正所在。
1、提取系统镜像
当时我手头只有一台设备,没有 ROM镜像,也没有刷机包。那怎么办呢?只能从设备的系统里提取 system 分区了。但没有 root 权限,也很难提取啊。所以第一步先设法获取 root 权限。adb 连接手机,进入shell,看看用的谁家的处理器:
adb shell cat /proc/cpuinfo
哦,MT67XX 处理器,原来是联发科家的呀…… 翻翻手头的漏洞军火库,正好,MTK处理器有一个漏洞,(CVE-2020-0069) ,可以通过一个叫 mtk-su 实现提权,获取MTK设备的 Root 权限:
adb push d:\mtk-su data/local/tmp adb shell chmod 777 data/local/tmp/mtk-su ./data/local/tmp/mtk-su
有了 Root 权限之后就好办了,我们使用 df 命令,找到 system 分区所对应的块设备,方便我们把它弄到本地:
从图中可知, /dev/block/mmcblk0p31 就是 system 分区的存放地址,我们使用 dd 命令把它 dump 下来:
dd if=/dev/block/mmcblk0p25 of=/data/local/tmp/system.img
稍等一会,系统镜像 system.img 就被保存到 /data/local/tmp 目录了,我们通过 adb 把system的镜像拉取到电脑上:
adb pull /data/local/tmp/system.img
这样我们就提取出了这台设备的系统镜像,下一步就可以从镜像中提取 Framework 的相关文件进行逆向分析啦。
2.反编译 Framework
system.img 其实相当于一个压缩包,我们目的是找 Framework 的相关文件,所以我们直接在硬盘上解压就行:
解压后的文件结构如上图。其中 framework 文件夹里面就是安卓框架相关的文件。联想到之前安装应用的时候弹出了”禁止安装第三方应用” 的 toast ,因为系统服务使用的资源位于/system/framework/ framework-res.apk 中,字符串也不例外,我们就先反编译 framework-res.apk , 并搜索该字符串(反编译过程略,可使用 jadx 、ApkIDE 等工具),最终在 \res\values\String.xml 文件中找到该字符串:
字符串的 string name 是 “prohibited_install” ,我们通过查找该关键字,在 \res\values\public.xml 中找到
prohibited_install 的资源 id 为 0x01030503,这是该文本在 framework 中被调用时的唯一索引。
下一步,就要反编译 Package Manager Service 这个服务所在的框架代码了。一般来说, PMS 的源码编译完之后都会存在于 services.jar 之中,但本次实验中解压出来的 services.jar 仅有 1KB ,反编译后也根本没有任何可用的信息,这是为什么? 带有 PMS 的文件又在哪里呢? 其实(划重点),现在许多Android手机的ROM包在生成过程中都启用优化,把jar文件抽空,生成odex/oat和vdex文件,以在运行时省掉编译时间。如果想对这些jar进行修改,就要修改它们所对应的odex或者oat文件。相应的,在本次实验中,我们需要反编译的文件是:\system\framework\oat\arm\services.odex。
这里要注意,由于我们反编译的是 Framework ,并不能像反编译普通 apk 一样直接用工具开整,而是要先将odex/oat和它的依赖复制到同一目录下这里有些麻烦,要确定好相应系统的 API ,一定要细心,如果在反编译odex/oat的时候,没有找到依赖,就会报错。(具体配置略,可参考:https://www.cnblogs.com/luoyesiqiu/archive/2004/01/13/11802947.html)
反编译完毕后,在生成的smali文件中 搜索前面我们搜寻到的“神秘代码”:1030503(不要加前缀 0x0 ,反编译出的源码里没有前缀的0),于 out\com\android\server\pm\PackageManagerService$InstallParams.smali 中定位出具体代码位置(第508行):
现在让我们开始分析代码吧!在源代码前没有任何秘密,从第455行开始,这段代码意思是:
455行 if-eqz v0, :cond_3b //如果v0的值不为 0 ,则跳到 cond_3b 处(第472行)
……
472行 cond_3b
……
508行~520行 toast 显示 1030503 对应的那串字符串
也就是说,第三方应用能否成功安装的关键,就在第 455 行之前的一系列判断。我们继续往上回溯,看到:
费劲周折,总算找到了该限制的命门所在——也就是说,第三方应用能否正常安装的关键,在于系统属性
“sys.packageinstall.status” 是否等于 1。这就简单了,查看设备的 build.prop (该设备的系统属性值存放于此处),发现并没有该属性。在里面添上一行“ sys.packageinstall.status = 1 ”,就完成了对该设备的破解!
翻来覆去研究了好久,发现解决问题的关键竟然就在0和1之间。前期大张旗鼓、费尽心力地去分析,只为最后能够轻描淡写地发出致命一击,也许这就是逆向工程的有趣之处吧!
参考资料:
Android应用安装过程及原理:https://blog.csdn.net/u012267215/article/details/88313259
Android odex,oat文件的反编译,回编译:https://www.cnblogs.com/luoyesiqiu/archive/2004/01/13/11802947.html
来源:freebuf.com 2021-01-30 16:35:31 by: 张召忠
请登录后发表评论
注册