现在新出品的手机,基本都加密了data分区,旨在保障用户数据的安全。其中高通方案的手机使用的加密方案是QSEE(高通安全执行环境,Qualcomm Security Executing Environment),每次启动设备时会有一个专门的过程来解密data分区。然而,TWRP Recovery默认并没有附带高通的加密组件,因此在启动时会无法解密和访问data分区,使得体验大打折扣。因此必须把高通的加密组件从Android系统中移植过来。
QSEE加密组件的组成
高通QSEE组件由本体qseecomd
程序、Keystore及其依赖库组成。
首先是qseecomd
。加解密过程全由qseecomd
完成,因此移植过程相对比较简单。
Keystore是加解密过程所必需的“钥匙”,为一个so
格式的库文件,每个高通方案设备有属于自己的Keystore,路径为/system/vendor/lib64/hw/keystore.<高通方案型号>.so
(32位处理器型号请将lib64
改为lib
)。
而qseecomd
运行还需依赖其它库文件,查看依赖可使用readelf -d qseecomd
:
Dynamic section at offset 0x2cb8 contains 35 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libc.so]
0x0000000000000001 (NEEDED) Shared library: [libcutils.so]
0x0000000000000001 (NEEDED) Shared library: [libutils.so]
0x0000000000000001 (NEEDED) Shared library: [liblog.so]
0x0000000000000001 (NEEDED) Shared library: [libdl.so]
0x0000000000000001 (NEEDED) Shared library: [libQSEEComAPI.so]
0x0000000000000001 (NEEDED) Shared library: [libdrmfs.so]
0x0000000000000001 (NEEDED) Shared library: [libc++.so]
0x0000000000000001 (NEEDED) Shared library: [libm.so]
......
值得注意的是,上述命令给出的依赖库,除了libQSEEComAPI.so
与libdrmfs.so
由高通提供外,其他的均为安卓公用的运行库,TWRP会提供它们。
另外,qseecomd
的运行还离不开解释器linker
,使用readelf -l qseecomd
,在输出中可以看到qseecomd
所使用linker
的绝对路径。
Elf file type is DYN (Shared object file)
Entry point 0x125c
There are 9 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040
0x00000000000001f8 0x00000000000001f8 R E 0x8
INTERP 0x0000000000000238 0x0000000000000238 0x0000000000000238
0x0000000000000015 0x0000000000000015 R 0x1
[Requesting program interpreter: /sbin/linker64]
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000001dd8 0x0000000000001dd8 R E 0x10000
LOAD 0x0000000000002c88 0x0000000000012c88 0x0000000000012c88
0x0000000000000540 0x0000000000000540 RW 0x10000
DYNAMIC 0x0000000000002cb8 0x0000000000012cb8 0x0000000000012cb8
0x0000000000000270 0x0000000000000270 RW 0x8
NOTE 0x0000000000000250 0x0000000000000250 0x0000000000000250
0x0000000000000038 0x0000000000000038 R 0x4
GNU_EH_FRAME 0x0000000000001cc0 0x0000000000001cc0 0x0000000000001cc0
0x0000000000000024 0x0000000000000024 R 0x4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
GNU_RELRO 0x0000000000002c88 0x0000000000012c88 0x0000000000012c88
0x0000000000000378 0x0000000000000378 R 0x1
找到加密所需的文件
在上一步中,我们已经知道了加密需要用到哪些文件,那么它们位于安卓系统的哪一个路径?根据下面的表格,整理出它们所在的路径,以及放置到TWRP根文件系统中的路径。
文件 | 在Android系统中的路径 | TWRP中的路径 |
---|---|---|
qseecomd | /system/bin/qseecomd | /sbin/qseecomd |
libQSEEComAPI.so | /system/vendor/lib64/libQSEEComAPI.so[1] | /vendor/lib64/libQSEEComAPI.so |
libdrmfs.so | /system/vendor/lib64/libdrmfs.so | /vendor/lib64/libdrmfs.so |
将文件复制到TWRP源码中
Android设备配置文件(/device/<厂商名>/<设备名>
)中,如果存在目录recovery/root
,那么该目录中的内容就会在编译TWRP时,自动复制到TWRP的根文件系统中。因此,请将上一步中的文件,根据上表的路径复制到其中。
使用patchelf
修改qseecomd
的linker路径
系统环境与TWRP的环境还是有所差别的。为了保证qseecomd
能够正确地找到解释器linker
,我们还需要使用patchelf
工具,对qseecomd
进行一番修改,否则运行时会提示“qseecomd: not found
”,尽管qseecomd
确实在指定的目录中。
qseecomd
默认的linker是/system/bin/linker64
,而TWRP的linker则是/sbin/linker64
。因此,我们需要这样修改linker路径:
patchelf --set-interpreter /sbin/linker64 qseecomd
修改完成后,再使用readelf -l qseecomd
进行检查,就可以发现修改成功了。
将qseecomd
注册为服务
qseecomd
以服务的形式运行,它会在运行之时自动对data分区进行解密。想让qseecomd
作为服务运行,需要修改init.recovery.qcom.rc
,这是Android的init配置文件之一。
创建该文件,在其中写入如下内容:
on property:ro.crypto.state=encrypted
stop qseecomd
start qseecomd
#
# qseecomd 的服务项
# 设置为随TWRP启动而自动启动
#
service qseecomd /sbin/qseecomd
user root
group root
seclabel u:r:recovery:s0
#
# 修复TWRP的一个Bug:删除无效的bootdevice链接并重新创建
# 默认地,TWRP会在/dev/block目录下创建一个bootdevice链接,指向启动设备,
# 但因为未知的原因使得该链接无效。因此需要重新创建之,否则qseecomd会无法识别启动设备,导致出错
#
on boot
exec u:r:recovery:s0 -- /sbin/busybox rm -r /dev/block/bootdevice
symlink /dev/block/platform/7824900.sdhci /dev/block/bootdevice
on fs
#
# 保险起见,在这个地方也尝试重新创建设备链接
#
symlink /dev/block/platform/7824900.sdhci /dev/block/bootdevice
#
# 设置qseecomd所需设备的权限
#
chmod 0660 /dev/qseecom
chown system drmrpc /dev/qseecom
chmod 0664 /dev/ion
chown system system /dev/ion
设置TWRP参数
TWRP本身有解密模块,并提供与qseecomd
对接的接口,但默认没有启用。因此我们需要在BoardConfig.mk
设置相应的选项:
TARGET_PROVIDES_KEYMASTER := true
TARGET_KEYMASTER_WAIT_FOR_QSEE := true
TARGET_PROVIDES_KEYMASTER
指定设备是否具有用于解密的keymaster,keymaster正是解密必不可少的“钥匙”,高通方案的设备会提供。而TARGET_KEYMASTER_WAIT_FOR_QSEE
则指定是否等待qseecomd
解密完成。其中,TARGET_KEYMASTER_WAIT_FOR_QSEE
至关重要,它是qseecomd
解密支持的重要开关,若不设置它,就和不带加密组件无异。
调试
检查工作状态
判断TWRP是否正常解密的依据,就是观察启动后会出现什么画面。如果启动非常快,且显示的是输入密码的窗口,而事实上手机并没有使用密码加密,而是保持默认加密状态,那么说明解密不成功,还需努力调试。如果启动时间延长(定格在splash画面),进入主界面后点击底部的“日志”按钮显示“Data successfully decrypted”,那么则说明解密成功。
获取日志
并不是所有的设备都能遵照上述步骤成功为TWRP启用加密支持,各种各样的问题都有可能出现。显然,我们可以根据日志,来检查qseecomd
的工作状态——qseecomd
会同时往内核日志和logcat中写入日志。使用dmesg
或cat /proc/kmsg
来获取内核日志;而在BoardConfig.mk
中启用logcat支持后,我们亦可以通过运行logcat
来获取qseecomd
输出的另一部分日志。
启用logcat支持的开关为:
# Logcat
TWRP_INCLUDE_LOGCAT := true
TARGET_USES_LOGD := true
除此之外,我们还可以从TWRP这一边展开分析。TWRP的解密过程也被记录在TWRP本身的日志中,阅读/cache/recovery/last_log*
即可了解。
总结体会
为TWRP加入解密组件,看似比较难,实则并不难,关键在于不断尝试。笔者能获得上面的成果,有赖于在反复失败后仍然能够反复尝试,最终取得成功。在整个过程当中,日志调试的作用举足轻重,它反馈了TWRP与qseecomd组件运行时的一系列状况,是分析故障的利器。
-
系统中,根目录有一个
/vendor
,链接到/system/vendor
。 ↩