*郑重声明:本文内容只为学习研究之用,如有人用于非法用途,产生的后果笔者不负任何责任。
设备:root过的Android7.1
在上篇文章中我们跟着沿着源码追溯了Android系统授权的过程,这篇文章记录下怎么绕过系统的校验。
可以看到检查权限时的一个分支为:
1 | // Root, system server get to do everything. |
也就是说,我们只需要将自己的app的uid伪装成0或者1000即可绕过授权,直接返回PackageManager.PERMISSION_GRANTED
.
在Android 8之前,uid判断在ActivityManager.checkComponentPermission
中。从Android 8开始,这段代码前置到了ContextImpl.java中:
1 | 1659 |
我们以新版本为例进行绕过实践。
修改uid之getuid
一开始我的思路是,既然设备已经root了,直接执行su
不就好了。实践证明不可行。
Runtime.exec(“su”)后 只是这个返回Process类的输出流其写入的命令行参数执行的环境才是uid=0的进程环境, 在此之外, 整个app 的uid并不是0, 而是不变。
那么,考虑利用su
直接修改记录了uid的/data/system.packages.list
,把对应的uid改为0。
代码如下:
1 | Process process = null; |
执行成功!
但是app读取的uid依然没变,说明判断权限时的UID不是从这个文件读取的。
ActivityCompat.checkSelfPermission()
中的调用语句:context.checkPermission(permission, android.os.Process.myPid(), Process.myUid())
可以看出uid是由Process.myUid()
返回的:
1 | public static final int myUid() { |
Os:
1 | /** |
Libcore类:
1 | 17package libcore.io; |
可知调用了BlockGuardOs对象的getUid()方法,实现在其父类ForwardingOs:
1 | public int getuid() { return os.getuid(); } |
这里的os即为传入的Posix对象,
1 | protected final Os os; |
Posix中:
1 | public native int getuid(); |
调用了Native方法。
http://androidxref.com/6.0.1_r10/xref/libcore/luni/src/main/native/libcore_io_Posix.cpp
1 | static jint Posix_getuid(JNIEnv*, jobject) { |
到这里,本该去看getuid()的实现,但是到这里就断了,没有找到。
没办法,只能回头看看uid是怎么产生的,看能不能在生成uid时动动手脚。
修改uid之生成uid
uid是应用安装时被分配的,具体过程的源码分析网上比较多,这里不具体说了。
涉及到的关键类PackageManagerService
,它负责解析AndroidManifest.xml,从中得到应用程序相关信息。
从PackageManagerService.main()
入手,它负责把PackageManagerService启动起来:
1 | public static PackageManagerService main(Context context, Installer installer, |
PackageManagerService
的构造方法是一个六百多行的方法,接下来我们详细分析。
首先,初始化一些保存各种信息的数据结构,其中比较关键的是Settings
对象。
1 | ...... |
Settings对象初始化时创建了一系列文件:
1 | 428 Settings(File dataDir, Object lock) { |
packages.xml
:存放系统中安装的所有应用的包名、前面、缓存目录、权限等信息
packages.list
:存放应用包名、uid、安装目录、gid等信息
接下来在PackageManagerService构造方法中,从SystemConfig中获取了权限配置,以及从SELinuxMMAC读物安装策略等。
然后,就该扫描系统中的apk文件了。
1 | synchronized (mInstallLock) { |
进入scanDirLI
:
1 | 5624 private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) { |
可以看到关键语句为:
scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,scanFlags, currentTime, null);
浏览并解析一个package:
1 | 5735 private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags, |
首先是解析APK包,进入PackageParser.parsePackage()
:
1 | 752 public Package parsePackage(File packageFile, int flags) throws PackageParserException { |
进入parseBaseApk
:
1 | 879 final int cookie = loadApkIntoAssetManager(assets, apkPath, flags); |
可以看到又调用了一个parseBaseApk
方法,但是参数变了,功能是解析Manifest:
1 | 1348 /** |
解析完apk包,调用了PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user);
:
1 | 6466 private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags, |
进入scanPackageDirtyLI
,给app分配uid过程就在其中:
1 | 6481 private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags, |
app包解析完,配置都获取到之后,就该写文件了,回到PackageManagerService
构造方法:
1 | 2288 updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags); |
1 | 8244 private void updatePermissionsLPw(String changingPkg, PackageParser.Package pkgInfo, |
1 | 8250 private void updatePermissionsLPw(String changingPkg, |
grantPermissionsLPw
, 经过一系列解析最终调用:
1 | 8600 for (int userId : changedRuntimePermissionUserIds) { |
1 | 5232 public void writeRuntimePermissionsForUserLPr(int userId, boolean sync) { |
那么,修改app的uid的方法考虑两种方案:
(1)反射调用writeRuntimePermissionsForUserLPr
,参数简单容易构造;
(2)修改最终写入的uid,packages.xml
目前还未实践出结果。
欲知后事如何,且听下回分解。
引用文献:
【2】https://blog.csdn.net/zhangyongfeiyong/article/details/52292347