• 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 Android selinux
    1.1 How to compile Android sepolicy
    restorecon -v /path/to/test
    chcon u:object_r:xxx_exec:s0 /path/to/test

    /system/bin/secilc \
    /system/etc/selinux/plat_sepolicy.cil \
    -m -M true -G -c 33 \
    /system/etc/selinux/mapping/33.0.cil \
    -o /dev/precompiled_sepolicy \
    /system_ext/etc/selinux/system_ext_sepolicy.cil \
    /system_ext/etc/selinux/mapping/33.0.cil \
    /product/etc/selinux/product_sepolicy.cil \
    /product/etc/selinux/mapping/33.0.cil \
    /vendor/etc/selinux/vendor_sepolicy.cil \
    /vendor/etc/selinux/plat_pub_versioned.cil

    Get version from /sys/fs/selinux/policyvers
    Get mapping version from /vendor/etc/selinux/plat_sepolicy_vers.txt
    /vendor/etc/selinux/precompiled_sepolicy will be written into /sys/fs/selinux/load

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

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

    1.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。

    1.4 查看设备节点的selinux权限
    ls -alZ /dev/kmsg
    getprop -Z persist.sys.usb.config
    ps -AZ
    restorecon -v /vendor/bin/xxx

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

    adb root
    adb shell

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

    1.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]

    1.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

    1.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

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

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

    2.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

    2.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()

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

    2.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

    2.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

    2.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

    2.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/

    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

  • 相关阅读:
    迈向企业开发Spring详解!
    电商项目之购物车
    Authing 入选 Gartner《2022 中国网络安全技术成熟度曲线》报告
    【精炼易懂】字符集、编码、乱码问题、ASCII、GBK、Unicode、UTF-8详解+实例说明
    【管理运筹学】第 10 章 | 排队论(5,多服务台排队系统、一般服务时间模型、P-K 公式、排队系统的经济分析)
    “Sources, Summary” report——“来源,摘要”报告
    Spring中@Lazy注解的使用
    linux Nginx+Tomcat负载均衡、动静分离
    C++ Qt 学习(八):Qt 绘图技术与图形视图
    【uni-app从入门到实战】get请求、数据缓存、图片上传预览
  • 原文地址:https://blog.csdn.net/zoosenpin/article/details/76104054