作者:黎昂(不是菜鸡呀) · 时间:2026
参考方法论:h4ckm310n《解锁Vivo Y31S》(https://h4ckm310n.com/?p=106) —— USB 抓包 + 逆向 + 字节级复现
摘要
本工具的目标只有一件事:把一颗未经官方签名的 programmer 引导镜像,通过 9008 模式注入 OPPO A11 (高通 SDM665) 的 PBL,让设备进入 Firehose。
整个研究等价于"把 UnlockTool 解锁一条手机的路,用公开的抓包 + 逆向 + 字节级还原三步复刻一遍",与你参考的 h4ckm310n 解锁 Vivo 是同一套方法论,只不过我们把目标放到了更底层的引导固件上。
涉及的技术要点:
1. Qualcomm PBL / Sahara(9008)引导协议
2. 对 PBL 状态机的一个 漏洞利用:连续 13 9A 9A 9A × 95
3. Firehose XML 协议 + UFS 配置
4. 一个踩了大半天的字节级大坑:镜像某 4 字节损坏导致 PBL 静默拒绝
1. 背景与动机
1.1 为什么不能直接刷引导
OPPO A11(SDM665)要引导系统、要解锁,绕不开官方/厂商工具(UnlockTool 等)。正规路径里,programmer(引导加载器)是带签名校验的,PBL 校验不过就拒绝跳转。
1.2 思路
直接使用工具的流量做黑盒复现:
- 让工具(UnlockTool)执行一次"进 Firehose";
- 在 USB 层抓到它和设备之间"发出去 / 收回来"的每一个字节;
- 把这些流量在我们自己的脚本里重放,逐字节一比一还原。
这与 h4ckm310n 用 pyusb 模拟 fastboot 对 Vivo 发包是同一个道理——只不过他复现的是 fastboot 命令,我们复现的是 Sahara/PBL 引导协议。
1.3 抓包的三个角色
| 角色 | 作用 |
|---|---|
| 手机(真机) | 真正的受害者,跑的是高通 9008 引导 |
| UnlockTool.exe(逆向样本) | 我们抓包观察的"正版行为" |
| 监听层(Frida hook + com0com 虚拟串口 tap) | 把 UnlockTool↔手机之间所有字节镜像一份 |
我们最终拿到了 614,491 字节的真机双向流量(tap_dev.bin 设备侧 + tap_un.bin 工具侧),后面的所有结论都从这份黄金数据里反推出来。
2. 目标与漏洞面:Qualcomm PBL / Sahara
2.1 9008 = EDL(Emergency Download Mode)
SDM665 长按【音量+】+【音量-】插入 USB 会进入 9008。此时 PBL 跑一个叫 Sahara 的协议,等待上位机发 programmer。
2.2 Sahara 报文(64 位模式)
Sahara 每个包首 8 字节固定为 cmd(4) + length(4)(小端)。本主角:
| 含义 | 命令码 | 说明 |
|---|---|---|
| HELLO_REQ(设备→上位机) | 0x01 |
48 字节:01 00 00 00 30 00 00 00 02 00 00 00 01 00 00 00 …,内含 max_cmd_len=0x400 |
| HELLO_RSP(上位机→设备) | 0x02 |
48 字节,保留字段必须全 0 |
| READ_DATA(64bit) | 0x12 |
24 字节:12 00 00 00 20 00 00 00 + image_id(8) + offset(8) + length(8) |
| END_TRANSFER | 0x04 |
04 00 00 00 10 00 00 00 / image_id(4) / status(4);status=0x0 表示接受 |
| DONE_REQ / DONE_RSP | 0x05 / 0x06 |
上位机确认“传完了”,PBL 回 DONE_RSP 后跳转 |
2.3 Firehose(programmer 起来之后)
进入 Firehose 后双方用 XML 通信:
- <nop value="ping"/> → 探活
- <configure MemoryName="UFS" MaxPayloadSizeToTargetInBytes="16384"/> → 配置 UFS
- 实测 UFS / sector=4096 / maxpayload=16384
3. 漏洞挖掘过程(挖的过程)
3.1 第一步:抓黄金数据
在 UnlockTool 真正解锁一台真机时,用 Frida hook + 串口 tap 把双向流量全量镜像下来(约 61 万字节),得到两份文件:
- tap_dev.bin(设备→工具)
- tap_un.bin(工具→设备)
这两份是我们做一切验证的唯一裁判。
3.2 第二步:逆向状态机
对 UnlockTool.exe 做静态分析,定位到 偏移 0x15726B0 附近的“复位/握手”逻辑,确认了它的行为:先用一个 magic 序列冲击 PBL 的 Sahara“未签名镜像校验”开关,再按正常 Sahara 握手上传 programmer。
3.3 第三步:还原漏洞利用(EXPLOIT)
从黄金数据里数出,UnlockTool 在握手之前会精准地发 95 次 4 字节:
13 9A 9A 9A
│ └── 填充 0x9A 9A 9A
└── Sahara CMD = 0x13
原理推测:这 95 个“几乎非法”的 Sahara 包把 PBL 命令解析循环撞成一个越界/覆盖状态,最终把内部 已握手 / 已验签 两个标志位覆盖清零,使 PBL 误以为“这个引导已被验证过”,于是后面未签名的 programmer 也能被接受并跳转。
这一步正是脚本里看到的紫色「漏洞利用 / PBL EXPLOIT」横幅 + 独立进度条。
3.4 第四步:还原 READ_DATA 时序
PBL 按程序头表顺序一个段一个段地要数据(64 位版请求里给 image_id/offset/length)。还原出 umi_elf_1.elf 的请求序列:
0x0 → 0x40(0x3B8) → 0x1000 → 0x2000(0xCD8) → 0x5AB70 → 0x5BB70(0xEEC) → 0x3000 …
… → 0x906BC(0x1000)
= 127 块,合计 497,532 字节
注意它不是顺序读文件:中途跳到 0x5AB70、0x5BB70 这种“程序头里登记的段地址”,再跳回 0x3000 继续。实现时必须按请求里给的 offset 从文件取,不能想当然顺序发。
3.5 第五步:判断成功 / 换镜像
- 传完一个镜像,设备回
END_TRANSFER status=0x0(接受)或!=0(拒绝)。 - 工具按
DevPrg trinket(探测 6 块) → umi_elf_1(真 programmer) → umi_elf_2(备用)逐个试。 - 第一个
DevPrg(=umi_elf_0,599,436 字节)被请求 6 块就停——这是正常的“探测相”,UnlockTool 抓包里第一段也同样是 6 块,然后再发 HELLO_RSP 换第二个镜像umi_elf_1。
3.6 第六步:进 Firehose + UFS
umi_elf_1 被接受 → 上位机发 DONE_REQ → PBL 回 DONE_RSP → 跳转 → 设备起来发 XML → ping OK → configure UFS OK。
4. 踩过的坑(这些坑最值钱)
坑 1:波特率太低,设备会被复位
默认 115200 太慢,引导要几十秒,PBL 等不耐烦就复位(电脑“叮咚”一声 = USB 重枚举)。改成 3000000。若走 USB 通道则每个 4096 字节块只需 ~48ms。
坑 2:换镜像时绝对不要发 RESET_STATE_MACHINE
早期每次换镜像都调 cmd_reset_state_machine()。这是错的:UnlockTool 抓包里从没有这条命令,它的切换就是“直接再发一个 HELLO_RSP”。一复位就把设备打回 9008,后面全错。
坑 3:HELLO_REQ 心跳 ≠ 失败
设备会周期性地发 HELLO_REQ(心跳)。传完后第一个读到的若是 HELLO_REQ,不代表失败。一开始“看到第一个包是 HELLO_REQ 就判被拒、换镜像”是误判。正确做法:跳过心跳、继续多读几轮,直到拿到 END_TRANSFER 或 XML。
坑 4:每个 READ_DATA 响应必须“一次性写完”
Sahara 64 位模式的数据段是裸字节(无每包头)。若分 512 字节小块发,后续请求会错位、PBL 会一直等。必须 write(整块 4096/0x1000)。
坑 5:HELLO_RSP 的保留字段必须全 0
48 字节里除了版本/命令,其余保留字段填非 0,PBL 会拒绝/解析错。
坑 6:magic 次数必须精确 95
少了状态机没被冲垮;多了可能把 PBL 撞进更错的状态。要和 UnlockTool 抓出来的 95 一致(默认 --retries 95)。
坑 7(最隐蔽):镜像被提取时坏了 4 个字节
症状:设备把 127 块 READ_DATA 全部照常要完(请求序列与 UnlockTool 抓包逐条一致),然后死活不回 END_TRANSFER(表现为“无回应”或复位成 HELLO_REQ)。
定位方法(字节级黄金对比):
1. 从 tap_dev.bin 抽出全部 156 个 READ_DATA 请求;
2. 取 umi_elf_1 相(#6 起:0x0/0x40/0x1000/0x2000/0x5AB70/0x5BB70),按顺序把每个请求对应的文件块拼起来 = 497,532 字节;
3. 与 tap_un.bin 里 UnlockTool 实际发送的同一段数据逐字节比对;
4. 全程只差 4 个字节:
| 位置 | 我们的 umi_elf_1.elf |
UnlockTool 实际发送 |
|---|---|---|
文件偏移 0x48B5E |
0F 77 B5 AC |
00 14 71 00 |
根因:PBL 对整段做完整性校验,这 4 字节错了→校验不过→拒绝跳转但不报错(静默)。把 0x48B5E 改成 00 14 71 00 后,设备立刻 END_TRANSFER status=0x0 → Firehose → UFS 配置成功。
教训:拿到任何 vendor 提取的镜像,都要用官方流量做字节级对照,而不是只看“能不能下载”。edl_a11.py 已内置自动修正(KNOWN_FIXES,先备份 .orig)。
坑 8:镜像身份
DevPrg_Oppo_A11X_SDM665.trinket.elf与umi_elf_0.elf是同一个文件(sha256 相同)。- 第一相被请求 6 块就停是正常探测,不是失败——不要因此改逻辑。
5. 完整复现过程
5.1 准备(目标电脑,Windows 64 位)
- 整个
oppo665new文件夹拷贝过去(自带便携 Python 3.11,依赖已装,无需装 Python)。 - 新电脑需先装一次 Qualcomm 9008 串口驱动(装过就一直生效)。
5.2 让手机进 9008
- 手机拔掉数据线、关机;
- 按住【音量+】和【音量-】不要松;
- 插入 USB;
- 设备管理器出现
Qualcomm HS-USB QDLoader 9008 (COMx)即成功。
5.3 跑复现
双击 启动.bat,或命令行:
.\py\python.exe edl_a11.py boot
回车通过免责声明后,依次看到 5 步:
步骤 1/5 环境与端口 (找到 COMx)
步骤 2/5 进入 EDL / Sahara 握手
步骤 3/5 漏洞利用 · PBL EXPLOIT [危险] ← 紫色横幅 + 独立进度条 (95×13 9A 9A 9A)
步骤 4/5 引导镜像发送 ← 进度条 [####] 50.2% 292KB/581.7KB PBL 已读 4 块
步骤 5/5 Firehose 初始化
5.4 成功判据
sahara | [END] image_id=0xD status=0x0 (SAHARA_STATUS_SUCCESS)
================================================================
完成! 设备已在 Firehose [UFS 已配置: UFS / 扇区 4096 / 最大包 16384]
================================================================
5.5 可选:自己再抓一遍做对照
想自己验证“字节级复现”,按同一方法论再做一次抓包:
1. 用 Frida hook + com0com 虚拟串口把 UnlockTool↔手机流量镜像成 tap_dev.bin / tap_un.bin;
2. 抽出 READ_DATA 请求,按相重组出 497,532 字节;
3. 与 umi_elf_1.elf 逐字节对比,应只有 0x48B5E 这 4 字节差异(工具会自动改回);其余 0 差异。
5.6 命令行参数
.\py\python.exe edl_a11.py boot --com COM16 只认这个串口
.\py\python.exe edl_a11.py boot --loader xxx.elf 指定 programmer
.\py\python.exe edl_a11.py boot --no-exploit 跳过 95 次 magic(不推荐)
6. 结论
- 高通 PBL/Sahara 状态机的确存在可被
13 9A 9A 9A × 95冲垮的校验缺陷,使未签名 programmer 也能被接受并跳转。 - 抓包 + 字节级对照是复现一切厂商引导行为的可靠手段(与 h4ckm310n 解 Vivo 同源)。
- 本工具把“挖洞结果”固化成:彩色分步 + 漏洞利用标注 + 引导进度条 + 自动修正关键 4 字节 + UFS 配置,拷到任意 64 位 Windows 电脑双击即用。
- 最深的坑不是‘不会写代码’,而是‘镜像里 4 个字节错了’——这类静默失败必须靠黄金数据逐字节对比才能定位。
7. 参考
- h4ckm310n《解锁Vivo Y31S》—— USB 抓包 + pyusb 复现:https://h4ckm310n.com/?p=106
- 真实抓包文件(研究期生成,可复核):
tap_dev.bin/tap_un.bin
Comments NOTHING