- Author: Jimmy Chen
- Version: v1.0
- Date:2018-04-13
最近在MSM8909+Android 8.1.0上熟悉高通平台的相关开发。在尝试进行FDE全盘机密的时候,出现了失败,显示Encryption unsuccessful,如下图显示
当看到这张图片是有点懵逼的,因为之前都在分析FBE,还没分析过FDE,不过想想大概分析流程应该是差不多的吧。首先当然是看看fstab的内容了
fstab.qcom
1 2 3 4 5 6 |
#<src> <mnt_point> <type> <mnt_flags and options> <fs_mgr_flags> #/dev/block/bootdevice/by-name/system /system ext4 ro,barrier=1 wait /dev/block/bootdevice/by-name/userdata /data ext4 nosuid,noatime,nodev,barrier=1,noauto_da_alloc,discard wait,check,encryptable=footer,quota /devices/soc/7864900.sdhci/mmc_host* /storage/sdcard1 vfat nosuid,nodev wait,voldmanaged=sdcard1:auto,noemulatedsd,encryptable=footer /dev/block/bootdevice/by-name/config /frp emmc defaults defaults /dev/block/bootdevice/by-name/misc /misc emmc defaults defaults |
从fstab中可以看到指定加密使用的是encryptable=footer
可以看到默认是不加密的,可以自行到setting->security中执行加密操作,footer表明加密的信息存储在userdata的尾部。
了解到这个信息后,我们接着看加密失败的log,首先是kernel log,搜索encrypt有用的信息比较少
kernel log
1 2 3 4 5 6 7 8 |
[ 28.077287] init: processing action (persist.sys.synaptics_dsx.qhd=false) from (/vendor/etc/init/hw/init.target.rc:231) [ 28.077936] init: processing action (encrypt) from (/system/etc/init/vdc.rc:8) [ 28.110739] init: starting service 'exec 4 (/system/bin/vdc --wait cryptfs enablecrypto inplace default noui)'... [ 28.115176] hyp_assign_table: Failed to assign memory protection, ret = -19 [ 28.115222] memshare: hyp_assign_phys failed size=2097152 err=-19 [ 28.144340] init: SVC_EXEC pid 412 (uid 0 gid 0+0 context default) started; waiting... [ 28.144630] init: Command 'exec - root -- /system/bin/vdc --wait cryptfs enablecrypto inplace default noui' action=encrypt (/system/etc/init/vdc.rc:10) returned 0 took 66ms. [ 28.293634] read descriptors |
看kernel log,有用的信息比较少,不过有我们熟悉的流程信息。init最后也是通过vdc进程进行加密相关操作的。加密使用参数为exec 4 (/system/bin/vdc --wait cryptfs enablecrypto inplace default noui)
。接下来再看看main log是否有可疑的信息
main log
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
01-03 01:55:28.954 279 429 E Cryptfs : Cannot load dm-crypt mapping table. 01-03 01:55:28.957 1208 1208 W PackageManager: Failed to parse /system/app/EasterEgg: Not a coreApp: /system/app/EasterEgg 01-03 01:55:28.964 279 429 I EncryptInplace: Encrypting ext4 filesystem in place... 01-03 01:55:28.964 279 429 I EncryptInplace: Encrypting group 0 01-03 01:55:28.992 1208 1208 W PackageManager: Failed to parse /system/app/ExactCalculator: Not a coreApp: /system/app/ExactCalculator 01-03 01:55:29.038 1208 1208 W PackageManager: Failed to parse /system/app/Exchange2: Not a coreApp: /system/app/Exchange2 01-03 01:55:29.042 279 429 E EncryptInplace: Error writing crypto_blkdev /dev/block/dm-0 for inplace encrypt 01-03 01:55:29.042 279 429 I EncryptInplace: Encrypted to sector 0 01-03 01:55:29.047 279 429 E EncryptInplace: Error encrypting groups 01-03 01:55:29.053 1208 1208 I PackageManager: /system/app/ExtShared changed; collecting certs 01-03 01:55:29.054 279 429 D EncryptInplace: cryptfs_enable_inplace_ext4()=-1 01-03 01:55:29.058 279 429 E f2fs_sparseblock: Not a valid F2FS super block. Magic:0x04e700 != 0xf2f52010 01-03 01:55:29.058 279 429 E f2fs_sparseblock: Failed to read superblock 01-03 01:55:29.058 279 429 E EncryptInplace: Failed to encrypt f2fs filesystem on /dev/block/bootdevice/by-name/userdata 01-03 01:55:29.058 279 429 I EncryptInplace: Encrypted to block -1 01-03 01:55:29.058 279 429 D EncryptInplace: cryptfs_enable_inplace_f2fs()=-1 01-03 01:55:29.058 279 429 E EncryptInplace: Encrypting filesystem in place... 01-03 01:55:29.058 279 429 E EncryptInplace: Cannot seek to previously encrypted point on /dev/block/dm-0 01-03 01:55:29.058 279 429 D EncryptInplace: cryptfs_enable_inplace_full()=-1 |
main log的有用信息就稍微多一些了,如上面列出的
- load dm-crypt mapping table失败
- 我们使用的是ext4格式,调用cryptfs_enable_inplace_ext4函数时的返回值是-1,代表失败了
- 接下来的两种系统格式也都失败
通过这点信息,可以猜测下是不是因为dm-crypt mapping table加载失败,导致在调用cryptfs_enable_inplace_ext4函数时传入的参数有误导致。既然这样,我们先来看看为什么加载dm映射表的过程会出问题。FDE过程中,是在cryptfs_enable_internal中调用create_crypto_blk_dev来完成的。
cryptfs_enable_internal
1 2 3 4 5 6 7 8 9 10 11 12 13 |
int cryptfs_enable_internal(char *howarg, int crypt_type, const char *passwd, int no_ui) { ........... // 参数准备,没什么好看的 // 获取加密的master key decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr, 0, 0); // <font color='red'>好的,加载dm映射表在这里进行,下面重点关注这个</font> create_crypto_blk_dev(&crypt_ftr, decrypted_master_key, real_blkdev, crypto_blkdev, CRYPTO_BLOCK_DEVICE); ........... // 反正重点关注create_crypto_blk_dev函数就是了 } |
cryptfs_enable_internal –>create_crypto_blk_dev
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
static int create_crypto_blk_dev(struct crypt_mnt_ftr *crypt_ftr, const unsigned char *master_key, const char *real_blk_name, char *crypto_blk_name, const char *name) { ................ // 参数准备略过 // 打开device-mapper驱动 if ((fd = open("/dev/device-mapper", O_RDWR|O_CLOEXEC)) < 0 ) { SLOGE("Cannot open device-mapper\n"); goto errout; } io = (struct dm_ioctl *) buffer; // 下面通过IOCTL初始化IOBUF,获取device-mapper的基本信息 ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0); err = ioctl(fd, DM_DEV_CREATE, io); if (err) { SLOGE("Cannot create dm-crypt device %s: %s\n", name, strerror(errno)); goto errout; } ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0); if (ioctl(fd, DM_DEV_STATUS, io)) { SLOGE("Cannot retrieve dm-crypt device status\n"); goto errout; } minor = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00); snprintf(crypto_blk_name, MAXPATHLEN, "/dev/block/dm-%u", minor); extra_params = ""; if (! get_dm_crypt_version(fd, name, version)) { /* Support for allow_discards was added in version 1.11.0 */ if ((version[0] >= 2) || ((version[0] == 1) && (version[1] >= 11))) { extra_params = "1 allow_discards"; SLOGI("Enabling support for allow_discards in dmcrypt.\n"); } } // 加载mapping table load_count = load_crypto_mapping_table(crypt_ftr, master_key, real_blk_name, name, fd, extra_params); if (load_count < 0) { // mapping table数量小于0出错 SLOGE("Cannot load dm-crypt mapping table.\n"); goto errout; } else if (load_count > 1) { SLOGI("Took %d tries to load dmcrypt table.\n", load_count); } /* Resume this device to activate it */ ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0); if (ioctl(fd, DM_DEV_SUSPEND, io)) { SLOGE("Cannot resume the dm-crypt device\n"); goto errout; } /* We made it here with no errors. Woot! */ retval = 0; errout: close(fd); /* If fd is <0 from a failed open call, it's safe to just ignore the close error */ return retval; } |
接着看load_crypto_mapping_table函数
create_crypto_blk_dev–>load_crypto_mapping_table
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
static int load_crypto_mapping_table(struct crypt_mnt_ftr *crypt_ftr, const unsigned char *master_key, const char *real_blk_name, const char *name, int fd, const char *extra_params) { ..................... // 参数初始化忽略 // 查看log,#ifdef为true #ifdef CONFIG_HW_DISK_ENCRYPTION if(is_hw_disk_encryption((char*)crypt_ftr->crypto_type_name)) { strlcpy(tgt->target_type, "req-crypt",DM_MAX_TYPE_NAME); if (is_ice_enabled()) convert_key_to_hex_ascii(master_key, sizeof(int), master_key_ascii); else convert_key_to_hex_ascii(master_key, crypt_ftr->keysize, master_key_ascii); } else { convert_key_to_hex_ascii(master_key, crypt_ftr->keysize, master_key_ascii); strlcpy(tgt->target_type, "crypt", DM_MAX_TYPE_NAME); } snprintf(crypt_params, sizeof(buffer) - buff_offset, "%s %s 0 %s 0 %s 0", crypt_ftr->crypto_type_name, master_key_ascii, real_blk_name, extra_params); SLOGI("target_type = %s", tgt->target_type); SLOGI("real_blk_name = %s, extra_params = %s", real_blk_name, extra_params); #else SLOGE("CONFIG_HW_DISK_ENCRYPTION has not defined!"); convert_key_to_hex_ascii(master_key, crypt_ftr->keysize, master_key_ascii); strlcpy(tgt->target_type, "crypt", DM_MAX_TYPE_NAME); snprintf(crypt_params, sizeof(buffer) - buff_offset, "%s %s 0 %s 0 %s", crypt_ftr->crypto_type_name, master_key_ascii, real_blk_name, extra_params); #endif crypt_params += strlen(crypt_params) + 1; crypt_params = (char *) (((unsigned long)crypt_params + 7) & ~8); /* Align to an 8 byte boundary */ tgt->next = crypt_params - buffer; // 尝试10次 for (i = 0; i < TABLE_LOAD_RETRIES; i++) { if (! ioctl(fd, DM_TABLE_LOAD, io)) { break; } usleep(500000); } // 加载10次还是失败,return -1 if (i == TABLE_LOAD_RETRIES) { /* We failed to load the table, return an error */ return -1; } else { return i + 1; } } |
从main log中可以看到,走的是#ifdef CONFIG_HW_DISK_ENCRYPTION为true这一段
1 2 |
01-03 01:55:53.384 279 429 I Cryptfs : target_type = req-crypt 01-03 01:55:53.384 279 429 I Cryptfs : real_blk_name = /dev/block/bootdevice/by-name/userdata, extra_params = fde_enabled |
下面重点来了:可以看到,这里device-mapper使用的加密插件是req-crypt,那么req-crypt是什么东西呢?其次,为什么CONFIG_HW_DISK_ENCRYPTION宏会被定义为true,这个宏看着像是使用硬件加密啊,8909能用?带着疑问继续分析
首先看看req-crypt这个加密插件,req-crypt是在kernel下的drivers/md目录下的dm-req-crypt.c文件
1 2 3 4 5 6 7 8 9 10 |
static struct target_type req_crypt_target = { .name = "req-crypt", .version = {1, 0, 0}, .module = THIS_MODULE, .ctr = req_crypt_ctr, .dtr = req_crypt_dtr, .map_rq = req_crypt_map, .rq_end_io = req_crypt_endio, .iterate_devices = req_crypt_iterate_devices, }; |
然后在drivers/md下的Makefile文件中找到编译条件
1 2 3 |
obj-$(CONFIG_DM_ERA) += dm-era.o <font color='red'>obj-$(CONFIG_DM_REQ_CRYPT) += dm-req-crypt.o</font> obj-$(CONFIG_DM_ANDROID_VERITY) += dm-android-verity.o |
但是这个CONFIG_DM_REQ_CRYPT宏在项目对应的msm8909_defconfig文件中没有找到定义,所以可以判断这个文件是不会编译进内核的。那加载映射表的时候,当然就找不到这个加密插件了。
其次就是CONFIG_HW_DISK_ENCRYPTION这个宏为什么会为true?因为在下面有一个判断的函数is_ice_enabled,所以估计这个硬件加密就是ICE(Inline Crypto Engine)。具体ICE是什么大家可以在kernel/documents下面找到msm_ice_driver.txt,里面会有详细的介绍。
is_ice_enabled
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
int is_ice_enabled(void) { char prop_storage[PATH_MAX]; int storage_type = 0; int fd; if (property_get("ro.boot.bootdevice", prop_storage, "")) { if (strstr(prop_storage, "ufs")) { // 所有的UFS设备都支持 storage_type = QCOM_ICE_STORAGE_UFS; } else if (strstr(prop_storage, "sdhc")) { // sdhc设备需要判断/dev/icesdcc是否存在,不存在就不支持 if (access("/dev/icesdcc", F_OK) != -1) storage_type = QCOM_ICE_STORAGE_SDCC; } } return storage_type; } |
博主这边使用的是EMMC,而且/dev/icesdcc也不存在。那么就暂且判断为不支持ICE了(这里存在另一个可能就是设备是支持ICE的,但是设备数没有配置好,这个哪个兄dei知道怎么判断不?请告知一声)。
如果不支持ICE,那就只能使用软件加密了,这个就比较影响性能了。要使用软件机密,需要解决的问题取消CONFIG_HW_DISK_ENCRYPTION宏的定义了。查看vold的Android.mk文件,有下面这一段玩意
1 2 3 4 5 |
ifeq ($(TARGET_HW_DISK_ENCRYPTION),true) common_c_includes += $(TARGET_CRYPTFS_HW_PATH) common_shared_libraries += libcryptfs_hw <font color='red'>vold_cflags += -DCONFIG_HW_DISK_ENCRYPTION</font> endif |
emmmmmm….,也就是说如果TARGET_HW_DISK_ENCRYPTION有定义为true,那在编译VOLD的时候就会加上CONFIG_HW_DISK_ENCRYPTION这个定义(这里吐槽下,坑爹的使用OpenGrok都没有办法找到这个Android.mk文件中的CONFIG_HW_DISK_ENCRYPTION字符串,只因为全面多了个D就不匹配了)。然后再到设备的BoardConfig.mk文件中看到定义:
1 2 3 4 5 6 7 8 9 10 11 |
# 使用的是QCOM不是AOSP ifeq ($(TARGET_USES_AOSP), true) ................. else # SDClang configuration SDCLANG := true #SDCLANG_PATH :=prebuilts/clang/host/linux-x86/sdclang-4.0/bin SDCLANG_LTO_DEFS := device/qcom/common/sdllvm-lto-defs.mk <font color='red'># 这里默认设置为true的,我们改为false即可</font> TARGET_HW_DISK_ENCRYPTION := true endif |
然后编译试试看,能不能行了?Interesting…………….