Android security知识点总结

Linux sepolicy uses ipk package, each binary has three sepolicy files, they are if (interface), fc (file context), te. opkg install selinux.ipk

1 Chain of Trust
1.1 qcom efuse
对bootloader签名,熔丝文件:sec.dat

1)烧写signed bootloader
2)将密匙文件sec.dat烧写入efuse
3)重启设备,设备的bootROM会读取efuse中的密匙pubk验证bootloader
4)bootloader验证通过,启动,开始基于信任链(chain of trust)的AVB验证流程

1.2 编译时关闭AVB
BoardConfig.mk
BOARD_AVB_ENABLE := false
BOARD_BUILD_DISABLED_VBMETAIMAGE := true

Android 8.0之后没有独立的recovery.img,boot.img根据cmdline参数来决定mount哪个ramdisk。
如果有skip_initramfs参数,那么mount打包在system.img(system-as-root)中的normal ramdisk;否则mount打包在boot.img中的recovery ramdisk。

参数BOARD_BUILD_SYSTEM_ROOT_IMAGE的配置决定是将normal ramdisk打包到boot.img中还是打包到system.img中。
打包到boot.img中:
BOARD_BUILD_SYSTEM_ROOT_IMAGE := false

打包到system.img中(system-as-root):
BOARD_BUILD_SYSTEM_ROOT_IMAGE := true

1.3 AVB验证流程
需要提前挂载分区的fstab来自于commandline的androidboot.android_dt_dir
1)bootloader使用内置的OEM pubk验证vbmeta.img,验证通过后用vbmeta.img中的boot pubk验证boot.img,如果验证通过就启动boot.img
2)init启动后,init/fs_mgr使用vbmeta.img中的vendor pubk、system pubk、odm pubk验证vendor.img、system.img、odm.img,验证通过就mount,否则不会mount

编译生成Metadata流程:
@ build/core/Makefile
->
@ build/tools/releasetools/build_image.py
->
BuildVerityTree() - 用来生成dm_verity需要的签名数据
BuildVerityMetadata() - 生成Metadata数据
->
@ system/extras/verity/build_verity_metadata.py
->
build_verity_metadata()

1.4 dm-verity
可以使用内核配置CONFIG_DM_VERITY_HASH_PREFETCH_MIN_SIZE(默认大小为128)来启用dm-verity哈希预提取大小,该修改可提升启动速度。

1.5 Android 8.0 userdebug版本刷机时禁止dm-verity
Android 8.0 dm-verity disable flag存在于vbmeta.img(keystore分区)中;而老版本是放置在system.img分区的dm-verity metadata中。
1)在设置中打开OEM unlocking选项
2)在设置中打开USB debugging选项
3)adb reboot bootloader
4)fastboot flashing unlock和fastboot oem unlock
5)fastboot --disable-verity --disable-verification flash vbmeta vbmeta.img
6)fastboot reboot
7)adb root
8)adb remount

1.6 vbmeta for AVB disabled
# commands from zcat out/verbose.log.gz
# PEM: Privacy Enhanced Mail
#!/bin/sh

MY_PATH=${PWD}
TARGET_DIR=${MY_PATH}/out/target/product/${TARGET_PRODUCT}
AVB_TOOL=${MY_PATH}/external/avb/avbtool
# or your own xxx_rsa4096.pem file
TESTKEY=${MY_PATH}/external/avb/test/data/testkey_rsa4096.pem

# in x86_64 platform, replace dtbo.img with tos.img
python ${AVB_TOOL} make_vbmeta_image --output ${TARGET_DIR}/vbmeta_disabled.img \
    --include_descriptors_from_image ${TARGET_DIR}/boot.img \
    --include_descriptors_from_image ${TARGET_DIR}/system.img \
    --include_descriptors_from_image ${TARGET_DIR}/vendor.img \
    --include_descriptors_from_image ${TARGET_DIR}/dtbo.img \
    --setup_rootfs_from_kernel ${TARGET_DIR}/system.img \
    --algorithm SHA256_RSA4096 \
    --key ${TESTKEY} \
    --padding_size 4096 \
    --set_hashtree_disabled_flag

1.7 EXT4 Encryption
FBE:加密的仅仅是文件内容和文件名,其他的信息比如文件大小,权限等都没有加密,这些内容就是filesystem metadata
Metadata分区:加密文件大小,权限等除了文件内容和文件名之外的文件信息,/metadata/vold/metadata_encryption/key
QSEE: Qualcomm Security Executing Environment

github ext4-crypt
add_key() - e4crypt add_key
keyctl_search()
keyctl_unlink()

