抽丝剥茧:理解Android权限机制
前一段时间面试官问我Android在Linux的基础上,权限做了哪些改变。霹雳呱啦说了一堆,但是说着说着,始终感觉自己说的缺了点东西,自己理解还是不够到位
,而且网上的很多文章在原理上基本都是大同小异,很多地方都是语焉不详,所以,自己半看源码半看文章的总结了一下。
一:Android权限是什么
这个是老生长谈的东西了,说到底,权限就是告诉系统我需要干什么,从访问物理数据,到访问第三方组件均是。
二:权限赋予
权限的赋予可分为两类,一类是高层的组件,例如应用和系统服务,这一部分一般采用包管理器依赖进行管理,查询。而另一部分则是低层的组件,这一部分则是利用了传统了Linux DAC机制进行管理,一般不直接访问包管理器。
1:低层权限管理
这里包括但不限于设备文件,UNIX套接字,网络套接字。Android进程主要通过UID,GID以及一组补充的GID实现的。众所周知,Android沙箱是以UID为基础实现的,每个进程拥有自己独特的UID(先不考虑共享UID)。进程的UID和GID会由包管理器映射到应用程序的UID。而补充gid则为额外的权限。值得一提的是,内置权限到组的映射是静态的。部分源码如下
<permission name="android.permission.BLUETOOTH_ADMIN" >
<group gid="net_bt_admin" />
</permission>
<permission name="android.permission.BLUETOOTH" >
<group gid="net_bt" />
</permission>
<permission name="android.permission.BLUETOOTH_STACK" >
<group gid="bluetooth" />
<group gid="wakelock" />
</permission>
如上,android.permission.BLUETOOTH_ADMIN,android.permission.BLUETOOTH 和GID net_bt_admin组是关联的.而在 android_filesystem_config.h中,组和GID是想映射的,如下
static struct android_id_info android_ids[] = {
....
{ "shell", AID_SHELL, },
{ "cache", AID_CACHE, },
{ "net_bt_admin", AID_NET_BT_ADMIN, },
....
}
#define AID_NET_BT_ADMIN 3001 /* bluetooth: create any socket */
#define AID_NET_BT 3002 /* bluetooth: create sco, rfcomm or l2cap sockets */
#define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */
#define AID_NET_RAW 3004 /* can create raw INET sockets */
#define AID_NET_ADMIN 3005 /* can configure interfaces and routing tables. */
#define AID_NET_BW_STATS 3006 /* read bandwidth statistics */
可得android.permission.BLUETOOTH_ADMIN映射的GID的3001。总而言之,包管理器在读取platfrom.xml时,并维护一个权限到GID的列表。在对一个安装中的包进行授权时,包管理器会检查每个权限是否有对应的GID。如果有,则加入在补充GID列表。当然,到这里只是确定了进程需要赋予哪些额外的gid。
并没有说怎么赋权的,这里要谈到一个叫zygote的进程,顾名思义,当Android启动新进程的时候,为了减少程序所需内存以及加快启动时间,Android会直接fork()zygote进程,并执行Android特有的函数进行分化而不执行固有的exec函数。简化代码如下(android / platform / dalvik / 7033bed / . / vm / native / dalvik_system_Zygote.cpp forkAndSpecializeCommon()):
pid = fork();
if(pid ==0 ){
err = setgroupsIntarray(gids); //设置补充gid
err = setrlimitsFromArray(rlimits); //设置资源限制
err = setresgid(gid, gid, gid); //设置实际用户/组id
err = setresuid(uid, uid, uid); //设置有效用户/组id
err = setCapabilities(permittedCapabilities, effectiveCapabilities); //设置进程权能
err = set_sched_policy(0, SP_DEFAULT); //设置调度策略
err = setSELinuxContext(uid, isSystemServer, seInfo, niceName); //SElinux
}
2.高层权限管理
在仔细讲解之前,先来看看包管理器所维护的安装程序包核心数据库,这个数据库以xml文件的形式放进了/data/system/packages.xml里,先来看看里面究竟有些什么。
<package name="com.android.protips" codePath="/system/app/Protips" nativeLibraryPath="/system/app/Protips/lib" flags="" ft="1560a280490" it="1560a280490" ut="1560a280490" version="" userId="">
<sigs count="">
<cert index="" />
</sigs>
<proper-signing-keyset identifier="" />
<signing-keyset identifier="" />
</package>
<package name="com.android.launcher" codePath="/system/priv-app/Launcher2" nativeLibraryPath="/system/priv-app/Launcher2/lib" flags="" ft="1560a29ae58" it="1560a29ae58" ut="1560a29ae58" version="" userId="">
<sigs count="">
<cert index="" key="308204a1406035504071302fc58d017971bd0f6b52c262d70819d191967e158dfd3a2c7f1b30fa1eaafc2a556f84" />
</sigs>
<proper-signing-keyset identifier="" />
<signing-keyset identifier="" />
</package>
<package name="com.android.widgetpreview" codePath="/data/app/WidgetPreview" nativeLibraryPath="/data/app/WidgetPreview/lib" flags="572996" ft="15bbc858e28" it="1560a27f8d8" ut="1560a27f8d8" version="22" userId="10052">
<sigs count="1">
<cert index="0" />
</sigs>
<perms>
<item name="android.permission.READ_EXTERNAL_STORAGE" />
<item name="android.permission.WRITE_EXTERNAL_STORAGE" />
</perms>
<proper-signing-keyset identifier="2" />
<signing-keyset identifier="2" />
</package>
从上面可以发现,这里包括了安装路径,版本号,签名证书,每个包的权限。上层的管理都是通过和包管理器和这个数据库进行交互的。由于组件不能在运行时改变权限,所以权限执行检查都是静态的。但它的执行一般分为两类,一类是静态,另一类是动态的。静态执行和动态执行流程大致相同:Binder.getCallingUid()和Binder.getCallingPid()获取调用者的UID和PID,然后利用UID映射包名,再获得相关权限。如果权限集合中含有所需权限即启动,否则抛出SecurityException异常。
三:共享UID(补充)
使用相同的密匙签发的Android应用可以使用共同的UID运行 ,并且可以运行在同一进程中。这个属性可以简单通过在AndroidManifest.xml的根元素中添加shareUserId属性即可开启,但是不能在已安装的应用里添加该属性,这只会导致它修改自身uid,以至于失去对自身文件的访问权限。
shareid内置了以下几种:
android.uid.system(SYSTEM_UID,1000)
android.uid.phone(PHONE_UID,1001)
android.uid.bluetooth(BLUETOOH_UID,1002)
android.uid.log(LOG_UID,1007)
android.uid.nfc(NFC_UID,1027)
它们在系统引导时自动添加。具有相同userid的进程,可以访问相同的系统资源,还可以对统一资源的组件进行特殊访问控制。
抽丝剥茧:理解Android权限机制的更多相关文章
- 全方位理解Android权限之底层实现概览
0000 这个阶段搞了很多和Android文件权限相关的问题,虽然一知半解,但也算是对Android权限机制有一些自己的理解.遂将这些内容整理出来.因为权限这部分涉及到的内容很多,故将知识分为几块内容 ...
- 理解Android安全机制
本文从Android系统架构着手,分析Android的安全机制以SE Android,最后给出一些Android安全现状和常见的安全解决方案. 1.Android系统架构 Android采用分层的系统 ...
- Android权限机制
Android系统是运行在Linux内核上的,Android与Linux分别有自己的一套严格的安全及权限机制, 很多像我这样的新手,尤其是习惯了windows低安全限制的用户,很容易在这方面弄混淆,下 ...
- 理解 Android Binder 机制(一):驱动篇
Binder的实现是比较复杂的,想要完全弄明白是怎么一回事,并不是一件容易的事情. 这里面牵涉到好几个层次,每一层都有一些模块和机制需要理解.这部分内容预计会分为三篇文章来讲解.本文是第一篇,首先会对 ...
- 深入理解Android消息机制
在日常的开发中,Android 的消息机制作为系统运行的根本机制之一,显得十分的重要. 从 Handler 发送消息开始 查看源码,Handler的post.send方法最终都会走到 public f ...
- 深入理解 Android 消息机制原理
欢迎大家前往腾讯云社区,获取更多腾讯海量技术实践干货哦~ 作者:汪毅雄 导语: 本文讲述的是Android的消息机制原理,从Java到Native代码进行了梳理,并结合其中使用到的Epoll模型予以介 ...
- 深入理解Android IPC机制之Binder机制
Binder是Android系统进程间通信(IPC)方式之一.Linux已经拥有的进程间通信IPC手段包括(Internet Process Connection): 管道(Pipe).信号(Sign ...
- Android Handler的使用示例:结合源码理解Android Handler机制(一)
什么是Handler? Android 的官方解释: 文档分节1:A Handler allows you to send and process Message and Runnable objec ...
- Android权限管理之Permission权限机制及使用
前言: 最近突然喜欢上一句诗:"宠辱不惊,看庭前花开花落:去留无意,望天空云卷云舒." 哈哈~,这个和今天的主题无关,最近只要不学习总觉得生活中少了点什么,所以想着围绕着最近面试过 ...
随机推荐
- [洛谷P1707] 刷题比赛
洛谷题目连接:刷题比赛 题目背景 nodgd是一个喜欢写程序的同学,前不久洛谷OJ横空出世,nodgd同学当然第一时间来到洛谷OJ刷题.于是发生了一系列有趣的事情,他就打算用这些事情来出题恶心大家-- ...
- Ubuntu12.04 SVN安装过程
一.安装SVN和配置SVN 1.安装SVN apt-get install subversion 2.创建SVN目录,项目目录和配置文件目录 mkdir /var/svn mkdir /var/svn ...
- K-近邻(KNN)算法
1,KNN算法对未知类别属性的数据集中的每个点依次执行以下操作: 计算已知类别数据集中的点与当前点之间的距离; 按照距离递增排序; 选取与当前点距离最小的k个点; 确定前k个点所在类别的出现频率; 返 ...
- webpack4.x 入门一篇足矣
前言: webpack4出了以后,一些插件变化很大,和之前的版本使用方式不一样,新手入坑,本篇将介绍如何从一开始配置webpack4的开发版本,对css,js进行编译打包合并生成md5,CSS中的图片 ...
- Vue组件-组件的属性
在html中使用元素,会有一些属性,如class,id,还可以绑定事件,自定义组件也是可以的.当在一个组件中,使用了其他自定义组件时,就会利用子组件的属性和事件来和父组件进行数据交流. 比如,子组件需 ...
- java===java基础学习(16)---final
final-----概念 1.当不希望父类的某个方法被子类覆盖(override)时,可以用final关键字修饰. 2.当不希望类的某个变量的值被修改时,可以用final修饰.如果要用final,则必 ...
- linux内核启动分析(2)
-----以下内容为从网络上整理所得------ 主要介绍kernel_init线程(函数),这个线程在rest_init函数中被创建,kernel_init函数将完成设备驱动程序的初始化,并调用in ...
- Java将CST的时间字符串转换成需要的日期格式字符串
已知得到的Date类型的变量meettingdate 的值为Sun Dec 16 10:56:34 CST :现在要将它改为yyyy-MM-dd类型或yyyy年MM月dd日: 变为yyyy年MM月dd ...
- jQuery Mobile + HTML5 获取地理位置信息
这个代码也非常简单,核心是HTML5中GeoLocation API,函数原型定义如下: void getCurrentPosition(in PositionCallback successCa ...
- 外部div不能包裹内部div的问题
转自http://www.du52.com/text.php?id=362 当设计网页时,如果内部div全部设置css属性float为左右浮动,那么外部div将不能包裹内部div 解决方法 1.在内部 ...