当前位置:   article > 正文

Android安全机制:_android系统的安全机制

android系统的安全机制

自主访问控制(DAC)和强制访问控制(MAC);Android中自主访问控制是通过Linux UID/GID实现,而强制访问控制则是使用的SEAndroid!

在Android中SEAndroid安全机制(MAC)与传统的Linux UID/GID安全机制(DAC)是并存关系的,也就是说,它们同时用来约束进程的权限。当一个进程访问一个文件的时候,首先要通过基于UID/GID的DAC安全检查,接着才有资格进入到基于SEAndroid的MAC安全检查。只要其中的一个检查不通过,那么进程访问文件的请求就会被拒绝。

DAC:

Android系统将每一个安装在系统的APK都映射为一个不同的Linux用户。也就是说,每一个APK都有一个对应的UID和GID。这些UID和GID是在APK安装的时候由系统安装服务PMS分配的;

在完成安装并运行程序后,可以通过ps -A | grep PACKAGENAME 查看程序uid:

angler:/system # ps -A | grep com.enjoy
u0_a72        7124   562 2402748 170132 SyS_epoll_wait 78511081d4 S xxxxx

得到当前程序进程ID为7124,然后通过cat /proc/7124/status查看:

cat /proc/7124/status
#输出
Name:   xxxxx
State:  S (sleeping)
Tgid:   7124
Pid:    7124
PPid:   562
TracerPid:      0
Uid:    10072   10072   10072   10072
Gid:    10072   10072   10072   10072
FDSize: 64
Groups: 9997 20072 50072

可以看到当前程序UID为10072,通过这种方式,就可以保证每一个APK进程都以不同的身份来运行,从而保证了相互之间不会受到干扰。这就是所谓的沙箱了,这完全是建立在Linux的UID和GID基础上的。

普通APP的UID从10000开始分配,最大到19999,而root用户的id为0。

如何才能让我们的进程通过Linux UID/GID的拦截呢?比如我们在AndroidManifest.xml中配置:

<uses-permission android:name="android.permission.INTERNET"/>

APP的UID和GID是在安装时候就由PMS分配好了的。为什么这样一个配置就能够让程序通过Linux UID/GID的拦截?

这是因为PMS在安装APK时,从Manifest文件中把App信息和权限存到/data/system/packages.xml/data/system/packages.list文件中。以《百度作业帮》为例,打开packages.list会存在下面的记录:

com.baidu.homework 10051 0 /data/user/0/com.baidu.homework default:targetSdkVersion=26 3002,3003,3001

其中3002,3003,3001代表的就是用户组,通过/system/core/include/private/android_filesystem_config.h 查看可知

#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 */

3001与3002代表了具备蓝牙相关权限的用户组,而3003则表示具备网络权限的用户组。PMS会将APK加入到相应的某个Linux用户组去,这样APK才能够具备对应的权限。

可以看到在Groups中存在3003,因此作业帮才具备网络访问的权限!Android应用权限与Linux UID/GID权限就是因此而关联起来的。

DAC的问题

在理想情况下,DAC机制是没有问题的。然而,现实很骨感。比如我们将某个系统文件的权限改为777(可读可写可执行),那是不是意味着我们的程序就能随意修改系统的配置了呢?我们还是以/system/manifest.xml为例:

我们通过以下命令将/system挂载为可读可写,并修改manifest文件读写权限:

adb root 
adb disable-verity
adb reboot
adb root
adb remount
adb shell
mount |grep system
#假设输出:
#/dev/block/dm-0 on /system type ext4 (ro,seclabel,relatime,inode_readahead_blks=8)
#挂载点 /system 设备为:/dev/block/dm-0
mount -o remount,rw /dev/block/dm-0 /system
chmod 777 manifest.xml

修改完成后,如果只有DAC机制那么任何用户都能具备对该文件的读写以及执行权限,造成严重的安全问题。然而Android中我们会发现哪怕修改了777权限,app仍然无法对该文件进行写操作,这是因为Android中还是使用了一种更为强有力的安全机制来保证系统的安全,这种机制就是MAC!