setenforce 0
# erase RPMB
qseecom_sample_client -v sampleapp 15 1
# program RPMB
qseecom_sample_client -v sampleapp 14 1

adb push keybox.xml /data
adb shell
cd /data
LD_LIBRARY_PATH=/vendor/lib/hw KmInstallKeybox keybox.xml true

1.8 lpunpack
lp: Logical Partition images

make lpunpack

simg2img super.img super_ext4.img
mkdir super_ext4
out/host/linux-x86/bin/lpunpack super_ext4.img super_ext4/

2 Android selinux
2.1 LSM的五种实现
LSM(Linux Security Module)的名字并不是特别准确,因为它并不是真正意义上的Linux模块,而是一些函数的hook机制。
AppArmor:应用盔甲,AppArmor is installed by default in Ubuntu
SELinux:Security Enhanced Linux,基于inode,Android当前使用的就是这种
SMACK:Simple Mandatory Access Control Kernel,基于inode
Tomoyo:日本女人名“智代”,日本人实现的代码,基于path
Yama:来自梵文,中文名为“阎罗”,只处理ptrace和文件链接

2.2 selinux权限检查原理
Linux系统先做DAC(Discretionary Access Control,自主访问控制,Linux传统权限)检查。如果没有通过DAC权限检查,则操作直接失败。通过DAC检查之后,再做MAC(Mandatory Access Control,selinux)权限检查。

编译时强制打开selinux。
BOARD_KERNEL_CMDLINE += androidboot.selinux=enforcing

2.3 struct security_hook_heads
struct security_hook_list selinux_hooks[]
security_add_hooks()
security_delete_hooks()
找到selinux_hooks的地址,使用ARRAY_SIZE算出有多少个element,每个element都是一个链表,是同一个hook对象的结合,譬如当检查file_open时,那么这个链表的所有节点的回调函数都要运行一次。使用security_delete_hooks()可以反注册selinux_hooks。

2.4 查看设备节点的selinux权限
ls -alZ /dev/kmsg
getprop -Z persist.sys.usb.config
ps -AZ

2.5 打开关闭sepolicy
需要Android版本是usereng/eng

adb root
adb shell

关闭:
# setenforce 0
打开:
# setenforce 1

2.6 添加到启动脚本中禁止security
on nonencrypted
    # A/B update verifier that marks a successful boot.
    exec - root cache -- /system/bin/update_verifier nonencrypted
    class_start main
    class_start late_start

in late_start script, add [setenforce 0]

2.7 根据avc log生成sepolicy
1)提取所有的avc log
adb shell "cat /proc/kmsg | grep avc" > avc_log.txt

or

adb shell
dmesg | grep avc > /dev/avc_log.txt
adb pull /dev/avc_log.txt .

2)使用audit2allow直接生成policy
sudo apt-get install policycoreutils
audit2allow -i avc_log.txt -o output_pol.te
vi output_pol.te

3)
external/selinux/prebuilts/bin/audit2allow \
-i abc.txt > abc.te

2.8 Android 8.0 adb root sepolicy
CTS SELinuxNeverallowRulesTest.java
private - not visible to OEM
public - visible to OEM

CIL: SELinux Common Intermediate Language
/system/bin/secilc
-N: disable neverallow check before build

1) extract adbd.cil and su.cil from userdebug /system/etc/selinux/plat_sepolicy.cil
2) before pack system image, write adbd.cil and su.cil to user /system/etc/selinux/plat_sepolicy.cil
- function build-systemimage-target, pass 3 args to build/tools/releasetools/build_image.py
- add the following 5 commands to function build-systemimage-target
  # Use the following command to add su to typeattributeset coredomain and typeattributeset mlstrustedsubject.
  # @sed -i "s#abcdzcb(.∗abcdzcb(.∗ xxx#\1 su xxx#" 1.txt
  @sed -i -e '/neverallow \
  adbd/d' /path/to/plat_sepolicy.cil
  @cat /path/to/adbd.cil >> \
  /path/to/plat_sepolicy.cil
  @cat /path/to/su.cil >> \ 
  /path/to/plat_sepolicy.cil
3) Android init process will call /system/bin/secilc compile CIL files to binary, then write binary to kernel.

mmm system/sepolicy
out/target/product/
/system/etc/selinux - AOSP
/vendor/etc/selinux - OEM

3 audit2allow python
#
# audit2allow, translate avc log to .te file
# Author: George Tso
#
# usage
# dmesg | grep "avc" > /dev/avc.log
# adb pull /dev/avc.log .
# audit2allow avc.log avc.te

