[车联网安全自学篇] ATTACK安全之检测BootLoader+内核+文件系统的安全启动固件的异常篡改行为「一」


也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大

少走了弯路,也就错过了风景,无论如何,感谢经历


0x01 前言

Android作为一个应用广泛的平台,同样支持Verified Boot。以硬件提供保护作为信任根,实现从bootloader到boot分区以及其他分区(system、vendor、oem等)完整信任链路,在每一步转交执行权限之前都会对数据和代码的完整性(integrity)和真实性(authenticity)进行验证。几个关键节点如下:

  • Android 4.4:提供内核dm-verity特性,实现Verified Boot,启动时如果分区损坏会提示用户
  • Android 7.0:禁止未验证的设备启动,同时引入前向纠错功能,支持自动修复非恶意的数据损坏
  • Android 8.0:引入支持Treble的Verified Boot,称为AVB(Android Verified Boot)或者Verified Boot 2.0,对分区尾部的数据格式进行格式化,并增加版本回滚保护的功能

这只是Android官方的feature时间结点,事实上许多OEM厂商也会针对启动功能进行自定义的配置,比如使用AOSP中的宏来设置或者在自己的私有bootloader中使用不同的校验方式

在 IoT以及IoV 中保护Android 设备安全性的重要一环就是保证固件、代码的完整性,以及被用来对付一些恶意软件试图感染系统的固件的安全威胁。通常为了对付这些安全威胁,一方面是及时修复现有攻击面所面临的漏洞,比如浏览器、蓝牙、调试接口;另一方面需要确保的是即便恶意代码获取了执行权限,也无法修改系统镜像进行持久化。针对这点所构造的安全方案通常称为 Secure Boot

Secure Boot 顾名思义它也称为安全启动,确保当前设备启动之后所加载执行的代码都是可信的,其中主要概念分为:

  • 信任链:保障系统执行流程的可靠交接
  • 信任根:保障系统初始信任代码的可信

Secure Boot (安全启动) 建立了一个完整的信任链 (通过签名公钥验证文件或程序的数据签名,确保启动文件或程序的完整和可信,以防止在启动过程中加载并运行了未经授权的程序),从受硬件保护的信任根开始到引导加载程序、启动分区和其他经过验证的分区,包括系统、vendor和OEM分区。在设备启动期间,每个阶段都会在移交执行之前验证下一个阶段的完整性和真实性。Secure Boot (安全启动) 被用来对付一些恶意软件试图感染系统的固件的安全威胁。用户可能会无意中禁用或软件可能会有意禁用安全启动。因此,配置不正确的话,系统就运行在一个不安全的平台上。它的原理是给引导程序签名,让你的计算机信任这个签名,并仅允许启动有可信签名的引导程序。一旦有人恶意往引导程序注入些东西时,由于签名被破坏,计算机就出于安全目的停止启动了

:在安全启动机制下,所有启动文件(如:启动引导程序、内核镜像、基带固件)均需先通过签名校验才被允许加载运行,在启动过程的任何阶段,如果签名验证失败,则启动过程会被终止。

安全启动要求在启动之前对所有固件代码和数据的真实性进行加密验证。这包括内核(从引导分区加载)、设备树(从dtbo分区加载)、系统分区、vendor 分区等

设备的安全启动流程如下:

  • 引导加载程序身份验证
  • 可信初始化
  • VBMeta 验证
  • Linux 内核验证
  • 文件系统完整性检查

在这里插入图片描述

: Android系统基于Linux,所以bootloader部分也是与传统的嵌入式设备上运行的Linux没有什么区别,由于除Google外的大部分Android厂商都没有提供bootloader的源代码,所以分析手机设备的bootloader需要使用逆向工程的手段,当然由于有了Google官方的开源bootloader代码做参考,能让分析工作轻松不少。需要注意Secure Boot对于不同的厂商,实现上可能会引入不同的名字,比如 Verified Boot、High Assurance Boot 等等,但本质上都是类似的

说道BootLoader,那也要说一下固件。固件(firmware)是一种写入硬件设备的软件,作用是对应用和各项系统功能实时控制。固件中包含底层代码,这些代码能实现软件对硬件的操作。运行固件的设备称为嵌入式系统,嵌入式系统的硬件资源在存储能力以及内存等方面往往具有诸多限制。举例来说,智能手机、智能终端、交通信号灯、无人机、机顶盒都是运行固件的嵌入式设备。

固件通常由bootloader、内核、根文件系统及其他资源组成。根据嵌入式Linux、嵌入式Windows(WinCE)、Windows IoT内核及各种实时操作系统(RTOS)的区别,固件也有多种类型

IoT设备构成分为硬件和软件,软件方面如下:

  1. BootLoader:在IoT设备系统启动前,它初始化了硬件设备,将固件加载到引导设备。它使系统的软件和硬件环境达到合适的状态
  2. 固件:固件包括了操作系统、文件系统和一系列服务程序。IoT设备上的安全研究通常从固件分析开始

1.1 根文件系统

根文件系统首先是内核启动时所mount的第一个文件系统,内核代码映像文件保存在根文件系统中,而系统引导启动程序会在根文件系统挂载之后从中把一些基本的初始化脚本和服务等加载到内存中去运行

Linux根文件系统中一般有如下的几个目录:

  • /bin目录

该目录下的命令可以被root与一般账号所使用,由于这些命令在挂接其它文件系统之前就可以使用,所以/bin目录必须和根文件系统在同一个分区中

  • /sbin 目录

该目录下存放系统命令,即只有系统管理员(最高权限的root)能够使用的命令,系统命令还可以存放在/usr/sbin、/usr/local/sbin目录下,/sbin目录中存放的是基本的系统命令,它们用于启动系统和修复系统等,与/bin目录相似,在挂接其他文件系统之前就可以使用/sbin,所以/sbin目录必须和根文件系统在同一个分区中

  • /dev目录

该目录下存放的是设备与设备接口的文件,设备文件是Linux中特有的文件类型,在Linux系统下,以文件的方式访问各种设备,即通过读写某个设备文件操作某个具体硬件

  • /etc目录

该目录下存放着系统主要的配置文件,例如人员的账号密码文件、各种服务的其实文件等。一般来说,此目录的各文件属性是可以让一般用户查阅的,但只有root有权限修改

  • /lib目录

该目录下存放共享库和可加载(驱动程序),共享库用于启动系统。运行根文件系统中的可执行程序,比如:/bin /sbin目录下的程序

  • /root目录

系统管理员(root)的主文件夹,即是根用户的目录,与此对应,普通用户的目录是/home下的某个子目录

  • /var目录

/var目录中存放可变的数据,比如spool目录(mail,news),log文件,临时文件

  • /proc目录

这是一个空目录,常作为proc文件系统的挂接点,proc文件系统是个虚拟的文件系统,它没有实际的存储设备,里面的目录,文件都是由内核临时生成的,用来表示系统的运行状态,也可以操作其中的文件控制系统

  • /mnt目录

用于临时挂载某个文件系统的挂接点,通常是空目录,也可以在里面创建一引起空的子目录,比如/mnt/cdram /mnt/hda1 。用来临时挂载光盘、移动存储设备等

  • /tmp目录

用于存放临时文件,通常是空目录,一些需要生成临时文件的程序用到的/tmp目录下,所以/tmp目录必须存在并可以访问

对于嵌入式Linux系统的根文件系统来说,一般可能没有上面所列出的那么复杂,比如嵌入式系统通常都不是针对多用户的,所以/home这个目录在一般嵌入式Linux中可能就很少用到,而/boot这个目录则取决于你所使用的BootLoader是否能够重新获得内核映象从你的根文件系统在内核启动之前。一般说来,只有/bin,/dev,/etc,/lib,/proc,/var,/usr这些需要的,而其他都是可选的

固件可采用的文件类型有很多种,常见的有squashfs、cramfs、ext2、jeffs2等。其中设备(尤其是消费级电子设备)最常采用的文件系统是squashfs,攻击者可使用unsqushfs工具对文件系统提取数据。需要注意的是,部分厂商采用了非标准的squasgfs压缩算法,如LZMA和XZ。例如以某路由潘多拉固件为模型,对该固件进行提取和分析,关于本固件的文件系统和压缩算法如下所示:

在这里插入图片描述

如上图,该固件文件即为squashfs文件系统,并使用LZMA和XZ压缩算法

:SquashFS是基于Linux内核使用的压缩只读文件系统。该文件系统能够压缩系统内的文档,inode以及目录,文件最大支持2^64字节。每一个文件都有对应的inode,里面包含了与该文件有关的一些信息。inode包含文件的元信息,具体来说有Size 文件的字节数、Uid 文件拥有者的User ID、Gid 文件的Group ID、Access 文件的读写执行权限等。linux可以用stat命令,查看某个文件的inode信息。

对squashsf文件系统的解压、提取,需要借助sasquatch工具,可从https://github.com/devttys0/sasquatch下载安装。如果sasquatch工具不起作用,也可以使用7-zip对后缀为squashsf的文件进行提取(推荐本方式)

:我们可以把文件系统看作存储配置文件、服务、账号口令、哈希、应用程序代码及启动脚本的地方

1.2 Android 各个分区介绍

  • boot:包含linux 内核和一个最小的root文件系统(装载到ramdisk中),用于挂载系统和其他分区,并开始runtime
  • system:这个分区几乎包含了除了Kernel 和 ramdisk之外的整个android 操作系统
  • userdata:用户数据区
  • cache:系统缓存区,临时的保存应用数据,OTA升级包及临时生成文件
  • recovery:包括了一个完整的Linux 内核和一些特殊的recovery binary,可以读取升级文件,用这些文件来更新其他的分区
  • misc:一个非常小的分区,4MB左右,recovery 使用这个分区来保存一些关于升级的信息,应对升级过程中的设备掉电重启的状况,bootloader启动的时候,会读取这个分区里面的信息,以决定是否进入recovery 或者 main system

1.3 Android 基本分区

  • bootloader(fastboot)

Android 系统虽然也是基于 Linux 系统的,但是由于 Android 属于嵌入式设备,并没有像 PC 那样的 BIOS 程序。 取而代之的是 BootLoade 系统启动加载器

一旦bootloader分区出了问题,手机变砖就很难救回了。除非借由高通9008或者MTK-Flashtool之类更加底层的模式来救砖,如果要刷机的话,一般刷机不动这个分区

大部分 bootloader 都支持 fastboot 协议或 download 模式,并支持对设备的持久性存储分区进行写入或者不刷入设备zhijie启动临时系统

  • recovery

用于存放recovery恢复模式的分区,刷机、root必须要动的分区。里面有一套linux内核,但并不是安卓系统里的那个,相当于一个小pe的存在。现阶段的刷机、root基本都要用第三方rec来覆盖官方rec

recovery 操作系统是一个小型的基于 Linux 的操作系统,包含有各种底层工具的 RAM 磁盘,和一个通过设备硬件按钮操作的小型UI。recovery 系统保存在一个特定的分区中,可以在 bootloader处于 download 模式的情况下刷入一个第三方的 recovery,之后即可替换对于公钥或者关闭签名验证。这样 recovery 就允许主系统被一个第三方操作系统镜像完全替换

  • boot

引导分区,虽然说是引导,但实际是在bootloader之后,与recovery同级的启动顺序。里面装了安卓的linux内核相关的东西,magisk就是修改了这部分程序,实现的root权限的获取

存放 kernel(以及附带的ramdisk) 的分区

  • userdata(data)

用户数据分区,被挂载到/data路径下。内置存储器(内置存储卡)实际也存在这个分区里,用户安装的apk、app的数据都在这个分区。目前主流安卓版本中data分区通过fuse进行了强制加密,密码一般都是屏锁密码,且加密的data分区未必能在recovery下成功解密,所以有时刷机需要清除整个data分区

  • cache

缓存分区,一般用于OTA升级进入recovery前,临时放置OTA升级包以及保存OTA升级的一些临时输出文件

主要用于旧Android设备(出厂Android版本 < 7.0) OTA 升级过程中。出厂版本 Android 7.0以及以上版本设备没有这个分区

  • system

系统分区。通常在Android运行时这个分区是挂载为只读的,只有在 OTA升级/官方刷机软件刷机时才会修改其中内容,root 后可以挂载为可写分区并在运行时任意修改其中内容

/system/app:ROM 预装的系统 app
/system/priv-app:ROM "私有的"预装的系统 app
  • data

数据(userdata)分区,即手机的内置存储(Internal Storage)物理分区

数据(userdata)分区里面包括:

/data/app:用户安装的 App
/data/app-private:部分 app 位于这里
/data/data:所有 App 的数据
/data/media:内置存储(Internal Storage),包括设备每个用户的 /sdcard
/data/dalvik-cache:Dalvik / ART Cache(某些手机里这是一个独立物理分区)
  • 其它分区
/radio:基带(baseband) 分区
/vendor:通常和 system 一样,也是仅在系统升级时修改。驱动分区,例如相机驱动、音频驱动等
/misc
  • Uboot

Uboot 引导内核启动后,内核将负责验证 Android 系统分区,验证通过后将其挂载为只读的系统分区。Android 启动后,modem_control服务负责校验和加载 modem 子系统的固件

  • 虚拟分区 / 挂载点

/storage:

存放 Internal Storage / External Storage (包括SD卡) 等挂载点的 tmpfs

/storage/emulated:

所有用户的 Internal Storage 存放路径。实际指向 /data/media

/sdcard:

等效于 Linux 的 ~ 用户根目录。是一个符号链接,实际指向 /data/media/{USER_ID},对于第一个()用户,即为 /data/media/0。(注:某些Android <= 5.0 的旧设备 /sdcard 和 /data 是两个不同的物理分区,这些旧设备经常会遇到 /sdcard 和 /data 其中一个可用空间不足的问题)

1)/storage/sdcard不是实际物理分区:

在 (较新版本)TWRP 里wipe /data 时不会清除/data/media,因此不影响 /sdcard 里内容。(所以这个 wipe 工作在文件系统层,而不是直接格式化块设备?);而 “format data”则会格式化整个 /data。TWRP 提供的 “Factory reset” 快捷操作按钮会重置Dalvik / ART Cache, /cache/data (除/data/media) 这3个分区内容。(Dalvik / ART cache实际上也位于 /data 分区里)

除一些小分区外,设备闪存划分为 /system /data 两个主要分区。/system一般出厂划分1-3GB,剩余的大部分空间都在/data

/system, /data等各分区大小是设备出厂时固定的。可以在 recovery 里用 fdisk 等工具重新分区以调整各分区大小,但过程十分复杂、与具体设备相关并且具有危险性(错误操作设备会变砖)。少数情况下,某些 OTA 升级包也会调整分区划分

/sdcard是通常 APP 可见的所谓“外置存储”(external storage)目录。这里的“外置存储”名字与“内置存储”(internal storage)相对,其名称来源是因为早期Android设备(小于Android 4.0)自带flash存储很小(几百MB),所有应用和数据都需要安装在额外插入的SD卡里

如果手机有 SD 卡槽,那么插入的 SD 卡会被挂载为 /sdcard1/sdcard /sdcard1都属于“外置存储”,“外置存储”对所属用户完全开放访问权限;APP 访问“外置存储”需要READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE权限。而 /data 里除 /data/media 以外的其他数据,以及 /data/media里非当前用户的主文件夹都是系统保护的,不是root无法访问

1.3.1 Android 系统分区介绍

  • hboot:系统开机引导类似电脑BIOS,这块刷错手机就会变成砖
  • radio:通讯模块、基带、WIFI、Bluetooth等衔接硬件的驱动软件
  • recovery:系统故障时负责恢复
  • boot:Linux嵌入式系统内核
  • system:系统文件、应用
  • cache:系统运行时产生的缓存
  • userdata:用户使用APP产生的缓存数据

1.4 BootLoader

CPU是手机上面最复杂,最贵的Soc(芯片),担任的也是手机中大脑的位置,是手机跑分性能的决定性硬件。智能手机发展到今天,各大手机CPU厂商也从春秋战国逐渐到了现在四国鼎立的时代(高通,MTK,三星,苹果A系列)。当然最大的CPU厂商还是Intel,只不过intel的主力是在x86架构的处理器,主打PC与服务器产品。而我们今天的主角还是ARM架构的移动端处理器(手机CPU)

手机CPU中的分类:

  • AP应用处理器

手机CPU中最主要的一部分,手机的系统运作还有APP的运行,靠的都是AP应用处理器。例如:苹果A9处理器指的就是AP

  • BP基带处理器:

其实很多玩家都只听过基带这个东西,但不知道这个到底是什么。基带处理器管理的是手机一切无线信号(除了wifi,蓝牙,NFC等等),一款手机支持多少种网络模式,支持4G还是3G,都是由基带部分决定的。BP做的最有名的是高通,其实高通发家靠的就是优秀的BP基带处理器,而不是AP应用处理器

可能大家对高通的BP没有什么印象,这里我就跟大家举几个使用高通BP的手机。iPhone4到iPhone6sp全系列手机都是使用高通的BP(这些手机都是我亲手拆解过的,iPhone3Gs等等我不能肯定是不是高通的BP),还有全部3G版的iPad,加上三星这几年的旗舰也是都是使用的BP,还有大量使用高通CPU的手机产品也都是使用高通的BP

  • CP多媒体加速器:

其实每个厂商对CP都有不同的名字,比如苹果把它叫做协处理器,高通820叫做“低功率岛”。在早期CP只用于解码视频和处理音频等等简单任务

但是各大厂商发现,CP的性能其实也可以很高,于是开始处理的东西越来越多。现在的CP已经可以处理虚拟现实,增强现实,图像处理,HIFI,HDR,传感器等等

ARM架构和X86架构的区别:

  • ARM架构:

ARM架构使用的是精简指令集,我们可以把它看成一辆汽车,在之前一直都是低功耗的代名词

  • X86架构:

X86架构使用的是复杂指令集,我们可以把它看成一架飞机,在之前一直都是高性能的代名词

最开始也是因为低功耗,所以移动端的设备,都是使用的ARM。但是随着移动端的高速发展,ARM架构的处理器的性能也开始变得原来越强大。

  • 那么ARM架构的CPU对比PC端的CPU实际运行起来到底有什么区别了?

比如,一条指令来了,要把一个货物从北京运送到上海,这个时候我们会发现飞机必须快过汽车,但是如果另一个指令是要把一个货物从街头运到街尾了?这个时候,飞机发现,它要想做到基本是不太可能,这个时候只有在增加一条新的指令集(相当于要重新制作一架大小适合的飞机了)

但是随着移动端设备的高速发展,ARM架构的性能已经变得越来越强了,ARM架构的性能超过X86架构已经只是时间问题了

查看CPU是哪个厂商的,输入如下命令:

cat /proc/cpuinfo

processor       : 0 	//系统中逻辑处理核的编号。对于单核处理器,则可认为是其CPU编号,对于多核处理器则可以是物理核、或者使用超线程技术虚拟的逻辑核
vendor_id       : GenuineIntel //厂商标识
cpu family      : 64	//CPU产品系列代号
model           : 79	//CPU属于其系列中的哪一代的代号
model name      : Intel(R) Xeon(R) CPU E5-2682 v4 @ 2.50GHz	//CPU属于的名字及其编号、标称主频
stepping        : 1	//CPU属于制作更新版本
microcode       : 0x1 //微码
cpu MHz         : 2499.996	//CPU的实际使用主频
cache size      : 40960 KB	//CPU二级缓存大小
physical id     : 0	//单个CPU的标号
siblings        : 1	//单个CPU逻辑物理核数
core id         : 0	//当前物理核在其所处CPU中的编号
cpu cores       : 1	//该逻辑核所处CPU的物理核数
apicid          : 0	//用来区分不同逻辑核的编号,系统中每个逻辑核的此编号必然不同
initial apicid  : 0	//初始逻辑核的编号
fpu             : yes	//是否具有浮点运算单元(Floating Point Unit)
fpu_exception   : yes	//是否支持浮点计算异常
cpuid level     : 13	//执行cpuid指令前,eax寄存器中的值,根据不同的值cpuid指令会返回不同的内容
wp              : yes	//表明当前CPU是否在内核态支持对用户空间的写保护(Write Protection)
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl eagerfpu pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm rdseed adx smap xsaveopt	//当前CPU支持的功能
bogomips        : 4999.99	//在系统内核启动时粗略测算的CPU速度(Million Instructions Per Second)
clflush size    : 64	//每次刷新缓存的大小单位
cache_alignment : 64	//缓存地址对齐单位
address sizes   : 46 bits physical, 48 bits virtual	//可访问地址空间位数
power management:	//对能源管理的支持

开头说了那么多废话,回到正题。在 Android 操作系统更多是软件的开源平台,但软件的某些方面受到制造商或谷歌的限制。在这方面,OEM Unlock是重中之重。您可能会喜欢自定义 ROM、内核和植根您的设备,如果您要在任何 Android 手机上解锁引导加载程序,那么首先您必须在 Android 手机上启用 OEM 解锁。OEM 解锁选项始于 2014 年的 Android 5 或棒棒糖(lollipop)。这是因为谷歌带来的安全原因。由于我们现在处于Android OreoAndroid Pie的时代,这也是必须要了解如何启用OEM 解锁的其中一个原因

默认情况下,大多数 Android 设备都附带一个已锁定的引导加载程序,这意味着用户无法刷写引导加载程序或设备分区。如果需要,你(以及启用了开发者选项的设备用户)可以解锁引导加载程序来刷写设备新映像

OEM 解锁现在是隐藏在手机中的 Android 安全性的永久部分,但是解锁OEM没有限制。这意味着可以在设置中非常轻松地解锁它。但在此之前,应该知道它是如何工作的,以及为什么我们需要在 Android 手机上解锁 OEM。如果想安装 ROM、 root手机和解锁引导加载程序,OEM功能是我们要进行下一步操作的第一步

如需解锁引导加载程序并允许重新刷写分区,可在设备上运行fastboot flashing unlock 命令。设置完毕后,解锁模式会在重新启动后保留

除非 get_unlock_ability设置为 1,否则设备应拒绝 fastboot flashing unlock命令。如果设为 0,用户需要启动进入主屏幕,打开设置 > 系统 > 开发者选项菜单,然后启用 OEM 解锁 选项(将 get_unlock_ability设置为 1)。设置完成后,此模式在系统重新启动和恢复出厂设置后将保持不变。

:发送 fastboot flashing unlock命令后,设备会提示用户,警告他们非官方映像可能存在问题。在用户确认收到警告后,设备应恢复出厂设置,以防止未经授权的数据访问。即使引导加载程序无法正确对设备执行重新格式化,也应重置设备。只有在恢复出厂设置后,才能设置持久标志,以便重新刷写设备。

所有尚未覆盖的 RAM 都应在 fastboot flashing unlock过程中被重置。此措施可防止出现读取上次启动剩余 RAM 内容的攻击。同样,解锁的设备应在每次启动时清除 RAM(除非这样做会造成不可接受的延迟),但应保留供内核的 ramoops 使用的区域

:当然,如需锁定引导加载程序并重置设备,请在设备上运行fastboot flashing lock命令。打算发布销售的设备应以锁定状态发货(get_unlock_ability返回 0),以确保攻击者不能通过安装新的系统或启动映像来入侵设备

  • 什么是OEM?

设备制造商(也称为OEM),一般来说,在开发者选项中基本都有一个名为「OEM 解锁」的选项。除了少部分流入我国市场的国外运营商有锁机外,此选项基本都可供用户开启