MAC强制访问控制

在MAC机制中,用户、进程或者文件的权限是由管理策略决定的,而不是由它们自主决定的。例如,我们可以设定这样的一个管理策略,不允许用户A将它创建的文件F授予用户B访问。这样无论用户A如何修改文件F的权限位,用户B都是无法访问文件F的。这种安全访问模型可以强有力地保护系统的安全。

SEAndroid安全机制又称为是基于TE(Type Enforcement)策略的安全机制。在/system/sepolicy目录中,所有以.te为后缀的文件均为策略配置文件。

安全上下文

SEAndroid安全机制中的安全策略是在安全上下文的基础上进行描述的,它通过主体和客体的安全上下文,定义主体是否有权限访问客体。主体通常就是进程,而客体就是指进程所要访问的资源,例如文件、系统属性等。

首先我们需要在/system/sepolicy/private/file_contexts中声明SEAndroid系统文件的安全上下文:

/system/bin/name-server     u:object_r:name-server_exec:s0

安全上下文实际上就是一个附加在对象上的标签(Tag)。这个标签实际上就是一个字符串,它由四部分内容组成,分别是SELinux用户、SELinux角色、类型、安全级别,每一个部分都通过一个冒号来分隔,格式为"user:role:type:sensitivity"。

在安全上下文中,只有类型(Type)才是最重要的,SELinux用户、SELinux角色和安全级别都几乎可以忽略不计的。正因为如此,SEAndroid安全机制又称为是基于TE(Type Enforcement)策略的安全机制。

在SEAndroid中,只定义了一个SELinux用户u,一个SELinux角色r,这意味着只有u、r和domain可以组合在一起形成一个合法的安全上下文,那么ps -Z查看进程应为 u:r:domain:s0 ,ls -Z查看文件则为: u:object_r:domain:s0,而其它形式的安全上下文定义均是非法的。

但是以init进程为例,执行 adb shell ps -Z | grep zygote (Windows为:adb shell ps -Z | findStr zygote),可以看到输出为:

u:r:zygote:s0 root 646     1 1577904  69500 poll_schedule_timeout eb7e743c S zygote

安全上下文u:r:zygote:s0,按照上面的分析,这不是应该是一个不合法的上下文吗?原因是在/system/sepolicy/public/zygote.te 中通过type声明了类型zygote并且将domain设置为类型zygote的属性:

type zygote, domain;
type zygote_exec, exec_type, file_type;

因此它就可以像domain一样,可以和SELinux用户u和SELinux角色组合在一起形成合法的安全上下文。

类型

在SEAndroid中,每一个用来描述文件安全上下文的类型都将file_type设置为其属性,每一个用来进程安全上下文的类型都将domain设置为其属性。

通过日志,完成对SEAndroid权限的赋予!

运行某个进程,如果该进程不具备对应的权限,则可以在logcat 或者在执行 adb root 后执行 adb shell dmesg查看:

avc: denied { bind } for pid=417 comm="name-server" scontext=u:r:name-server:s0 tcontext=u:r:name-server:s0 tclass=tcp_socket permissive=0
    
avc: denied { connectto } for pid=417 comm="name-server" scontext=u:r:name-server:s0 tcontext=u:r:netd:s0 tclass=unix_stream_socket permissive=0    
说明案例
缺少什么权限{ bind }
谁缺少权限scontext=u:r:name-server:s0
对谁缺少权限tcontext=u:r:name-server:s0
什么类型的权限tclass=tcp_socket

此时就需要在TE文件中声明:

#allow [谁缺少权限]  [对谁缺少权限]:[什么类型的权限] [缺少什么权限]
#当sconext与tcontext都是自己时候,可以将 [对谁缺少权限]写为:self
allow name-server self:tcp_socket {bind}
allow name-server netd:unix_stream_socket {connectto};
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/684693
推荐阅读
相关标签
  

闽ICP备14008679号