才发现好久都没有写博客了,但是其实一直也有做一些有的没的的学习和复现,这里记录一下前几个月的一个关于解BL锁的一个事情的探索吧。期间参考了很多大佬的博客才勉强看懂了原理。
小米解锁细节学习
三月初的时候,小米爆出来一个大新闻,大意是配合高通8 GEN5 的漏洞,普通用户能够直接解开小米的BL锁,相关新闻突然发的到处都是。
漏洞细节
小米的这一次提权,本质上是三个漏洞串联出发的。网上有许多新闻提到,这里大致介绍一下
- 一个位于
fastboot oem的参数注入漏洞,使得手机能够以SELinux宽容模式启动 - MIUI 原生服务的
miui.mqsas.IMQSNative存在的提权问题,能够使得用户获得一个root权限执行的能力,解锁的时候,会利用这个漏洞将解锁用的efi写入efisp分区。 - 高通引入了自研的GBL(The Generic Bootloader 通用引导加载程序)而未对引导程序本身进行签名验证,于是可以强制刷入二进制实现解锁。

三个漏洞一环扣一环,最终实现了对小米BL锁的解锁。
BL锁?
Bootloader是指在操作系统自身运行之前运行的一段程序,其主要任务是初始化系统硬件,然后加载操作系统内核到内存并执行。BL锁的作用是阻止用户安装非官方的操作系统或者修改设备的系统文件,以保护设备的安全和稳定性。
说的通俗一点,在手机上,BootLoader负责【加载手机上的操作系统】,而Bootloader Lock【防止我们随便的修改手机操作系统】
解开BL锁的意义
那为什么很多人热衷于解锁BL呢?这里讲几个解锁之后能做到的事情:
- 替换定制操作系统
- 刷入Magisk之类的工具
这些工具能够做到很多事情,举几个简单的例子:
- 防撤回,以后可以直接看到大家撤回的内容
- 去除所有预装的广告
- 安装自己定制化的工具,让手机【一键日卫星】
fastboot OEM 漏洞介绍
FastBoot简介
The fastboot protocol is a mechanism for communicating with bootloaders over USB or ethernet. It is designed to be very straightforward to implement, to allow it to be used across a wide range of devices and from hosts running Linux, macOS, or Windows.
FastBoot是一种通过USB或者网络,与引导加载程序通信的机制。也就是说,可以通过
- USB
- 网络请求
对手机的引导程序进行操作。相当于是所谓的【工厂模式/维修模式】之类的概念。这样就能对手机做一些更加【底层】的操作,比如说
- 擦写分区
- 查询设备状态
- etc
在这个模式下,可以理解成Fastboot是手机厂商在ABL(Android BootLoader)阶段对外暴露的一个调试接口。
为什么需要一个FastBoot?
安卓系统中的SELinux非常严格,即便是root权限的shell,也会严格的根据安全策略限制访问的资源。在Android系统启动后,它会具备完整的权限管理机制,许多底层操作无法在正常状态下运行,所以必须要在系统启动的之前的Fastboot阶段来完成。