import re
import string
import sys

def write_outfile(outfile, hashmap):
    for (hm_key, hm_value) in hashmap.items():
    #{
        allow_value = ''
        hm_value.sort()

        if (len(hm_value) == 1):
            allow_line = hm_key +
                ' ' + hm_value[0] + ';' + '\n'
        else:
            for value in hm_value:
                allow_value += ' ' + value
            allow_line = hm_key +
                ' {' + allow_value + ' };' + '\n'

        outfile.writelines(allow_line)
    #}

def has_proc(line, proc):
    e_list = line.split()
    for e in e_list:
    #{
        if (e.find('scontext') > -1):
            sub = e.split(':')
            if (proc == sub[2]):
                return True
            else:
                return False
    #}
    return False

def _generate_te(proc_list):
    src = ''
    tgt = ''
    tclass = ''
    got_tclass = False
    hashmap = {}
    got_key = False
    repeat = False

    outfile = open(sys.argv[2], 'w')
    for proc in proc_list:
    #{
        outfile.writelines('\n\n===============' +
            proc + '================\n')
        file = open(sys.argv[1], 'r')
        for line in file.readlines():
        #{
            line = line.strip()
            if not len(line) or line.startswith('#'):
                 continue

            if (has_proc(line, proc) == False):
                # not this process, continue
                continue

            # regular expression to extract {}
            perm = re.findall(r'[{](.*?)[}]', line)
            #print(perm[0].strip())

            e_list = line.split()
            for e in e_list:
            #{
                if (e.find('scontext') > -1):
                    sub = e.split(':')
                    src = sub[2]
                elif (e.find('tcontext') > -1):
                    sub = e.split(':')
                    tgt = sub[2]
                elif (e.find('tclass') > -1):
                    sub = e.split('=')
                    tclass = sub[1]
                    got_tclass = True

                if (got_tclass == True):
                    got_tclass = False

                    allow_key = 'allow' + ' ' + src +
                        ' ' + tgt + ':' + tclass.strip()
                    allow_value = perm[0].lstrip().rstrip()
                    hm_key = ''
                    hm_value = []

                    for (hm_key, hm_value) in \
                        hashmap.items():
                    #{
                        if (hm_key == allow_key):
                            got_key = True
                            break;
                    #}

                    if (got_key == True):
                        got_key = False

                        for value in hm_value:
                        #{
                            if (value == allow_value):
                                repeat = True
                                break;
                        #}
                        if (repeat == False):
                            hm_value.append(allow_value)
                            hashmap[allow_key] = \
                                hm_value
                        repeat = False
                    else:
                        hm_value = []
                        hm_value.append(allow_value)
                        hashmap[allow_key] = hm_value
            #} end of for e in e_list
        #} end of for line in file.readlines()

        write_outfile(outfile, hashmap)
        hashmap.clear()
        file.close()
    #} for proc in proc_list
    outfile.close()

def generate_te():
    # STEP 1 - FIND ALL THE PROCESSES
    proc_list = []
    repeat = False

    file = open(sys.argv[1], 'r')
    for line in file.readlines():
    #{
        line = line.strip()
        if not len(line) or line.startswith('#'):
             continue

        e_list = line.split()
        for e in e_list:
        #{
            if (e.find('scontext') > -1):
                sub = e.split(':')
                for proc in proc_list:
                #{
                    if (proc == sub[2]):
                        repeat = True;
                        break;
                #}
                if (repeat == False):
                    proc_list.append(sub[2])
                repeat = False
                break
        #}
    #}
    file.close()
    proc_list.sort()
    print proc_list

    # STEP 2 - GENERATE OUTPUT FILE
    _generate_te(proc_list)

if __name__ == '__main__':
    if (len(sys.argv) < 3):
        print(sys.argv[0] +
            ' ' + '' +
            ' ' + '')
        exit(0)
    generate_te()

4 Abbreviations
avb:Android Verified Boot,用dm-verify验证system分区的完整性,用在Android 8.0之后的fstab中
AVC:Access Vector Cache
cmnlib.mbn: qcom trustzone commonlib, qseecom_sample_client
DAC:Discretionary Access Control,自主访问控制
devcfg.mbn: qcom QUP访问权限控制,SPI片选和时钟线分别对应I2C的SDA和SCL
FRP:Factory Reset Protection
km41.mbn: qcom keymaster v4.1
LSM:Linux Security Module
seccomp: Android 8.1 secure computing
TE:Type Enforcement

你可能感兴趣的:(System,EXT4,Encryption,Security)