Appearance
作者按: 本文是我继网络安全进阶之后写的第二篇技术文。我的观点是,看问题要抓住本质,我力图通过我认为的本质开始给大家讲刷机过程。 本文为我写作草稿后由Claude Sonnet 5重新生成并多轮对话,但是因此该文一版缺少革命性,我进行了增补,但未必能解决问题。 本文不涉及具体机型操作,因为这块实在太散了。AI又不道德感高不愿意写,也就先这样了,可以到xmpp带着具体问题问我。同时,正是由于太散了,如果有错误请指出。 我认为,共产主义,甚至只是自由软件的要求,就说知识应该是协作产生记录的。我非常欢迎大家来提PR或者通过其他方式对本文进行增补。
前置知识:哈希(对称与不对称加密),已在《网络安全进阶》中讲过,不再重复。
序言
刷机以摆脱国产系统的实时监控是非常重要的。甚至于从经济上来讲,强制用户使用某一系统并观看广告等,尤其是当大多用户并不知道手机可以不用官方的狗屎系统时,从资产阶级的私有制上也不是很说的过去。
但是无论如何,厂商还是限制了刷机,以至于很多时候不得不通过漏洞来达到目的。首先,我认为,要学好刷机,从本质上理解,就要先学会启动链。
手机的安全体系比网络协议栈更"物理"——它的信任根扎在芯片里,一层一层往上验证,直到 Android 系统起来。刷机、解锁、root,本质上都是在和这条验证链打交道:要么找到它允许你做的操作(官方解锁),要么找到它没想到的缝隙(漏洞),要么绕开它直接物理层面写入(深刷)。
权限问题
ARM 架构定义了四级特权(Exception Level,EL),数字越大权限越高。同时 ARM 还划分了**安全世界(Secure World)和非安全世界(Non-secure World)**两个平行的空间——安全世界可以访问非安全世界的所有资源,反过来不行,这是一堵单向的墙。手机上,安全世界跑的是 TrustZone 体系(高通叫 QTEE,联发科叫 TEE,业界统称 TEE),装着密钥、指纹模板、DRM 密钥这类不能让 Android 系统碰的东西。
完整地画出来是这样:
EL3 ── TrustZone(密钥、指纹模板、DRM 密钥、敏感状态的实际管理者)
安全世界(Secure World,高通生态里通常整体归为 EL3 / TrustZone)
Secure EL1 ── TEE 操作系统(QTEE / QSEE / Trusty 等,密钥与敏感状态存放处)
Secure EL0 ── Trustlet(可信应用,指纹匹配算法、DRM 插件等具体逻辑跑在这里)
非安全世界(Non-secure World,下文默认视角)
EL2 ── Hypervisor
EL1 ── Bootloader 各阶段(PBL/XBL 早期例外,见下节)/Linux Kernel
EL0 ── Android 系统进程与用户应用一个术语上的补充说明:更严格的 ARM/GlobalPlatform 标准模型里,EL3 其实只对应一个很薄的 Secure Monitor(负责世界间切换、转发 SMC 调用),真正跑密钥管理、可信应用这些业务逻辑的“TEE 操作系统”运行在 Secure EL1(比如 OP-TEE、Trusty 这类跨厂商的开源实现,都是这么分层的)。但在高通自己的实现和社区习惯表述里,“TrustZone”经常被整体归到 EL3 讨论,不细分内部是否存在 Monitor 和 TEE OS 的分层——本文后续沿用高通生态里更常见的这种笼统说法,遇到确实需要区分内部分层的地方会单独说明。
EL3 几乎是一切的主宰。安全世界的密钥、指纹模板、DRM 密钥、启动链后续每一环要不要放行,最终都攥在它手里;其他所有等级,相对它而言都只是被允许存在的租户。这篇文章接下来讲的所有权限问题,本质上都是“谁能不能求得动 EL3”的问题。
约定:下文提到
EL0~EL2,如无特殊说明,均指非安全世界的对应等级;安全世界只讨论到需要展开的地方(见下一节),不再逐次注明“非安全 EL几”。
持久性存储与分区
计算机,就是进行计算的机器。计算的前提是输入。输入内容不依靠外界,那么就依赖储存。甚至于,部分计算本身,也是通过更低阶的指令和储存内容加载上来的。
持久化存储大致分四类:
- 片上 ROM(Mask ROM):芯片流片时刻死的只读代码,出厂后无法修改,是整条信任链的根。
- eFuse:一次性可编程熔丝阵列,烧录密钥哈希、设备唯一 ID、防降级计数器,只能烧一次,改不回去。
- UFS / eMMC:手机上的“硬盘”,存放系统和用户数据。由于历史原因,又被称为字库。
- RPMB,上面的硬盘中,其中有一块特殊区域叫 RPMB(Replay Protected Memory Block),写入前必须用一把 TEE 和存储控制器各持一份的密钥算出 HMAC 签名,双方校验一致才真正写入。设备解锁状态、防重放计数器这类敏感状态通常存在这里,而不是普通分区。
下文中储存,未说明一律代指主体的 UFS 或者 eMMC 闪存。
硬盘按 GPT 分区表划成几十个逻辑分区。接下来从启动视角,介绍各个分区。
刷机的本质,就是往这些分区写入符合格式、通过校验的数据;所谓“砖”,通常是某个分区的内容损坏或版本不对,导致上一阶段拒绝加载下一阶段。
下文中,对于分区名,若无特殊说明,通常是程序本身名称。
安全启动
安全启动链
近十年左右的骁龙平台,启动流程大致是这样的:
- PBL(Primary Boot Loader):CPU0(CPU的一个核心) 上电后从片上 ROM 直接执行,是唯一“天生被信任”的一环。PBL 从存储中加载 XBL 到 SRAM(即 CPU 高速缓存,这里涉及通电顺序问题,和安全关系不大),用烧录在 eFuse 里的厂商公钥哈希验证其签名。
- XBL(eXtensible Boot Loader):初始化 DRAM(外置大内存,也就是通常理解中的 RAM,也是上电顺序问题),按顺序验证并加载 TrustZone、Hypervisor、ABL,正式劈开安全世界和非安全世界。以及启动其他核心。
- ABL(Android Boot Loader):跑在非安全 EL1,初始化屏幕,根据 BL 锁状态决定是否做 AVB 校验(见上一节),校验结果决定启动状态是绿/黄/橙/红,然后跳转执行 Linux Kernel。
- Kernel 初始化其他硬件,挂载
super,启动 Android。其主体位于boot分区。
这里我简单补充一下一点关于分区的事情
这套划分本身是十年间逐步演化出来的:
- A/B 分区(约 2016 年,Android 7 Nougat 引入):每个关键分区(
boot、system等)都有_a/_b两份,OTA 升级时写另一份不用的槽位,升级失败可以整体回滚到还在用的那一份,实现“无缝更新”。老设备(A-only)没有这个机制,升级失败容易变砖。- Treble 与 vbmeta(约 2017 年,Android 8 Oreo 引入):
system和vendor被拆成两个独立分区,系统镜像和厂商驱动/HAL 解耦,这样 Google 推系统更新时不需要等厂商重新适配底层驱动。与之配套的是 AVB(Android Verified Boot 2.0):vbmeta分区存放一棵哈希树的根,逐级覆盖boot、system、vendor等分区内容的哈希,任何一个被改动,AVB 校验就会失败,进而决定启动状态是绿色(完整签名验证通过)、黄色(用户自签名验证通过)、橙色(已解锁)还是红色(校验失败,拒绝启动)。前面说 ABL“校验 vbmeta”,指的就是这一步。- 动态分区 /
super(约 2019 年,Android 10 引入):system、vendor、product等不再是独立的物理分区,而是“逻辑分区”,一起装在一个大的物理分区super里面,可以在不重新分区、不刷整个存储的情况下调整各逻辑分区的大小。这也是为什么前面提到fastboot的一部分刷写能力要转交给 recovery 里的fastbootd:物理分区表在 fastboot 阶段是固定的,但逻辑分区的增删改需要挂载super之后才能操作,这个环境 fastboot 本身给不了。- GKI,通用内核镜像(约 2020~2021 年,Android 11/12 引入):过去每个厂商的内核和 vendor 驱动模块是编译在一起、和
boot镜像打包在一起的,导致 Google 想统一内核版本、加快安全更新几乎不可能。GKI 把这件事拆开:boot分区现在只装 Google 提供的通用内核本体,设备相关的驱动模块、设备树叠加层等被移到vendor_boot分区;再往后(GKI 2.0)连“内核起来之后第一个跑的 init 进程所在的通用 ramdisk”都从vendor_boot里独立出来,单开了一个init_boot分区。这也是为什么这几年 Magisk 之类的 root 方案在新设备上要去 patchinit_boot而不是boot——真正归厂商定制的东西已经被挪到别的地方了。
联发科命名不同,结构类似:BootROM(简称 BROM) → ( Preloader → bl2_ext(EL3,负责验证之后各阶段)) → ( TEE , GenieZone(联发科的 Hypervisor), LK(Little Kernel,相当于 ABL)) → Kernel → System。
至于三星 Exynos 和 Google Tensor:两者链条结构大同小异(都是“ROM 里的根 → 各厂商命名的一级/二级 bootloader → TEE/Hypervisor → 内核”),只是各自在这套骨架上叠了自己的东西——Exynos 见下文 Download 模式一节;Google 则是把安全芯片独立了出去,Pixel 的 Titan M2 单独负责锁屏密钥派生、防暴力破解、设备唯一身份等本该在 TEE 里的敏感职能,相当于把最要紧的那部分从 SoC 内部搬到了一块独立芯片上,攻击面更小。这两家的具体实现本文不展开画图。
补充:关于虚拟化
修改内存页表这类操作需要 EL2 权限,普通 Linux Kernel 默认不在 EL2(EL2 留给 Hypervisor)。约 Android 14 前后,Google 推动 pKVM(protected KVM),让 Kernel 本身可以在 EL2 里跑一个受保护的 hypervisor 从而支持虚拟化。联发科天玑 9000 系列、高通骁龙 8 Gen 3 及以上具备硬件支持,但能否跑起来还取决于 Kernel 是否编译进了对应支持——很多厂商量产内核默认没开。从非安全 EL1 请求切换到 EL2 通常需要 root 权限触发对应接口。
联发科(MTK)部分老平台(如天玑 1100/1200,MIUI 12.5 时代)出现过 Preloader 误将 EL2 权限赋给了 LK 及 Kernel 的情况,理论上可用于虚拟化,但当时厂商内核版本停留在 4.14,过于陈旧——Windows 客户机基本无法完整虚拟化,GNU/Linux 可以跑但支持不完整,还需要自行编译一个开启了虚拟化选项的内核才能利用上这个权限。
两类公私密钥对
启动链上验证用到两类密钥:
- 厂商密钥:芯片厂商的根密钥,验证 PBL 之后第一级代码(XBL/Preloader)的签名,出厂烧录进 eFuse。对于之后的启动链,通常是上一级启动链文件携带公钥。多设备共用。
- 设备密钥:烧录在芯片中,一般是 eFuse。签名本地状态数据(比如 BL 锁状态),或者参与派生用户数据加密密钥。每个设备独一无二。
设备密钥由 eFuse 烧录或者 RPMB 记录,只有 EL3 或者其允许的程序能真正使用。跑在非安全 EL2 及以下的代码(包括拿到 root 的 Linux Kernel)无法直接拿到这把密钥,只能通过 SMC 请求间接使用它。这也是为什么厂商密钥体系被攻破的后果远大于普通 root 之一:root 拿到的是非安全 EL0 的最高权限,解开了 BL 也就是非安全 EL1,密钥始终待在安全世界摸不到。
锁屏加密逻辑
现代 Android 设备的文件级加密(File-Based Encryption)体系里,
data分区的加密密钥并不直接等于锁屏密码,而是由设备密钥(存在 TEE 里,用户拿不到)与锁屏密码派生出的密钥结合运算得到。这意味着:即便攻击者拿到了整块存储芯片的原始数据镜像,没有设备密钥也无法离线暴力破解——密钥派生和校验都得经过 TEE,TEE(利用RPMB)或外置安全芯片(通常更安全) 会对暴力猜测的速率和次数做限制。这也是为什么"物理提取存储芯片"这类手段对现代设备的实际威胁远小于对老设备。在TEE可信的前提下,系统都无法通过暴力枚举得到我们的密码以及数据。不过有一种特殊可能,就是设备密钥很多时候是出厂随机生成烧录的,如果芯片厂商有保存,可能导致设备密钥泄漏。
另外,上述内容是针对原厂系统,对于类原生系统,由于厂商并不开源安全组件,要想正确调用TEE实现硬件级防护是有一定难度的,许多类原生并没有实现这方面。其加密仅是系统层面的,无法防止刷入其他系统或者recovery后暴力枚举破解,甚至无法防止被读取到其他设备上后被暴力枚举破解。你可以尝试故意多次输入密码看会不会要求等待来判断系统是否正确调用了TEE。
然而,一方面,我认为,尤其是国内厂商,其向中国政府出卖我们,利用安全启动链特性,签发用于破解的镜像的可能性非常之大。同时,这些安全措施本身是存在漏洞的,也可被利用针对。比如说刷了石墨烯pixel相对一般设备之所以安全,一方面其回锁了bl,攻击者操作空间大大减小,同时,独立安全芯片相比于RPMB安全性更高,漏洞更少,还有更多安全机制。
学习这方面有利于我们认识电子取证。我的看法是,一旦手机物理落入敌人手中,基本可以认定厂商会出卖我们,然后敌人就可刷入后门分区,读取数据和设备密钥,暴力枚举。因此,要么通过长密钥进行加密,要么就是避免设备落入敌手。
BL 锁的本质
设备是否"已解锁",通常本质是存在一个专门分区里的一个标志位(高通常见于类似 secinfo 的分区,联发科是 seccfg),这个分区由设备密钥签名/校验。有时也储存在RPMB中,但是本质上还是由EL3控制。极少数情况下是通过厂商密钥控制的,这种情况下,后面说到的强解BL会比较难,但是极为少见。ABL/LK 启动早期读取这个状态,决定是否放宽 AVB 校验、是否允许 fastboot flash。
两点常被误解:
第一,BL 锁只是 ABL 这一阶段的行为开关,不代表对芯片的完全控制权。 PBL、XBL 依然只信任厂商签名,与 BL 锁状态无关。
第二,解 BL 锁的本质是 ABL 向 TrustZone 发起一次请求,而不是 ABL 自己直接写。 改这个分区需要设备密钥签名,ABL 摸不到密钥,只能"求"TEE 帮忙改。这个"求"要走什么样的验证,正是解锁流程安全与否的核心。
Google Pixel(Tensor 芯片) 的安全芯片独立于主 SoC,型号 Titan M2,负责锁屏密钥派生、防暴力破解等本该在 TEE 里的职能。同时,他也使得用户可以自定义BL锁。
但是需要注意,Pixel只是相对安全,自定义的只是第三阶段启动链,一旦Google出卖你,他就可以签名程序,使得取证人员可以通过物理接触,刷入程序来读取你的数据和设备私钥,进行暴力枚举。
ARB 反降级
Anti-Rollback(ARB)是烧录在 eFuse 里的一组单调递增计数器,每个启动阶段绑定一个"最低允许版本号",刷入版本号低于门槛的固件会直接验证失败,防止攻击者刷回存在已知漏洞的旧版本。计数器只增不减。
值得一提的是不同厂商对"能不能降级"这件事的策略并不统一,而且往往和 BL 锁状态绑定:据社区长期观察,小米的策略大致是——设备处于锁定状态时不允许降级到低于当前 ARB 门槛的版本,一旦解锁则允许降级(相当于把降级权限和"你已经表明愿意承担风险"这件事挂了钩);三星则把 ARB 计数器和 BL 锁的开关位放在了同一套校验逻辑里,这也是三星设备解锁/降级流程比较严格、经常"回不去"的原因之一。其他厂商的具体策略这里不逐一展开,感兴趣可以自行对照实测。
刷机的本质:写入分区
刷机要做的事情很朴素:往对应分区写入正确格式的数据。按能接触到的层级,手段大致分几类。
深刷(EDL / 9008 模式等)
PBL 通常预留了一个底层烧录接口(高通俗称 9008/EDL,联发科是 BootROM 模式),一般需要特殊按键按法或专用工程线或者短接测试点或者其他厂商内部手段才能进入。
进入底层模式后,可以加载一个厂商签名的引导程序(高通叫 firehose,联发科叫 DA/Download Agent),签名校验通过后才会被执行。这段程序本身可以读写分区,但是否直接放你写,取决于厂商策略——有的要求先验证售后账号等身份信息。
为了口中的安全,厂商通常只对签约的售后网点开放深刷能力。但是文件毕竟是文件,如果没有小米等的动态校验,在资本主义制度下,为了金钱什么都可以交换。这类文件的泄露自然不是什么新奇事。因此,为了解决这个问题,厂商通常会通过推送特定的更新来利用熔断efuse,来让设备不再信任这个文件。同时,面对漏洞,他们也会进行系统更新以及ARB熔断。
举两个例子,oppo及一加基本上在一个大版本更新之后就会熔断9008,以至于售后都无法再次使用深度刷机。小米做的倒是不错,基本上没有熔断过,当然这应该主要是因为小米大部分的firehose都是有要授权验证售后人员身份机制的。不过也有例外,比如小米的8Gen2和8Elite的免授权Firehose就泄露了(东芝凯侠字库不可用,会触发自动全盘格式化)。例外MTK那边自从后面提及的BROM漏洞之后就引入了更严格的授权机制,但是还是有人破解了这个机制,也有可能只是出售这个内部渠道,总之可以绕过。
提一嘴那两个文件怎么泄露的 这类付费服务的核心通常是掌握着一份能让设备接受深刷的特定文件。但这份文件要真正在用户设备上起作用,就必须完整地发给设备本身——制作过程可以藏起来,但结果必须原样传出去,这是没法回避的。正因为这份文件要以完整、可用的形态经过一次网络传输,这个传输环节在过去一段时间里成了流量分析(抓包)的重点目标:只要能截获这次传输,拿到的就是那份已经成品化、可以直接复用的文件本身,跟做这份文件用了什么方法完全无关。比如说我们后面会提到的,自从2.1左右小米8E5漏洞公开依赖,就进入了大抓包时代,然后这两份文件被拿到并公开了。
因为 PBL 固化在片上 ROM 里、不可能被写坏,深刷这条路径理论上"无惧黑砖"。但也有反例:分区表本身损坏时,写入操作可能触发存储芯片的保护机制而挂起,需要彻底断电复位才能恢复。
很可笑的事情是,深刷渠道的存在,本来应该意味着设备本身是基本上刷不死的。但是厂商为了所谓的安全,还是对这个渠道大加限制。导致不少人的机器由于操作失误陷入了本不应该存在的黑砖状态。 有人说,但是厂商官方还是提供了这个渠道啊,但是请注意,厂商的售后人员未必专业到能完美利用这个渠道,同时,有的厂商甚至不惜彻底锁死这个渠道。更讽刺的是,这些个大公司,在实际上存在高效利用哈希算法和efuse实现多Firehose签名可任意取消的时候,还是让事态变成了彻底关闭深刷端口。 顺着那套潜在机制说下去,其实关于安全启动,厂商完全存在能完全让用户自由又能确保手机安全的手段,也就是efuse存储用户自己的信任根以及允许熔断厂商的信任根,但是没有人做。
fastboot
ABL 内部实现的一套协议,允许在 ABL 阶段查询设备信息、切换 slot、刷写分区。工作在非安全 EL1,能做什么完全取决于当前锁状态和厂商实现。
三星是个例外:它的设备管理协议叫 Download 模式,本质上是三星在自己的 ABL 定制层里实现的一套自有协议,而不是标准 fastboot——官方工具是 Windows 平台的 Odin3,社区也有一个开源、跨平台的命令行替代品叫 Heimdall,可以在 Linux 下完成刷写。三星固件包通常拆成几个独立文件对应不同职责:AP(Android Platform,系统本体,含 boot/system 等)、BL(bootloader,即 XBL/ABL 这一层)、CP(基带/Modem 固件,下一节详述)、CSC(Consumer Software Customization,运营商/地区定制配置)。这种拆分本身就体现了三星把"谁能刷什么"划得比较细——比如很多渠道只能刷同地区的 CSC,跨区刷机可能触发校验失败或功能缺失。
CP 协处理器与基带
手机上除了应用处理器(AP),通常还有一块独立的基带处理器(Modem/CP,Communication Processor),负责蜂窝网络通信。CP 有自己独立的一套固件和签名体系:由 XBL 在启动早期初始化并验证
modem分区镜像的签名,modem 固件启动后再自行验证nvram、nvdata等配置分区的签名——这些配置分区通常融合了厂商签名和设备签名两重校验。基带签名体系是设备厂商核心资产之一:它锁定了 IMEI、频段配置等出厂写入的核心参数,用户无法主动修改。历史上出现过的所谓"改 IMEI"工具,本质上都是在利用某个基带固件版本对
nvram/nvdata校验不严的漏洞,而不是绕过了签名体系本身——这类漏洞一旦被厂商发现同样会被修复。改IMEI可以绕过中修的互联网移动网络入网实名制(用物联卡)。通常来讲,联发科机型的如果能改识别电阻,取消对imei的检验。不过很显然,这条道也在遭受封堵,能用的一般是五年前到十年前的老手机。
recovery
设备上装着的第二套、功能精简版的系统,用来执行 OTA 升级、数据擦除等操作。较新的动态分区方案下,fastboot 的一部分刷写能力被移交给了 recovery 内部实现的 fastbootd,因为涉及逻辑分区的操作需要一个完整的用户态环境才能完成。
启动状态与写分区的限制
设备有一套"启动状态机":进入到特定阶段之后,某些分区就不再允许被修改——即便是拿到 root 权限的 Kernel 也写不了它们,因为写入权限从来就不在非安全 EL1 手里。TrustZone 会拒绝已经启动完成、和 Kernel 同属非安全 EL1 的运行时代码去修改锁状态分区,但会在 ABL 阶段放行这类请求。这不是 TEE 技术上做不到接受运行时的 SMC 调用,而是它内部维护着一个和启动进度绑定的状态机:一旦确认设备已经完成启动(进入 Kernel/Android 阶段),就不再受理"修改锁状态"这一类请求,不管发起请求的代码权限有多高。这也是为什么单纯拿到 root 权限、哪怕是内核态执行权限,也没法反过来让 TEE 帮你解锁——请求本身会被状态机直接拒绝,问题不在权限,而在"你是谁、在哪个阶段问的"。小米"解锁 168 小时冷静期"之类的规则,本质是 ABL 自己的业务逻辑判断要不要在还允许提问的这个窗口向 TEE 发起解锁请求,跟 TrustZone 本身的状态机是两回事。
这里有必要把"root"这个词的权限层级说清楚,因为很容易和"控制启动链"混为一谈。Android 上通常说的 root,指的是用户态进程拿到了 UID 0、即使关闭了 SELinux 强制模式后获得的高权限上下文——这仍然是非安全 EL0,只是 EL0 里权限最高的那一档。它可以向内核(非安全 EL1)发起大量原本受限的请求,但它本身并不是 EL1。真正能被称为"控制了启动时的非安全 EL1",需要的是能够任意读写 boot(以及 GKI 时代的 init_boot)这类内核所在分区并让设备真的用它启动——这一步要么走 fastboot/recovery 这类官方允许的路径(前提是已解锁),要么需要一个能绕过分区写入限制的漏洞,两者都不是单纯拿到用户态 root 就能做到的。
举两个近期的例子帮助理解这条界线:Linux 内核里出现过的通用本地提权漏洞(比如 2026 年披露的 epoll 子系统竞态条件 CVE-2026-46242、以及 futex/rtmutex 子系统的 CVE-2026-43499),都能让一个本来没有特殊权限的用户态进程拿到内核态执行权限,从而具备了前面说的"EL0 里权限最高的一档",甚至可能借此关闭 SELinux、伪造签名校验的执行结果——但这类漏洞发生在内核已经跑起来之后,是运行时的问题,重启后失效,并不天然赋予"重启时能让 ABL 加载一个未签名的 boot 镜像"这种能力,因为分区写入权限和启动时的验证逻辑是两套独立的机制。真正动摇启动链信任根的漏洞,出现在 PBL/XBL/ABL 各阶段本身,而不是内核里。
砖的分级
boot(内核)或系统分区坏了:一般会自动跳转到 recovery。recovery也坏了:卡在 bootloader,至少还能进 fastboot。ABL或XBL坏了:直接黑砖,只能走深刷这条路。
我们之前提到过,启动过程中,磁盘中的程序会被加载到内存里,因此如果你破坏了磁盘分区中的文件,不会马上砖,但是下一次启动就出问题了。
官方解锁流程
以最常见的"开发者选项解锁"路径为例:
- 打开开发者模式里的 OEM 解锁开关,这一步只是让 ABL 在收到解锁命令时愿意受理,本身不代表已经解锁。
- ABL 内部可能有自己的业务判断(比如联网验证账号、等待冷静期),判断通过后向 TEE 发起解锁请求。
- TEE 最终执行对状态分区的签名写入,完成解锁。
这条链的每一步都是厂商设计好、也愿意开放(尽管实际上可能并不真的想让人用,点名批评曾经小米澎湃OS,现在不用批评了,因为彻底关闭了)的路径。
漏洞:强解 BL
在展开各阶段案例之前,先讲一个更抽象、跨厂商都适用的分类,理解这个分类比记住具体哪个型号有问题更有用,因为它揭示的是启动链设计本身的共性弱点。目前公开报道过的启动链漏洞,大致可以归进三个大类:
- 经典的溢出导致任意代码执行:某段处理外部输入(比如通过 USB/串口收到的数据、或者证书文件里的字段)的代码没做好边界检查,输入超出预期长度或范围,直接覆盖了控制流相关的数据(返回地址、函数指针之类),攻击者由此劫持执行流程。这是最"传统"的一类,很多语言和平台上都存在,不是启动链特有的问题。
- 溢出导致校验状态被破坏,进而使校验本身失效或给出错误结果:这一类更隐蔽——被溢出覆盖的不是控制流数据,而是和"这个镜像有没有通过校验"直接相关的状态变量或者缓冲区。代码本身该做的校验逻辑可能一步没少,但因为相关状态已经被污染,最终校验的结果不可信,等于"检查流程走完了,但检查的东西已经不是原来那份了"。
- 配置/策略问题导致某一级根本没有触发下一级的校验:这一类往往不涉及任何内存破坏,纯粹是工程配置的问题——比如某个阶段该不该校验下一级,是由一张配置表或者一个标志位决定的,如果这张表本身配错了,或者多个阶段各自维护的配置表没有严格保持一致,就会出现"该查的东西压根没查"这种最朴素也最容易被忽视的问题。前面提到的联发科 Preloader/BL2_ext 两套安全策略表可能不同步,就是这一类的典型场景。
这三类里,第一类是内存安全问题,第二类介于内存安全和逻辑问题之间,第三类是纯粹的工程配置问题——三者对应的修复难度和"防得住防不住"完全不同:第一、二类原则上可以通过更严格的输入校验、更安全的内存管理来系统性地减少;第三类更依赖流程管理,很难靠单点的代码修复彻底杜绝,因为问题根源是"多处配置没有保持一致"这件事本身。
第一、二类本质上是内存安全问题,这也是为什么这几年安全社区一直在推用内存安全语言重写启动链关键代码这件事——像 Rust 这样的语言,凭借编译期的所有权和借用检查,从语言层面就不允许越界访问、悬垂指针这类操作,想写出这一类的溢出漏洞会困难得多(不是完全不可能,unsafe 代码块里理论上还是能出问题,但攻击面会被大幅压缩)。Google 这几年在 Android 底层组件里持续推进用 Rust 替换部分 C/C++ 代码,出发点也是这个;类似的思路正在向更底层的固件和 bootloader 领域扩散,虽然目前主流芯片厂商的启动链代码大部分还是 C,但"新写的关键校验逻辑用内存安全语言实现"正在变成一种越来越被认可的工程实践,而不只是学术界的提议。第三类配置一致性问题,Rust 这类语言帮不上太多忙,本质上还是要靠更严格的工程流程(比如把安全策略表也纳入自动化的一致性检查)来解决。
按启动链的阶段分类,漏洞的可以导致的结果完全不同,归根结底是计算机体系中权限分明的体现:
PBL / BootROM 级
跑在最高权限、且是信任根本身,一旦存在通常意味着可以绕过整条签名链的验证,因为它自己就是验证的起点。由于固化在 ROM 里,厂商完全无法整体 OTA 重写 PBL;实际能做的补救通常是通过 eFuse 烧录一个新的门槛/标记位,让 PBL 在加载时读到这个标记后精确禁用某一段已知有问题的代码路径或函数入口——是"打补丁式的局部禁用",不是重新烧一份完整的 PBL。这类修补生命周期极长,往往要等下一代芯片流片才能从根上解决。据长期公开、可查证的信息(包括厂商公告与 CVE 记录):高通骁龙 835 上存在过相关问题但难以实际利用;845、855 上有可实际利用的先例;865 没吃到更新也可以,但高通后续通过 eFuse 修订堵上了这条路径,更新的芯片没有公开的同类方法。联发科这边,天玑 1200(对应内部型号 MT6893)及更早、更低端的芯片上,社区工具(如 mtkclient)长期公开覆盖了 BootROM 级别的操作能力,官网与相关 CVE 记录也可查证;更新的芯片没有公开方法。这些都是芯片厂商多年前已经处理过的老问题,属于历史信息。
现在基本没有公开PBL漏洞了。
XBL / Preloader 级
影响通常局限在"XBL 之后的验证被绕过"。
这里联发科的问题比较大。举个例子,在近期,联发科将preloader拆分为了preloader和bl2_ext。但是这似乎带来了混乱,出现了各类权限配置问题,有的时候仅仅是bl2_ext不校验lk,暴露EL1权限而已,有的时候甚至是preloader不校验bl2_ext,相对于暴露了EL3权限。
除了上述的配置失误,近期联发科还同时存在校验失误,也就是特定构造的分区镜像可以绕开校验,实行任意代码执行。相对于可以让preloader加载自定义的bl2_ext(并进一步自定义TEE,GenieZone,lk,或者给LK EL2权限),或者仅让bl2_ext加载自定义lk。这些问题存在于天玑9000之之后的机型上,老版固件基本上都存在,近两年零零散散在修补。
这里也是"拿到原语"和"真正做成一件事"之间距离的一个好例子:
即便拿到了同一个"可以加载未验证 LK"的原语,不同实现者的做法可以完全不同——有的选择用这个原语正确地向 TEE 发起一次解锁请求,把设备状态如实改写成"已解锁",这样后续厂商推送 OTA 更新时不会因为状态不一致而出问题;有的则图省事,直接把 LK 里"我已经验证过了"的标志位强行置位跳过检查,设备能正常跑,但状态记录和实际情况对不上,后续 OTA 很可能直接失败或者把设备刷成砖。这不是漏洞本身的问题,是利用方式的工程选择问题。
ABL / lk 级
ABL 级是近年公开报道最多的一类,典型模式是新增功能引入的验证断层或参数校验不严。厂商行为在这里差异巨大:三星有 ARB(Anti-Rollback,防降级 BL 位),严格限制降级;而小米在未解 BL 时不允许降级,但解了 BL 后就可以降级。
近期在 ABL 层面,有几个典型的漏洞链案例:
- GBL 漏洞:这是 8E5 时期(骁龙 8 Elite / 8850,以及 8 Gen 5 / 8845)引入的特定加载阶段。它恰好落在“芯片厂商私有验证链不再覆盖,而标准 Secure Boot 因出厂配置缺失被跳过”的缝隙里。仅影响这两代芯片,后续芯片在设计时已堵上。此前有传言 888 也有类似问题,但无公开可查证材料。
- 工程 ABL 漏洞链:这是一个经典的组合拳。首先,工程ABL刷入特定修改的分区表(如将 vbmeta 改为 xbmeta,且哈希完整性校验存在问题),导致可以 boot 未官方签名镜像,最终以未完成启动的状态让 EL1 请求 EL3 解锁。为什么强调未启动呢?因我 kernel(EL1)无法成功请求 EL3 解锁,因为 TEE 一旦感知到系统已经正常启动,就会拒绝解锁请求。然后,正好正式版 ABL 某个命令参数忘记校验的漏洞关闭 SELinux(注意:这与早期内存任意写可以关 SELinux 不同,小米的系统服务是受SELinux限制的)。重启后,利用小米某个系统服务的漏洞拿到 root 权限,最后通过
dd刷入工程 ABL。这条链影响了 8 Gen 1 到 8E5 及同期所有高通机型,在2.1的 Android 安全补丁中才被修复。 - Release ABL 漏洞:同样是利用特殊分区表导致 release 版本的 ABL 不进行校验。这个漏洞目前基本没修,仅小米系列中高端机型有公开利用,其他机型多存在于付费渠道。
还有一个维度值得单独说清楚:破坏安全启动链的攻击,按触发方式大致能分成"冷启动"和"热启动"两类,难度天差地别。
冷启动指的是设备真正断电重来,从 PBL 读片上 ROM 开始,一路 XBL、ABL 全部重新执行、逐级重新验证——这条路上不存在任何"上次已经验证过,这次就不用查了"的捷径,每一级都独立地、完整地重新校验它信任的下一级,攻击者没有任何可以偷懒利用的残留状态,因此冷启动路径上的漏洞往往更硬核,也更难找:你得真正让某一级在从零开始的情况下做出错误判断。
热启动(或者说软件触发的重启/warm reset)则不同:一部分 SoC 的设计里,软件发起的重启不会完全清空所有硬件状态,某些子系统会带着上一次运行时的假设继续走,隐含的逻辑是"这部分东西冷启动的时候已经查过了,warm reset 不用重来"——这个假设一旦在某个环节不成立(比如攻击者能在触发热重启之前修改了某块本该被清空但实际没清空的内存或寄存器状态),就可能被利用来跳过原本该有的校验。历史上这类"热启动/warm reset 假设不成立"导致的漏洞在嵌入式安全领域并不罕见,是一个独立于具体某颗芯片的通用漏洞类别。这也是为什么本文前面反复强调"root 权限不等于控制启动时的非安全 EL1":大多数运行时漏洞(不管是内核提权还是别的)本质上都是热启动语境下的问题,重新断电冷启动一次,链条又会从头验证一遍,之前的战果基本清零——这正是冷启动这条路径天然更"硬"的原因。
现在很少有单个漏洞独立起作用,大多是运行时权限问题(如 SELinux 漏洞)与启动链验证缺陷串成的完整链条。 伦理与安全的权衡:启动链漏洞导致手机丢失后难以找回或破解账号锁,但也能让用户自定义系统。然而,像 bad epoll 这种远程可利用漏洞,是真正危害用户数据安全的,必须严格走负责任披露流程。对于用户而言,利用本地启动链漏洞可以不升级系统,但面对远程漏洞,及时跟进修补才是正道(解完BL就可以自定义系统只补安全补丁)。
基本上,国产手机不用考虑更新官方系统,任何时候都是。实际上,一般也是越更新系统越烂。 除非你是打算完全国内生态用到底,除了Pixel,没有任何更新系统必要。
这几年漏洞挖掘和逆向的节奏明显加快了,一个重要原因是 AI 辅助显著加速了这个过程——无论是芯片厂商私有验证逻辑的逆向,还是通用内核里竞态条件的挖掘,都能看到这个趋势。防守方目前普遍还没跟上这个节奏,这大概率是因为防守方体量大、协调成本高,效率天然落后于分散的研究者群体;等防守方也普遍用好 AI 辅助审计和修复流程之后,这种攻防速度不对等的局面应该会变化。
附录:刷机之后,命名与生态碎片化
安卓生态碎片化的来源是厂商开源的积极程度:Linux Kernel 遵循 GPLv2,理论上厂商必须开源魔改后的内核源码,但实践中不少厂商只在设备发布时开源第一版,后续维护版本的源码要么迟迟不放,要么和实际刷机包版本不完全匹配。这直接导致了类原生系统(如 LineageOS)适配困难——移植者拿到的源码和手里设备实跑的固件对不上号,只能靠逆向和试错去补差异。
这其中,Google的Pixel,联想的摩托拉多,和OPPO的一加,是开源较为积极,类原生生态较为丰富的。小米曾经也是。
再谈安全启动
我知道,之前讲的比较粗略,现在我们详细讲讲:
安全启动链本质上是一套所有权界定机制:它决定了一台设备的最终控制权归谁。厂商把这套机制包装成"用户安全",但仔细看它真正防的是谁——它防的不是窃取者,因为窃取者拿到设备后大概率会走深刷这条路直接清空一切,锁不锁 BL 对他们影响有限;它真正约束的是设备的合法持有者本人,防止你自己修改自己买下的这块芯片上跑的代码。这本质上是一种 DRM 逻辑的延伸:厂商通过控制启动链,间接控制了设备之上能跑什么系统、能不能卸载预装软件、能不能拦截厂商自己的遥测和广告。
所以每一次启动链漏洞被公开,本质上是把这块设备的所有制从厂商手里,短暂地移交给了物理接触这台设备的人。这句话听起来像是在给"黑客"辩护,但更准确的说法是:黑客和窃贼只是在野的敌人,而对用户不负责任的厂商才是在朝的敌人。以国内某些厂商为例,云控服务、系统内置的种种问题、对用户数据的处理方式,早已经不是"信任厂商就够了"能解释得过去的。在这种现实下,用户想拿回自己设备的控制权,动机是完全正当的。
比较理想的方案,其实不需要靠漏洞:让用户能够自定义信任根,自己决定信任哪把公钥去验证 XBL 之后的代码。Google给了用户自定义中间信任的权限,不过也没给信任根;桌面端也类似,但是信任根也没给。
有关Pixel刷入石墨烯OS有多安全
Pixel刷入石墨烯os是目前最安全的手机,甚至Google官方介入也无法攻破。这是因为,虽然Google自身可以修改系统固件,或者说主处理器固件,但是独立安全芯片TitanM2的更新时,他会先判断用户是否已经解锁设备,否则不会允许自身固件更新。因此TEE在没有漏洞时非常安全,你的数据无法被解密,即使Google亲自下场。事实就是,顶级情报部门摩萨德也不得不承认无法攻破石墨烯OS,西班牙警方甚至因此怀疑持有Pixel手机的人很可能是毒贩。
这里,在私有制下,有一个需要权衡的地方:启动链上的本地漏洞是一把双刃剑。它给了用户拿回控制权的机会,但代价是设备丢失、被盗之后,账号锁(比如小米账号锁、三星 Find My Mobile 之类的防盗机制)本质上也依赖同一条信任链来保证"不知道账号密码就无法刷掉锁定状态"——启动链上的口子越多,找回机制的保底能力就越弱。这不是一个能简单站队的问题,是"用户自主权"和"防盗底线"两种诉求之间实打实的取舍,本文不替读者做这个选择。但另一类漏洞不存在这种取舍:可远程触发、影响运行时安全(比如通用 Linux 内核里那些可以让任意本地进程提权到 root 的竞态条件类漏洞)才是真正意义上危害用户日常安全的问题,因为它不需要物理接触设备,理论上可以被恶意 App 或者被入侵的相邻进程利用。这类漏洞该走的是标准的负责任披露流程,厂商也应当第一时间推送修复——愿不愿意升级系统是一回事,但涉及远程安全的补丁,该跟的还是要跟。
附录:设备如何解锁并刷入类原生
解锁
官方解锁通常是,进入系统,打开开发者模式,打开OEM解锁,进入fastboot,通过按键或者命令解锁。
想到的一点小补充,后面继续补
- 小米高通方面 近期低版本用低版本漏洞。8Gen2, 8E非凯侠可以9008刷工程ABL然后走低版本漏洞。高版本要用新版解法。845可以强解,855没有利用。
- 联发科 天玑1200及以前走mtkclient,之后的看新漏洞有没有公开方案。
- 三星 OneUI 8的如果bit位没更新,降级7官方解锁。
找包
包的兼容性,以及找包,最推荐看Product Name,通常通过fastboot getvar productname获取,以避开厂商混乱的命名逻辑,productname对一般可以通刷。如果是小差别一般问题不大。
刷入类原生
通常要保证依赖和开发者系统依赖一致,以及注意ARB问题。通常来说,依赖一致指的是厂商私有组件一致(各类vendor头分区。通常启动链各组件兼容性挺高的),如果开发者给得包中有,那这种时候可以不刷底包。
接下来,通常首先fastboot刷入第三方recovery,然后recovery执行双清,清除data和cache,然后进入adb模式,sideload进去。
有的时候也有fastboot直接刷的。
通常没有深刷完整包的。
就砖
本质上就是
- 保证现有完好分区不被破坏
- 修好损坏分区,使其可以工作
参考文献
- 高通 GBL 启动链漏洞工程化利用分析(LibXZR) —— 对已修复的骁龙 ABL/GBL 断层类漏洞的事后分析,本文相关架构结论参考此文,具体机型覆盖范围该文评论区已有读者指出存在出入,请以官方公告为准。
- https://www.cnblogs.com/sakrain/p/19643843
- https://blog.lyc8503.net/post/android-kvm-on-mediatek/
- 对
mtkclient(bkerler)、fenrir(R0rt1z2)等公开项目文档的交叉参考。