1 Efuse and Dm-verity
1.1 Efuse-Sign the bootloader
Fuse file: sec.dat
1) Burn signed bootloader
2) Burn the key file sec.dat to efuse
3) Restart the device, the bootROM of the device will read efuse The key pubk in the verification bootloader
4) Bootloader verification is passed, start, start the AVB verification process
1.2 Turn off AVB when compiling
@ BoardConfig.mk
BOARD_AVB_ENABLE := false
BOARD_BUILD_DISABLED_VBMETAIMAGE := true
After AndroidO, there is no independent recovery.img, boot.img according to cmdline Parameters to determine which ramdisk to mount.
If there is skip_initramfs parameter, then mount the normal ramdisk packaged in system.img; otherwise, mount the recovery ramdisk packaged in boot.img.
The configuration of the parameter BOARD_BUILD_SYSTEM_ROOT_IMAGE determines whether to pack the normal ramdisk into boot.img or into system.img.
Pack it into boot.img:
BOARD_BUILD_SYSTEM_ROOT_IMAGE := false
Pack it into system.img:
BOARD_BUILD_SYSTEM_ROOT_IMAGE := true
1.3 AVB verification process
The fstab of the partition that needs to be mounted in advance comes from the androidboot.android_dt_dir of the commandline
1) The bootloader uses the built-in OEM pubk to verify the vbmeta.img, and after the verification is passed, use the boot pubk in the vbmeta.img to verify the boot.img , If the verification is passed, start boot.img
2) After init is started, init/fs_mgr uses the vendor pubk, system pubk, and odm pubk in vbmeta.img to verify vendor.img, system.img, odm.img, and mount if the verification passes. Otherwise, it will not mount
Figure 1-1 Dm-verity workflow
编译生成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 Disable System Dm-verity
@ device/{ro.boot.hardware}/{ro.board.platform}/fstab.{ro.boot.hardware}
Change
/dev/block/bootdevice/by-name/system /system ext4 ro,barrier=1,discard wait, avb
to
/dev/block/bootdevice/by-name/system /system ext4 ro,barrier=1,discard wait
You can use the kernel configuration CONFIG_DM_VERITY_HASH_PREFETCH_MIN_SIZE (the default size is 128) to enable the dm-verity hash prefetch size. This modification can improve the startup speed.
1.5 AndroidO userdebug version prohibits dm-verity when running
AndroidO dm-verity disable flag exists in vbmeta.img (keystore partition); while the old version is placed in dm-verity metadata in system.img partition.
1) Open the OEM unlocking option
in the settings 2) Open the USB debugging option in the settings
3) adb reboot bootloader
4) fastboot flashing unlock and fastboot oem unlock
5) fastboot reboot
6) adb root
7) adb disable-verity
8) adb reboot
9) adb root
10) adb remount
1.6 AndroidO userdebug version prohibits dm-verity
AndroidO dm-verity disable flag exists in vbmeta.img (keystore partition) when flashing, while the old version is placed in dm-verity metadata in system.img partition in.
1) Open the OEM unlocking option
in the settings 2) Open the USB debugging option in the settings
3) adb reboot bootloader
4) fastboot flashing unlock and fastboot oem unlock
5) fastboot --disable-verity --disable-verification flash vbmeta vbmeta.img
6) fastboot reboot
7) adb root
8) adb remount
2 Android selinux How-to
2.1 Five implementations of LSM (Linux Security Module)
SELinux: Security Enhanced Linux, based on inode, Android currently uses this
SMACK: Simple Mandatory Access Control Kernel, based on inode
Tomoyo: Japanese female name "Chidai", implemented by the Japanese Code, based on path
AppArmor: application armor, AppArmor is installed by default in Ubuntu
Yama: from Sanskrit, Chinese name is "Yan Luo", only ptrace and file links are processed
2.2 Annotation
avc-Access Vector Cache
avd-Access Vector Decision, Access Vector Decision
ssid-the security identifier of the
subject tsid-the security identifier of the
object tclass-the security type of the object
requested-requested permission to check
auditdata-additional audit data, which mainly generates audit logs for the kernel kauditd and user auditd to save logs to disk. When the permission check fails, the kernel will print the log that the audit failed. Refer to another article Linux auditd
2.3 compile selinux
BOARD_KERNEL_CMDLINE += androidboot.selinux=enforcing
2.4 View the sepolicy permissions of the device node
ls -alZ /dev/kmsg
2.5 not_full_treble
If you encounter execute_no_trans that cannot pass the compilation neverallow check, you need to use this macro
2.6 According to Avc log automatically generates Android Selinux policy
2.6.1 Generates policy text file
1) Extract all 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) Use audit2allow to directly generate policy
sudo apt-get install policycoreutils
audit2allow -i avc_log.txt -o output_pol.te
vi output_pol.te
2.6.2 Insert directly into the sepolicy file
adb shell
dmesg> /dev/kern_msg.txt
adb pull /dev/kern_msg.txt.
cat kern_msg.log | audit2allow -p out/target/product/<device>/ root/sepolicy
2.7 Opening and closing Sepolicy
requires the android version to be usereng/eng
adb root
adb shell to
close:
# setenforce 0 to
open:
# setenforce 1
2.8 Add to the startup script to prohibit 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.9 Find all permission strings supported by
user space User space macro definition
@ sepolicy/global_macros
kernel space permission string
@ security/selinux/selinuxfs.c
security_load_policy()
->
@ security/selinux/include/classmap.h
struct security_class_mapping secclass_map[]; // corresponding to tclass, permission bitmap (each permission occupies 1bit of a 32bit integer)
2.10 Dump sepolicy DB
@ security/selinux/ss/services.c
#include <linux/moduleparam.h>
static int oem_sepolicy_db_show(char *buffer, const struct kernel_param *kp)
{ unsigned i, j; struct sidtab_node *cur; char *context_name = NULL; u32 length; i = 0; while (secclass_map[i].name) i++; printk(KERN_DEBUG "(tclass_cnt, current_mapping_size, SIDTAB_SIZE) = "
"(%d, %d, %d)\n\n",
i, current_mapping_size, SIDTAB_SIZE);
// step1
if (!current_mapping) {
goto out;
}
for (i = 0; i < current_mapping_size; i++) {
printk("%d (value, num_perms, permission_bitmap) = (%d, %d",
i, current_mapping[i].value, current_mapping[i].num_perms);
if (!current_mapping[i].num_perms) {
printk(")\n");
continue;
} else {
printk(" - ");
}
for (j = 0; j < current_mapping[i].num_perms; j++) {
if (j == (current_mapping[i].num_perms - 1)) {
printk("%x", current_mapping[i].perms[j]);
} else {
printk("%x ", current_mapping[i].perms[j]);
}
}
printk(")\n");
}
// step2
printk("\n");
for (i = 0; i < SIDTAB_SIZE; i++) {
cur = sidtab.htable[i];
while (cur) {
if (context_struct_to_string(&(cur->context),
&context_name, &length) < 0)
continue;
printk("(%d %s)", cur->sid, context_name);
kfree(context_name);
cur = cur->next;
if (cur) { printk(" ### "); } } printk("\n"); } out: // no output log to userspace return 0; } static struct kernel_param_ops oem_sepolicy_db_ops = { .get = oem_sepolicy_db_show, }; module_param_cb(oem_sepolicy_db, &oem_sepolicy_db_ops, NULL, 0600); MODULE_PARM_DESC(oem_sepolicy_db, "show sepolicy db"); 3 Abbreviations avb: Android-Verified Boot In fstab after Android 8.0, AVC: Access Vector Cache DAC: Discretionary Access Control, autonomous access control
FRP:Factory Reset Protection
LSM:Linux Security Module
Appendix
audit2allow python demo for OEM
#
# 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] + ' ' + '<input_file.log>' + ' ' + '<output_file.te>')
exit(0)
generate_te()