Android安全机制.docx
《Android安全机制.docx》由会员分享,可在线阅读,更多相关《Android安全机制.docx(14页珍藏版)》请在冰豆网上搜索。
![Android安全机制.docx](https://file1.bdocx.com/fileroot1/2023-2/21/5b0b3fee-261a-43a0-9313-b33d1cfe6fe9/5b0b3fee-261a-43a0-9313-b33d1cfe6fe91.gif)
Android安全机制
1Android安全机制概述
Android是一个权限分离的系统。
这是利用Linux已有的权限管理机制,通过为每一个Application分配不同的uid和gid,从而使得不同的Application之间的私有数据和访问(native以及java层通过这种sandbox机制,都可以)达到隔离的目的。
与此同时,Android还在此基础上进行扩展,提供了permission机制,它主要是用来对Application可以执行的某些具体操作进行权限细分和访问控制,同时提供了per-URIpermission机制,用来提供对某些特定的数据块进行ad-hoc方式的访问。
1.1uid、gid、gids
Android的权限分离的基础是建立在Linux已有的uid、gid、gids基础上的。
UID。
Android在安装一个应用程序,就会为它分配一个uid(参考PackageManagerService中的newUserLP实现)。
其中普通Android应用程序的uid是从10000开始分配(参见Process.FIRST_APPLICATION_UID),10000以下是系统进程的uid。
GID。
对于普通应用程序来说,gid等于uid。
由于每个应用程序的uid和gid都不相同,因此不管是native层还是java层都能够达到保护私有数据的作用。
GIDS。
gids是由框架在Application安装过程中生成,与Application申请的具体权限相关。
如果Application申请的相应的permission被granted,而且中有对应的gids,那么这个Application的gids中将包含这个gids。
uidgidgids的详细设置过程:
请参考ActivityManagerService中的startProcessLocked。
在通过zygote来启动一个process时,直接将uid传给给了gid。
再通过zygote来fork出新的进程(zygote.java中的forkAndSpecialize),最终在native层(dalvik_system_zygote.c)中的forkAndSpecializeCommon中通过linux系统调用来进行gid和uid和gids的设置。
1.2permission
一个权限主要包含三个方面的信息:
权限的名称;属于的权限组;保护级别。
一个权限组是指把权限按照功能分成的不同的集合。
每一个权限组包含若干具体权限,例如在COST_MONEY组中包含android.permission.SEND_SMS,android.permission.CALL_PHONE等和费用相关的权限。
每个权限通过protectionLevel来标识保护级别:
normal,dangerous,signature,signatureorsystem。
不同的保护级别代表了程序要使用此权限时的认证方式。
normal的权限只要申请了就可以使用;dangerous的权限在安装时需要用户确认才可以使用;signature和signatureorsystem的权限需要使用者的app和系统使用同一个数字证书。
Package的权限信息主要通过在AndroidManifest.xml中通过一些标签来指定。
如标签,标签等标签。
如果package需要申请使用某个权限,那么需要使用标签来指定。
2Androidpermission管理机制
2.1Frameworkpermission机制
2.1.1安装入口
permission的初始化,是指permission的向系统申请,系统进行检测并授权,并建立相应的数据结构。
绝大多数的情况下permission都是从一个package中扫描所得,而这发生在package安装和升级的时候。
一般有如下几种安装入口:
npackageInstaller,package被下载安装时会触发使用。
packageInstaller会通过AppSecurityPermissions来检查dangerous的权限,并对用户给出提示。
npm命令。
nadbinstall。
最终还是调用pminstall来安装apk包。
n拷贝即安装。
PackageManagerService中使用AppDirObserver对/data/app/进行监视,如果有拷贝即触发安装。
这些安装方式最终都会通过调用PackageManagerService中的函数来完成程序的安装。
2.1.2permission创建
第一步,从AndroidManifest.xml中提取permission信息。
主要提取如下信息:
²shareduid
指定与其它package共享同一个uid。
²permission
提取permissions标签指定属性。
它使用permissionInfo来描述一个权限的基本信息。
需要指定protectedLevel信息,并指定所属group信息。
它将被添加到这个package的permissions这个list结构中。
²permission-tree
提取permissions-tree标签属性。
permissions-tree也通过permissionInfo来描述,并被添加到package的permissions这个list结构中。
permission-tree只是一个名字空间,用来向其中动态添加一些所谓Dynamic的permission,这些permission可以动态修改。
这些permission名称要以permission-tree的名称开头。
它本身不是一种权限,没有protectedLevel和所属group。
只是保存了所属的packge和权限名(带有package前缀的)。
²permission-group
定义permission组信息,用PermissionGroup表示。
本身不代表一个权限,会添加进入package的permissionGroups这个list中。
²uses-permission
定义了package需要申请的权限名。
将权限名添加到package的requestedPermissions这个list中。
²adopt-permissions
将该标签指定的name存入package的mAdoptPermissions这个list中。
Name指定了这个package需要从name指定的package进行权限领养。
在systempackage进行升级时使用。
第二步。
获取Package中的证书,验证,并将签名信息保存在Package结构中。
1.如果该package来自systemimg(系统app),那么只需要从该Package的AndroidManifest.xml中获取签名信息,而无需验证其完整性。
但是如果这个package与其它package共享一个uid,那么这个共享uid对应的sharedUser中保存的签名与之不一致,那么签名验证失败。
2.如果是普通的package,那么需要提取证书和签名信息,并对文件的完成性进行验证。
第三步。
如果是普通的package,那么清除package的mAdoptPermissions字段信息(系统package升级才使用)。
第四步。
如果在AndroidManifest.xml中指定了shareduser,那么先查看全局list中(mSharedUsers)是否该uid对应的SharedUserSetting数据结构,若没有则新分配一个uid,创建SharedUserSetting并保存到全局全局list(mSharedUsers)中。
mUserIds保存了系统中已经分配的uid对应的SharedUserSetting结构。
每次分配时总是从第一个开始轮询,找到第一个空闲的位置i,然后加上FIRST_APPLICATION_UID即可。
第五步。
创建PackageSettings数据结构。
并将PackageSettings与SharedUserSetting进行绑定。
其中PackageSettings保存了SharedUserSetting结构;而SharedUserSetting中会使用PackageSettings中的签名信息填充自己内部的签名信息,并将PackageSettings添加到一个队列中,表示PackageSettings为其中的共享者之一。
在创建时,首先会以packageName去全局数据结构mPackages中查询是否已经有对应的PackageSettings数据结构存在。
如果已经存在PackageSettings数据结构(比如这个package已经被uninstall,但是还没有删除数据,此时package结构已经被释放)。
那么比较该package中的签名信息(从AndroidManifest中扫描得到)与PackageSettings中的签名信息是否匹配。
如果不匹配但是为systempackage,那么信任此package,并将package中的签名信息更新到已有的PackageSettings中去,同时如果这个package与其它package共享了uid,而且shareduid中保存的签名信息与当前package不符,那么签名也验证失败。
第六步。
如果mAdoptPermissions字段不为空,那么处理permission的领养(从指定的package对应的PackageSettings中,将权限的拥有者修改为当前package,一般在systemapp升级的时候才发生,在此之前需要验证当被领养的package已经被卸载,即检查package数据结构是否存在)。
第七步。
添加自定义权限。
将package中定义的permissionGroup添加到全局的列表mPermissionGroups中去;将package中定义的permissions添加到全局的列表中去(如果是permission-tree类型,那么添加到mSettings.mPermissionTrees,如果是一般的permission添加到mSettings.mPermissions中)。
第八步。
清除不一致的permission信息。
1.清除不一致的permission-tree信息。
如果该permission-tree的packageSettings字段为空,说明还未对该package进行过解析(若代码执行到此处时packageSettings肯定已经被创建过),将其remove掉。
如果packageSettings不为空,但是对应的package数据结构为空(说明该package已经被卸载,但数据还有保留),或者package数据结构中根本不含有这个permission-tree,那么将这个permission-tree清除。
2.清除不一致的permission信息。
如果packageSettings或者package结构为空(未解析该package或者被卸载,但数据有保留),或者package中根本没有定义该permission,那么将该permission清除。
第九步。
对每一个package进行轮询,并进行permission授权。
1.对申请的权限进行检查,并更新grantedPermissions列表
2.如果其没有设置shareduserid,那么将其gids初始化为mGlobalGids,它从permission.xml中读取。
3.遍历所有申请的权限,进行如下检查
1)如果是该权限是normal或者dangerous的。
通过检查。
2)如果权限需要签名验证。
如果签名验证通过。
还需要进行如下检查
*如果程序升级,而且是systempackage。
那么是否授予该权限要看原来的package是否被授予了该权限。
如果被授予了,那么通过检查,否则不通过。
*如果是新安装的。
那么检查通过。
4.如果3中检查通过,那么将这个permission添加到package的grantedPermissions列表中,表示这个permission申请成功(granted)。
申请成功的同时会将这个申请到的permission的gids添加到这个package的gids中去。
5.将permissionsFixed字段标准为ture,表示这个packge的permission进行过修正。
后续将禁止对非system的app的权限进行再次修正。
2.1.3Dynamicpermission的管理
PackageManagerService提供了addPermission/removePermission接口用来动态添加和删除一些权限。
但是这些权限必须是所谓的动态权限(BasePermission.TYPE_DYNAMIC)。
一个Package如果要添加Dynamicpermissions,首先必须要在manifest中申明标签,它实际上是一个权限的名字空间(例如,“com.foo.far”这个权限就是permission-tree“com.foo”的成员),本身不是一个权限。
一个Package只能为自己的permission-tree或者拥有相同的uid的package添加或者删除权限。
Package不能够通过这种接口去修改在manifest中静态申请的权限,否则抛出异常。
首先查找这个permission在全局permission列表mSettings.mPermissions中是否存在。
如果存在,而且类型为BasePermission.TYPE_DYNAMIC那么根据传入的权限信息修改全局表中的权限信息,并触发permissions.xml的持久化。
如果在全局的permission列表mSettings.mPermissions中没有找到,先找到这个permission所在permissionTree,然后添加到全局permission列表mSettings.mPermissions中去,并触发permissions.xml的持久化。
2.1.4Uripermission的管理
下面两个接口主要用于Uripermission的管理(其实现在ActivityManagerService中)。
//为指定的uid和targetPkg添加对某个contentUri的读或者写权限。
publicvoidgrantUriPermission(IApplicationThreadcaller,StringtargetPkg,Uriuri,intmode)throwsRemoteException;
//清除所有通过grantUriPermission对某个Uri授予的权限。
publicvoidrevokeUriPermission(IApplicationThreadcaller,Uriuri,intmode)throwsRemoteException;
grantUriPermission主要的实现过程分析。
grantUriPermission分析:
1.验证caller的ProcessRecord和targetPkg不为空。
否则检测不通过。
2.验证所请求的mode为Intent.FLAG_GRANT_READ_URI_PERMISSION或者为Intent.FLAG_GRANT_WRITE_URI_PERMISSION,否则不通过。
3.确保参数Uri是一个contentUri。
否则,则检测不通过。
4.通过Uri得到目标ContentProvider,如果不存在,则检测不通过。
5.从PackageManagerService中获得targetPkg对应的uid。
6.检查targetuid所对应的package是否真正需要这个权限?
先判断要申请的是读还是写权限,然后查看对应的ContentProvider中对应的readPermissionwritePermission字段是否保存了权限名称。
如果该字段不为空,则以targetuid和该权限名去PackageManagerService中去查找该uid是否被granted了该权限。
如果已经获得了该权限,那么无需再去为这个Activity去申请这个Uri权限了,返回。
否者继续执行如下操作。
7.检查这个ContentProvider的grantUriPermissions开关变量,是否允许对其它package进行权限的grant操作。
如果禁止,那么抛出异常。
8.检查这个ContentProvider是否设置了Uri的过滤类型uriPermissionPatterns,如果设置了过滤类型,则将需要申请权限的Uri与之匹配。
匹配不同过,则抛出异常。
9.检查调用者自己是否有权限访问这个Uri。
如果没有,抛出异常。
10.从mGrantedUriPermissions中取得targetuid对应的HashMap数据结构。
用targetuid和Uri生成UriPermission并保存在mGrantedUriPermissions中。
revokeUriPermission实现分析。
找到该Uri对应的ContentProvider,然后删除mGrantedUriPermissions中与Uri对应的所有权限。
2.2permission的动态检查
这里的动态检查是指是package在程序运行过程中进行某些操作或者数据访问时才进行的check,与之对应的是应用程序安装或者升级时PackageManagerService通过扫描包中的静态权限信息相对应。
系统与权限检查相关的机制的实现主要集中在PackageManagerService和ActivityManagerService中。
ActivityManagerService主要负责的是底层的uid层次的身份检查;PackageManagerService则维护了uid到自己拥有的和被授予的权限的一张表。
在通过ActivityManagerService的身份检查后,PackageManagerService根据请求者的uid来查看这张表,判断其是否具有相应的权限。
除此之外,per-URIpermission机制的实现也需要一张表,它维护在ActivityManagerService中,它建立了从contentURI到被授权访问这个URI的component之间的映射。
但是它也需要借助PackageManagerService的机制来辅助实现。
2.2.1framework提供的接口
Androidframework中提供了一些接口用来对外来的访问(包括自己)进行权限检查。
这些接口主要通过ContextWrapper提供,具体实现在ContextImpl中。
如果package接受到外来访问者的操作请求,那么可以调用这些接口进行权限检查。
一般情况下可以把这些接口的检查接口分为两种,一种是返回错误,另一种是抛出异常。
主要包含如下几组:
npermission和uid检查API
下面这一组接口主要用来检查某个调用(或者是其它package或者是自己)是否拥有访问某个permission的权限。
参数中pid和uid可以指定,如果没有指定,那么framework会通过Binder来获取调用者的uid和pid信息,加以填充。
返回值为PackageManager.PERMISSION_GRANTED或者PackageManager.PERMISSION_DENIED。
publicintcheckPermission(Stringpermission,intpid,intuid)//检查某个uid和pid是否有permission权限
publicintcheckCallingPermission(Stringpermission)//检查调用者是否有permission权限,如果调用者是自己那么返回PackageManager.PERMISSION_DENIED
publicintcheckCallingOrSelfPermission(Stringpermission)//检查自己或者其它调用者是否有permission权限
下面这一组和上面类似,如果遇到检查不通过时,会抛出异常,打印消息。
publicvoidenforcePermission(Stringpermission,intpid,intuid,Stringmessage)
publicvoidenforceCallingPermission(Stringpermission,Stringmessage)
publicvoidenforceCallingOrSelfPermission(Stringpermission,Stringmessage)
nper-URI检查API
为某个package添加访问contentUri的读或者写权限。
publicvoidgrantUriPermission(StringtoPackage,Uriuri,intmodeFlags)
publicvoidrevokeUriPermission(Uriuri,intmodeFlags)
检查某个pid和uid的p