如果不开启SELinux会有什么效果呢?这里介绍两个大家最常用也最容易理解的:
- 手机能直接通过su进程提升至于root
- 可以随便访问不同进程的句柄
Fastboot OEM 是什么?
而OEM(Original Equipment Manufacturer,手机制造商自定义)是fastboot预留的一个接口,这个接口允许手机发行商实现非AOSP(Android Open Source Project 安卓官方开源项目)的一些额外的功能。常见指令包含
- 解锁引导加载程序(fastboot oem unlock)
- 返回厂商指定的特定的状态(fastboot oem device-info)
等等。由于这些指令不在AOSP标准中,而是由厂商自己在ABL的代码中定义和实现,所以安全性需要由厂商自己保证
漏洞点
网上流传的payload的形式为
1 | fastboot oem set-gpu-preemption-value 0 androidboot.selinux=permissive |
根据前文我们知道,这是小米自己实现的set-gpu-preemption-value指令引入的类似命令注入的问题。
可以参考这篇文章分析的固件,看到这边漏洞点所在的上下文如下:
1 | STATIC VOID |
关键点在于arg 这个位置,这个变量为我们传入的" 0 androidboot.selinux=permissive",根据逻辑,这里仅仅会做一个去除空格的判断,这之后会直接将我们的输入作为GpuPreemptionValue的参数的一部分存入,最后拼接成这个样子:
1 | msm_kgsl.preempt_enable=0 androidboot.selinux=permissive |
这个值会直接作为参数变量,被写入到EFI配置中:
1 | Status = gRT->SetVariable ((CHAR16 *)L"GpuConfiguration", |
这段代码会将我们之前指定的变量写入Kernel中,关于OEM的GpuConfiguration配置项中。而在最终开机的过程中,会经过
1 | AppendToCmdLine( |
这样的拼接函数,将我们的输入与对应的命令行拼接在一起,最终导致这个宽容模式的参数被传入。
漏洞触发目的
利用Fastboot oem的参数注入,我们即可关闭这个严格的SELinux,从而能够实现对Android底层资源的访问。其中就包括了我们一会儿需要操作的efisp分区
修复策略
修复链接看这边
1 | + if ((AsciiStrLen(arg) != 1) || (arg[0] != '0' && arg[0] != '1')) { |
新增了对arg的判断,要求输入必须是数字,而且必须是0/1,从而保证了命令注入的发生
小米漏洞介绍
有了第一层宽容模式的突破,此时adb的权限高了许多,然而我们一般的发行机器上是没有su这样的二进制的,这导致我们还是不能直接提升到root。不过关闭了宽容模式,意味着我们的adb shell具备了与安卓原生服务通信的能力,此时就可以利用原生服务中的漏洞,实现权限提升
Android 进程分布
在安卓中,有这些基本进程:

其中有一些比较基础的服务型进程可以称为Android Native系统服务,也就是常说的Native 守护进程(Native Daemon)。这类进程通常会提供一种服务供用户使用。
Android系统基于Linux内核,每个应用都运行在独立的进程中,拥有独立的内存空间和 ART 运行时环境。这使得Android需要提供一套IPC服务以实现进程间调用(IPC),这就是Android 中的Binder机制。而在更为上层的封装中,这个机制被包装成了供开发者使用的AIDL接口和Intent系统。
MQSNative 服务介绍
根据分析,这个服务主要是是小米内部用的一个系统级别的服务,这个服务作为一个Android Native级别的服务,他的权限如下:
1 | service hypsys_system /system_ext/bin/hypsys_system |
这个服务本身是以root权限运行,并且其注册函数大致为:
1 | v41 = (MQSNativeDaemon *)operator new(0x30uLL); |
这种形式注册的服务,我们就能够使用
1 | shell service call miui.mqsas.IMQSNative [opid] |
这样的形式来调用这个注册binder提供的服务函数。
漏洞分析
这段逻辑的PoC大致为如下的形式:
1 | shell service call miui.mqsas.IMQSNative 21 i32 1 s16 "dd" i32 1 s16 'if=/data/local/tmp/gbl_efi_unlock.efi of=/dev/block/by-name/efisp' s16 '/data/mqsas/log.txt' i32 60 |
其对应的处理逻辑为
1 | this->vptr->captureLogByRunCommand( |
由于函数直接没有任何的参数过滤,直接就能调用,这就相当于【root权限下执行任意命令的能力】。于是上述的poc就直接的被执行了,参数对应关系为:
- cmds_vec : dd
- args_vec : if=/data/local/tmp/gbl_efi_unlock.efi of=/dev/block/by-name/efisp
- logFilePath : /data/mqsas/log.txt
- timeout : 60
此时的逻辑变
1 | 将 /data/local/tmp/gbl_efi_unlock.efi 这个文件写入 /dev/block/by-name/efisp |
高通漏洞介绍
该漏洞出现在高通SM8850这个型号的芯片中,漏洞的核心原理源自于一个引导区检查的疏忽
高通引导区权限模型
高通的权限模型分为这四类
1 | +---------------+ |
在整个高通芯片引导一个操作系统启动的时候,经过了如下的流程:
- 制作过程中会在芯片中直接融入一个数学上证明安全的主引导加载程序 (Primary Bootloader PBL)
- 其会将可扩展引导加载程序安全(eXtensible Bootloader Security,XBL_SEC)加载到EL3这个安全层上。
- 这个
XBL_SEC将会负责配置TrustZone并且验证XBL_Loader(可扩展引导加载程序) - 这之后
XBL_Loader程序将会加载应用引导程序(Application Bootloader ABL) - 将其加载到EL1中(OS Kernel)。此ABL将会用于验证和启动Android的Linux内核。
流程图大致如下:
1 | +------------------+ |
其中,在 SM8850 中,采用了EDK2框架,与上述模型存在一定的差异,就是这个ABL阶段变成了一个巨大的UEFI程序,在EL1环境中执行。
UEFI Secure Boot
UEFI启动的时候,有四个步骤
- SEC(Security Phase 安全检测,完成从实模式(real mode)过渡到保护模式(protected mode))
- PEI(Pre-EFI Initialization EFI前期初始化,初始化各个模块,CPU/IO等等)
- DXE(Driver Execution Environment 初始化各类驱动,并且验证驱动签名)
- BDS(Boot Device Select 负责选择从哪个设备启动,初始化键鼠驱动,VGA等等)
其中,UEFI最常见的攻击面就是在DXE阶段,我们可以通过引入恶意的驱动来实现对UEFI的一些验证的绕过
双重签名 与 OEM 具体实现
在高通的设计中,EL3 和 EL2 中执行的程序必须同时具备高通的根证书和**手机制造商自定义(OEM)**的双重证书。 而EL1 中,也就是Linux OS运行的环境中,只需要验证 OEM 的证书即可。
BL锁发展史
小米手机的BL锁最初存放在标准的存储空间中,这个存储空间就是通常使用adb shell能够访问的绝大部分的文件系统分区:
- boot 分区 : Linux 内核部分
- system : Android系统位置
- devinfo : 存放设备信息,里面会存放BL锁信息
根据报告,早期的小米BL锁存放在一个普通的分区中,,只要我们能够拿到Linux内核的su 权限,我们就能直接去篡改中存放的BL锁状态,实现解锁。
The bootloader locking bit of such devices is held in the devinfo partition, parsed by the Android Bootloader.
1 | > firehorse.py -t ugglite target read_partition devinfo |
当我们具备root权限后,就可以使用dd指令,将上述框框中的两个字节修改为01,即可实现BL锁解锁。
为了防止这种情况的出现,现在架构将解锁状态矩阵(unlock sate matrix) 放到了eMMC/UFS RPMB空间中,
1 | UFS 存储芯片 |
这个RPMB(Replay Protected Memory Block 可重放保护内存块)需要使用一个256bit的HMACA-SHA256的认证key,这个key被存放在EL3 TrustZone中。Linux内核时没办法向这个位置写入数据的。虽然Linux内核可以调用Secure Monitor Call(SMC) 来调用涉及底层的敏感调用(例如电源管理,指纹识别),然而这个TrustZone必须要有 OEM 令牌才能够正确使用。
然而,在启动期间的ABL执行阶段,虽然它执行在EL1,但是因为还未开机完成,此时TrustZone会公开一些特殊的UEFI协议,这些特殊的协议接口将会允许程序去修改RPMB中的BL锁部分。
所以这个漏洞利用的实际就是在ABL阶段,实现对目标BL锁的修改
漏洞成因
ABL框架使用的EDK2 框架中定义的GetBlkIOHandles 会遍历当前的执行分区,找到一个叫做efisp的分区,然后尝试加载他的GBL
1 | // Pseudocode representation of the ABL GPT traversal |
这个分区是骁龙8 Elite Gen 5(SM8850) 这个架构新引入的暂存分区。根据上述逻辑,程序会尝试加载这个分区中有效的PE32/PE32+文件(一般就是大家所致的efi文件),并且将其交给用于加载对引导程序的gBS->LoadImage()服务中
正常来说,根据UEFI的逻辑,只要UEFI Secure Boot启动了,那么这个gBS->LoadImage只能接受被签名的二进制。这个安全验证依赖于一个UEFI的接口DxeImageVerificationHandler,这个接口会根据一个烧录在主板上烧录的一个PK作为验证的密钥,对这个二进制进行身份验证。
然而,根据逆向可以发现,这个芯片中的PK值是空的,这就意味着整个程序的二进制都不会被PK进行校验。在校验逻辑中,如果PK密钥忘记设置了,此时安全模式会从User Mode转变为 SetUp Mode
1 | /** |
在Setup Mode中,无论传入的二进制是什么,DxeImageVerificationHandler都一定会返回EFI_SUCCESS,此时校验总是通过的,于是我们就能够执行一个写入到efisp分区的二进制了。
在这份报告中 给出了符合对应协议的EFI程序,利用此程序,即可关闭RPMB 分区的BL锁:
1 | /** @file VbRwStateApp.c |
BL锁解锁固化
为了不留痕迹,可以将EFISP中的固件擦除。此时我们的RPMB分区已经写入了解锁数据,此时解锁就被永久留了下来。
刷机策略
一旦解锁了BL锁,一般会刷入以下的内容
1 | boot.img ← 包含 Linux 内核,可以刷入 Magisk 获得 root |
这样我们就能够有一个自己定制化的操作系统,并且能方便的获取root权限。
一些 QA
BL锁到底工作在哪一层?
BL锁的校验状态写在了EL3层,但是BL锁的校验逻辑其实在EL1层。每当ABL启动的时候,其会通过SMC的指令请求EL3中的RPMB,然后获取加锁状态。
另一个漏洞
在那几天,还爆出来了另一个漏洞。漏洞大致的原理是:USB初始化阶段,初始化的内存只有8字节,但是却会拷贝32字节的数据,发生堆溢出

于是攻击者可以尝试构建一个USB脚本,通过堆溢出来实现程序劫持,最终将运行在EL1层的BL锁check结构体中的一个表示【unLock】的字段修改,最终通过执行
1 | fastboot flash unlock |
实现永久接触BL锁
修复策略
由于这个PK没有烧入芯片这个事情涉及硬件,所以没办法直接修复。最后厂商的修改方式其实是【去除了当检测到EFISP分区就自动加载其中的EFI程序】这一段逻辑。经测试,在新版的小米系统中,即便是往EFISP分区拷入了EFI引导区文件,似乎也不会被引导启动了。
参考博客
https://bestwing.me/preempted-unlocking-xiaomi-via-two-unsanitized-strings.html
https://tryigit.dev/qualcomm-uefi-gbl-bootchain-zero-day-exploit/