解锁 BootLoader 后会立即清空手机中所有数据,但这也是很多玩机操作必经的第一步,建议尽早开启。解锁 BootLoader 还意味着手机等私密设备的安全性大大下降,不少厂商也会在解锁政策上附加上不少条款

  • 什么是bootloader?

顾名思义,BootLoader是操作系统在启动之前需要执行的一段小程序(通过这段小程序,可初始化硬件设备、建立内存空间的映射表,从而建立适当的系统软硬件环境,为最终调用操作系统内核做好准备)。在系统上电时开始执行,主要功能是初始化硬件设备、准备好软件环境,最后调用操作系统内核。BootLoader种类比较多,常见的X86上的有LILO、GRUB,ARM架构上比较有名的是U-boot、VIVI。以及其他的RedBoot/Etherboot

:在嵌入式系统中,通常没有像BOIS那样的固态程序,因此整个系统的加载启动任务就有bootloader来完成,bootloader程序通常安排在嵌入式系统最开始运行的地址处0x0000 0000

bootloader的作用主要包括RAM的初始化(存储易失性数据),串口初始化、设备类型检测、内核参数链表设置、initramfs(基于RAM的初始文件系统)加载及内核镜像调用等(简单地来说就是,bootloader:主要功能是对硬件环境进行初始化、更新固件及认识操作系统的文件格式并将内核加载到内存中去执行)。BootLoader通过板机支持包(BSP)初始化硬件驱动。简单的说,bootloader和PC的BIOS在启动时的作用是一致的。ARM、MIPS架构架构中常见的bootloader包括redboot、u-boot及barebox等。在bootloader启动内核后,文件系统也就完成了加载过程

:虽然,Android系统是开源的,但是设备制造商为了保证系统的稳定性,以及维护自身某些方面的利益,并不希望用户更换为其它厂商的ROM。因此,通常各家设备制造商都会对BootLoader进行加锁。在设备BootLoader处于锁定状态时,无法对ROM进行更改,不能进行root操作,也无法刷机为别的ROM。而要解除这些限制,就需要对BootLoader进行解锁

软件层面的攻击对应着设备构成中bootloader和固件的软件部分,它主要包括以下五个方面:不安全的bootloader、不安全的操作系统、固件敏感信息泄露、不安全的应用服务、不正确的配置策略

  1. 不安全的bootloader

因为bootloader是一段在设备运行后加载的代码,因此是一个容易被忽略的攻击点。它的功能是初始化并加载固件,因此当问题出现时它的危险程度很高。例如checkm8这个Boot ROM 漏洞被称为是iphone、ipad、apple TVapple watch上的史诗级漏洞

1.4.1 引导加载程序logcat日志中的一些常见含义

引导加载程序logcat日志中的一些常见含义介绍,如下:

关键日志含义
boot_progress_start 代表着系统进入用户空间,标志着kernel启动完成,Android屏幕点亮,开始显示启动动画
boot_progress_preload_start Zygote启动
boot_progress_preload_end Zygote结束
boot_progress_system_runSystemServer ready,开始启动Android系统服务,如PMS、APMS等
boot_progress_pms_startPMS开始扫描安装的应用
boot_progress_pms_system_scan_startPMS先行扫描/system目录下的安装包
boot_progress_pms_data_scan_startPMS扫描/data目录下的安装包
boot_progress_pms_scan_endPMS扫描结束
boot_progress_pms_ready PMS就绪
boot_progress_ams_ready AMS就绪
boot_progress_enable_screen AMS启动完成后开始激活屏幕,从此以后屏幕才能响应用户的触摸
sf_stop_bootanimSF设置service.bootanim.exit属性值为1,标志系统开机动画结束
wm_boot_animation_done开机动画结束,这一步用户能直观感受到开机结束

1.4.2 什么是OEM?

设备制造商(也称为OEM)

一般来说,在开发者选项中基本都有一个名为「OEM 解锁」的选项。除了少部分流入我国市场的国外运营商有锁机外,此选项基本都可供用户开启

解锁 BootLoader 后会立即清空手机中所有数据,但这也是很多玩机操作必经的第一步,建议尽早开启。解锁 BootLoader 还意味着手机等私密设备的安全性大大下降,不少厂商也会在解锁政策上附加上不少条款

1.4.3 如何在 Android 上启用 OEM 解锁?

它是设备设置中开发者选项下的一个隐藏选项,它在安全性中也起着重要作用。我们无法在不解锁 OEM 的情况下强制重置设备、擦除所有数据,但擦除数据和强制重置是非常不同的任务

有两种方法可以在各种安卓设备上启用 OEM 解锁:

  • 第一种方法:在大多数设备的开发者选项下启用 OEM 解锁
  • 第二种方法:没有直接选项的设备的话,我们首先进行开发者选项下启用USB调试,然后利用fastboot工具来解锁

1.4.4 什么是 OEM 解锁?

OEM解锁是什么意思?为防止对设备造成任何意外损坏,Android 设备制造商已锁定引导加载程序。因此,为了解锁 BootLoader 我们必须在 Android OS 的开发人员选项菜单中启用(原始设备制造商)OEM 解锁,此时设备才会允许您安装自定义恢复(例如 TWRP)、使用自定义 ROM 刷新设备、root 设备以及修改内核。设备制造商也是有意隐藏此功能,以防止普通用户意外弄乱自身使用的操作系统

OEM 解锁是 Android 设备附带的隐藏安全功能,以防止任何意外访问,主要用于在任何 Android 设备上启用引导加载程序。首先,要进行解锁引导加载程序之前要先 OEM 解锁。OEM 代表原始设备制造商

在这里插入图片描述

当您在开发者选项下打开“OEM Unlock”开关时,它会将“unlock_ability”标志设置为:“1”。即使,重新启动设备或执行恢复出厂设置,此标志也会保持不变。除非手动将其关闭,否则此选项将保持启用状态

1.4.5 OEM解锁有什么用?

启用 OEM 解锁允许您自定义 Android 操作系统,并最终允许您解锁智能手机上的引导加载程序。解锁OEM后我们能干什么?

  • 允许访问引导加载程序和管理设置
  • 允许安装自定义恢复和 ROM
  • 安装自定义内核
  • 删除预安装的臃肿软件
  • Android 设备的 Root权限
  • 安装被管理阻止的应用程序
  • 管理分区和修改系统文件
  • 安装著名的模组,例如Viper4Android (Magisk)
  • 超频甚至降频以节省电池
  • 在最新软件上运行Android设备
  • 简单的快速启动命令以删除系统应用程序

:通常启用“OEM 解锁”只是在开发者选项中打开一个开关(如果开发者选择中没有OEM解锁按钮,可利用fastboot工具来解锁),但除非您解锁引导加载程序(这是解锁 OEM 后的下一步操作),否则您无法对您的 Android 设备进行任何刷入新的固件等操作。需要注意解锁引导加载程序会使您的设备保修失效,但在Android 5.0 及更高版本中引入了一项新的功能,就是选项按钮开关,这为了防止未经授权的访问解锁引导加载程序。且就算启用此选项也不会使设备的保修失效,但如果继续操作并解锁设备的引导加载程序,那么你的设备保修可能会失效(不同的制造商对引导加载程序解锁有不同的政策,对于大多数制造商来说,解锁引导加载程序就代表着保修失效)

OEM 解锁用于开发目的和安全原因:

  • 它用于解锁任何 Android 设备上的 BootLoader
  • 它提供来自未知用户的安全性
  • OEM 包含用于识别的附加功能
  • 如果我们想 Root 或安装自定义 ROM,OEM 是第一个关键
  • 有助于摆脱任何手机上的引导循环

OEM解锁的主要原因:是解锁引导加载程序,引导加载程序用于授予对设备的访问权限以对我们的设备进行 Root 或安装恢复和闪存(ROM)

1.4.5.1 iToolab UnlockGo (Android)

如果您忘记了启用 OEM Unlock 的 Android 密码,可尝试iToolab UnlockGo (Android)工具。这是一个无需输入密码即可移除任何锁的神奇工具,而且适用于所有 Android 版本和许多品牌的手机,包括摩托罗拉、小米、华为、三星等

iToolab UnlockGo(Android):Android OEM解锁器

  • 具有简洁 UI 的简单可靠的软件
  • 高效去除15+手机品牌的5种锁屏
  • 在早期型号的三星手机中没有任何数据丢失
  • 绕过装有 Android 5-12 的三星设备上的 FRP 锁定

UnlockGo for Android 是一款专业工具,它有助于绕过 FRP 安全功能、图案、PIN 或任何屏幕锁定,然后即可启用 Android OEM解锁选项。它可以有效地删除密码并节省大量时间,下载地址:https://itoolab.com/android-phone-unlocker/

在这里插入图片描述

1.4.5.1.1 删除忘记密码/PIN 的步骤

使用iToolab UnlockGo 删除OEM密码,以便我们开启 OEM 解锁选项的步骤

1)第 1 步:将Android设备连接到 PC

首先,通过 USB 充电线将您的 Android 设备连接到您的 PC。然后,启动 iToolab UnlockGo (Android) 软件并在该工具的主界面上单击解锁屏幕锁定选项

在这里插入图片描述

2)第2步:确认Android设备信息

进行下一步操作之前,请从下拉菜单中选择你的 Android 设备的品牌,然后单击解锁按钮

在这里插入图片描述

3)第 3 步:将Android设备置于恢复模式

接下来,你将按照屏幕上显示的说明将设备置于恢复模式。这些步骤会有所不同,具体取决于您的 Android 手机上是否有主页按钮

在这里插入图片描述

4)第 4 步:完成解锁过程

按照说明绕过 Android 上的锁定屏幕。最后重启你的设备,这将允许你在你的Android设备上设置一个新的OEM密码

在这里插入图片描述

1.4.6 启用 OEM 解锁的简单方法

一般情况下,Android手机设备商为了防止其设计的界面被人随意更改,会给设备在bootloader上加锁,安装任何系统时需要进行认证,这导致了用户不能够安装第三方的系统,即自定义ROM

首先解锁开发人员选项下的OEM选项:

  • 打开设备中的设置
  • 然后进入到设置中的系统选项
  • 接着找到内部版本号选项,狂点几次进入开发者选项
  • 最后解锁OEM
  • 不同机型以及不同Android版本的BootLoader的解锁方式略有不同,但基本思路和步骤都是类似的

在这里插入图片描述

有些Android 设备上在Android 设备开发者模式中有OEM Unlock,此时即可进行如下操作解锁bootloader:

  • 打开Android 设备开发者模式并勾选 OEM Unlock
  • Android 设备允许调试后,通过adb将设备启动到bootloader模式(重启手机到 BootLoader 界面)
    • adb reboot bootloader

安装第三方系统,通常情况下有两种方法:

  • 第一种方法:制作一个专门的工具,使得用户跳过设备认证,也就是常说的金卡
  • 第二种方法:解除bootloader上的锁定
    • 进入fastboot模式,输入指令fastboot oem unlock对于其它手机,通常情况下可通过更换SPL(Second Program Loader)的方式来解锁bootloader【进行解锁,部分厂商需要在此附上解锁码】
    • 对于新款设备(2015 年及之后发布的设备):fastboot flashing unlock
    • 对于老款设备(2014 年及之前发布的设备):fastboot oem unlock
    • 最后屏幕确认解锁

解锁 BootLoader 后会立即清空手机中所有数据,但这也是很多玩机操作必经的第一步,建议尽早开启。解锁 BootLoader 还意味着手机等私密设备的安全性大大下降,不少厂商也会在解锁政策上附加上不少「条款」:三星设备解锁后会永久性熔断 KNOX 安全认证;大部分手机的版权认证 DRM 等级也会从 L1 下降至 L3、无法通过 Play 商店认证等

  • Android 10(Android Q)及其以后的安卓版本,除了软件为Debug,还需要解锁,才能开放控制权限
  • Android 9(Android 9)及其以前的版本,软件为Debug,不需要解锁,就有控制权限

1.4.7 Android 设置锁定/解锁属性

ro.oem_unlock_supported属性应在构建时根据设备是否支持刷写解锁来设置,如下:

  • 如果设备支持刷写解锁,请将 ro.oem_unlock_supported设置为 1
  • 如果设备不支持刷写解锁,请将 ro.oem_unlock_supported设置为 0

如果设备支持刷写解锁,引导加载程序应通过将内核命令行变量 androidboot.flash.locked设置为 1(如果已锁定)或 0(如果已解锁)来指示锁定状态。在 Android 12 中,必须在bootconfig而不是在内核命令行中设置此变量

对于支持 dm-verity 的设备,请使用 ro.boot.verifiedbootstatero.boot.flash.locked的值设置为 0;如果启动时验证状态显示为橙色,此操作可解锁引导加载程序

1.4.8 OEM 源码分析

不同厂商终端设备出厂时,都存在自定制的OEM,这里以别的作者的分析文章为案例介绍,感兴趣的同学可以移步前往了解

Android平台OEM解锁分析:

  • https://tjtech.me/analyze-oem-unlocking-under-android.html

在这里插入图片描述

oemlock hal:

  • https://www.cnblogs.com/pyjetson/p/14769333.html

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.4.9 OEM 锁保护措施

Android 设备应支持锁定和解锁关键部分(指将设备启动到引导加载程序所需的任何部分)。这些部分可能包括 fuse、传感器中枢的虚拟分区、第一阶段引导加载程序等。如需锁定关键部分,您必须采用一种机制,阻止设备上运行的代码(内核、恢复映像和 OTA 代码等)故意修改任何关键部分。如果设备处于锁定关键部分状态,OTA 应无法更新关键部分

从锁定状态转换为解锁状态应需要与设备进行物理交互。此类交互的效果类似于运行fastboot flashing unlock命令,但要求用户按下设备上的实体按钮。设备不应允许在没有进行物理交互的情况下以程序化方式从lock critical 状态转换为 unlock critical状态,并且设备不应以 unlock critical状态推出

1.4.10 引导加载程序认证

开机后,处理器 ROM 加载程序使用 AHAB 和OTP SRK efuse 中的值验证 U-Boot 和 Trusty OS(引导加载程序映像身份验证概述)。ROM loader 只有在验证成功后才会执行 U-Boot 和 Trusty OS;否则,引导将中止

在这里插入图片描述

引导加载程序工件包括 U-Boot 和 Trusty OS。此映像必须在构建后使用PKI(公钥基础结构)树进行签名。请参阅签署引导加载程序映像

公钥的哈希值在设备上的 SRK OTP 位中编程(请参阅保护设备)。这允许 ROM 加载程序验证签名的引导加载程序映像中包含的公钥(请参阅引导加载程序映像身份验证概述)

:数字签名是一种广泛使用的安全技术,用于确保数据的完整性和签名者的身份验证

片内引导程序(ROM SoC Bootloader)是在芯片制造时被写入芯片内部只读 ROM 中的一段引导程序,出厂后无法修改,设备上电后最先执行此代码,以小米的MIUI 安全启动过程为例(如下图):

在这里插入图片描述

例如小米的设备上电后,片内引导程序执行基本的系统初始化,从 Flash 存储芯片中加载一级引导程序,并利用保存在主芯片内部 Fuse 空间的公钥对一级引导程序镜像的数字签名进行校验,验证成功后运行一级引导程序。随后,一级引导程序加载、校验和执行 TEE OS 镜像,TEE OS 运行起来后,由 TEEOS 和一级引导程序共同校验、加载和执行二级引导程序。以此类推,直到整个系统启动完成,从而保证启动过程的信任链传递,防止未授权程序被恶意加载运行

MIUI 系统的启动过程支持 Android 的 Verified Boot 2.0(AVB2.0)功能。在设备启动过程中,从受硬件保护的信任根到引导加载程序,再到启动分区和其他已验证分区(包括 system、vendor和可选的 OEM 分区),无论是在哪个阶段,都会在进入下一个阶段之前通过加密认证方式验证代码可靠且没有任何已知的安全缺陷之后才会执行。AVB 有助于防止永久驻留的 Rootkit 恶意软件持有ROOT 权限危害设备,可确保设备在启动过程中的安全性

1.4.10.1 验证和启动签名固件

对于固件验证,最常见的方法是在制造过程中将公钥编程到设备的安全存储器中。安全存储保证公钥不能被更改并且由签名者编程。验证和启动签名固件映像的方案如下所示:

在这里插入图片描述

1.5 可信初始化

在引导加载程序验证和执行之后,Trusty 初始化“安全环境”和受信任的应用程序(avc、hwcrypto、trusty_gatekeeper)。Trusty 检查对重放保护内存块 (RPMB) 安全存储的访问,以获取继续启动过程所需的数据

在这里插入图片描述

1.5.1 可信执行环境(TEE)

可信执行环境(Trusted Execution Environment(TEE), 可信执行环境)安全操作系统,它对代码和数据应用信任级别,保证它们的机密性和完整性。一般而言,TEE 提供了一个执行空间,可为设备上运行的受信任应用程序 (TA) 提供更高级别的安全性。TEE 可以构建一个隔离于主操作系统的小型操作系统,让具有安全、隐私诉求的应用隔离于 Android 系统运行于此。以小米的可信执行环境逻辑架构图,如下:

在这里插入图片描述

TEE 所能访问的软硬件资源是与主操作系统分离的,TEE 提供了可信应用的安全执行环境,同时也保护可信应用的资源和数据的保密性、完整性及访问权限。为保证 TEE 本身的可信根,TEE 在安全启动过程中需要通过验证并且与主操作系统隔离。在 TEE 中,每个可信应用是相互独立的,而且在未授权的情况下不能互相访问。TEE 内部 API 主要包含了密钥管理、密码算法、安全存储、安全时钟等资源服务,以及扩展的可信 UI 等

:可信 UI 是指在关键信息的显示和用户关键数据(如口令)输入时,屏幕显示和键盘等硬件资源完全由 TEE 控制和访问,Android 系统中的软件不能访问

TEE操作系统通常由具有较高特权的内核和具有较低特权的多个应用程序(称为可信应用程序,TA,Trusted Applications)组成。TA之间彼此隔离,且与TEE内核隔离,此时如果有应用程序被攻陷,它就无法危害到其他应用程序或TEE内核。一个强大的TEE机制可实现下述三类隔离:

  • TEE与REE之间的隔离
  • TA和TEE内核之间的隔离
  • TA之间的隔离

为了达到这些安全需求,TEE需要硬件原语的支持,以强制进行隔离。硬件和软件之间的配合是至关重要的,并且需要持续配合

广义上来说,TEE由多个组件组成,具体包括:

  • TEE感知硬件
  • 强大的安全启动(Secure Boot)链,用于初始化TEE软件
  • TEE OS内核,用于管理安全区域和可信应用程序
  • 可信应用程序,用于向TEE提供功能。

Trusty 是一个安全的操作系统 (OS),它为 Android 提供了 TEE。Trusty OS 与 Android OS 在同一处理器上运行,但 Trusty 通过硬件和软件与系统的其余部分隔离。Trusty 和 Android 并行运行。Trusty 可以访问设备主处理器和内存的全部功能,但完全隔离。Trusty 的隔离保护它免受用户安装的恶意应用程序和可能在 Android 中发现的潜在漏洞的影响

在 ARM 处理器上,Trusty 依靠ARM Trustzone虚拟化主处理器并创建安全的可信执行环境

在这里插入图片描述

1.5.1.1 设备证明

为确保Android 设备自身是可信的,设备制造商通常在设备出厂时在 TEE 中预置了设备证书,用于标识该设备的身份,TEE 的公钥统一存储在设备制造商或供应商的服务器中,可以采集设备信息返回给服务器并发起验证,以确定该设备的真实性

1.5.1.2 设备唯一密钥(HUK)

HUK(Hardware Unique Key,设备唯一密钥)出厂时固化在手机主板上,每台手机的 HUK 都不相同且无法被篡改,仅有硬件加密引擎可以访问。HUK 为锁屏密码保护和文件系统加密功能所使用的密钥提供了设备唯一性保证

1.5.1.3 硬件加密引擎

加解密操作是一个非常复杂的过程,需要强大的计算能力。但对于移动设备而言,速度、节能和安全都至关重要。Android 设备制造商,通常使用的加密引擎的主要算法如下:

  • 3DES
  • AES-128、AES-256
  • SHA-1、SHA-256
  • HMAC-SHA1、HMAC-SHA256
  • RSA-1024、RSA-2048
  • ECDSA-256

1.5.2 重放保护内存块 (RPMB) 安全存储

RPMB 是 eMMC 设备中的一个独立物理分区,专为安全数据存储而设计。对 RPMB 的每次访问都经过身份验证,这允许系统以经过身份验证和重放保护的方式存储或检索数据。在访问 RPMB 之前,开发人员必须编写一个RPMB 身份验证密钥来进行身份校验

1.6 VBMeta 验证

VBMeta在构建过程中由主机使用AVB(Android 验证启动)私钥签名

AVB 密钥的公共部分需要可用于在运行时验证 VBMeta 图像。公钥存储在 RPMB 安全存储中

Trusty访问Replay Protected Memory Block (RPMB) 安全存储以获取AVB(Android 验证启动)公钥并使用它来验证 VBMeta 映像

在这里插入图片描述

1.7 Linux 内核验证

U-Boot在跳转到 Linux 内核之前使用VBMeta中的数据验证 Linux 内核(在引导分区中)和设备树(在dtbo分区中) 。它们通过将整个内容加载到内存中然后计算其哈希来验证。该计算的哈希值与预期的哈希值进行比较。如果值不匹配,Android 不会启动

在这里插入图片描述

1.7.1 VB元图像

Android 分区表有一个包含 VBMeta 映像的vbmeta分区。此映像包含验证数据(例如加密摘要),用于验证启动所需的 Android 映像(boot、dtbovendor_boot

AVB 中使用的中心数据结构是 VBMeta 结构。该数据结构包含许多描述符和其他元数据。描述符用于图像哈希、图像哈希树元数据和所谓的链式分区

vbmeta分区包含:

  • 哈希描述符中引导分区的哈希。引导映像构建为链式分区,因此它包含由AVB 引导密钥签名的 VBMeta 结构
  • 对于系统和供应商分区,vbmeta分区在哈希树描述符中保存哈希树的根哈希、盐和偏移量,而哈希树跟随每个分区的数据

验证 VBMeta 映像后,其内容用于验证其余启动映像。因为vbmeta分区中的 VBMeta 结构是加密签名的,所以U-Boot 可以检查签名并验证它是由 AVB 密钥的所有者(通过嵌入 AVB 密钥的公共部分)制作的,从而信任用于引导、系统的哈希和供应商

1.8 文件系统完整性检查

dm-verity在挂载时检查分区的完整性。来自 Android 文件系统的数据访问将由dm-verity 验证以确保完整性

不适合内存的大型分区(例如文件系统)使用哈希树,其中验证是在数据加载到内存时发生的连续过程。在这种情况下,哈希树的根哈希是在运行时计算的,并根据预期的根哈希值进行检查。如果在某些时候计算的根哈希值与预期的根哈希值不匹配,则不会使用数据并且 Android 会进入错误状态

在这里插入图片描述

1.8.1 系统整体安全目标

  • TPM

可信计算技术中最核心的部分,它是一个含有数字签名、身份认证、硬件加密、访问授权、信任链建立和完整性度量hash值存放、密钥管理等可信计算所必须的核心硬件模块,基于对用户身份、应用环境、网络环境等不同底层认证 ,彻底防止恶意盗取信息和病毒侵害,实现可信启动(对系统上电到操作系统启动进行度量,写入度量日志)并解决底层硬件设施的安全问题 。TCM是我国采用自主设计密码算法提出的TPM模块

  • 度量

从2.6.30开始,IMA被引入到Linux内核中,IMA是一个开源的可信计算组件。IMA维护一个运行时度量列表,在有硬件TPM芯片的情况下,能够把此列表的hash完整性度量值固定到TPM里,这样即使攻击能破坏度量列表,也能被发现;即使访问了恶意文件,该恶意文件的度量值(hash值)也会在访问该文件之前提交给TPM,而恶意代码无法删除此度量值(如果没有硬件TPM芯片,理论上来说,还是存在恶意代码不被发现的情况下伪造假的度量列表的可能)

整体上来说,IMA度量、鉴定和证实并不是保护系统的完整性,它的目标是当威胁攻击发生时/后至少能被发现,以便及时处置

:可以用内核命令行参数开启IMA度量:ima_tcb=1。默认ima_tcb度量策略是度量可执行文件、mmapped、库、kernel module、firmware和root用户打开的文件。这些度量、度量列表和聚合完整性值可用于证明系统的运行时完整性。基于这些度量,可以检测关键系统文件是否已被修改,或者是否已执行恶意软件。可以通过Linux Security Modules (LSM)规则自定义设置measure(度量)或dont_measure(不度量)。将自定义IMA策略放在/etc/IMA/IMA policy中。Systemd启动的时候将自动加载自定义策略;或者通过“cat”自定义IMA度量策略并将输出重定向到<securityfs>/IMA/policy”。例如:日志文件一般不用度量,可以添加自定义规则:dont_measure obj_type=var_log_t,这样就不会度量日志文件

TPM+Linux IMA就构成了可信在Linux操作系统中的落地。下面是框架示意图:基于Linux TPM-IMA可信计算框架底层实现示意图

在这里插入图片描述

系统大概分为三个主要安全目标:

  • 完整性
  • 真实性
  • 保密性

最终期待的主要效果如下:

  • 非授权者窃取不了重要数据
  • 系统和信息篡改不了,程序防注入,防病毒木马
  • 异常行为能被发现
  • 攻击行为能被发现
  • 模式成熟后还能进一步自动阻断和处置
  • 程序版本更新、新程序、修改后的程序加载执行前都会产生新的度量值,对这些新度量值的比对会失败(除非加入白名单或者基准度量列表中),从而判断该平台是否可信
1.8.1.1 完整性(Integrity)

完整性是最基本的安全属性,没有完整性,真实性或机密性就无从谈起,因为真实性和机密性机制本身可能会受到损害。对于一个文件来说,完整性通常被理解为“不会变”,即一旦安装,文件不会因恶意或意外的修改而变动

从密码学上讲 hash校验可用于检测文件内容是否已变动,比对文件哈希前后是否一致,即可发现文件是否变动。在极端情况下,完整性不仅意味着不会变,而且意味着“不可改变”,任何操作都不能修改文件。例如BSD操作系统具有不可变文件的概念,只能在单用户管理模式下更改

现在流行的Android操作系统也是将其所有系统文件保存在一个分区中,该分区在正常情况下是只读使用,并且只能在引导期间由恢复程序修改。不可变的系统虽然提供了更大的完整性,却难以做到系统组件的正常更新安装或修补。IMA遵循可信计算组(TCG)的完整性开放标准,把hash校验和不可变更机制组合起来用于创建一个新的完整性解决方案。BSD和Android的方法是使文件本身不可变,而TCG的方法是把这些不可变文件的hash值存储在硬件上,如可信平台模块(TPM)、移动可信模块(MTM) 或类似的装置。IMA维护所有被访问文件的hash列表,如果存在硬件安全组件如TPM,即使软件攻击hash列表也不会成功。通过管理平台集中监视、判断、告警它们是否被改变或破坏

1.8.1.2 真实性(Authenticity)

真实性是完整性概念的延伸。不仅仅是告诉具有完整性,而且文件的出处也是已知的。例如,我们不仅知道一个文件没有被恶意修改,而且还知道文件是由真实供应商提供的原始文件。简单的hash不足以保证真实性。而数字签名技术能保障文件的真实性, 使攻击者无法伪造出处的证据。通常是公钥签名,如 RSA用于真实性,但也可以使用对称密钥hash,如HMAC

公钥签名的一个优点是,中心机构是唯一必须保护私钥机密性的实体,而所有其他系统只需要保护用于验证的公钥的完整性

公钥签名的缺点是,它们只适用于从不变更的文件,如果文件发生变更,本地系统将无法对其重新签名。而系统上的许多安全关键文件需要不定期变更,因此这些文件实际上只能使用基于本地对称密钥的签名进行保护。用于身份验证的本地对称密钥需要妥善保管,否则攻击者可能盗取本地对称秘钥并用它在恶意文件上伪造本地签名

在Linux中,一个新的IMA鉴定扩展和一个新的扩展验证模块(EVM) 使用对称密钥HMAC 一个文件的数据和元数据,使用本地对称密钥签名的方式来保障可允许更改文件的真实性。对于不希望更改的文件,IMA鉴定签名扩展名将存储一个授权的RSA公钥签名。带有RSA签名的保障文件便不会被允许更改(但仍可被删除)

1.8.1.3 保密性(Confidentiality)

大家往往误解是加密技术既能做到保密性,又能保护文件的完整性。本质上来说,加密本身并不能保证完整性。比如流密码RC4,它常常被用于无线和互联网(https)通信中的标准加密,但是攻击者还是较为容易在加密流中进行“比特旋转”,使完整性收到破坏

使用块密码的情况下,攻击者也能在文件和会话之间被剪切、粘贴和重放加密块,而且成功率不低。另外加密系统文件会带来很大的性能损失

```Linux kernel``````里已经实现了安全密钥管理机制:

  • Linux kernel提供了一个内核密钥环服务(keyring),在该服务中,对称加密密钥对用户空间来说永远不可见。密钥是在内核中创建的,由硬件设备(如TPM)密封,用户空间只看到密封的密钥信息。恶意或受损的应用程序无法窃取受信任的密钥,因为只有内核才能看到未密封的密钥信息
  • 通过可信密钥,将获取密钥与完整性度量绑定在一起,这样在脱机攻击中密钥就不能被窃取,例如通过从CD或USB引导未锁定的Linux映像。由于度量结果不同,TPM芯片将拒绝解封密钥,即使对于内核也是如此。这样,Linux将机密性和完整性绑定在了一起,系统的完整性是解密系统机密文件的前提条件,结合起来的自身的基础防御图示例如下:

在这里插入图片描述

1.8.2 dm-verity

dm-verity是一个内核扩展,在挂载分区时和运行时运行。但是,Android Verified Boot 进程会在内核加载之前启动。更准确地说,你设备的 ROM 引导加载程序会在加载内核 (boot.img) 之前验证它的完整性,从而防止内核被损坏

Device Mapper verity (dm-verity) 内核功能支持块设备的透明完整性检查。这允许用户确保在启动设备时,它处于与编程时相同的状态。vbmeta分区包含对system、system_ext、vendor和product分区进行 dm-verity 检查所需的元数据(哈希树描述符:根哈希、盐和 hast 树的偏移量) 。启用 dm-verity 后,任何破坏分区中编程的映像一致性的操作都会导致 dm-verity检查失败,从而导致系统启动失败

设备在制造时,厂商会做一些加固,包含如下部分:

  • 厂商生成公钥对(公钥PuK和私钥PrK)作为准备
  • 供应商使用使用公钥加密的签名算法,例如 RSA-PSS,使用私钥 PrK 生成开发软件的签名
  • 厂商在制造终端时将软件及其签名写入 ROM,并将公钥 PuK 写入处理器中预留的 OTP(一次性可编程)存储器。由于OTP内存只能写入一次且不易被篡改,因此在设备中用作信任根
  • 若Android终端的启动程序在SoC(System on a Chip)内部存储器上使用签名验证算法,则通过OTP存储器中的公钥PuK验证软件签名
  • 若此签名有效,则假定它未被篡改并运行软件

:Android启动时对除userdata.img之外的三个镜像(bootloader.img、boot.img、system.img)应该进行安全启动,即可检测到系统区域有没有被篡改;userdata.img是存储用户添加的数据的地方,一般不用进行安全启动

三个镜像(bootloader.img、boot.img、system.img)应该进行安全启动的镜像中,system.img在大多数终端中实际上是不安全启动的,原因如下:

  • 第一个原因:由于镜像体积很大(大约是 boot.img 的 100 倍,在某些情况下可以达到几 GB),因此使用安全启动方法对其进行验证会消耗大量内存资源并对系统启动速度有影响
  • 第二原因:由于OTA(Over The Air)更新,即更新补丁是在文件级别进行的,更新后的system.img无法保持位级别的身份(文件内容为偶数)如果它们相同,则元数据可能不同),并且无法实现需要完整身份的安全启动

为了解决上面说的与 system.img 验证相关的两个问题,从 Android 6.0 作为强制功能引入的技术是“ dm_verity”。dm_verity 将system.img划分为每个 4 KB 的块,并以树的形式预先计算出一个由每个块的哈希值组成的“哈希树”。并且通过仅对从此哈希树创建的小元数据 (dm_verity_table) 的一部分进行安全启动验证,与验证整个 system.img 相比,启动时间在实际时间内,如下图:

在这里插入图片描述

另外,Hash Tree 本身在 Android 启动时不会被验证,但是当 Android 运行时加载文件系统时,会动态计算并检查Hash Tree的完整性,这项功能方便安全人员检测系统篡改

:在应用 dm_verity 的系统上,由于读取块的哈希验证失败,用户可以检测到恶意软件对 /system 的简单文件重写以及I/O错误。另一方面,如果恶意软件在假设 dm_verity 的情况下以二进制级别重写与块对应的哈希树,则可以通过在启动时验证根哈希的签名来检测篡改。由此,从Android 6 版本时,便可通过dm_verity和安全启动,不仅可在Linux内核中验证篡改,还可在Android 系统层验证篡改

1.9 信任根

信任根是任何现代安全协议的基石。这是一系列严格的制衡,从硬件层面而不是软件层面开始。此功能为设备增加了一定程度的安全性,使其难以受到攻击,因为硬件的可变性不如软件

信任根解决了许多复杂的安全问题,例如:

  • 我们如何知道受感染的操作系统是否在运行时启动?
  • 我们能相信我们的证书是安全存储的吗?
  • 漏洞是否修改了内核或其他系统软件?

1.10 A/B System

A/B系统是在Android N中推出的一个新特性,主要目的是优化OTA升级的过程,实现无缝升级(seamless update)

在使用A/B之前,系统的OTA升级过程如下:

  • 下载更新包到cache或者data分区。系统校验更新包的证书/system/etc/security/otacerts.zip,校验通过后提示用户可以进行升级
  • 用户点击升级后系统重启到recovery,并根据/cache/recovery/command中的内容找到OTA包
  • recovery再次使用公钥/res/keys校验签名
  • 根据OTA包中的指令进行更新

值得一提的是,对于开启了dm-verity校验的文件系统进行文件修改会导致校验失败,也就说在OTA之后设备将无法正常启动;为了解决这个问题需要将file-based OTA改为block-based OTA

在非AB系统的升级过程中,如果升级失败,则系统无法继续正常启动,解决的唯一办法只有重新升级(线刷或卡刷)。AB系统正是为此而生的。在AB系统中,系统分为两套分区(slot),通常是slot A和slot B。这样的好处是在OTA中无需修改当前slot,以便在升级失败后可以回退到正常可启动的分区。

每个slot都有下面三个属性:

  • active:表示该slot是活跃分区,即下一次启动时bootloader将优先加载的分区
  • bootable:表示该slot中包含可以启动的系统镜像
  • successful:表示该slot中的系统成功启动过

升级过程可以分为以下几步:

  • OTA下载开始后,当前的slot将被标记为successful,另一个slot标记为unbootable
  • 下载payload metadata,包含OTA的操作指令
  • 下载payload extra data,对应metadata命令中涉及到的数据,并进行对应更新(支持流式更新)
  • 对更新后的分区进行校验
  • 在当前系统中执行post-install,即新系统的某个可执行文件,如/postinstall_mount/usr/bin/postinstall
  • post-install失败后需要重新尝试其他payload,成功后则将unused slot标记为active并重启
  • 重启更新成功后update_verifier执行dm-verity验证(在zygote之前),验证成功后将当前slot标记为successful,完成升级

A/B系统带来的好处不言而喻,可直觉上对每个分区都分成两份似乎造成了很大的空间浪费。由于实施了A/B分区,也减少了一部分磁盘的开销,比如:

  • 不再需要cache分区
  • 不再有单独的recovery分区,但boot分区中包含recovery的ramfs
  • system分区直接挂载到根分区,因此boot中原本的ramfs也不再需要,即system as root

A/B中的boot分区也就相当于以前的recovery分区。以Pixel手机为例,Google给出的实际A/B额外开销只有300多MB,如下所示:

Pixel partition sizesA/BNon-A/B
Bootloader50*250
Boot32*232
Recovery032
Cache0100
Radio70*270
Vendor300*2300
System2048*24096
Total50004680

显然,A/B系统需要bootloader支持。通常bootloader对于各个Vendor而言是不同的,而且通常是闭源的。Google需要厂商实现HAL接口boot_control以支持A/B系统,接口文件为:https://android.googlesource.com/platform/hardware/libhardware/+/master/include/hardware/boot_control.h

同时也需要实现bootloader中AB升级相关的状态机:

在这里插入图片描述

值得一提的是,system-as-root并不是只有AB系统能用,非AB系统通过更新分区实现,见 :https://source.android.com/devices/bootloader/system-as-root#about-system-as-root

1.11 Android recovery 原理

Android recovery,是一种Android可以对内部进行修改的模式,可以进行系统的备份、升级或者恢复出厂设置

1.11.1 相关概念

  • recovery:recovery 模式指的是android 对内部的数据或者系统进行升级的模式,也指recovery 分区
  • OTA:over-the-air technology,即空中下载技术,可以实现远程对android 设备进行批量、全量、定向的升级。主要过程分为:申请 ota 升级,下载升级包,校验、安装升级包
  • bootloader:bootloader 是嵌入式系统上电后执行的第一段代码,在它完成cpu和相关硬件的初始化之后,再将操作系统镜像或者固化的嵌入式应用程序装载在内存中,然后跳转到操作系统所在的空间,启动操作系统进行
  • BCB:bootloader control block启动控制信息块,位于misc 分区,从代码上看,就是一个结构体

1.12 Android 启动流程

Android 启动流程大致步骤:

  • bootRun(加电+系统自检):启动电源以及系统启动:当电源按下,系统加电后,CPU复位,引导芯片代码开始从预定义的地方(固化在ROM (Boot ROM))开始执行,然后加载引导程序到RAM (该程序指令会将BootLoader程序加载到内存中开始执行)中执行
  • BootLoader:BootLoader又叫作“引导加载程序”,是底层代码,包括一堆指令。引导程序是在Android操作系统开始运行前的一个小程序,且是系统运行的第一个程序,因此它是针对特定的主板与芯片的。引导程序分两个阶段执行:
    • 第一阶段:检测外部的RAM以及加载(另外一段BootLoader)对第二阶段有用的程序,之后调到其中执行
    • 第二阶段:引导程序设置网络、内存等等(另外一段BootLoader,设置运行kernel所需要的网络,内存等条件,之后找到对应的kernel镜像,并将其加载到物理内存中)。这些对于运行内核是必要的,为了达到特殊的目标,引导程序可以根据配置参数或者输入数据设置内核。此时经过这一步,Kernel的相关镜像已经加载到了物理内存的指定地址处,并建立了内核运行所需的基本环境。接下来BootLoade就将控制权交给了Kernel,内核开始执行
  • kernel:Android kernel的加载过程与Linux Kernel加载过程类似,随着内核启动,开始设置缓存、受保护内存、计划列表、调度和加载驱动程序。当内核完成这些系统设置后,它首先在系统文件中寻找”init”文件(/system/core/init),然后启动root进程或者系统的第一个进程
  • init进程:init进程是Android系统的第一个用户进程,可以说是root进程或者说有进程的父进程。init进程有两个责任
    • 第一个责任:挂载目录。比如挂载/sys,/dev,/proc等文件目录
    • 第二个责任:运行init.rc脚本。比如解析运行init.rc中的相关配置,解析init.rc后主要完成了以下几件事情:
      • 创建一些关键文件目录、设置权限策略
      • 开启ServiceManager、VndServiceManager等本地守护服务
      • fork出Zygote进程
  • Native service启动:在此阶段,会启动Android Native Service 包括 Zygote、sufaceFlinger、media server、bootanimation
  • JavaService 启动:Android 会启动java的service。并进行package的扫描,包括所有的app以及其所有的package
  • 启动Home界面:一旦系统服务在内存中跑起来了,Android便启动home界面

1.12.1 Android 启动的三种模式

Android 启动的三种模式:

  • Recovery模式(恢复模式):在该模式下,用户可以进行系统备份、系统升级、还原出厂设置等操作,进入恢复模式后,用户可以根据需要进行备份、刷机、清除数据等操作
  • Fastboot模式(快速引导模式):该模式是一种比Recov-ery模式更底层的刷机模式,当手机的系统无法进入恢复模式时,可以通过Fastboot进行重新刷机,恢复设备
  • 正常启动模式:在该模式下,手机会启动Android系统,并加载系统程序到System分区,然后读取用户应用程序,初始化后正常启动

1.12.2 CPU 内部的安全机制

下图是一个比较粗糙的,各个启动阶段,软硬件组件之间的关系。这只是我接触比较多的一种架构,当然还有别的不同的架构,因为接触得不多这里就不列举了

在这里插入图片描述

1.12.2.1 BootROM

所有支持 Secure Boot 的 CPU 都会有一个写死在 CPU 中的 bootROM 程序。CPU 在通电之后执行的第一条指令就在 bootROM 的入口。bootROM 拥有最高的执行权限,也就是 EL3。它将初始化Secure Boot 安全机制,加载Secure Boot Key 等密钥、从 eMMC加载并验证 First Stage BootLoader(FSBL),最后跳转进 FSBL 中

bootROM 是完全只读的,这个在 CPU 出厂时就被写死了,连 OEM 都无法更改。bootROM 通常会被映射到它专属的一块内存地址中,但是如果你尝试向这块地址写入内容,一般都会出错或者没有任何效果

有些芯片还会有一个专门的寄存器控制 bootROM 的可见性,bootROM 可以通过这个寄存器禁止别的程序读取它的代码,以阻止攻击者通过逆向 bootROM 寻找漏洞

1.12.2.2 iRAM

为了避免使用外部 RAM 芯片,支持Secure Boot 的 CPU 上都会内置一块很小的 RAM,通常只有 16KB 到 64KB (称之为 iRAM)。这块 iRAM 上的空间非常宝贵,bootROM 一般会用 4KB 的 iRAM 作为它的堆栈。FSBL 也会被直接加载到 iRAM 上执行

1.12.2.3 eFUSE

所有支持Secure Boot 的 CPU 都会有一块很小的一次性编程储存模块 (称之为 FUSE 或者 eFUSE),因为它的工作原理跟现实中的保险丝类似:CPU 在出厂后,这块 eFUSE 空间内所有的比特都是 1,如果向一个比特烧写 0,就会彻底烧死这个比特,再也无法改变它的值,也就是再也回不去 1 了

一般 eFUSE 的大小在 1KB 左右,OEM 从 CPU 厂家购买了芯片,组装了产品后,一般都要焼写 eFUSE 的内容,包括产品的运行模式:测试、开发、生产等。面向终端消费者的产品都会被焼写为生产模式。这个模式下 bootROM 会禁用很多权限,更大面积地限制用户的能力

另外一个很重要的焼写内容就是根密钥了,一般有两种根密钥:一个是加密解密用的对称密钥 Secure Boot Key,一般是 AES 128 的,每台设备都是随机生成不一样的;另一个是一个 Secure Boot Signing Key 公钥,一般用的 RSA 或 ECC,这个是每个 OEM 自己生成的,每台设备用的都一样,有些芯片会存公钥的 Hash 来减少 eFUSE 的空间使用

只有Secure World才能访问 eFUSE 的寄存器。除了读写 eFUSE 的基础寄存器之外,还有一些控制寄存器可以禁止别的程序访问 eFUSE,来保护其中的密钥。因此 eFUSE 中的根密钥以及 bootROM 将作为Secure Boot 的根信任

1.12.2.4 Security Engine

有些 CPU 中还会有一个专门负责加密解密的模块(称之为 Security Engine)。这个模块也是只有在Secure World 中才能访问。这个模块通常会有若干个密钥槽(Keyslots),可以通过寄存器将密钥加载到任意一个 Keyslot 当中,Keyslot 的加载操作将复盖之前加载过的密钥。通过寄存器操作 DMA 读写,可以使用 Keyslot 中的密钥对数据进行加密、解密、签名、HMAC、随机数生成等操作,但是没有办法从一个 Keyslot 中读取已经加载的密钥值

1.12.2.5 First Stage Bootloader(FSBL)

FSBL 的作用是初始化 PCB 板上的其他硬件设备,给外部 RAM 映射内存空间,从 eMMC 的 GPT 分区上加载验证并执行接下来的启动程序

1.12.2.6 根信任的建立

CPU 通电后执行的第一行指令就是 bootROM 的入口,bootROM 将初始化各种 CPU 内部的模块,但最主要的是,它会读取 eFUSE 上的内容,首先它会判断当前的运行模式是不是生产模式,是的话会开启Secure Boot 功能,然后把 Secure Boot Key加载到一个 Security Engine 的 Keyslot 当中,有时候它还会通过 Key Derivation Secure Boot Key或别的 eFUSE 内容生成多几个不同用途的密钥,分别加载到不同的 Keyslots 中。然后它会从 eMMC 上加载 FSBL,FSBL 里面会有一个数字签名和公钥证书,bootROM 会验证这个签名的合法性,以及根证书的 Hash 是否和 eFUSE 中的Signing Key 的 Hash 相同。如果验证通过,说明 FSBL 的的确确是 OEM 正式发布的,没有受到过篡改。于是 bootROM 就会跳转到 FSBL 执行接下来的启动程序。有些 CPU 在跳转之前会把 bootROM 的内存区间设为不可见,防止 FSBL 去读取 bootROM。有些 CPU 还会禁止 eFUSE 的读写,或者至少Secure Boot Key区域的读取权限,来防止 FSBL 泄漏根信任的解密密钥。还有要注意的是,FSBL 是被加载到了 iRAM 上执行的,而且 FSBL 仍然拥有 EL3 级别的权限

FSBL 会进一步初始化 PCB 板上的别的硬件,比如外部的 RAM 芯片等等,使其不再受限于 iRAM 的内存空间。然后它会进一步加载 eMMC 上的内容到 RAM。我们接下来会着重讲讲跟 Secure Boot 密切相关的启动内容

1.12.3 信任链的延伸

一般 FSBL 都非常靠近 eMMC 的第一个扇区,bootROM 一般不会管 eMMC 有没有用 GPT 分区,而是简单的从第一个扇区读取一些头信息然后直接找到 FSBL 的扇区进行加载的。所以一般我们要做 GPT 分区的话都会放在 FSBL 后面。FSBL 加载一个 GPT 分区也是要跳过 eMMC 前面的非 GPT 扇区再进行加载

1.12.3.1 TrustZone

从 FSBL 开始我们就要引入 TrustZone 的技术概念了。TrustZone 是 ARM 家的 Trusted Execution Environment(TEE)实现。相对应的,Intel 家的叫做 Software Guard Extensions(SGX),AMD 家的叫做 Platform Security Processor(PSP)

TrustZone 将 CPU 的执行环境划分为了 Secure World Normal WorldSecure World 拥有更高的权限,可以访问 Normal World 的内存、eFUSE 寄存器、Security Engine 寄存器等等。但是 Normal World 中的最高权限却无法访问Secure World 的内存

但是 Normal World 可以通过 SMC 指令调用Secure World EL3 中注册的命令,然后 EL3 程序再将 Normal World的请求转发给 Secure World,从而达到两个世界之间的通讯

1.12.3.2 Secure Monitor

这是一个运行在 Secure World EL3 权限中的程序,Secure Monitor 的主要作用就是注册 SMC 指令的回调,转发两个世界之间的消息

1.12.3.3 Trusted OS

这是一个通常运行在 Secure World EL1 权限中的内核程序,比较常见的是基于开源的ARM Trusted Firmware 进行扩展修改的,别的实现还有基于 Little Kernel 的,以及一些芯片厂家自己的实现。它的主要作用是给 Secure World 中运行的程序提供一个基本的系统内核,实现多任务调度、虚拟内存管理、System Call 回调、硬件驱动、IPC 通讯等等

1.12.4 Trusted Application(TA) / Trustlet / Secure Service

这些是在 Secure World EL0 中运行的程序、Daemon。根据不同的 Trusted OS 的实现,TA 的格式、运行模式也会有所不同,比如 ARM Trusted Firmware 的 TA 使用的是 ELF 格式,而高通的 TA 用的是自家的格式

1.12.4.1 Keybox

因为 eFUSE 的大小有限,而且不可更改,有些体积比较大的,或者有可能更新的密钥数据,包括像各种 DRM 系统、HDCP、Attestation 等等的密钥,都会用Secure Boot Key 或者一个 Derived Key 进行加密后储存在 eMMC 的一个 GPT 分区上,因此基本上没什么大小限制。FSBL 一般会将这个分区加载到 RAM 中,然后 TA 再通过Security Engine 中已经载入的 Keyslot 对其进行解密获得相应的密钥

1.12.4.2 Second Stage Bootloader(SSBL)

SSBL 的作用是给Normal World 做初始化,加载验证并执行Linux/Android 内核。常见的 SSBL 是基于 U-Boot 项目改的,通常这里已经支持像 Fastboot、Recovery boot等等的常见启动对象了

1.12.4.3 搭建信任链

FSBL 的下一阶段一般包含三个程序:Secure Monitor、Trusted OS、Second Stage Bootloader。FSBL 会分别从 eMMC 加载验证这些程序,最后跳转到Secure Monitor 执行

Secure Monitor会初始化 TrustZone 环境,设置 SMC 回调,然后开始执行 FSBL 已经加载好的 Normal World 中的 Second Stage BootloaderSecure World 中的 Trusted OS

Trusted OS会初始化 Secure World中的系统内核环境,然后从 eMMC 加载各种 Trusted Applications(TA),认证它们的数字签名,然后执行它们。每个 TA 都会有自己独立的虚拟内存空间。TA 如果要访问特定的硬件,比如 Security Engine,会在一个描述 TA 的头信息中申请,TA 的签名也会涵盖这部分头信息。Trusted OS如果同意 TA 访问这些硬件,会把对应的寄存器地址 mmap 映射到 TA 的虚拟内存空间中

Second Stage Bootloader 也就是我们通常说的 Android Bootloader,它会加载验证然后执行Android Kernel

可以看出来,从 bootROM 到 Normal World 的 Kernel,到 Secure World 的 TA,每一步的加载都是要经过数字签名认证的,而所有这些签名认证的根证书,是要跟 eFUSE 中的 Signing Key 匹配的。这就形成了一个信任链

1.13 Android 验证启动 (Verified Boot)

现代 Android 设备采用了 Verified Boot 技术来保证系统引导的安全性。设备在出厂时附带厂商的公钥,作为信任根(Root of Trust),在设备启动时,引导加载程序(固化在 ROM 中)会对操作系统的完整性进行检验。被检验的数据包括 boot 分区(Linux 内核和引导所需文件)、system 分区(Android 系统)和 vendor 分区(设备厂商提供的系统文件)等。对于 boot、vendor 等较小的分区,引导加载程序会在引导时进行验证,对于较大的 system 分区,系统会保存其默克尔树(Merkle Tree),在使用时动态的验证被访问到的部分,如果验证不通过则产生 I/O 错误

Android 引导加载程序往往是被锁定(LOCKED)的。位于锁定状态下的引导加载程序仅接受厂商在 ROM 中内置的信任根,而拒绝加载未经厂商验证的操作系统,这使得攻击者即使获得物理访问或者 root 权限,也无法对系统进行持久化的修改,因为任何修改都会使得自底向上信任链失效,进而使设备拒绝引导/工作

另一方面,Android 引导加载程序往往允许用户进行解锁。位于解锁(UNLOCKED)状态下的引导加载程序允许用户自定义被加载的操作系统。一些实现允许用户将自己的公钥作为信任根,并安装自行签名的操作系统;用户也可完全关闭引导验证功能,让引导加载程序加载任何操作系统。攻击者在拥有设备的物理访问后,可通过解锁引导加载程序来修改操作系统,进而完全控制设备并窃取用户数据,考虑到这一安全隐患,引导加载程序在解锁时会清空所有用户数据。同时,为了避免用户的设备在不知情的情况下被解锁并植入恶意软件,已解锁的引导加载程序会在设备启动时显示警告信息,告知用户其设备正在加载自定义操作系统

Android 设备往往使用 Recovery 环境更进行系统更新与还原,该模式位于 recovery 分区中,包含一个由厂商预先写入的小型操作系统,同时,该分区同样受 Verified Boot的保护。Recovery 模式往往仅接受由厂商验证过的操作系统。为了防止降级以利用旧版本的安全漏洞,有些厂商也会拒绝安装比现有版本更低的操作系统。在解锁引导加载程序后,用户也可替换原有的 Recovery 环境,方便进行自定义操作系统的安装与维护

Android 验证启动(Verified Boot)实现基于 dm-verity 设备块(device-mapper)完整性检测目标(target),旨在保证设备软件(从硬件信任根直到系统分区)的完整性。在启动过程中,无论是在每个阶段,都会在进入下一个阶段之前先验证下一个阶段的完整性和真实性。device-mapper是一个 Linux 内核框架,提供了一个通用方法实现虚拟块设备。它是 Linux 的逻辑卷管理器(LVM)的基础,也可被用来实现全盘解密、RAID 阵列和分布式冗余存储

Android 验证启动(Verified Boot)是Android一个重要的安全功能,主要是为了访问启动镜像被篡改,提高系统的抗攻击能力,简单描述做法就是在启动过程中增加一条校验链,即 ROM code 校验 BootLoader,确保 BootLoader 的合法性和完整性,BootLoader 则需要校验 boot image,确保 Kernel 启动所需 image 的合法性和完整性,而 Kernel 则负责校验 System 分区和 vendor 分区

由于 ROM code 和 BootLoader 通常都是由设备厂商 OEM 提供,而各家实际做法和研发能力不尽相同,为了让设备厂商更方便的引入 Verified boot 功能,Google 在 Android O上推出了一个统一的验证启动框架 Android verified boot 2.0,好处是既保证了基于该框架开发的verified boot 功能能够满足 CDD 要求,也保留了各家 OEM 定制启动校验流程的弹性

device-mapper 的功能本质上是将一个虚拟块设备映射到一个或多个物理块设备上,并可在传输时选择性修改传输数据

设备启动安全级别状态,用绿黄橙红四种颜色表示:

  • 绿色代表所有信任链关系上的镜像都被正常校验通过
  • 黄色代表boot分区不是被OEM key认证成功,启动bootloader显示警告和使用的公钥的摘要信息(fingerpring)
  • 橙色表示系统软件是可自由烧写状态,其完整性需用户进行判断,bootloader显示警告
  • 红色表示启动过程中有校验失败,启动终止
  • Device state 设备状态分为:
    • locked:设备不能被烧写,可能启动到绿黄红三种状态
    • Unlocked:设备可以被自由烧写,启动到橙色状态
  • dm-verity:Linux 内核驱动,用来作分区完整性检查
  • OEM key:OEM提供的一个固定的不可被篡改密钥,在bootloader中可见,用来验证boot镜像

在这里插入图片描述

设备处于“绿色”启动状态时,除了正常设备启动所需的用户互动外,用户应该不会看到任何其他用户互动。设备处于“橙色”和“黄色”启动状态时,用户会看到一条至少持续 5 秒的警告。如果用户在这段时间内与设备互动,该警告持续显示的时间至少会延长 30 秒,或者直到用户关闭该警告。设备处于“红色”启动状态时,该警告会显示至少 30 秒,之后设备将会关机

要实现完整的信任链,需要启动分区上的引导加载程序和软件支持,这个引导加载程序需要完成:

  • 初始化TEE及其绑定的信任根。也就是ROOT OF TRUST
  • 验证启动分区的完整性,并显示状态中的警告

在启动分区验证和TEE初始化完成后,引导加载程序会将启动分区签名时的公钥和当前设备状态信息传递给TEE,绑定keymaster信任根

1.13.1 dm-verity 前面部分补充内容

如下内容来自:https://blog.csdn.net/u010206565/article/details/109888855

介绍信任链的时候我们说到,每次加载新的代码或数据之前都需要对其进行验证。对于比较小的分区,如boot或者dtbo,可以直接加载到内存并计算他们的hash,然后将其与预置的hash进行比对。预置的hash通常存放在对应分区文件的头部或者尾部,或者存放在独立的分区中。不论他们的位置在哪,都是会使用信任根进行直接或间接签名的

但是对于较大的分区,比如system分区,实际上包含了整个文件系统,是无法全部读取到内存里的。这时就需要其他的方法,在Android中使用的是hash tree。当数据加载到内存时,系统就会计算该hash treeroot hash,并与预置的root hash进行比对验证

Dm-verity 使用加密散列树提供块设备的透明完整性检查,每个块以 4k 的大小来划分,都有一个 SHA256 的值。树中的每个节点是加密 hash,其中叶节点包含物理数据块的 hash,并且中间节点包含其子节点的 hash。因为根节点中的哈希是基于所有其他节点的值,所以只有根哈希需要被信任才能验证树的其余部分。对任何一个节点块的改动都破坏整个加密 hash。整个哈希树的结构如图所示:

在这里插入图片描述

验证时使用包含在 boot 分区中的 RSA 公钥来执行。设备块在运行时通过计算读取的块的哈希值并将其与散列树中的记录值进行比较来检查。如果值不匹配,则读取操作将导致I/O 错误,指示文件系统已损坏。因为所有的检查都是由内核执行的,所以启动过程需要验证 boot.img 的完整性,以便验证引导工作

在 Android 中被校验的分区始终挂载为只读状态,只能在 OTA 块设备升级时才可做更改。其它任何对分区的操作都会破坏分区的完整性,比如 root 等操作

:Android 是使用 dm-verity 来保护系统分区的完整性。Dm-verity 使用分区中每个 4KB 块内容的 SHA-256 哈希树。树中的中间节点包含它们下面的节点的哈希值(可能是块节点或其他较低的中间节点),直到根节点,它具有代表整个磁盘的哈希值。该哈希使用来自引导映像的 ramdisk 的密钥进行签名

更多内容请移步(Android 安全架构深究):https://github.com/firmianay/Life-long-Learner/blob/master/Android-Security-Internals/README.md

1.13.1.1 在Android中的实现方法

Dm-verity device-mapper 目的最初是为了在 Chrome 操作系统中实现验证启动而开发的,并且已经在 Linux 内核的 3.4 版本中集成。它使用CONFIG_DM_VERITY 内核配置项来进行开关

但 Android 的具体实现方式和 Chrome 有所不同。用于验证的 RSA 公钥在 boot 镜像的 ramdisk 中,文件名是 verity_key,用于验证目标设备的 root hash签名。被验证的目标分区,有着一个包含了哈希表和它自身签名的元数据块,被附加到镜像的最后。如果要启用对某一分区的校验,需要在 ramdisk 中的 fstab 文件中对特定设备添加 verify 标签

当系统启动过程中,检测到该标签,则会使用verity_key 公钥加载校验该分区最后附加的元数据。如果签名验证通过,则文件系统管理器解析 dm-verity 映射表,并将其传递给 Linux 设备映射器,设备映射器使用映射表中包含的信息来创建虚拟的 dm-verity 块设备。然后将该虚拟块设备安装在 fstab 中指定的安装点上,代替相应的物理设备。因此,所有读自底层物理设备的数据都会用预先生成的散列树进行透明验证。对设备任何修改或添加文件,甚至将分区重新挂载为读写都会导致完整性验证和I/O 错误。虚拟设备的挂载如下所示:

$ mount | grep system
/dev/block/dm-0 /system ext4 ro, seclabel, relatime, data=ordered 0 0

但因为具体的实现方式原因,用于校验的 RSA 公钥 verity_key,直接被放在了 ramdisk中。这给了替换该公钥文件进行攻击的可能性。而且对目标设备是否进行校验,也直接用明 verify 签进行判断,这也是一个明显的安全缺陷

1.13.1.2 启用Verified Boot

在谷歌的官方文档描述中,要完全启用verified boot,除了要配置整个编译系统开启相关选项,还需要引导加载程序实现相关对boot镜像的完整性校验

AOSP 源代码中,开发 key 包括公钥和私钥,它们位 build/target/product/security/目录。用来给 boot/recovery/system 镜像签名,以及验证 system 分区的真实性元数据块表。在启用 verified boot 时需要生成自己的公私钥,但某些机型设备依然是使用的默认的密钥,这相当于是导致了密钥的泄露,对设备来说没有任何安全性可言了

1.13.1.3 Device Mapper

在介绍dm-verity之前,先了解其中的dm,即Device Mapper的作用。Device Mapper是Linux内核中提供的一个映射框架,可以方便用户程序通过ioctl自行创建和管理设备之间的映射。其中涉及到3个核心元素:

  • Mapped Device
  • Target Driver
  • Target Device

  • 业界流行的方案基本都是基于Merkle tree,系统在初始化的时候只需验证Top Hash可信,则整个Merkle tree可信。在块设备上的每个块(通常为4K)被访问的时候,系统验证该块的完整性,不一致则拒绝访问。但要使用基于块(dm-verity)则必须满足三个条件,如下:
    • 当前系统拥有良好的分区方案,例如boot分区,系统分区,数据分区,缓存分区,临时文件分区,用户数据分区
    • 程序配置、数据分离。例如可能需要修改可执行程序源码,甚至重写
    • 当前系统拥有安全的OTA更新系统。例如当前运行的系统无法写入boot分区,系统分区
    • 检查DM-verity 是否关闭,使用命令mount |grep data查看挂载的分区里是否有dm-x的内容,如下:
    • DM-verity 开启显示的内容:/dev/block/dm-0 on /vendor type ext4 (ro,seclabel,relatime,data=ordered)/dev/block/dm-1 on /vendor type ext4 (ro,seclabel,relatime,data=ordered)
    • DM-verity 关闭显示的内容:/dev/block/msdadw35 on /vendor type ext4 (ro,seclabel,relatime,data=ordered)/dev/block/msdadw36 on /vendor type ext4 (ro,seclabel,relatime,data=ordered)
      -以及检查 vbmeta, boot, system ,vendor分区等是否在write protect 区间里来判断DM-verity 是否关闭,若在write protect区间里则是开启,否则就是关闭
  • 第二种基于文件检测(IMA+EVM)
    • 基于文件来做系统完整性检测话,可能会对系统性能产生影响,例如某些系统文件大小是可变的,甚至是超过G大小的文件,当然也可以是很小的几个字节
    • 基于文件来做系统完整性检测话,可能会存在兼容性差的问题,因为Linux系统的开放性,所以不同的厂商可自定制系统,就产生了系统兼容性的问题
    • 基于文件来做系统完整性检测话,是需要验证所有文件的数字签名,对服务器性能消耗要求较大。如果是基于块(dm-verity)来做的话就只需验证Top Hash的数字签名即可

其中Mapped Device是创建的虚拟设备,通过Target Driver描述的映射关系,创建到Target Device的映射。Target Device可以是最终的物理设备,也可以是其他的Mapped Device,也就是说映射关系是可以级联

在这里插入图片描述

BIO是对于块设备的基本IO操作单位,Device Mapper拓展的核心就是提供了BIO的具体映射,包括线性映射dm-linear,测试映射dm-flakey、dm-error、dm-delay以及加密映射dm-crypt等等

基于Device Mapper框架实现的应用有逻辑卷管理器LVM、软件阵列RAID以及Docker(COW)等。当然,dm-verity也是其中一个

1.13.1.4 Device Mapper Verity

dm-verity的代码在内核中为drivers/md/dm-verity.c(以Linux4.4为例,在upstream中进行了重构),主要作用是用来验证文件系统中data block的完整性,主要验证的调用链路如下:

  • verity_map
  • verity_end_io
  • verity_work
  • verity_verify_io
    • crypto_shash_final(desc, result)
    • memcmp(result, io_want_digest(v, io), v->digest_size)
    • v->hash_failed = 1

当block验证失败后,内核会根据v->mode选择是打印错误(DM_VERITY_MODE_LOGGING)还是重启系统(DM_VERITY_MODE_RESTART)

预置的root hash则是在mapped device的构造函数verity_ctr中传入的,如下所示:

  /*
   * Target parameters:
   *  <version>   The current format is version 1.
   *          Vsn 0 is compatible with original Chromium OS releases.
   *  <data device>
   *  <hash device>
   *  <data block size>
   *  <hash block size>
   *  <the number of data blocks>
   *  <hash start block>
   *  <algorithm>
   *  <digest>
   *  <salt>      Hex string or "-" if no salt.
   */
  static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
  {
    	// 处理arg[0-7] ...
      v->root_digest = kmalloc(v->digest_size, GFP_KERNEL);
      if (!v->root_digest) {
          ti->error = "Cannot allocate root digest";
          r = -ENOMEM;
          goto bad;
      }
      if (strlen(argv[8]) != v->digest_size * 2 ||
          hex2bin(v->root_digest, argv[8], v->digest_size)) {
          ti->error = "Invalid root digest";
          r = -EINVAL;
          goto bad;
      }
    	// ...
  }
1.13.1.5 Hash Tree

dm-verity将系统镜像切分为4k的块(block)大小,并对每一个块计算hash,这些hash以树状结构组合,称为hash tree,即哈希树。哈希树中的每个结点都是一个哈希,对于叶子结点值是对应块的hash,对于中间结点其值是所有子结点的hash。因此中间结点所包含的子节点(hash)容量也是一个块,和所采用的hash算法以及对应块大小有关。一个含有32768个块,块大小为4096字节且采用sha246 hash算法的哈希树结构示例如下:

alg = sha256, num_blocks = 32768, block_size = 4096

                                 [   root    ]
                                /    . . .    \
                     [entry_0]                 [entry_1]
                    /  . . .  \                 . . .   \
         [entry_0_0]   . . .  [entry_0_127]    . . . .  [entry_1_127]
           / ... \             /   . . .  \             /           \
     blk_0 ... blk_127  blk_16256   blk_16383      blk_32640 . . . blk_32767

由此可见,任意一个块结点的修改都会导致其hash变化,从而导致root hash变化,因此验证hash tree的完整性可以通过验证root hash来实现

在AOSP构建环境中,生成hash tree的工具为build_verity_tree,代码在system/extras/verity/build_verity_tree.cpp

$ build_verity_tree -h
usage: build_verity_tree [ <options> ] -s <size> | <data> <verity>
options:
  -a,--salt-str=<string>       set salt to <string>
  -A,--salt-hex=<hex digits>   set salt to <hex digits>
  -h                           show this help
  -s,--verity-size=<data size> print the size of the verity tree
  -v,                          enable verbose logging
  -S                           treat <data image> as a sparse file

输出verity哈希树镜像文件verity.img,并在标准输出打印哈希树的root hash以及使用的salt

1.13.1.6 Verity Table

Verity Table也称为dm-verity mapping table,该映射表包含目标设备的位置、对应hash表的位置、hash treeroot hash值和salt等。其值是一个字符串,在AOSP中通过build_verity_metadata.py脚本生成

$ ./system/extras/verity/build_verity_metadata.py build -h
usage: build_verity_metadata.py build [-h] [--signer_args SIGNER_ARGS]
                                      blocks metadata_image root_hash salt
                                      block_device signer_path signing_key

positional arguments:
  blocks                data image blocks
  metadata_image        metadata image
  root_hash             root hash
  salt                  salt
  block_device          block device
  signer_path           verity signer path
  signing_key           verity signing key

optional arguments:
  -h, --help            show this help message and exit
  --signer_args SIGNER_ARGS
                        verity signer args

一个完整的verity table字符串以及每个字段的含义示例如下:

0 417792 verity 1 /dev/sdb /dev/sdc 4096 4096 52224 1 sha256 2aa4f7b7b6...f4952060e8 762307f4bc8...d2a6b7595d8..
|    |     |    |     |     |        |    |    |    |    |              |                        |  
start|     |    |  data_dev |  data_block | #blocks | hash_alg      root_digest                salt  
     size  |  version    hash_dev         |     hash_offset  
         target                       hash_block  

可以看到从version开始后面的内容和传递到内核dm-verity驱动构造函数中的参数是一致的,在上面有介绍到。每个参数的详细含义除了源码也可以参考内核的文档Documentation/device-mapper/verity.txt

1.13.2 VBoot 1.0

如下内容来自:https://blog.csdn.net/u010206565/article/details/109888855

Verified Boot中对于不同的磁盘镜像有不同的校验方式。比如对于较小的镜像,如boot、recovery,可以直接加载到内存中进行校验;而对于较大的镜像,比如system、vendor等,则无法一次性载入内存计算hash,因此需要借助dm-verity实现块级别的校验。下面分别介绍两种类型校验的实现

1.13.2.1 system.img

在Android中基于dm-verity实现可信启动的步骤如下:

  • 生成ext4系统镜像
  • 生成该镜像的哈希树
  • 生成目标哈希树的dm-verity表(即前面提到的verity table字符串)
  • 对dm-verity表进行签名
  • 将dm-verity表及其签名打包为元数据(verity metadata)
  • 将系统镜像、元数据和哈希树进行打包,生成最终的镜像

这些操作可以用下图来表示:

在这里插入图片描述

前3步在上面已经有介绍了,第4步中对verity表签名使用的是RSA-2048算法,公钥在最终烧写在目标机器的根目录下/verity_key。值得一提的是,其私钥在AOSP中的build/target/product/security目录里,该目录除了包括verity_key,还有testkey、platform、shared、media相关的key。不同之处是后面4个key使用development/tools/make_key脚本生成,而verity可以用make_key也可以用out/host/linux-x86/bin/generate_verity_key生成(需要先编译make generate_verity_key)

# 生成verity_key.pub
generate_verity_key -convert verity.x509.pem verity_key
mv verity_key.pub verity_key

其中verity.x509.pem是公钥的x509格式,转换的内部实现摘要如下:

// system/extras/verity/generate_verity_key.c
static int convert_x509(const char *pem_file, const char *key_file) {
  f = fopen(pem_file, "r");
  cert = PEM_read_X509(f, &cert, NULL, NULL);
  pkey = X509_get_pubkey(cert);
  rsa = EVP_PKEY_get1_RSA(pkey);
  write_public_keyfile(rsa, key_file)
}

verity_key包含RSA公钥信息,使用的是Android的特殊编码方法android_pubkey_encode

1.13.2.2 boot.img

前面说过对于较大的磁盘镜像如system.img需要通过dm-verity在运行时访问磁盘block的过程中进行校验,但是对于较小的镜像,则可以直接加载到内存中进行校验。比如boot.imgrecovery.img。对于二者的签名和校验可以通过boot_signer工具完成:

$ boot_signer -verify out/target/product/angler/boot.img
Signature is VALID
# 相当于使用verity key进行校验
$ boot_signer -verify out/target/product/angler/boot.img -certificate build/target/product/security/verity.x509.pem
NOTE: verifying using public key from build/target/product/security/verity.x509.pem
Signature is VALID

boot_signer是一个使用Java写的小工具,其实现代码为system/extras/verity/BootSignature.java,从代码中可以看到boot.img的签名是保存在原镜像末尾的

签名数据的格式使用ASN.1定义如下:

AndroidVerifiedBootSignature DEFINITIONS ::=
BEGIN
    formatVersion ::= INTEGER
    certificate ::= Certificate
    algorithmIdentifier ::= SEQUENCE {
        algorithm OBJECT IDENTIFIER,
        parameters ANY DEFINED BY algorithm OPTIONAL
    }
    authenticatedAttributes ::= SEQUENCE {
        target CHARACTER STRING,
        length INTEGER
    }
    signature ::= OCTET STRING
END
1.13.2.3 验流程

verified boot 1.0中,boot.img的校验通过其头部的证书和签名信息进行验证。boot镜像中包括ramdisk文件系统,其中在根目录下就包含了校验其他镜像所用到的/verity_keyboot.img由内核进行校验和挂载,内核负责启动其中的init进程

系统启动后通过fs_mgrsystem.img中的hashtree进行校验(root hash),获取verity table,使用其中的信息初始化dm-verity驱动并挂载镜像,从而实现运行时block级别的校验

如下图所示:

在这里插入图片描述

对于AB系统则略有不同,因为boot.img中不再包含根文件系统(而是在system.img),因此使用内核中的keyring保存verity的公钥信息。而dm-verity驱动的参数则通过DTB进行保存,DTB可以保存在内核镜像之后,也可以作为独立的镜像文件保存。后续对于system分区的校验则是类似的

:这里是内核负责获取system镜像的metadata,提取dm-verity参数并初始化dm-verity驱动

在这里插入图片描述

1.13.3 VBoot 2.0 (AVB)

如下内容来自:https://blog.csdn.net/u010206565/article/details/109888855

AVB是Android 8之后对于Verified Boot的一个参考实现,也称为Verified Boot2.0。在AVB中一个重要的数据结构就是VBMeta,其中包括了一系列描述符和元信息,并且其中所有的信息都是签名的。描述符包括hash描述符(boot.img、dtbo.img等小镜像)以及hashtree描述符(system.img、vendor.img等大镜像)。除了签名,VBMeta中还包含版本信息以及Rollback Index,从设计上就考虑了降级攻击的安全威胁风险

1.13.3.1 vbmeta

在大部分AVB实现中,都有一个独立的vbmeta.img镜像文件,这个文件格式的定义在 external/avb/libavb/avb_vbmeta_image.h中,截取部分代码如下:

  typedef struct AvbVBMetaImageHeader {
    /*   0: Four bytes equal to "AVB0" (AVB_MAGIC). */
    uint8_t magic[AVB_MAGIC_LEN];

    /*   4: The major version of libavb required for this header. */
    uint32_t required_libavb_version_major;
    /*   8: The minor version of libavb required for this header. */
    uint32_t required_libavb_version_minor;

    /*  12: The size of the signature block. */
    uint64_t authentication_data_block_size;
    /*  20: The size of the auxiliary data block. */
    uint64_t auxiliary_data_block_size;

    /*  28: The verification algorithm used, see |AvbAlgorithmType| enum. */
    uint32_t algorithm_type;
    ...
  }

Pixel 4XL最新的原厂固件为例进行分析,其中的vbmeta.img文件如下:

$ head -c 200 vbmeta.img | xxd
00000000: 4156 4230 0000 0001 0000 0000 0000 0000  AVB0............
00000010: 0000 0240 0000 0000 0000 1000 0000 0002  ...@............
00000020: 0000 0000 0000 0000 0000 0000 0000 0020  ...............
00000030: 0000 0000 0000 0020 0000 0000 0000 0200  ....... ........
00000040: 0000 0000 0000 0be8 0000 0000 0000 0408  ................
00000050: 0000 0000 0000 0ff0 0000 0000 0000 0000  ................
00000060: 0000 0000 0000 0000 0000 0000 0000 0be8  ................
00000070: 0000 0000 5eb0 ac80 0000 0000 0000 0000  ....^...........
00000080: 6176 6274 6f6f 6c20 312e 312e 3000 0000  avbtool 1.1.0...
00000090: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000c0: 0000 0000 0000 0000                      ........

使用avbtool工具可以查看镜像的详细信息:

$ avbtool info_image --image vbmeta.img
Minimum libavb version:   1.0
Header Block:             256 bytes
Authentication Block:     576 bytes
Auxiliary Block:          4096 bytes
Algorithm:                SHA256_RSA4096
Rollback Index:           1588636800
Flags:                    0
Release String:           'avbtool 1.1.0'
Descriptors:
    Chain Partition descriptor:
      Partition Name:          vbmeta_system
      Rollback Index Location: 1
      Public key (sha1):       8c44014b96f0f41f3daa3825d4af410233372b65
    Prop: com.android.build.product.fingerprint -> 'google/coral/coral:R/RPP4.200409.015/6455311:user/dev-keys'
    Prop: com.android.build.product.os_version -> '10'
    Prop: com.android.build.product.security_patch -> '2020-05-05'
    Prop: com.android.build.vendor.fingerprint -> 'google/coral/coral:R/RPP4.200409.015/6455311:user/release-keys'
    Prop: com.android.build.vendor.os_version -> '10'
    Prop: com.android.build.vendor.security_patch -> '2020-05-05'
    Prop: com.android.build.boot.fingerprint -> 'google/coral/coral:R/RPP4.200409.015/6455311:user/release-keys'
    Prop: com.android.build.boot.os_version -> '10'
    Prop: com.android.build.boot.security_patch -> '2020-05-05'
    Prop: com.android.build.dtbo.fingerprint -> 'google/coral/coral:R/RPP4.200409.015/6455311:user/release-keys'
    Hash descriptor:
      Image Size:            34037760 bytes
      Hash Algorithm:        sha256
      Partition Name:        boot
      Salt:                  a5d378a2cf0b56ad731bc531760791a68eda4c11903388d92a872c0610c011fb
      Digest:                f9968f0e26ede13f1e8769163e22a0cb53e747af8ea67915fc6a3ccc4b239744
    Hash descriptor:
      Image Size:            3330722 bytes
      Hash Algorithm:        sha256
      Partition Name:        dtbo
      Salt:                  7cfa6cab9ee5db15f872b86afc8767b4a440e8d3411096fe5effd3e9ccf3c35b
      Digest:                215c5440b139eac525ece71f2decfca1c6b553906b188fad12adb2b3048edf3d
    Hashtree descriptor:
      Version of dm-verity:  1
      Image Size:            1976545280 bytes
      Tree Offset:           1976545280
      Tree Size:             15568896 bytes
      Data Block Size:       4096 bytes
      Hash Block Size:       4096 bytes
      FEC num roots:         2
      FEC offset:            1992114176
      FEC size:              15753216 bytes
      Hash Algorithm:        sha1
      Partition Name:        product
      Salt:                  9cc26ca7a5b14b40af3dc0afffd29748a5f56fec335ec3974572ce665d2cb22b
      Root Digest:           7511feaf0fbc611576daac502cbfc328d931604f
    Hashtree descriptor:
      Version of dm-verity:  1
      Image Size:            764170240 bytes
      Tree Offset:           764170240
      Tree Size:             6025216 bytes
      Data Block Size:       4096 bytes
      Hash Block Size:       4096 bytes
      FEC num roots:         2
      FEC offset:            770195456
      FEC size:              6094848 bytes
      Hash Algorithm:        sha1
      Partition Name:        vendor
      Salt:                  9cc26ca7a5b14b40af3dc0afffd29748a5f56fec335ec3974572ce665d2cb22b
      Root Digest:           dfca98b6e4014f2fe6b3d12402368225c47783b1

从中可以看出vbmeta中包含了boot、dtbo镜像的签名,以及product、vendor镜像的hashtree签名。而system和system_ext的签名信息则保存在vbmeta_system.img

1.13.3.2 校验流程

在AVB 2.0中,bootloader必须集成libavb,负责处理hashtree描述符,并转换为dm-verity参数。通过将参数写入内核cmdline启动内核时初始化dm-verity驱动。/systemhashtree描述符可以在/system中,也可以在/vbmeta

bootloader中集成的OEM公钥负责用来校验vbmeta和内核(即boot.img),随后vbmeta中的其他公钥用于校验对应的分区

在这里插入图片描述

1.13.4 其它

除了使用预置的公钥,新版的AOSP也支持设置用户信任根(user-settable root of trust):

avbtool extract_public_key --key key.pem --output pkmd.bin
fastboot flash avb_custom_key pkmd.bin
fastboot erase avb_custom_key

启动校验流程如下:

在这里插入图片描述

1.13.4 Android 验证启动部分总结

Secure Boot是保障系统完整性和内部软件安全的一个重要屏障,本文主要针对Android智能设备的Secure Boot实现进行梳理和分析。Android Secure Boot实现主要有两个版本,一个是Verified Boot 1.0,另一个是Verified Boot 2.0,也称为AVB。前者主要通过在镜像末尾添加证书和签名等元信息,而后者将这些信息统一成VBMeta,并根据实现可置于独立分区中。由于两种不同的分区策略(AB和non-AB),两种Secure Boot的具体校验流程也略有不同,但整体大同小异

虽然设计在理论上比较完善,但设备厂商的具体实现也可能存在缺陷,比如使用了错误的秘钥、eFuse不完全、或者bootloader中添加了隐藏的功能等等,这都将导致系统的完整性遭到破坏,从而影响产品的整体安全性。因此,设备厂商也应该遵循合理的安全开发流程,在发版之前由安全工程师进行审计或者使用自动化工具进行测试验证,使系统的信任根和信任链路得以充分安全实现

1.13.5 Android 关闭验证启动

将备份的vbmeta.img重新刷入并关闭验证即可:

fastboot --disable-verity --disable-verification flash vbmeta vbmeta.img

1.14 Secure Boot

安全启动其实就是一种常见的 Android 机制,用于防止 Android 设备启动未经批准的软件。与大多数计算机一样,Android 设备有一个非常小的基于 ROM 的主引导加载程序,用于进行基本的硬件初始化,找到一个包含更多引导软件的文件系统,然后加载并跳转到该辅助引导软件

该辅助引导加载程序可能会加载 Android 操作系统,或跳转到另一个引导加载程序,具体取决于所选的硬件和软件。按照惯例,操作系统之前的最后一个引导加载程序通常称为 aboot

Secure Boot 安全机制的原理:旨在将最为核心的安全机制整合到最关键的主 CPU 中。因此就算攻击者可以监听电路板上的线路,甚至拆装个别芯片单独调试,也无法破坏Secure Boot的安全机制

安全启动的目的:安全启动的根本目的是为了防止消费者从软硬件层面对产品的部分关键系统进行读写、调试等高权限的操作,确保设备仅运行授权和受信任的软件对于最终用户、设备制造商 (OEM) 和运营商等都至关重要,因为OEM 可能希望保护它们的设备免于运行未经授权的软件。不真实的软件可能会降低运营商网络或设备的性能,以及恶意应用程序可能会危害从用户的私人或财务数据到无法挽回地损坏物理设备本身的任何内容,甚至执行不受信任的恶意应用程序存在许多风险和潜在后果(当然,厂家是不会这样宣传 Secure Boot 的,要不然自己的产品咋卖出去?通常它们的文案都是通过这项技术保护用户的隐私,防止恶意软件修改系统软硬件等等。不论文案如何,随着 ARM 架构的广泛授权,基于 TrustZone Secure Boot 也越来越普遍了),例如:

  • 假设一个尝试恶意注入或修改存储中的软件映像的攻击者。攻击者可以在加载的软件链中越早破坏图像,他们获得的控制权就越大。设备软件通常分阶段加载,其中每个软件映像通常配置为比链中的前一个映像具有更少的权限和控制。具体来说,加载的第一个软件映像几乎可以完全控制设备。这些要加载的第一个映像称为引导加载程序映像
  • 如果攻击者可以用他们自己的恶意映像替换第一个要执行的软件映像,那么他们就可以控制设备的其余执行。这使得引导链的完整性变得至关重要。用恶意映像替换存储中的引导加载程序映像可能会导致持久性漏洞利用,该漏洞将控制该软件映像和任何要在其之后运行的映像中的执行

:实施“安全启动”链,旨在确保这些映像中的每一个都未被修改,并且是阻止恶意或危险软件执行的一种方法。安全启动被定义为一个启动序列,其中每个可执行软件映像都由先前验证的软件进行身份验证。此序列旨在防止未经授权或修改的代码运行。我们根据这个定义构建我们的信任链,从第一个用完只读存储器 (ROM) 的不可变软件开始。第一个 ROM 引导加载程序以加密方式验证链中下一个引导加载程序的签名,然后该引导加载程序以加密方式验证下一个或多个软件映像的签名,依此类推(如下图)

在这里插入图片描述

上图描述了安全启动序列的示例:操作系统验证的三个映像已通过信任链进行身份验证,该信任链返回到硬件中的第一个 ROM 引导加载程序。此链中的每个图像都已通过锚定到根证书的证书链进行加密验证,根证书也锚定在硬件中。任何将潜在有害代码注入图像的尝试都将被阻止

具有安全启动技术的 Android 手机使用数字证书来确保在操作系统之前加载的软件是可信的。这意味着它由设备供应商进行了数字签名,并以加密方式防止篡改。因此,主引导加载程序不只是找到并运行辅助引导加载程序文件;相反,它会读取辅助引导加载程序并验证数字签名以确保其未被篡改。如果验证失败,则引导过程停止

1.14.1 当消费者成为威胁

如下内容来自:https://bbs.pediy.com/thread-260399.htm

Secure Boot的安全模型建立在消费者是攻击者这一假设上。消费者在物理上拥有产品硬件,可以对产品进行物理连接、拆机、改装等等物理上的操作。可以说跟传统的安全模型中的攻击者相比根本不在一个层面上

消费者作为攻击者的目的,一般常见的有刷机安装自定义的操作系统(Mod)、绕过厂家封闭的支付平台(IAP)和应用商城安装自定义的应用程序、绕过版权保护系统(DRM)达到复制厂家保护的数字产品内容等等。这些操作往往都会直接影响厂家的利益,因此需要一种能抵抗消费者攻击的安全机制

上面提到了,消费者可以直接拆机干涉产品的硬件模块。比如比较专业的消费者甚至可以使用数字示波器监听 CPU 和 RAM 、eMMC 之间的数据传输来读取非常底层的数据传输。如下图所示,我拿某厂的电视盒子的 PCB 拍了张照方便大家理解,如图中圈出来的,CPU 、RAM 、eMMC 都是分开的芯片,中间的连接电路是有可能被监听的,有些研究人员可以焊接极细的导线到电路上,来监听、拦截、甚至篡改不同芯片之间数据的传输

在这里插入图片描述

而且像 eMMC 这种芯片通常都是业界标准化的,攻击者甚至可以把芯片拆下来,然后用市面上现成的通用 eMMC 编程工具来读写上面的内容

不过业界还是给攻击者的能力设置了一个上限。这个上限通常是认为攻击者不至于能够剥离芯片的封装,然后用电子显微镜等纳米级别精度的显像设备来逆向芯片的内部结构。或者说能成功攻破芯片安全机制的一次性投资成本至少需要在十万美元以上才可以认为是安全的

Secure Boot 安全机制的原理,就是将最为核心的安全机制整合到最关键的主 CPU 中。因此就算攻击者可以监听电路板上的线路,甚至拆装个别芯片单独调试,也无法破坏 Secure Boot 的安全机制

1.14.2 Secure Boot 缺点

安全引导是通过每个引导加载程序以加密方式依次验证下一个引导加载程序的签名来实现的,使用证书链,其信任根驻留在硬件中。如果验证在任何步骤失败,则引导过程终止

虽然Secure Boot 可以有效防止未经授权的引导加载程序,但它无法区分不同的授权二进制版本。例如,安全启动无法区分具有已知漏洞的引导加载程序和后来的修补版本,因为这两个版本都有有效的签名。然而,引入可信引导来验证相同的引导加载程序、内核和平台构建即可解决这个问题

1.14.3 Secure Boot 流程

一台 Android 设备是由硬件和软件组成,当按下开机键时系统从硬件到软件,在到最后进入 Android 系统,是一个完成的引导过程。以高通为例,在一个常规的引导过程中,CPU 引导芯片代码 PBL(Primary Boot Loader,类似于 x86 的 BIOS,有时也被成为 BootROM)从预定义的地方(固化在 ROM)开始执行,PBL由高通做好后烧写在芯片中,不可更改,是 RoT(Root of Trust,信任根)。使用烧录在fuse中的根公钥校验并加载引导程序 SBL1(Secondary Boot Loader),跳转到 sbl1 执行,SBL1 加载校验 APPSBL(aboot),最后 APPSBL 加载校验 boot 分区。内核启动后,会通过内核中的 dm-verity功能模块校验系统分区的完整性。这样就完成了整个系统的安全引导。整个流程如下所示:

在这里插入图片描述

Android 完整性校验过程,分为两部分:

  • 第一部分是官方的 verified boot,由内核中的 dm-verity 来确保 system 分区没有经过被篡改
  • 第二部分是针对于不同设备的引导加载程序的安全引导来验证最终 boot 镜像的完整性。这两部分共同建立了一个从 bootloader 到系统镜像的信任校验链

1.14.4 Secure Boot 组成

如下内容来自:https://blog.omitol.com/2017/09/30/Bypass-QCOM-Secure-Boot

1.14.4.1 引导加载程序的安全引导

这个过程是一个安全认证校验链,都是由上一阶段的程序加载校验下一阶段的要执行的程序,通过签名校验机制,来确保系统不被经过任何形式的篡改,只执行制造商的固件。此过程是特定于设备的,通常通过使用不可更改的特定于硬件的密钥来实现被“烧录”(写入只写存储器)到设备中。该密钥用于验证每级的引导加载程序到最终boot镜像的完整性

高通的 bootloader 是开源项目,在 Android 代码树的 bootable/bootloader/lk 下可以看到它的代码。是针对特定的主板与芯片编写的,并不是Android操作系统的一部分。由于 SBL 代码是闭源代码,分析起来是一个复杂的过程。论文在分析研究引导加载程序的安全引导过程中只从 APPSBL开始进行分析

BootLoader是 OEM 厂商或者运营商加锁和限制的地方。当 bootLoader 上锁后就不允许在非解锁状态下对手机固件进行修改或者刷第三方系统。这些限制取决于 OEM 和运营商的具体决策,可能会有所不同,但普遍都会采用密码学的签名校验机制来阻止设备被刷机或者执行未经合法签名的代码。如果用户想要刷机就需要先对 bootloader 进行解锁。现 OEM 都会采用专门的机制,比如需向官方申请解锁码,申请通过后得到解锁码才可以解锁设备。设备解锁后 bootloader 将不再对 boot 和 recovery 分区进行签名校验,也就是不在进行安全引导,允许进行刷机和清除用户数据等操作机制进行解剖分析,以及研究具体的安全保护措施,分析存在的安全缺陷和隐患

1.14.4.2 Verified Boot

Android 4.4 起,Android 支持使用 Linux 的 Device-Mapper框架中的 dm-verity功能进行验证启动。dm-verity是为块完整性检查而设计开发,使用加密散列树提供块设备的透明完整性检查。它可以验证每个设备块在从磁盘读取时的完整性,如果块检出,则读取成功

如果没有,读取会产生I/O 错误,就好像块被实际损坏了一样。在 Android 中用来保护系统重要分区如 system 分区或 vendor 分区的完整性。系统分区被挂载为只读模式,不再允许被挂载为读写模式。校验系统分区时使用的密钥在 boot 镜像的 ramdisk 中

1.14.5 Secure Boot 机制

Secure Boot 功能是负责在SOC启动时验证bootloader 二进制的合法性。BootLoader二进制是由SOC的Rom Code启动的

bootloader的合法性认证由Rom Code实现,那么bootloader认证使用的公钥存储在哪?

  • 存在SOC的efuse单元里,在产品的生成阶段工厂需要使用SOC厂商提供的工具来把公钥烧录到SOC中(一次烧录之后再也不能更改)

Secure Boot功能完全有SOC硬件实现的,如果设备系统有Secure Boot需求,首先查看SOC的datasheet看芯片支不支持这个功能,而市面上从2016年开始的SOC基本都支持Secure Boot (比如车载信息娱乐系统(IVI) SOC)

Secure Boot负责校验上电之后运行的第一个用户二进制文件,比如u-boot

如下内容来自:https://blog.omitol.com/2017/09/30/Bypass-QCOM-Secure-Boot

1.14.5.1 user keystore校验漏洞挖掘

BootLoader 序是一种专门的,特定于硬件的程序,当设备首次通电(ARM 设备复位时)执行。其目的是初始化设备硬件,提供最小的设备配置接口,然后找到并启动操作系统。引导设备通常需要经历不同的阶段,这涉及每个阶段的单独的引导加载程序,本文只分析 APPSBL(aboot)加载引导程序。Android 引导加载程序通常是专有的,特定于芯片 SoC 的系统。设备和 SoC 制造商在其引导加载程序中提供不同的功能和级别的保护

在整个校验链中由aboot来提供验证 boot.img 的完整性,其开源代码 LK 可在Code Aurora Forum 下载。在 LK 中有两个可用于校验的不同的密钥:

  • 一个是 oem_keystore,被编译到 aboot 中,定义在 platform/msm_shared/include/oem_keystore.h
  • 一个是 user_keystore存储在 keystore 分区中

引导过程中始终尝试使用OEM keystore 来验证boot.img 和 recovery.img。但在 keystore 分区不为空的时候,会使用 OEM keystore 对其签名进行验证,如果验证通过,将从里面读取 user_keystore,然会用其验证 boot.img 和 recovery.img

user_keystore 包含了用于验证的 RSA 公钥。以 CAF 代码 LA.BR.1.3.2_rb3.14分支为例,整个基本函数和逻辑执行如图所示:

在这里插入图片描述

最终调用的verify_image_with_sig() 采用的是user_keystore中的公钥进行校验。而 user_keystore 的值由boot_verifier_init 调用 read_oem_keystore,将
oem_keystore赋值 user_keystore。接着对 keystore 分区进行验证,如果验证通过,则将分区中的数据赋值 user_keystore。这样就完成了对 user keystore 的利用

但是read_user_keystor() 方法中调用verify_keystore 验证 user keystore 时,在if-else 判断中的 385 行因缺少花括号,导致无论验证成功与否,都会user_keystore 进行赋值。具体代码如图所示:

在这里插入图片描述

这样就造成了一个明显的安全漏洞,user keystore 不用经过 OEM 的签名也可以用于校验 boot 或 recovery 镜像。我们只需要自己签名生成keystore.img 通过其它漏洞写入手机就可以绕过安全引导机制

1.14.5.2 解锁标记位的保护分析

在高通的分区表中,有一个名为 devinfo 的分区,大小 1024K。在 app/aboot/devinfo.h 中定义了其数据结构,包括了is_unlocked 解锁状态标记位;is_tampered 篡改标记位等。通过 fastboot oem device-info 命令可以获取相关信息

在 LK 启动时,通 aboot\_init()->read_device_info(&device)->read_device_info_mmc() 读取,若is_unlock 为 true,就跳过校验,允许执行 flash 命令等。使用fastboot oem unlock 命令后,会通过write_device_info_mmc(&device) 对 devinfo 分区的标记位进行操作

高通源代码中并为对该标记位进行加密签名等保护,直接修改标记位就可以使用对手机的解锁。但是在 OEM 的实现中,大多都会对该分区进行保护,修改分区名和使用加密签名等手段,保证分区不被非法篡改

但 Android 系统碎片化的存在,以及厂商技术水平的参差不齐,依然有很多设备未对该部分进行修改,留下了安全隐患

1.14.5.3 高通下载模式分析

高通有着自己的下载协议,一般在设备生产的时候通过该协议烧录固件。在 lk 代码 aboot_init 中,通过监控按键等操作可以选择到进入到不同的模式,如 recovery 或fastboot 模式等。代码中默认当同时按下音量上下键时,则进入到 DLOAD 模式,也就是下载模式。然后通过高通专有的 sahara 或 firehose 协议工具进行固件的下载更新

同样在 kernel/drivers/power/reset/msm-poweroff.c 中相关代码由宏 CONFIG_MSM_DLOAD_MOD 控制,可开启或关闭是否可以通过 adb reboot edl/dload 命令重启进入到下载模式

高通的升级工具及协议在下载的过程中,并不会对固件进行任何的校验,如果下载了错误或损坏的固件,则直接会让设备变砖。但是,厂商为方便开发、生产、售后等需求,并不会完全关闭掉高通的下载模式,有的会留下隐蔽的接口来进入到该模式

1.14.6 Secure Boot 技术方案的目的

Secure Boot实现的技术目的 :

  • 建立完整trust chain,保证Android设备加载的Kernel 和Android系统不被篡改
  • 防止rootkit、bootkit等软件对系统的攻击

1)Secure Srom

是安全芯片内部一段电路逻辑,它会在芯片每次启动时被执行,它配合efuse存储的rsa public key,验证外部存储设备(Nand /Emmc)上的bootloader 完整性。Secure Brom的不可被修改性和强制执行,保证了整个安全启动过程

2)Efuse

帮助存储验证使用到的Root RSA public key。每个厂商保存自己的Root rsa private key,在量产时候将对应Root rsa public key刷入到芯片efuse指定区域。Efuse只能一次写入的特性,决定了芯片在量产刷写后,芯片Root rsa public key就被厂商所控制 ,永远不会被第三方修改。而掌握了Root rsa key的厂商,就掌握了芯片固件的控制权

3)Crypto engine

提供给固件验证算法支持和加速。目前使用到的硬件加速算法是RSA 非对称解密算法和 SHA数字摘要算法

1.15 Trusted Boot 和 TrustZone

TrustZone技术是ARM开发的一种硬件体系结构,它允许软件在安全与非安全的两个域中执行。这是使用“NS”位来进行标识的,这一位可以指示master是在安全模式下还是非安全模式下运行。这里所说的master可以是CPU内核,也可以是硬件外设(例如DMA或加密引擎)。master是否安全,可以在设计中通过硬连线来定义,也可以通过配置定义。例如,可以通过调用SMC指令(稍后详细介绍)或切换SCR寄存器中的NS位来切换CPU内核的安全状态

现代,当我们启动任何设备时,从断电的处理器跳转到运行受信任软件的设备都需要硬件支持。在30 多年前老旧的基本输入/输出系统 (BIOS) 没有提供任何保护(它几乎无法加载操作系统)。从那时起,系统供应商一直在尝试在引导过程中构建更多的安全性。统一可扩展固件接口(UEFI)等行业标准方法奠定了基础并创造了最佳实践

当然,现代的Android 系统也需要同样的保护,最终的目标是确保智能手机运行受信任的软件。在这里有两个组件(为了定义slave(例如外围设备或内存)的访问限制,TrustZone通常包括两个组件,分别是TrustZone地址空间控制器(TZASC)和TrustZone保护控制器(TZPC)),有助于确保Android系统的可信引导进行安全引导,以及通过基于 TrustZone 的完整性管理架构 (TIMA) 进行内核完整性检查

为了定义slave(例如外围设备或内存)的访问限制,TrustZone通常包括两个组件,分别是TrustZone地址空间控制器(TZASC)和TrustZone保护控制器(TZPC)

TZASC可以用于定义DRAM中的安全范围。ARM提供了两种不同的实现方式,最新的是TZC-400。下图概述了其通常情况下在SoC中的实现方式,引自官方技术文档

TZASC概述:

在这里插入图片描述

从上图可看出,任何对DRAM存储器的访问都会首先通过TZASC,然后转发给内存控制器。TZASC可以基于一组内部规则来判断是否允许访问内存

TZASC包含一个始终启用的基础区域(区域0),能够跨越整个DRAM内存范围。此外,还定义了许多其他安全区域,可以限制对其的访问。详细来说,在其他区域可以设置以下内容:

  • 起始地址和结束地址
  • 安全读取和写入的权限。这些权限将应用在尝试访问内存范围的任何安全master上。请注意,TZASC没有委派给MMU的执行许可和强制执行的概念
  • 非安全ID过滤。可以将区域配置为允许访问非安全的master。对于TZC-400来说,可以为读取和写入权限来设置位掩码,以便对允许哪些非安全master访问内存范围进行精细化控制

TZPC实现了类似的概念,但适用于内部外围设备和SRAM,不适用于外部DRAM。其中包含一个寄存器(R0size),以4KB为单位指定安全分片上SPAM的大小。与TZASC相比,它不太灵活,因为它只允许定义一个安全区域和一个非安全区域,安全区域是从0开始直到指定的大小,剩余的SRAM直接被视为非安全区域

然后,还有很多其他的寄存器,用于为每个外围设备指定其安全性(只能由安全的master访问)。TZPC寄存器中的不同位对应哪些外设并没有定义,并且它是完全针对不同的SoC

通常,TZASC和TZPC的大多数设置都是在初始化期间配置的,并且永远不会更改。但是,其中有一些需要在运行时动态修改。这里的一个示例是用于执行安全付款的可信用户界面(TUI)。以S10手机的三星支付为例,当用户需要输入PIN来授权付款时,TEE将会接管,并直接控制显示屏和触摸传感器。这里的底层逻辑是,由于PIN是一个敏感数据,因此交由TEE来处理整个过程,而不再使用不受信任的Android OS。因此,必须使用TZPC将显示器和触摸控制器重新配置为“安全”,这样一来,即使是在Android中运行内核级代码的攻击者也无法获取到PIN。要在屏幕中显示图像,需要在DRAM中存储一个安全的帧缓冲区,因此TEE还会使用TZASC将DRAM中的一部分重新配置为“安全”,并将其作为帧缓冲区。在用户输入完PIN后,TZASC和TZPC将其恢复为之前的值,然后Android再次接管

安全和非安全模式之间的转换由名为“安全监视器”(Secure Monitor)的组件来管理。这个监视器是TEE和REE之间的主要接口,并且是唯一可以修改内核安全状态的组件

与在REE中一样,TEE在内核和TA之间保持用户模式与内核模式之间的隔离。TEE OS还负责加载TA,并在REE和TA之间传递参数。TA在安全区域的用户空间中运行,并为REE提供服务

ARMv8-A CPU在每个区域中支持四个特权级别,也将其称为异常级别,分别是:

  • (S-)EL0:用户模式/APP
  • (S-)EL1:内核
  • EL2:管理程序(Hypervisor)
  • EL3:安全监视器(Secure Monitor

在这里插入图片描述

在REE中,我们的Android应用程序在EL0上运行,而Linux内核则在EL1上运行

EL2仅以非安全模式存在(在ARMv8.4-A版本之前),称之为管理程序(Hypervisor)。它最初被设计为一种处理以较低特权级别并行运行的多个虚拟环境的方法,但是在Android环境中,通常将其用作内核加固机制。例如在三星手机中也是如此,管理程序组件被称为实时内核保护(RKP,Real-time Kernel Protection),除了这些用途之外,它还限制了内核可以访问的内存,并将某些内核结构设置为只读,从而增加了内核漏洞利用的难度

最后,来分析安全组件,我们研究的目标是EL3(始终以安全模式运行)、S-EL0和S-EL1。关于TEE的实现方式,有多种方式,但是目前最常见的示例是:在EL3上运行一个非常小的组件,负责在两个区域之间进行切换;在EL1上运行一个成熟的内核;在EL0上运行多个TA。三星的TEE OS TEEGRIS也采用了这样的设计方式

尽管完全隔离的环境非常安全,但在使用的过程中,它还是需要与Android中运行的其他不受信任的组件进行通信。REE和TEE之间的通信使用名为“安全监视器调用”(SMC)的专用指令触发。两个指令都可以在EL > 0时调用该指令,这意味着Android应用程序无法直接启动与TEE的通信。通常的情况是Linux内核充当代理并公开驱动程序,应用程序可以使用该驱动程序与TEE进行交互。这种设计的优势在于,可以将访问限制策略(例如使用SELinux)应用于访问驱动程序的场景中,以确保只有部分应用程序可以与TEE通信,从而收敛了攻击面。对于S10手机来说,情况也是如此,仅允许有限的应用程序和服务与TEE通信

假设攻击者具有与TEE进行通信的能力。在使用Magisk这样的工具对手机进行root时就是这种情况,或者,也可以获取Linux内核的运行时控制,还可以获取允许与TEE通信的Android应用/服务的运行时控制。一旦执行了SMC指令,就会在EL3中运行的安全监视器中生成一个中断。SMC处理机制会将SMC路由到相应组件。如果监视器可以直接处理SMC,那么就进行处理并立即返回。否则,会将请求转发到TEE内核(在S-EL1运行),然后在其内部进行处理,或者继续将其转发到在S-EL0运行的TA

再回过头来,说说安全启动。安全启动其实就是一种常见的 Android 机制,用于防止 Android 设备启动未经批准的软件。与大多数计算机一样,Android 设备有一个非常小的基于 ROM 的主引导加载程序,用于进行基本的硬件初始化,找到一个包含更多引导软件的文件系统,然后加载并跳转到该辅助引导软件

该辅助引导加载程序可能会加载 Android 操作系统,或跳转到另一个引导加载程序,具体取决于所选的硬件和软件。按照惯例,操作系统之前的最后一个引导加载程序通常称为 aboot

安全启动的目的:安全启动的根本目的是为了防止消费者从软硬件层面对产品的部分关键系统进行读写、调试等高权限的操作,确保设备仅运行授权和受信任的软件对于最终用户、设备制造商 (OEM) 和运营商等都至关重要,因为OEM 可能希望保护它们的设备免于运行未经授权的软件。不真实的软件可能会降低运营商网络或设备的性能,以及恶意应用程序可能会危害从用户的私人或财务数据到无法挽回地损坏物理设备本身的任何内容,甚至执行不受信任的恶意应用程序存在许多风险和潜在后果(当然,厂家是不会这样宣传 Secure Boot 的,要不然自己的产品咋卖出去?通常它们的文案都是通过这项技术保护用户的隐私,防止恶意软件修改系统软硬件等等。不论文案如何,随着 ARM 架构的广泛授权,基于 TrustZone 的 Secure Boot 也越来越普遍了),例如:

  • 假设一个尝试恶意注入或修改存储中的软件映像的攻击者。攻击者可以在加载的软件链中越早破坏图像,他们获得的控制权就越大。设备软件通常分阶段加载,其中每个软件映像通常配置为比链中的前一个映像具有更少的权限和控制。具体来说,加载的第一个软件映像几乎可以完全控制设备。这些要加载的第一个映像称为引导加载程序映像
  • 如果攻击者可以用他们自己的恶意映像替换第一个要执行的软件映像,那么他们就可以控制设备的其余执行。这使得引导链的完整性变得至关重要。用恶意映像替换存储中的引导加载程序映像可能会导致持久性漏洞利用,该漏洞将控制该软件映像和任何要在其之后运行的映像中的执行

:实施“安全启动”链,旨在确保这些映像中的每一个都未被修改,并且是阻止恶意或危险软件执行的一种方法。安全启动被定义为一个启动序列,其中每个可执行软件映像都由先前验证的软件进行身份验证。此序列旨在防止未经授权或修改的代码运行。我们根据这个定义构建我们的信任链,从第一个用完只读存储器 (ROM) 的不可变软件开始。第一个 ROM 引导加载程序以加密方式验证链中下一个引导加载程序的签名,然后该引导加载程序以加密方式验证下一个或多个软件映像的签名,依此类推(如下图)

在这里插入图片描述

上图描述了安全启动序列的示例:操作系统验证的三个映像已通过信任链进行身份验证,该信任链返回到硬件中的第一个 ROM 引导加载程序。此链中的每个图像都已通过锚定到根证书的证书链进行加密验证,根证书也锚定在硬件中。任何将潜在有害代码注入图像的尝试都将被阻止

具有安全启动技术的 Android 手机使用数字证书来确保在操作系统之前加载的软件是可信的。这意味着它由设备供应商进行了数字签名,并以加密方式防止篡改。因此,主引导加载程序不只是找到并运行辅助引导加载程序文件;相反,它会读取辅助引导加载程序并验证数字签名以确保其未被篡改。如果验证失败,则引导过程停止

签名验证和篡改检查是一种标准的公钥基础设施 (PKI) 操作:被验证的文件经过哈希处理,并使用公钥/私钥对对哈希进行签名。例如三星设备,此公钥/私钥对由三星控制,并通过一系列证书到达三星安全启动证书,该证书作为平台硬件信任根的一部分加载

  • 例如,这意味着三星手机无法运行摩托罗拉辅助引导加载程序,因为该文件尚未经过三星签名

链中的每个引导加载程序,一直到操作系统,都负责验证软件是否已经过数字签名并且不可篡改。一些设备供应商允许禁用安全启动功能,或者是为了开发人员(他们可能需要加载未签名的实验软件),或者是因为他们希望鼓励爱好者运行他们自己的软件和操作系统

安全启动可确保引导加载程序链未被篡改,并由受信任的机构(通常是设备供应商)签名。三星 Knox添加了一项名为 Trusted Boot 的增强功能,它通过在启动过程中拍摄快照并将结果存储在 TrustZone 可信执行环境 (TEE) 中更进一步

受信任的引导的目标是确保作为回滚预防过程的一部分,不能使用可能存在安全漏洞的旧的、受信任的引导加载程序。在系统启动时,TrustZone Trustlets 检查快照。如果他们确定使用了较旧的引导加载程序,则可以阻止某些安全关键操作

1.16 安全内核

Android 原生的 SELinux特性,对系统中的进程、文件、目录等所有资源的操作均实施强制访问控制,任何进程想在 SELinux 系统中执行操作,都必须先在安全策略配置文件中赋予权限,而访问控制的策略在设备启动过程中会被保护起来,无法被第三方更改。通过 SELinux,可阻止系统进程读写受保护的数据,以及阻止系统进程绕过内核的安全机制或攻击其他进程

UEFI镜像(image)文件:指UEFI二进制文件,如,Grub.EFI以及Rom.efi等。UEFI镜像文件中至少包含原始镜像内容。签名过的UEFI镜像文件中,还包含镜像签名信息(AuthData)。镜像签名信息是使用所有者的密钥和证书对原始镜像内容进行一系列密码运算操作生成的一段数字签名数据,用于在加载UEFI镜像文件前,对UEFI镜像文件进行签名验证,以判断UEFI镜像文件是否合法。镜像签名信息至少包括算法标识信息、摘要以及加密后的摘要。算法标识信息包括哈希算法标识和加密算法标识

固件启动方法,包括:在UEFI安全启动过程中,若判断出UEFI镜像文件中不包含镜像签名信息则产生告警

  • 在UEFI安全启动过程中,若判断出待签名验证的UEFI镜像文件中包含镜像签名信息,则通过国密算法对UEFI镜像文件进行第一签名验证;若判断出UEFI镜像文件中不包含镜像签名信息,则通过国密算法对UEFI镜像文件进行第二签名验证
    • 若通过国密算法,判断出UEFI镜像文件中的镜像签名信息或原始镜像内容的摘要符合以下任一验证失败条件,则验证失败,如下:
      • 镜像签名信息中的算法标识信息与国密算法的算法标识信息不匹配
      • 镜像签名信息中的摘要与原始镜像内容的摘要不一致
      • 黑名单包含原始镜像内容的摘要
      • 基于黑名单中的黑名单密钥对镜像签名信息中包含的加密后的摘要解密成功且白名单未包含原始镜像内容的摘要
      • 基于白名单中的白名单密钥对镜像签名信息中包含的加密后的摘要解密失败且白名单未包含原始镜像内容的摘要
  • 在签名验证成功的情况下,加载UEFI镜像文件,以执行下一阶段的启动

:简而言之,就是计算受保护文件的摘要值,将其与数据库中的值进行比较,若摘要值不同,说明该受保护文件被修改,则产生告警;若摘要值相同,不做任何操作

判断Android 系统的完整性,包括:

  • 开机自启动顺序特征:根据设备开机时自动启动的Android应用程序来做判断
  • 摘要值特征:根据受保护文件的摘要值,将其与数据库中的值进行比较,若摘要值不同,说明该受保护文件被修改,则产生告警;若摘要值相同,不做任何操作
  • su权限特征:根据Root权限提升模块,来判断是否通过执行su命令提升为Root权限
  • 受保护文件的特征:根据受保护文件判断标准来判断
    • 需要保护的文件指那些恶意软件企图篡改的文件;恶意软件一般会攻击与系统的运行和功能有关的文件,即可执行文件或一些动态链接库等,例如init文件,init.rc文件,system/bin/下的文件等需要保护的文件指那些不轻易改变的文件;若文件经常改变,系统无法分辨是来自于恶意软件的修改还是系统本身的修改

在UEFI安全启动过程中,判断出待加载的UEFI镜像文件是否符合要求(如下),不符合即产生告警

  • 若判断出待加载的UEFI镜像文件符合镜像加载条件,则确定该UEFI镜像文件不需要签名验证,直接加载该UEFI镜像文件
  • 若判断出待加载的UEFI镜像文件符合签名验证条件,则确定该UEFI镜像文件为待签名验证的UEFI镜像文件
  • 若判断出待加载的UEFI镜像文件不符合签名验证条件,则确定该UEFI镜像文件验证失败,且该UEFI镜像文件不需要签名验证,并结束对UEFI镜像文件的签名验证流程

:镜像加载条件为:UEFI镜像文件的镜像文件类型为指定类型。签名验证条件为UEFI镜像文件的镜像文件类型不为指定类型,且UEFI镜像文件中包含的格式字段为指定格式。指定类型可以为UEFI固件卷(Firmware Volume,FV)文件。指定格式可以为PE32格式

更多固件启动方法、装置、计算机设备及可读存储介质的内容,请移步:https://patents.google.com/patent/CN114499892B/zh

1.17 UEFI 安全启动

为避免系统启动时被恶意程序篡改,在统一可扩展固件接口(Unified Extensible Firmware Interface,UEFI)安全启动过程中,通常采用公钥加密算法(Rivest Shamir Adleman,RSA)等算法,对UEFI镜像文件进行签名验证,以加载验证通过的UEFI镜像文件,实现UEFI的安全启动,但这种验证方式速度较慢,且影响系统启动速度

1.17.1 lark-Wilson 完整性模型

UEFI Secure Boot 链可应用于 1987 年制定的Clark-Wilson 完整性策略。 Clark Wilson 模型包括以下概念:

  • 数据项:
    • 约束数据项 (CDI)
    • 无约束数据项 (UDI)
  • 程序:
    • 完整性验证程序 (IVP)
    • 转换程序 (TP)
  • 规则:
    • 认证规则 (CR) – 完整性监控
      • C1:(基础:IVP 认证)所有 IVP 必须正确确保所有 CDI 处于有效状态
      • C2:(基本:有效性)所有 TP 都必须经过认证才能有效。对于每个 TP 和它可能操纵的每组 CDI,安全人员必须指定一个“关系”,格式为:(TP, {CDI})
      • C3:(职责分离证明)E2 中的关系列表必须经过证明以满足职责分离要求
      • C4:(期刊认证)所有 TP 必须经过认证,才能将所有必要的信息写入仅附加 CDI(日志),以允许重构操作的性质
      • C5:(转换认证)任何将 UDI 作为输入值的 TP 必须经过认证,以便对 UDI 的任何可能值仅执行有效转换或不执行转换。转换应将输入从 UDI 转换为 CDI,否则 UDI 将被拒绝。
    • 执行规则 (ER) – 完整性保护
      • E1:(基本:有效性执行)系统必须维护 C2 中指定的关系列表,并且必须确保只有经过认证可以在 CDI 上运行的 TP 才能操作该 CDI
      • E2:(职责分离的执行)系统必须将用户与以下形式的关系列表中的每个TP和一组CDI相关联:(用户,TP,{CDI})。它必须确保只执行关系之一中描述的执行
      • E3:(用户身份)系统必须验证每个尝试执行 TP 的用户的身份
      • E4:(发起)只有被允许认证实体的代理才能更改与其他实体相关联的此类实体的列表,特别是与 TP 相关联的实体。可以证明实体的代理人可能不具有与该实体有关的任何执行权

该模型基于经过身份验证的主体、程序和数据项之间的关系。这种关系的元素被称为“Clark-Wilson Triple”(用户、TP、{CDI})。Clark-Wilson 模型显示了满足完整性安全属性所需的规则:(来自Blake)

Clark-Wilson模型,如下:

名称描述规则
Integrity保证只能以受限制的方式修改 CDI 以生成有效的 CDIC1、C2、C5、E1、E4
Access Control控制对资源的访问的能力C3、E2、E3
Auditing能够确定对 CDI 所做的更改并确保系统处于有效状态C1、C4
Accountability将用户与其操作唯一关联的能力E3

在这里插入图片描述

由于 Clark-Wilson 侧重于职责和交易,因此更适用于业务和行业流程。目前,一些论文描述了如何将Clark-Wilson 完整性模型应用于现有系统,例如Windows、Java或可信计算组 (TCG) 安全性

更多关于UEFI 安全启动链的内容,请移步:

  • https://edk2-docs.gitbook.io/understanding-the-uefi-secure-boot-chain/
  • https://laurie0131.gitbooks.io/understanding-uefi-secure-boot-chain/content/additional_secure_boot_chain_implementations/android_verified_boot.html

0x02 利用漏洞绕过安全引导

如下内容来自:https://blog.omitol.com/2017/09/30/Bypass-QCOM-Secure-Boot/

2.1 攻击方法的设计

前面分析了存在安全缺陷和漏洞。利用自签名的 user keystoreboot/recovery 镜像,并且在 boot 的 ramdisk中移除对 system 分区的进行校验的 verify 标记。或者替换 ramdisk 中的verity_key,并对 system 镜像进行签名。刷写到手机中,就能实现绕过某些机型的安全引导机制,对设备进行自由修改

但是在设备处于LOCKED状态时,是无法通过 fastboot 进行刷写操作的。但是 OEM 一般都会预留自己的下载模式,比如三星的 ODIN,联发科的SP Flashtool。而高通 SoC 的dload/edl 模式,该模式在固件镜像下载过程中并不做任何的校验,直接能刷写进去

案例:测试机器为红米Note3 全网通版,系统的版本为V7.2.3.0Android 6.0版本,内部开发代号 kenzo。Kenzo 在几次的升级后,逐渐关闭了按键进入,adb reboot dload/edl重启进入下载模式的途经,开启了dm-verity,来保护手机不被破解和刷机。但是通过IDA Pro逆向分析 aboot 分区中的 emmc_appsboot.mbn 引导加载程序镜像时,发现了 reboot-edl的命令。如图所示:

在这里插入图片描述

正如其命名一样,是标准fastboot协议是不支持此命令的,为此需要修改 fastboot 源码。fastboot 源代码在 AOSP 源码树system/core/fastboot 中。分析 fastboot 源码,命令最后是通过fb_queue_command 发送给 bootloader,修改代码添加对该命令的支持。然后就能成功的重启到 edl 模式。核心代码示例如图所示:

在这里插入图片描述

2.2 漏洞利用测试过程

整个测试过程是为了验证本文对安全引导机制进行分析研究后挖掘出的相关安全漏洞,达到在设备处于 LOCKED 状态时,篡改并修改设备,绕过 Android 安全引导机制。过程如下:

  • 生成自己的公私钥对
  • 利用公私钥对生成并签名keystore.img
  • boot.img重新打包,移除 system 分区 verify flag
  • boot.img 进行重新签名
  • 使用修改后的 fastboot,执行fastboot reboot-edl进入下载模式
  • 使用 emmcdl 工具分别刷入keystore.img boot.img

若设备能成功开启,则证明完全绕过了 Android 的安全引导机制。System 分区也不在不挂载为虚拟设备,而是真实的物理设备块。如下所示:

$ mount | grep system
/dev/block/sde20 on /system type ext4 (ro, seclabel, noatime, discard, data=ordered)

0x03 固件攻击的思路

如下内容来自:https://bbs.pediy.com/thread-260399.htm#msg_header_h1_3

3.1 信任链攻击思路

回顾前面定义的威胁模型,如果攻击者的目的是刷自定义 ROM,那至少要同时拿到Normal World Secure World 的 EL1 权限才能勉强让一个自定义 ROM 正常运作。如果攻击者的目的是破坏由TrustZone 保护的 IAP 支付机制,或者 DRM 保护机制,则至少要拿到实现这些保护机制的 TA 的权限才行,也就是至少要拿到 Secure World EL0 的权限

目前接触到的攻击思路,基本上分为两类:Top Down Bottom Up

3.2 Top Down 攻击思路

所谓 Top Down,就是从最上层的程序,也就是最低级的权限一步步提权,每一次提权就获得更底层一点的权限,慢慢渗透到目标权限层

一个特别适合了解 Top Down 的案例来自 Quarkslab 的 Breaking Samsung’s ARM TrustZonePDF & GitHub

在这里插入图片描述

这个案例它们假设一开始只有 Normal World 中 EL0 的权限,但是可以自己写程序调用 TrustZone 的 Driver,通过 Driver → Android Kernel → SMC → Secure Monitor → Trusted OS Kernel → Trusted Application这条线路调用 TA 的相关功能。它们逆向了 TA 的代码,找到了一处 memcpy 越界漏洞,从而拿到了该 TA 的 Secure World EL0 权限

但是这个 TA 的权限有限,他们又通过 Trusted Application → System Call → Trusted OS → Secure Service的线路调用同是Secure World EL0,但是有更多 System Call 权限的一个 Secure Service。他们同样在这个 Secure Service 中找到了一处 memcpy 越界漏洞,从而拿到了更高权限的 Secure World EL0 执行权限

然后,它们发现这个 Secure Service 的其中一个 System Call是一个任意地址 mmap,而且没有任何限制。于是他们可以直接把Secure Monitor 的物理地址直接 mmap 到 Secure Service 的虚拟地址空间,然后直接改写Secure Monitor 的代码,直接拿到Secure Monitor 所在的Secure World EL3 权限。基本上来说,拿到 EL3 权限,就已经可以做任何攻击者自己想做的事情了

Top Down 的思路需要在各个权限层都能找到漏洞进行利用,可以说难度非常大,而且所有这些都是基于软件上的漏洞,OEM 可以通过系统更新来进行修复。不过大部分厂家没有防回滚机制,所以攻击者可以通过降级刷机刷回一个有漏洞的版本,再进行提权

一种防回滚的操作是每次重大安全更新都烧掉一个eFUSE比特,然后每个版本的固件都会检查当前烧掉的 eFUSE 比特数是否等于当前的版本号,如果大于的话会拒绝执行,如果小于的话会烧掉相应的比特。如果设备有防回滚机制,攻击者会尽量保持使用旧版固件,然后尽力阻止固件更新

Top Down的一种捷径是通过Diff 分析对比更新前后固件的变化,找到安全更新修复的漏洞,然后进行利用

3.3 Bottom Up 攻击思路

对应的,Bottom Up 就是直接找 bootROM 的漏洞。因为 bootROM 是整个信任链的根基,拥有最高的执行权限,如果可以做到Code Execution,那所有的Secure Boot 保护措施都将形同虚设。而且因为 bootROM 是写死在 CPU 中的,连 OEM 都无法更改,所以一旦可以被利用,厂家将永远无法修复它,只能通过发售新的修复过的硬件来避免它

一个最适合了解Bottom Up 的案例是 Glitching the Switch,这个漏洞几乎同时被好几个研究团队发现,称为 Fusée Gelée

Nintendo Switch 使用的是 NVIDIA 的 Tegra X1 芯片,这款芯片的 bootROM 是不可读的,原理是在 bootROM 即将跳转到 FSBL 的时候,会通过一个专门的寄存器,改变 bootROM 的可读区间,使得大部分的 bootROM 代码变得不可读。这个不论是开发设备还是消费产品都是如此

所以他们攻击 bootROM 的第一步就是要 dump 出来 bootROM 的代码。他们用了一种 Glitching 的手段来做到这一步。Glitching 的硬件原理在视频中有详细介绍,简单来说就是通过在非常精确的时间点,执行微秒级的电压骤变使得 bootROM 在写那个可视性寄存器的时候出现错误,导致 bootROM 没有被不可视化,进而他们可以在一块开发板上用自己写的 FSBL 读取 bootROM 代码

拿到 bootROM 之后他们根据芯片的数据手册和自己的实验,分析出各种寄存器的用途,然后对 bootROM 的 USB 层进行逆向分析。最后他们发现了在一个叫做 RCM 的 USB 模式下,可以通过栈溢出拿到 bootROM 的执行权限

拿到 bootROM 权限之后,他们就可以禁用掉 FSBL 的签名验证,相似的,Secure Boot 接下来的所有环节的签名验证都可以被禁用掉,那基本上就是想干什么都可以了

3.4 总结

现在Secure Boot 攻击的 Holy Grail 就是 bootROM 级别的漏洞,因为它最为强大,而且无法被厂家修复。但是 Top Down 的思路在没有 bootROM 级别漏洞的情况下也是一种考虑方式,而且Top Down 的话对每一个 Secure Boot 的组成部件能有更深的了解,作为学习 Secure Boot 来说也是一个不错的选择

0x04 检测方法


安全启动:

  • BootLoader安全启动固件篡改测试
    • 测试BootLoader安全启动固件是否能被篡改
  • BootLoader安全启动逆向测试
    • 逆向BootLoader安全启动流程,验证是否存在漏洞
  • 内核安全启动固件篡改测试
    • 测试内核安全启动固件能否被篡改
  • 内核安全启动逆向测试
    • 逆向内核安全启动流程,验证是否存在漏洞
  • 文件系统安全启动固件篡改测试
    • 测试文件系统安全启动固件能否被篡改
  • 文件系统安全启动逆向测试
    • 逆向文件系统安全启动流程,验证是否存在漏洞

:上面说的BootLoader、文件系统、内核的安全启动固件是否能被篡改,用通用的检测方法都可覆盖检测范围

基于硬件的可信环境解决安全问题,包括:Android 的安全增强功能、实时内核保护(RKP)、基于 TrustZone 的完整性测量架构、基于 TrustZone 的安全性服务、安全启动、可信启动和硬件信任根

在这里插入图片描述

:基于硬件建立一个值得信任的环境,一旦设备开始运行,则对这个环境进行维护,保证它一直处于信任的状态,并在需要的时候,通过证明设备的完整性来检验设备是可以被继续信任的以及将这些数据信息采集回来进行分析

监控系统启动过程。结合操作系统已有的Security boot等启动流程,对系统启动过程的关键信息和关键过程进行采集,监控操作系统image的完整性和有效性,对异常的数据进行上报

Android 是一个可定制的系统,开发人员可对其进行修改以满足开发的需求。例如,如果有一个已停止更新的旧版 Android,开发者可刷写自定义 ROM 并进行更新

build prop sys.oem_unlock_allowed


4.1 方法一

根据系统属性的值来做判断,但不是每一个Android设备都是默认的sys.oem_unlock_allowed属性,不太推荐:

  • getprop |grep lock
  • 判断sys.oem_unlock_allowed该值是否为1

4.1.1 检测Boot状态

  • 检测系统属性的ro.bootmode值

参数决定系统是正常启动、还是关机充电状态,排除掉正常状态的值,非正常状态的值一概告警,因为不同厂商系统定义的值都不同,很难全部都概况进来(此方法会有超多误报,不建议使用)

设备状态有如下四种:

  • 按键或命令执行后进入fastboot模式
    • fastboot:fastboot是PC与bootloader的USB通信的命令行工具,通过向bootloader传送刷机文件(.img)实现Android系统分区重烧。fastboot需要bootloader支持,且需要使用USB数据线连接,因此常称为线刷模式
  • 按键或命令执行后进入recovery模式
  • 按键或命令执行后进入卡升级模式
  • 正常启动或命令执行后进入手机正常启动

4.2 方法二

检测方法:

  • 定时检测system分区下的文件与数据库中对应的hash值做对比
  • 自身签名验证:
    • 固件被篡改后唯一的ID或key会改变,对唯一的ID或key进行校验检测
  • 如果只是定时检测system分区下的文件,不用pull出来,直接在手机上计算好(busybox sha1sum /system/xxx),和PC上相应文件的hash值做对比即可

Android 5.x 以后的机器都支持verity,只要配置verity功能,system分区别篡改后将无法挂载及启动,这个功能是在底层块设备之上加的hash tree来实现的,即每个blockhashtree形式组织放在system分区尾部

  • http://source.android.com/security/verifiedboot/index.html

很多Android 用户会Root安卓系统以DIY自己的手机。在某种程度上,Root后的Android设备可以变得更安全。然而事实并非如此,越来越多恶意软件盯上了使用Android系统的个人和企业,由此引发的安全问题愈发突出。为此,谷歌为使Android 7.0 牛轧糖更加安全添加了一种称为verified boot的新特性,使得系统难以被root

Android 7.0牛轧糖系统中,在开机时首先会验证加密完整性是否被篡改;一旦检测到系统文件被篡改(或者是修改过的boot image),则禁止启动或限定功能。(实际上再Android 6.0时代,系统也会进行完整性检查,只不过那个时候检测到系统被篡改的话,系统只是发出警告)。然而root过程中必须修改系统文件,提升用户权限。如此一来,也就理解了为什么Android 7.0牛轧糖的Root难度加大了

许多同学都知道,任何想要修改系统内核(操作系统的核心组件)的操作都会被verified boot所察觉。这也带来了一个问题:哪怕这有一个字节的改动也会被verified boot检查到,导致数据丢失

4.3 方法三

分区验证:

  • 比如xxx分区为某特定值时才可以进行解锁,其他值都不能解锁
    • 验证 a 分区和 b 分区在执行XFL 的功能没有差异。步骤:
      • 进入刷机模式,查看a 为活跃分区,dump a 分区所有文件,进入刷机模式,flash boot 包到b 分区,设备b 分区为活跃分区,进入刷机模式模式,擦除a 的所有分区,再次拔开进入刷机模式模式,查看活跃分区是b
    • 验证设备在unlock 下可以把文件刷到确定的分区中。步骤:
      • 当a 分区为活跃的,进入fastboot,cmd 输入命令fastboot flash boot+ 文件,可以成功刷到当前的活跃分区中,擦除刚刚刷入的文件,设置b 为活跃分区,fastboot set_active b,输入上面的adb 命令,可以成功刷入当前的活跃分区即b,再擦除,指定刷到a 和b 两个分区fastboot flash boot_a/boot_b+文件,都可以成功
    • 验证手机unlock设备在fastboot 模式下,可以刷入所有的分区信 息。前提条件:设备为unlock。步骤:把所有的文件dump 出来,然后用fastboot flash 分区名分区文件路径命令刷入

可信链: BootROM作为可信根,验证加载第一阶段Boot LoaderFSBL (First Stage Bootloader));

  • 然后 第一阶段Boot Loader加载 Trusted OSTrusted OS中 应用程序,以及 第二阶段Boot LoaderSecond Stage BootLoader
  • 然后 第二阶段Boot Loader加载验证和运行kernel;jetson上 第二阶段Boot Loader中的验证程序又称为 CBOOT

系统启动过程:bootROM → FSBL (First Stage Bootloader) → secure Moniter → Trusted OS →(SSB) Second Stage Bootloader → kernel start

  • bootROM作为可信根,会读取eFUSE内容,判断是否为生产模式来开启secure boot,然后从emmc中加载FSBL,并比对与eFUSE中key是否相同,验证FSBL的合法性
  • FSBL 加载验证secure Moniter 、 Trusted OS 、 second Stage Bootloader 是否正常,然后跳转到secure Moniter执行
  • secure Moniter初始化secure world中环境,并从emmc加载各种TA,并为其映射虚拟内存空间,然后跳转到SSB
  • Second Stage BootLoader 加载和验证kernel,并且跳转到kernel运行

4.4 方法四

Android设备终端开机启动模式的检测:

  • 判断设备开机启动的模式是正常启动,还是Recovery模式(恢复模式)、或Fastboot模式(快速引导模式),如果是Recovery模式(恢复模式)、或Fastboot模式(快速引导模式)则产生告警
  • 此处通过厂商自行设置对应模式启动标识的寄存器值来决定进入那个模式来做判断
  • https://developer.android.google.cn/training/articles/direct-boot?hl=zh-cn
  • 补充知识:Fastboot实现原理分析:https://www.cnblogs.com/linhaostudy/p/15067914.html

4.5 方法五

frp (Factory Reset Protection) lock

  • Android 5.1 设备默认启用的设备锁,实现 “find my device” 功能;lock 状态时,如果 factory reset 后会要求输入之前设备登录的 Google 帐号密码,否则就是砖机状态
  • bl解锁,frp也会解锁
  • 也会出现bl 已经解锁但 frp 还是加锁状态,在系统“开发者选项”里 “enable oem unlocking” 会是未选中状态且灰色不可切换

4.6 方法六

根据设备 System 分区挂载来判断是否绕过Android安全引导机制:

  • 通过判断Android系统System分区是否挂载在虚拟设备上,如果不是挂载在虚拟设备上,而是在真实的物理设备块上的话,则代表当前系统已完全绕过了Android的安全引导机制
# 虚拟设备挂载状态时展示的信息(此时代表 Dm-verity 是正常启用的)
$ mount | grep system
/dev/block/dm-0 /system ext4 ro, seclabel, relatime, data=ordered 0 0

# 真实的物理设备挂载状态时展示的信息(此时代表 Dm-verity 被禁用了)
$ mount | grep system
/dev/block/sde20 on /system type ext4 (ro, seclabel, noatime, discard, data=ordered)

在这里插入图片描述

原因:因为,在 Android 7 之后,对分区会进行相应的验证,例如 system 分区,不能之前的版本一样,使用adb rootadb remount 对 system 分区进行挂载,需要先关闭分区检测功能,所以用 adb disable-verity来关闭分区检测功能,前提是需要 root 权限

Android 7 版本默认会打开 system verified boot,对分区会进行相应的验证,即在 userdebug和user版本会把 system 映射到 dm-0设备,然后再挂载。挂载前会检查 system 分区数据完整性,如果 system 分区被恶意修改了,则不允许挂载 system

Android 官网对 verifiedboot 的介绍

  • Android 4.4 增加了对启动时验证和 dm-verity 内核功能的支持。这种验证功能组合就是启动时验证
  • 以前的 Android 版本会在发现设备损坏时向用户发出警告,但仍然允许他们启动设备;从 Android 7.0 开始,系统会严格强制执行启动时验证,从而使已破解的设备无法启动。Android 7.0 还增加了对前向纠错功能的支持,有助于更可靠地防范非恶意数据损坏
  • Android 8.0 及更高版本包含 Android 启动时验证 (AVB),AVB 是启动时验证的一个参考实现,可与 Project Treble 配合使用。除了与 Treble 配合使用外,AVB 还对分区脚本格式进行了标准化并增添了回滚保护功能

:若上传一些文件到设备 system 分区后,就不要再尝试启用 Dm-verity 功能,因为system 分区数据已经发生变化,再次启用后,设备将会无法开机。另外,adb disable-verity/enable-verity 命令只能在 userdebug 模式下使用。user 版本不支持关闭 dm-verity

# 使用 adb disable-verity 来关闭分区检测功能
# 但需要拥有 root 权限
1、adb root  获取root权限

2、adb disable-verity  关闭分区检测功能

3、adb reboot  执行adb disable-verity后需要重启设备

4、adb root   设备重启后再次获取root权限

5、adb remount  使system分区为可读可写模式

4.7 方法七

根据设备的完整性(可信启动),来检测设备是否被篡改:

  • 收集测量数据以及适用于 Android 的 SE 强制执行设置,以形成设备健康状况判定的基础
  • 检查内核和平台组件的完整性,来判断设备是否使用来自同一构建的正确签名的组件进行引导

4.8 方法八

引导加载程序是否被篡改的检测:

  • 引导加载程序测量(基于TrustZone 的完整性测量架构(TIMA) 架构提供了许多核心功能来防止移动设备受到损害)在设备引导期间记录在安全的 TrustZone 内存中。在运行时,在安全 TrustZone 中运行的应用程序可以使用这些测量来做出安全关键决策,例如:
  • 是否从 Knox 密钥库中释放加密密钥
  • 是否启动工作资料应用程序容器

4.9 方法九

检测和阻止最常见的内核攻击:

  • 内核代码:RKP 防止修改内核代码和逻辑
  • 内核数据:RKP 防止修改关键内核数据结构
  • 内核控制流:RKP 防止了面向返回编程 (ROP) 和面向跳转编程 (JOP) 攻击,这些攻击重用现有内核逻辑来拼凑来自内核自身代码的漏洞利用

4.10 方法十

定期内核测量 (PKM)【内核完整性子系统】:

  • PKM 定期监控内核,以检测合法的内核代码和数据是否被恶意修改。PKM 还监控 OS 内核内存中 Android 数据结构的关键 SE,以防止恶意攻击破坏和可能禁用 SE for Android。PKM 保护 Linux 内核代码和数据页面免受恶意攻击,并有助于防止试图禁用SE for Android 的攻击
  • 在设备固件构建期间,每个内核代码和只读数据页的 SHA1 哈希值都会被计算并收集到一个测量文件中
  • PKM 运行时,读取 SE for Android 使用的物理内存地址,以确定是否:
    • SE for Android 已启用
    • SE for Android 处于强制执行模式
    • 如果恶意代码设法禁用SE for Android,或将其切换到许可模式,PKM 会检测到状态变化并报告违规情况,以帮助管理员快速诊断问题

4.11 方法十一

SysIntegrity API检测系统完整性(适用华为系统),例如检测被恶意病毒、木马软件利用root权限植入病毒、篡改用户设备信息和破坏系统等操作:

  • 使用华为提供系统完整性检测(SysIntegrity API),可检测应用运行的设备环境是否安全,如设备是否被root、被解锁等
  • 更多关于华为系统完整性检测,可前往:https://github.com/HMS-Core/hms-safetydetect-demo-android/blob/master/README_ZH.md
  • 华为开放的安全检测服务,包括:
    • SysIntegrity API:系统完整性检测API,您可通过该API评估其App所运行的设备环境是否安全(如设备是否被root)
    • AppsCheck API:应用安全检测API,您可通过该API获取恶意应用列表
    • URLCheck API:恶意URL检测API,您可通过该API来确定特定URL的威胁类型
    • UserDetect API:虚假用户检测API,您可通过该API判断当前App的交互对象是否为虚假用户
    • WifiDetect API:恶意Wi-Fi检测API,您可通过该API检测当前尝试连接的Wi-Fi是否安全
  • 支持的设备表如下:
特性设备类型OS版本HMS Core(APK)版本
系统完整性检测华为手机、华为平板EMUI 3.0及以上4.0.0.300及以上
系统完整性检测非华为手机、非华为平板Android 4.4及以上(API Level 19及以上)4.0.0.300及以上
应用安全检测华为手机、华为平板EMUI 5.0及以上4.0.0.300及以上
恶意URL检测华为手机、华为平板EMUI 3.0及以上4.0.0.300及以上
恶意URL检测非华为手机、非华为平板Android 4.4及以上(API Level 19及以上)4.0.0.300及以上
虚假用户检测华为手机、华为平板EMUI 3.0及以上4.0.0.300及以上
虚假用户检测非华为手机、非华为平板Android 4.4及以上(API Level 19及以上)4.0.0.300及以上
恶意Wi-Fi检测华为手机、华为平板EMUI 4.1及以上4.0.3.300及以上

:通过构造指定文件的完整性样本库(快照),作为比对标准,当这些文件发生改动时,其对应的校验值也必然随之变化,我们便可识别这些变化从而产生对应告警信息。监控的属性变化主要包括:权限、文件类型、属主、属组、文件大小、创建时间、最后修改时间(文件修改日期)、最后访问时间、增加的大小以及链接数(符号连接),并能够使用SHA1、MD5等算法为每个文件生成校验码。简而言之,通过对系统做快照,记录下HASH值、修改时间、以及管理员对文件做的预处理。这个快照可以让我们建立一个数据库,然后存储到外部设备进行保管。当想要对系统进行一个完整性检测时,只需将之前构建的数据库放置一个到系统可访问的区域,然后将当前系统的状态和数据库进行对比,最后将检测到的当前系统的变更情况报告的数据采集回来到云端并产生对应的告警信息

4.12 方法十二

根据监听设备功能按键要进入fastboot 模式的模式来做检测:

  • 检测设备功能按键进入fastboot 模式,是否在UI界面提示一些固定的信息(不同厂商定义内容不同,需自行确认)
  • 设备重启后是否UI界面出现一些特殊的显示信息,跟正常开启设备不同
  • 解锁后是否系统属性locked的值出现变化(不同厂商定义内容不同,需自行确认,甚至可能不会在系统系统里面用某个值来判断locked是否解锁)

4.13 方法十三

在Android原生系统中该选项显示是:已加密,但在小米,华为等手机上该选项一定是显示:已加密,因为Google把该选项的权限放给了各手机厂家,各手机厂家可以根据自己的要求是否要默认加密手机,通常厂商出厂发售时默认都会做加密。如果手机加密了(即显示手机已加密)用户是不办法取消的,除非BL解锁,那么我们即可通过DE(Device Encrypted,设备加密)是否被使用来判断设备是否解锁

Android 具有加密文件系统以保护用户数据的功能,Android 加密分为:

  • 全盘加密(Full Disk Encryption)
  • 文件级加密(File-Based Encryption)

全盘加密引入于 Android 4.4,在 Android 10 中去除。其原理是,系统在初始化时随机生成一个 128 位的主密钥,并使用此密钥与 AES 算法将数据分区(data)加密。当用户设置密码时,主密钥被使用用户设置的密码加密后储存起来(根据设备支持情况,储存在闪存或硬件安全存储中)。当用户修改密码时,主密钥不会改变,所以数据不需要被重新加密

全盘加密有一个缺点 :由于应用程序依赖于数据分区,而在设备启动后,用户第一次输入密码前,设备的数据分区是未被解密的,所以用户无法使用包括闹钟、电话在内的任何功能

为了解决解锁前无法使用任何设备功能的问题,Android 7.0 引入了文件级加密。文件级加密可对每个文件单独进行加密,并且支持对不同文件使用不同的密钥加密。一般来说,文件级加密体系中有 CE(Credential Encrypted,凭据加密)和 DE(Device Encrypted,设备加密)两个密钥,前者用于加密用户的大部分数据,后者用于加密需要在解锁设备前访问的数据。CE 所用的密钥被以类似于全盘加密主密钥的形式加密存储,DE 所用的密钥被直接存储。应用程序可以选择其存储的特定数据使用 CE 还是 DE 进行加密,例如闹钟应用程序可将设定的闹钟列表存储在 DE 中,在设备启动后解锁前即可工作。而安全敏感的数据,如 Google 账户凭据则存放在 CE 中,仅在用户解锁设备后可访问

:可通过DE(Device Encrypted,设备加密)是否被使用来判断设备是否解锁了

4.14 SafetyNet Attestation API

SafetyNet Attestation API 是一种反滥用 API,可以让应用开发者评估运行其应用的 Android 设备。该 API 应该用作滥用检测系统的一部分,以帮助确定您的服务器是否与在真实 Android 设备上运行的真实应用互动。

SafetyNet Attestation API 提供采用加密签名的证明,用于评估设备的完整性。为了创建证明,该 API 会检查设备的软件和硬件环境,以查找是否存在完整性问题,并将相应数据与已获批 Android 设备的参考数据进行比较。生成的证明会绑定到调用方应用提供的 Nonce。该证明还包含生成时间戳以及发起请求的应用的元数据

最可靠的解决方案是使用SafetyNet Attestation。但是,这不会检测到所有引导加载程序解锁(例如,未检测到运行 Magisk 的我的 Nexus 6)。此外,您可能会因为其他原因导致设备出现故障,例如作为模拟器、运行自定义 ROM、被 root、API 挂钩,甚至只是拥有未经 Google 认证的手机

:比如说各种游戏和银行应用。其中有相当数量都使用了Google提供的SafetyNet API。它的“基本完整性”检查会检测设备的BootLoader是否被解锁,SafetyNet API 它是Google官方用于检测设备固件是否被修改。刷过机的玩家应该都知道,解了锁的BootLoader才能允许玩家刷入经过修改的各种Recovery、内核

如果您对保证引导加载程序被解锁的情况感兴趣,请使用 SafetyNet Attestation API并查看建议字段。如果 API 检测到未锁定的引导加载程序,{"advice": "LOCK_BOOTLOADER"}将出现在令牌中。请注意,在正常使用情况下,不会有建议字段

可通过SafetyNet Attestation API来判断引导加载程序是否已解锁,官网详细介绍:https://developer.android.com/training/safetynet/attestation

:对于国内这些OEM厂商来说,要想预装谷歌服务,必须通过谷歌的GTS验证

如下这些情况无法使用SafetyNet验证:

  • Android 设备在出厂时并未通过谷歌的GTS验证
  • Android 设备通过了谷歌GTS验证,但是你解除了BootLoader锁
  • Android 设备开启了root
  • Android 设备刷入了非官方ROM
  • 其他一些情况(Xposed、太极等)

4.14.1 如何避开SafetyNet检测?

出于增加安全性的考虑,Google 推出了 SafetyNet 这样的检测,以确保 Android Pay 等一些 App 的安全运行

所谓SafetyNet检测,就是谷歌开发的一套Root机制检测程序,通过检测系统是否被篡改而判断环境是否安全的方式

已知港澳台和国外的所有银行客户端,以及某些在Play Store上架的手游均使用到了SafetyNet检测。至于中国境内的银行的Root检测,因为没办法使用谷歌服务,无法进行SafetyNet检测,因此绕过其检测相对绕过SafetyNet要容易的多

已知英国Barclays银行客户端使用了三组Root检测机制:

  • SafetyNet
  • 检测常用的Root管理程序是否存在,通过检测包名得知
  • Xposed检测,一旦检测到必定崩溃闪退

0x05 Android 安全启动保护措施

首先说说OEM锁,Android 设备应支持锁定和解锁关键部分(指将设备启动到引导加载程序所需的任何部分)。这些部分可能包括 fuse、传感器中枢的虚拟分区、第一阶段引导加载程序等。如需锁定关键部分,您必须采用一种机制,阻止设备上运行的代码(内核、恢复映像和 OTA 代码等)故意修改任何关键部分。如果设备处于锁定关键部分状态,OTA 应无法更新关键部分

从锁定状态转换为解锁状态应需要与设备进行物理交互。此类交互的效果类似于运行fastboot flashing unlock命令,但要求用户按下设备上的实体按钮。设备不应允许在没有进行物理交互的情况下以程序化方式从lock critical 状态转换为 unlock critical状态,并且设备不应以 unlock critical状态推出

再来说说Trusted Boot TrustZone 这些必要的保护措施,但一旦 Android 运行,安全启动和可信启动过程就会停止。检查 Android 本身的完整性由称为设备映射器验证 ( dm-verity) 的内置 Android 功能处理,该功能在非常低的级别提供完整性检查。例如三星的 dm-verity版本包括一些增强功能,使运营商更容易使用固件无线更新在设备上修补 Android

  • 比如三星的 TIMA (三星智能手机通过一系列三星专有的安全功能超越了基本的 Android 检查,这些安全功能为 Android 添加了完整性检查,称为 TIMA) 在 TrustZone TEE 内运行,该 TEE 提供多种安全服务,包括认证、可信用户界面、KeyStore、客户端证书管理以及 TIMA 实时保护的两个组件:实时内核保护( RKP)和周期性内核测量(PKM)

  • PKM

PKM 是一种被动检查,它是在 TrustZone TEE中运行的软件,无论是否有任何东西试图触及 Android 内核。PKM 定期检查内核以检测代码或数据是否已被恶意软件修改。PKM 还检查 SE for Android 使用的关键数据结构的完整性,以检测禁用这些安全检查的尝试

  • PKP

RKP 是一种主动安全检查,旨在阻止篡改内核。使用 RKP,可以在 TrustZone TEE 中拦截和检查关键内核事件。可以阻止或记录影响内核的事件以指示可疑的篡改。篡改警报可用于移动设备管理(MDM) 和企业移动管理(EMM) 软件,这意味着检查这些日志是具有安全意识的 IT 经理的一项关键任务

假设Android 设备在TrustZone TEE 的受保护状态下运行。RKP 会试图阻止篡改;如果有任何东西通过 RKP 或绕过 RKP,那么 PKM 可以阻止它。无论哪种情况,当检测到安全问题时,都可以在安全运营平台中看到相对应的威胁告警

Android 设备借助通过TrustZone TEE 进行的被动和主动完整性检查,以及Trusted Boot 技术,Android 设备会具有强大的硬件辅助安全设置,可为当今的企业创建易于部署和受保护的硬件

5.1 Chain Of Trust

可信启动的一个核心思路就是在当前启动代码加载下一级代码之前,对所加载的代码进行完整性校验,并且使用PKI公钥基础设施进行核实。这些启动代码通常可以分为若干个阶段(stage),例如在ARM中有:

  • BL1:CPU复位之后执行的第一段代码,即复位向量所指向的位置。通常该位置在ROM里,执行过程中将自身数据复制到SRAM,并进行最小的初始化,比如寄存器和CPU等的初始化操作,随后加载BL2代码执行
  • BL2:主要工作是执行架构和平台相关的初始化,比如配置MMU完成内存地址和权限的映射,完成外部存储器的初始化等。随后加载BL3代码并执行
  • BL3:执行运行时的初始化操作,并加载内核执行
  • 内核 -> init -> …

实际上每个启动阶段还会进行细分,但这里的重点是需要清楚信任链的作用是在每一阶段代码加载执行下一阶段代码时都会进行验证

5.2 Root Of Trust

信任链的作用是对下一阶段要执行的代码进行校验,那么就会回归到一个问题:最初的代码由谁来校验?其实上面有提到,最初的代码即BL1的代码,是保存在BootROM中,出厂烧写后不可修改的。因此BootROM代码需要尽可能简单,只需要进行必要的初始化操作。

这样一来,信任根就变成了可以烧写BootROM代码的芯片厂商。信任是可以传递的,芯片厂商作为信任根将代码执行权限交给下一级之后,比如OEM厂商,下级代码就拥有了信任链所有权,也就是说下级代码就变成了新的信任根。但是ROM的空间有限,所以通常还使用OTP(One-Time-Programmable)来保存不同阶段的签名信息。OTP是支持一次性编程的硬件,如多晶硅熔断器(poly-silicon fuses),烧毁之后无法恢复,从而保证写入后无法被篡改

0x06 Android 刷机

Android 刷机的几个大步骤:

  • 解锁bootloader
  • 安装recovery
  • 使用recovery刷机

一般情况下,用户启动Android设备后,引导程序会检查设备是否已解锁以及是否有任何修改的分区。如果设备被锁定且任何分区被修改,设备将无法启动

如果设备已解锁,设备将正常启动,且启动时设备会在分区内验证设备是否已解锁:SECCFG。此时,启动程序后,分区SECCFG便不可被读取。由于这个原因,一些变量设置被写入分区 NVDATA,因为系统可以读取分区 NVDATA。这些变量设置包含有关锁定/解锁引导程序的详细信息以及有关开发人员设置和一些敏感数据(如 IMEI、Wifi MAC 和蓝牙 MAC)的许多其他详细信息。因此,我们只需要将未锁定的 SECCFG 与锁定的 NVDATA 混合在一起即可

6.1 安装刷机SDK

Windows 步骤:

  • 下载一个android-sdk-windows.zip文件或SDK安装文件,下载链接:https://developer.android.com/studio
  • 解压或安装SDK至相应目录下,在刷机时所需要用到的SDK中的工具:
    • adb的全称是Android Debug Bridge,就是调试桥。它能够让用户输入指令来操作文件传输,安装APK文件等功能
  • 首先运行cmd,进入adb工具所在目录下,如D://android-sdk-windows/tools/
  • 然后输入指令: adb devices,查看设备有那些
  • fastboot 可理解为一个Android下一个ghost系统。在fastboot状态时,可修改Android手机中系统的许多信息。我们这里就是通过fastboot刷Android系统

6.2 解锁bootloader

Android 操作系统更多是软件的开源平台,但软件的某些方面受到制造商或谷歌的限制。在这方面,OEM Unlock 是重中之重。您可能会喜欢自定义 ROM、内核和植根您的设备,如果您要在任何 Android 手机上解锁引导加载程序,那么首先您必须在 Android 手机上启用 OEM 解锁。OEM 解锁选项始于 2014 年的 Android 5 或棒棒糖(lollipop )。这是因为谷歌带来的安全原因。由于我们现在处于Android OreoAndroid Pie的时代,这也是必须要了解如何启用OEM 解锁的其中一个原因

6.2.1 开启和关闭关机充电模式

fastboot oem enable-charger-screen
fastboot oem disable-charger-screen

6.2.2 查看devinfo分区数据

$ fastboot oem device-info
(bootloader) Device tampered: false
(bootloader) Device unlocked: true
(bootloader) Device critical unlocked: true
(bootloader) Charger screen enabled: false
(bootloader) Display panel:
OKAY [ 0.015s]
Finished. Total time: 0.016s

6.2.3 fastboot刷机语法

fastboot协议是一种通过USB连接与bootloader通讯的机制。它被设计的非常容易实现,适用于Linux、Windows或者macOS等多种平台。fastboot是Android系统提供的一种较recovery更底层的通过USB更新文件系统的方式。

Android开发包提供了fastboot.exe工具用于与Android系统通信,主要完成分区镜像烧录、分区擦除、设备重启、获取设备状态信息等操作。当需要通过fastboot协议与Android系统交互时,Android系统需要启动到bootloader模式,此时只有最基础的硬件初始化,包括按键、USB、存储系统、显示等模块,以支持分区镜像的写入和系统启动模式的切换

  • 设备必须解锁,开始刷机(这个不同的手机厂商不同)
fastboot  flashing  unlock
  • 擦除分区
fastboot erase {partition}  
  • 擦除 frp 分区,frp 即 Factory Reset Protection,用于防止用户信息在手机丢失后外泄
fastboot  erase  frp   
  • 刷入 boot 分区
fastboot  flash  boot  boot.img 
  • 刷入 system 分区
fastboot  flash  system  system.img  
  • 刷入 recovery 分区
fastboot  flash  recovery  recovery.img  
  • 烧写所有分区,注意:此命令会在当前目录中查找所有img文件,将这些img文件烧写到所有对应的分区中,并重新启动手机
fastboot flashall   
  • 格式化 data 分区
fastboot  format  data
  • 设备上锁,刷机完毕
fastboot  flashing lock 
  • 自动重启设备
fastboot  continue
  • 重启手机
fastboot reboot 
  • 重启到bootloader 刷机用
fastboot reboot-bootloader

6.2.4 进入Fastboot模式

分析fastboot启动模式,要从手机启动过程开始,如下图(Linux Kernel启动流程),其实fastboot就是在bootloader阶段中运行的:

在这里插入图片描述

  • A1:上电后执行BootROM代码,探测启动媒介,查找第一阶段的引导加载程序bootloader
  • A2:一旦boot媒介顺序确定,Boot ROM会试着装载bootloader的第一阶段到内部RAM中,一旦bootloader就位,BootROM 代码会跳到并执行bootloader
  • B1:bootloader第一阶段会检测和设置外部 RAM
  • B2:一旦外部RAM可用,系统会准备装载主bootloader,把它放到外部RAM 中
  • B3:bootloader第二阶段是运行的第一个重要程序,它包含了设置文件系统,内存,网络支持和其他的代码
  • B4:一旦bootloader完成这些特殊任务,开始寻找Linux内核,它会从boot媒介上装载 Linux内核,把它放到 RAM 中,同时它也会在内存中为内核替换一些在内核启动时读取的启动参数
  • B5:跳到Linux内核

通常进入Fastboot的两种方式:

  • 一种是在关机状态通过按键进入
    • 通过按键方式进入fastboot模式的过程:bootloader完成硬件初始化之后,启动Linux内核时,启动流程会检测按键,如果检测到对应的按键组合则将启动模式设置为fastboot模式
    • 设置启动模式时各个ODM厂商可根据自身业务需求进行客制化调整
  • 另外一种是在Android系统启动之后通过adb指令进入到bootloader模式
    • 通过adb指令进入fastboot模式的指令为: adb reboot bootloader
    • 这条指令会将启动模式作为参数传到Linux的启动过程中,按键检测完成之后,还会检测是否有启动模式传入,如果存在参数传递,则重新设置启动模式,忽略之前的按键检测
    • 在最终获取的启动方式为FASTBOOT_MODE时,系统将进入fastboot模式启动流程,主要完成USB设备的设备的初始化,并启动fastboot指令处理线程;注册fastboot指令处理函数;显示fastboot菜单,并初始化菜单按钮检测程序,处理菜单切换和选择事件,对于一些厂商会客制化一些鉴权过程,鉴权失败则会切换到正常启动流程

:Fastboot 是依次判断设备是否锁定,分区是否存在,是否为保护分区,校验通过之后才给手机端发送flash指令

6.3 安装recovery

一般情况下,刷机方法是用Recovery来完成的,此处介绍两种比较简单的方法安装Recovery

首先,把recovery放到SDK下tools目录中,并打开cmd且进入adb 所在目录下,以及让手机先进入fastboot模式。每个设备进入fastboot的方法都由厂商自由设定,所以并不相同。或者,可尝试在cmd中输入进入fastboot模式: adb reboot bootloader

  • fastboot boot recovery.img:刷入 boot 内核。直接将recovery放在缓存中,并运行,并不是真正意义上的安装
  • fastboot flash recovery recovery.img:刷入第三方recovery(TWRP Recovery)。在手机中其实已经存在了recovery这个分区,将想要刷入手机的recovery覆盖到原有的,是真正意义上的安装
    • 第三方recovery下载地址:https://twrp.me/about
  • fastboot flash userdata userdata.img:成功完成以上各文件的刷入后,重启手机即可进入新的系统
    • :刷机前请备份重要数据
  • 进入recovery界面,可能包括以下几个部分:
    • reboot system now:重启手机
    • apply sdcard:update.zip:通过安装一个名为update.zip的文件刷机,且该文件是放在sdcard的根目录下的
    • apply any zip from sdcard:通过安装一个名字后缀为.zip的文件来刷机,文件是放在sdcard的根目录下的
    • wipe data/factory reset:擦除数据/还原至出厂设置,就是把所有的用户使用数据删除
      • 刷机前请先擦除数据,因为系统的更新可能导致数据的不能使用,而造成刷机的失败,当然先备份你所需要的数据到其他地方,再擦除。并且点进该项后,能看到其包括许多选项,其中有一项关于sdcard分区的,擦除后可能导致sdcard上的数据消失
    • backup/restore:备份或还原
    • usb toggle:usb模式,这时手机可以相当与一个u盘,用户可以将sdcard上的数据拷贝出来,或者将需要使用的刷机包放入sdcard中

参考链接

https://orangey.blog.csdn.net/article/details/124602736

https://source.android.com/devices/tech/perf/boot-times?hl=zh-cn

https://source.android.com/devices/automotive/camera-hal

https://blog.csdn.net/manjianchao/article/details/80515191

https://blog.csdn.net/qq_23452385/article/details/113057861

https://www.ytechb.com/enable-oem-unlock-android/

https://itoolab.com/unlock-android/enable-oem-unlock-missing/

https://sspai.com/post/67932

书籍《智能硬件安全》

https://source.android.com/docs/core/bootloader/locking_unlocking

https://blog.men.ci/mobile-security-compare/

https://insights.samsung.com/2019/09/04/samsung-trusted-boot-and-trustzone-integrity-management-explained/

https://www.qualcomm.com/news/onq/2017/01/secure-boot-and-image-authentication-mobile-tech

https://bbs.pediy.com/thread-260399.htm

https://blog.csdn.net/u010206565/article/details/109888855

https://sakura-paris.org/Android

https://blog.omitol.com/2017/09/30/Bypass-QCOM-Secure-Boot

https://evilpan.com/2020/11/14/android-secure-boot/

https://www.modb.pro/db/478501

https://docs.samsungknox.com/admin/fundamentals/welcome.htm

https://www.digi.com/resources/documentation/digidocs/embedded/android/dea11/cc8x/android-trustfence_r_secure-boot-intro

https://security.tencent.com/index.php/blog/msg/38

http://support-cn.samsung.com/Upload/DeveloperChina/DeveloperChinaFile/20190612174302747A417D089B4.pdf

https://trust.mi.com/docs/miui-security-white-paper-global/2

https://edk2-docs.gitbook.io/understanding-the-uefi-secure-boot-chain/

https://www.riscure.com/blog/samsung-investigation-part1

https://mp.weixin.qq.com/s/dRgkcVayeGvh8SSm8B2a2A

https://cloud.tencent.com/developer/article/1043636

https://patents.google.com/patent/CN114499892B/zh

https://patents.google.com/patent/CN103530559A/zh

https://blog.csdn.net/feelabclihu/article/details/115774916


你以为你有很多路可以选择,其实你只有一条路可以走


  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: IAP是In-App Purchases(应用内购买)的缩写,是一种让用户在应用内购买虚拟物品、功能或者服务的功能。通过IAP,用户可以在应用程序内直接购买并支付虚拟商品,而不需要离开应用程序去购买。 Bootloader(引导程序)是计算机系统中的一个特殊程序,它在系统启动时最先被加载执行,用于初始化硬件、引导操作系统以及其他系统初始化工作。 App是Application(应用程序)的缩写,是一种在移动设备上安装和运行的应用软件。MFC是Microsoft Foundation Classes(微软基础类库)的缩写,是一个用于开发Windows应用程序的C++类库框架。 因此,"IAP bootloader app MFC" 的意思是,在使用MFC类库框架开发的Windows应用程序中,通过IAP功能实现引导程序的更新或者应用程序的更新。 一个典型的应用场景是,如果用户在使用某个应用程序时,开发者可以使用IAP功能提供新的引导程序或者应用程序更新的提醒,并提供购买选项供用户选择。一旦用户购买了更新,应用程序会通过IAP功能将更新的引导程序或者应用程序直接下载并进行安装,而不需要用户离开应用程序或者手动进行更新的安装操作。 这种方式可以让用户方便地在应用程序内更新或购买新的功能或虚拟物品,提升用户体验,也给开发者提供了一种增加收益和提供新功能的方式。 ### 回答2: IAP(In-App Purchases)是指在移动应用程序中进行购买和付款的功能。通过IAP,用户可以使用应用内购买来解锁额外的功能、获取虚拟物品或订阅服务等。在移动设备上,IAP是一种常见的商业模式,为开发者提供了一种方式来获取收入。 Bootloader启动引导程序的意思,它负责在计算机或移动设备启动时加载操作系统。在移动设备上,Bootloader负责验证和加载操作系统,确保设备能够正确启动Bootloader通常由设备制造商提供,且不可修改。 App是Application的缩写,指移动设备上的应用程序。MFC(Microsoft Foundation Classes)是一种C++应用程序框架,通常用于开发Microsoft Windows操作系统上的应用程序。 综合来看,IAP bootloader app mfc可以理解为在移动设备上使用MFC框架开发的应用程序,具备IAP功能,并且由设备制造商提供的启动引导程序负责加载该应用程序。这种应用程序可以通过IAP进行付款和购买,提供额外的功能、虚拟物品或订阅服务等。这样的应用程序可以为开发者提供收入,同时由于使用了MFC框架,开发者可以更快地开发出功能丰富的应用程序。总之,IAP bootloader app mfc结合了应用内购买功能、启动引导程序和MFC框架,为用户提供更好的应用体验,同时为开发者带来商业机会。 ### 回答3: IAP(In-App Purchase)是应用内购买的简写,指的是在移动应用程序中实现购买和下载额外功能或内容的机制。通过IAP,用户可以使用应用内部的货币或在线支付购买额外的功能、解锁附加内容或去除广告等。 Bootloader(引导加载程序)是指在计算机硬件上最先运行的程序,用于启动操作系统。它位于计算机的ROM芯片中,负责调用操作系统的内核,并加载操作系统至内存中,以便开始系统的正常运行。 App(应用程序)是指在移动设备上使用的软件程序,可以用于各种不同的用途,例如社交媒体、游戏、生产力工具等。移动应用程序通常是通过应用商店(如App Store、Google Play)进行下载和安装的。 MFC(Microsoft Foundation Classes)是微软提供的一套C++类库,用于开发基于Windows平台的图形用户界面(Graphical User Interface, GUI)应用程序。通过MFC,开发人员可以使用更高层次的抽象来简化Windows编程的过程,使开发更加高效和便捷。 综上所述,IAP是一种用于在移动应用中进行购买和下载额外功能或内容的机制。Bootloader是计算机硬件上最先运行的程序,用于启动操作系统。App是移动设备上使用的各种不同用途的软件程序。而MFC是微软提供的一套用于开发Windows平台GUI应用程序的类库。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橙留香Park

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值