量产车机里的调试后门:从固件逆向到 Root 持久化
这次分析最重要的发现,是量产 Android 车机的 adbd 中保留了一套厂商调试提权入口。它使用设备标识和固件内的固定盐派生凭据;验证通过后,adbd 可以直接以 root 身份重启。
这不是一段无法到达的工程残留。我从固件中还原出校验逻辑后,在自有设备上完成了临时 root 验证。继续检查,又发现 Bootloader 解锁、系统保留调试属性、SELinux 为 Permissive,Verified Boot 也处于非绿色状态。几个原本应该彼此补位的安全层,在这套量产系统里同时留出了缺口。
研究的起点其实只是车内一个普通功能。为了确认 Android 车机到下层控制器之间的通信路径,我开发测试 App、收集日志、反编译原车程序,并最终解包了厂商升级固件。功能协议尚未完全还原,调试后门却先浮了出来,后续工作也随之转向启动链、权限边界和持久化风险。
本文重点记录这套厂商遗留调试入口为何能够成立、它会影响哪些系统边界,以及从临时 root 延伸到持久化时必须面对的 AVB、A/B 和恢复问题。所有结论均区分静态证据、实车验证和仍未验证的推测。
一、脱敏与边界说明
本文涉及量产车辆和厂商遗留调试机制。为避免将一次针对自有设备的研究写成可直接复制的利用链,以下信息均已脱敏:
- 车辆品牌、具体车型和年款
- 车机供应商及内部平台名称
- 固件完整版本号
- 设备序列号、车机 IP 和 ADB 地址
- 原车应用包名、服务名和工程入口
- 调试属性的准确名称
- 固定盐、派生结果及实车口令
- 灯控记录号、VHAL 属性号和二进制偏移
算法只保留到足以说明问题的层面:厂商使用“设备标识 + 固定盐”的 MD5 结果作为调试凭据。盐值和实车派生值不公开。
另外,本文记录的是我自己的车辆和离线固件副本。持久化候选镜像目前只完成离线制作与审计,没有刷入实车,也不能视为已经验证可用的教程。
二、起点:向氛围灯控制器发送自定义指令
目标车辆搭载一套 Android 车机。原车氛围灯支持开关、亮度和少量固定颜色,但没有开放任意 RGB 调节。
最初做了一个普通 Android App 原型,界面包括:
- HSV 色盘与 RGB 数值
- 亮度和开关
- 颜色预设
- 静态、动态和音乐模式
- USB CDC 通信框架
- 独立调试页面
为了避免一开始就误碰车辆总线,初版只使用 Mock Transport,不包含原始 CAN ID、任意 payload 或总线注入功能。
之后又开发了只读诊断 App 和电脑端 ADB 采集脚本,用来收集:
- 系统构建、内核和 SELinux 状态
- 安装包、权限与系统服务
- Binder / HIDL / VHAL 线索
- USB、串口、网络接口和设备节点
- 全局 logcat 与事件时间戳
然后通过实体按键、原车设置页面和测试 App 的操作做差分,希望定位颜色与亮度的控制路径。
三、原车软件确实藏着多色灯控代码
反编译原车车辆设置程序后,发现它并不是只为当前车型编写的单一版本,而是一套覆盖多个车型和硬件配置的通用程序。
其中已经包含:
- 多档颜色选择器
- 亮度控制
- 静态模式
- 动态模式
- 音乐律动模式
- 对应的 Vehicle HAL 属性读写代码
但当前车型的配置只开放基础氛围灯界面,多色和律动功能被车型开关隐藏。
这说明车机软件平台本身具备更完整的灯控协议,但并不能直接证明车上的三色灯控 ECU 支持全部功能。界面被隐藏,可能是产品定位,也可能是硬件能力不同。
实车测试很快验证了这种差异:
- 普通未签名 App 可以通过原车代理接口控制亮度
- 亮度改变后,实灯和原车设置条都会同步更新
- 颜色和模式写入可以返回“成功”
- 但灯光没有变化,真实回复值也没有接受新的颜色
也就是说:
HAL 接受了一次调用,不等于灯控 ECU 执行了这次调用。
部分回复只是 Android 本地立即合成的状态,而不是车辆侧确认。
四、为什么开始解包厂商升级固件
在 App 和在线日志中继续盲试,价值已经很低。
普通第三方 App 还会受到平台签名权限限制,无法直接使用完整的厂商车辆扩展接口。即使当前 SELinux 是 Permissive,Android Framework 仍会按调用者 UID 检查签名权限;Permissive 只会放宽 SELinux 的强制执行,并不会自动绕过 Java/Binder 权限模型。
于是研究转向厂商升级包,希望从离线固件中确认:
- VHAL 到下层通信的映射关系
- 是否存在可复用的系统代理接口
- 灯控记录如何封装
- 是否错误打包了平台密钥或工程凭据
- Boot、System、Vendor 和校验链的结构
升级包恢复出了多个分区镜像,包括:
1 | |
没有找到 Android 平台签名私钥。固件里虽然存在若干 PEM、证书和升级密钥材料,但与平台证书不匹配,主要服务于 ECU 升级或诊断链路,无法拿来给普通 APK 添加系统签名。
这一点符合预期:量产 OTA 通常只包含签名结果、公钥证书和校验信息,不应包含 OEM 平台私钥。
五、还原 VHAL 到 IOC 的灯控链路
固件逆向最终找到了几类灯控逻辑记录,分别对应:
- 亮度
- 静态模式
- 动态模式
- 多色索引
- 音乐律动
Android 侧并不是直接发送原始 CAN 帧,而是写入一段厂商 DataStream。其结构可以抽象为:
1 | |
下层路径大致为:
1 | |
其中 ipc_server 负责厂商帧的 CRC、ACK、序号、重传和流控。因此,从 Android 侧看到的几字节 DataStream 只是上层逻辑消息,不是带 Arbitration ID、DLC 和原始数据段的 CAN 帧。
这也解释了为什么车机上没有 can0 或 can1,无法直接运行 candump。内核虽然带有部分 SocketCAN 协议能力,但没有启用物理 CAN 设备框架;物理总线很可能终止在独立 IOC,而不是 Android 主系统。
原本的灯控研究到这里已经有了比较清楚的方向:使用原厂 IPC 库发送经过白名单限制的灯光逻辑记录,比直接盲写串口安全得多。
但在搜索 IPC、诊断和工程模式代码时,出现了一个完全意外的发现。
六、量产固件里的 OEM adbd 调试后门
反汇编车机中的 adbd 后,发现它保留了一段 OEM Root 校验代码。
逻辑可以概括为:
adbd读取设备唯一标识- 把设备标识与固件内硬编码的固定盐拼接
- 计算 MD5
- 将结果转为小写十六进制字符串
- 从一个厂商私有系统属性中读取外部提交的值
- 两者一致时,允许
adbd以 root 身份重启
算法原理为:
1 | |
这里真正的问题不在 MD5 本身,而在整个信任模型:
- 设备标识可以从系统中读取
- 盐值固定写在量产固件中
- 算法也位于客户端固件
- 没有服务端
challenge,也没有不可导出的硬件密钥参与
这套设计中,真正随设备变化的只有可读取的设备标识。盐值和算法虽然没有出现在用户界面里,却都作为固件的一部分交付。把它们称为“秘密”,本质上依赖的只是分析者暂时没有打开固件。
为什么把它视为后门
它最初可能是工厂测试或售后维护入口,但进入量产固件后,判断标准不再是开发者的原始用途,而是它实际提供了什么能力:一条绕过正常授权流程、能够把 adbd 切换到 root 的隐藏路径。
这条路径没有用户确认、没有在线授权、没有时效性,也没有真正由设备硬件保护的凭据。一旦派生逻辑被还原,它就不再是只掌握在厂商手中的维护能力,而是固件持有者可以重复计算的本地提权入口。
安全影响与利用边界
这不是一个已经证实可从互联网直接触发的远程漏洞。利用仍然需要接触可用的 ADB 或厂商工程通道、获得设备标识,并完成针对固件的分析。本文没有发现开放到公网的入口,也没有证明攻击者可以在完全无接触的情况下取得 root。
但只要前提成立,影响就不只是“多看一些调试日志”。root adbd 可以跨过普通 App 沙箱和签名权限边界,读取受保护数据、控制系统服务、检查厂商 IPC,并为进一步修改运行状态或持久化文件提供条件。对车机而言,这还意味着攻击者能够接近原本只对系统组件开放的车辆服务和诊断链路。
更值得警惕的是可复制性。token 虽然与设备标识绑定,但固定盐和派生算法如果在同平台固件中复用,那么攻破一份固件后,分析方法就可能迁移到同系列设备。逐设备变化的结果,并不能抵消派生机制本身已经公开这一事实。
另一方面,获得车机 root 不等于已经获得任意 CAN 控制权,也不等于能够越过独立 IOC、网关和 ECU 自身的校验。本文把这条路径定义为严重的主机侧提权入口,但不会把它夸大成已经验证的整车远程接管。
我没有在本文中公开准确属性名、盐值、序列号或派生结果。
七、临时 Root 的实车验证
根据反汇编结果计算设备专用 token 后,在已开启的 ADB 会话中写入对应私有属性,再请求 adbd 以 root 模式重启。
重连后验证结果为:
1 | |
这证明了三件事:
- 反汇编得到的派生算法正确
- 该逻辑不是未使用的残留字符串,而是实际可达的控制流
- 当前固件允许通过这条路径获得 root ADB shell
但这仍然只是临时 root:
- 重启后私有属性不会自动恢复
adbd仍按原启动流程运行- 普通 App 的 UID 不会因为 root ADB 存在而自动变成 root
- 也没有通用
su授权管理
如果只为了继续分析灯控,这种 root shell 已经足够进行只读挂钩、日志采集和 IPC 调试;如果希望车机每次冷启动后都保留能力,就需要进入持久化问题。
八、其他安全配置进一步放大了后门风险
获得 root 后,对系统启动和安全状态做了完整检查,结果如下:
| 项目 | 实际状态 | 含义 |
|---|---|---|
| Verified Boot | orange |
设备处于非锁定可信状态 |
| Bootloader 锁 | flash.locked = 0 |
引导加载程序报告为解锁 |
| Android 构建 | ro.debuggable = 1 |
保留调试能力 |
| SELinux | Permissive |
SELinux 拒绝只记录、不强制阻止 |
/vendor |
dm-verity 只读 | 分区内容仍受哈希树保护 |
| 启动槽位 | A/B 结构 | 存在槽位机制,但回退行为未实测 |
这些配置会放大调试后门被利用后的影响:ro.debuggable 保留了更多调试行为,SELinux Permissive 削弱了强制访问控制,Bootloader 解锁则降低了进一步研究启动链和分区修改的门槛。它们单独出现时未必能直接形成完整利用链,同时出现时却让临时 root 更容易向系统深处延伸。
不过,这组状态仍不能简单解释为“随便刷都能启动”。
Bootloader 解锁只说明部分验证错误可能被放行,不等于 OEM 一定接受任意未签名 vbmeta;SELinux Permissive 也不等于 Framework 权限、文件系统校验和启动信任链全部失效。
尤其 /system、/vendor 仍在 AVB/dm-verity 体系中。修改文件内容后,原哈希树和签名描述必然失效。
九、Root 之后能做什么,不能做什么
一旦调试入口把 adbd 切换到 root,操作者面对的就不再是普通第三方 App 的权限边界。Android 沙箱、平台签名权限和受保护文件权限仍然存在,但已经无法约束这个 root shell;它可以直接观察和干预许多原本只允许系统组件访问的对象。
在这台车机上,root 最直接的能力不是让系统突然出现原始 CAN 接口,而是打开厂商软件栈内部原本不可见的数据和服务。
已经确认可以研究的路径包括:
- 挂钩原厂 IPC 库的读写函数
- 记录 VHAL 与 IOC 之间的明文逻辑消息
- 观察
ipc_server对高速串口的收发 - 调用厂商诊断 HIDL 的基础接口
- 构造受控 helper,通过原厂库完成封包和重传
但 root 无法凭空补出不存在的内核驱动:
- 系统没有物理
can0/can1 - 没有通用 CAN/LIN 字符设备
- 内核强制校验模块签名
- 自行编译的未签名 CAN 驱动不能直接加载
此外,实车研究还暴露了一个很重要的安全边界。
曾经尝试用全进程 strace/ptrace 观察 VHAL、IOC 和诊断服务。虽然确实抓到了大量厂商通信,但 ptrace 会短暂停顿实时线程,测试过程中一度触发车道辅助系统故障提示。停止跟踪后告警随即消失,核心服务也没有崩溃。
因此后续明确放弃全进程 ptrace,不对未知记录做回放。更合理的方向是低开销的 uprobe/eBPF,或者使用外接 CAN/LIN 分析仪做纯被动监听。
这次经历也提醒我:
在普通 Linux 上属于“只读观察”的动作,放到实时车辆通信链路中也可能改变系统行为。
十、最初的持久化思路为什么是错的
获得临时 root 后,第一个直觉是修改 boot.img 的 ramdisk,在 early-init 阶段写入调试 token 和 root 开关。
这种思路在许多 Android 设备上成立,但检查实车启动参数后发现:
1 | |
也就是说,这台车机正常启动采用 system-as-root。正常系统的 init 配置来自 system 镜像根目录,而 boot.img 中的 ramdisk 实际是 Recovery 环境。
继续解包 boot 后也验证了这一点:
- ramdisk 中存在 recovery 主程序
- 使用独立的 recovery
adbd - 还包含 IOC 电源管理、最小 IPC 服务和 watchdog
- 它们都属于恢复环境,不能按普通手机模板随意替换
所以:
只改 boot ramdisk,最多可能影响 Recovery,不能证明正常开机后会获得持久 root。
持久化目标必须转到正常启动真正加载的 system init 配置,并同时处理 AVB 校验。
十一、持久化 Root 的设计目标
这里没有选择直接移植 Magisk 或安装通用 su,而是先定义一个更小的目标:
正常冷启动后,当原车按自身配置启动 adbd 时,让 adbd 保持 UID 0;不强制开启 ADB,不改变现有网络方式,也不自动给第三方 App 授权。
这样做的原因是:
- 修改面更小
- 更容易离线审计
- 不需要替换原厂
adbd - 不引入额外的通用提权管理器
- 回滚时只需恢复匹配的系统与校验元数据
原则上的启动流程为:
1 | |
具体属性名、token 和 init 片段在本文中省略。
如果以后要把能力提供给氛围灯 App,更推荐在 root shell 之上增加一个最小权限 Broker:
- 只允许经过签名白名单的 App 调用
- 只提供灯光、日志等固定方法
- 不接受任意 shell 字符串
- 每次操作检查驻车状态和参数范围
- 默认只监听本机 loopback
这比让所有 App 都能执行 su 更适合车机环境。
十二、System 镜像与 AVB 的处理思路
正常启动使用 system 根目录后,候选方案只对一份匹配固件的 system.img 副本做最小修改:在现有 init 配置中增加 early-init 动作。
修改时保持:
- 原文件路径
- UID / GID
- 权限位
- SELinux 标签
- 原有 ADB 开关和启动条件
- Boot、Recovery、内核、IOC 与车辆 MCU 不变
离线构建结果中,2.5 GiB 的 system 镜像只有约 180 字节发生变化,主要来自新增 init 内容及文件长度字段。
但文件系统本身正确,并不代表启动校验会接受它。
顶层 vbmeta 同时描述了 boot、system、vendor 和可信执行环境等分区。system 内容一旦改变,原有 hashtree 和 FEC 就会过期。继续搭配原 enforcing vbmeta,可能导致拒绝挂载、重启或进入损坏处理。
离线候选因此包含一份实验性 vbmeta:
- 保留原分区 descriptor
- 不伪造厂商签名
- 明确标记为未签名实验产物
- 关闭 hashtree 校验,而不是假装原签名仍然有效
这条路线的代价也很明确:关闭的不只是 system 哈希树,vendor 的完整性保护也可能一并降低。即使 Bootloader 报告为解锁,也仍需实车确认 OEM 启动器是否接受这种组合。
因此这份候选只能叫:
1 | |
不能叫“可刷镜像”。
十三、离线候选做了哪些验证
为避免“脚本跑完就算成功”,候选镜像做了多层检查:
- 固定校验原始 system 与 vbmeta 的 SHA-256
- 只在镜像副本中工作,不覆盖原件
- 修改前后执行只读文件系统检查
- 从候选镜像重新读回 init 文件
- 核对权限、属主和 SELinux 标签
- 对完整 2.5 GiB 镜像逐字节比较
- 确认变化只落在预期配置块、inode 和长度字段
- 独立解析新 vbmeta 的结构
- 保存输入、输出散列和修改块清单
最终确认:
- 文件系统检查通过
- 候选内容读回一致
- 原件没有被修改
- system 实际变化约 180 字节
- Boot 和可信执行环境镜像未改动
这些检查只能证明“候选按照设计生成”,不能证明 OEM Bootloader 会接受,也不能证明实车一定能启动。
十四、为什么目前仍然不能刷
这台车机没有可供用户直接操作的电源键和音量键,不能照搬手机常见的按键救砖流程。
目前从 Bootloader 固件中找到了这些静态线索:
- USB Fastboot
- TCP Fastboot
- A/B 槽位重试与回退
- IOC 参与的启动目标选择
- 验证错误放行分支
- 某些加载失败后进入 Fastboot 的路径
这些线索增强了方案的可行性,但都没有达到“救援入口已经验证”的强度。
仍然缺少的实车证据包括:
- Android 无法启动时,外部 USB Fastboot 是否仍可达
- 具体哪个物理 USB 口连接到预启动控制器
- 无实体按键时如何独立进入和退出恢复模式
- 备用槽是否真的可启动
- 自动回退的计数和触发条件
- OEM 是否接受实验性未签名 vbmeta
- 如何从真实分区恢复原始 system 与 vbmeta
adb reboot bootloader 依赖一个仍然活着的 Android 系统,它不能证明设备变砖后仍有独立恢复入口。
原厂升级包存在,也不能证明插入 U 盘就能在无法启动时自动恢复。升级入口、包验签、目标槽位、回滚限制和掉电恢复都需要单独确认。
所以目前明确不提供直接刷写命令,也没有在实车上执行写分区操作。
十五、真正可接受的部署顺序
如果以后继续实车验证,顺序必须是:
1. 只读盘点
确认实际版本、启动参数、当前槽位、分区表、Verified Boot 状态和 Bootloader 策略。候选必须与实车当前固件完全匹配。
2. 真实分区备份
在临时 root 下只读备份 boot、system、vendor、vbmeta、可信执行环境和启动元数据,并核对设备端与主机端的长度和散列。
OTA 原件只能作为参考,不能代替当前设备的逐分区备份。
3. 独立救援验收
必须先证明存在不依赖 Android 的恢复入口,包括 USB 枚举、Fastboot 可达性、进入/退出方法和原始镜像恢复能力。
4. 槽位与回滚验证
读取两套槽位的实际状态,不猜测备用槽,也不把“有 A/B 分区”自动理解为“必然自动回退”。
5. 受控部署
只有前四项都有证据,才制定精确的写入和读回流程。system 与 vbmeta 必须作为匹配组合处理,保留一个已经验证可启动的原始路径。
6. 冷启动与车辆功能验收
验证完全断电后的再次启动、ADB 身份、原车设置、灯控、休眠唤醒、倒车显示和车辆通信。仅重启 adbd 不算持久化成功。
十六、最终希望形成的灯控架构
如果持久 root 和恢复链最终通过验证,最合理的灯控实现不是开放一个万能 root,而是建立受限链路:
1 | |
Broker 只暴露“设置亮度、设置受支持模式、读取状态”等固定接口,不提供任意命令执行。所有写操作都限制在:
- 车辆驻车
- 参数白名单
- 单次发送
- 可恢复默认值
- 完整操作日志
这样做仍然是在利用原车已有通信栈,由 ipc_server 处理 CRC、ACK、序号和重传,而不是绕过它直接向高速串口盲写。
至于当前三色灯控 ECU 是否真的接受任意 RGB,仍然需要继续从 IOC 固件或外接 CAN/LIN 被动采集中确认。Root 能帮助观察链路,但不能替代对真实硬件协议的验证。
十七、从调试后门得到的安全结论
1. 固定盐不是密钥
盐值和算法只要存在于可分发固件中,就应假设它们最终会被读取。把设备标识混入计算可以让结果逐设备变化,却不能把一套离线可复现的派生函数变成可靠认证。
2. 维护入口进入量产固件后就是攻击面
无论它最初用于产线、售后还是研发,只要它能绕过正常授权并取得 root,就必须按后门能力评估。隐藏属性名、删除界面或不写文档,只会降低入口的可见度,不会缩小它成功触发后的权限。
3. 利用需要前提,不代表风险可以忽略
当前证据支持的是可接触 ADB 或工程通道后的本地提权,不是无条件远程攻击。但车机可能进入维修、二手流转、第三方改装和非可信 USB 环境;一条稳定的 root 路径会把这些接触场景从普通调试风险提升为完整主机失陷。
4. 多层宽松配置会产生叠加效应
SELinux Permissive、可调试构建、Bootloader 解锁和非绿色 Verified Boot 各自都有不同边界。它们与 root adbd 同时存在时,攻击者从提权、观察系统到研究持久化所遇到的阻力会显著降低。
5. Root 车机不等于接管整车
独立 IOC、网关、总线协议和 ECU 校验仍然构成边界。没有证据时,不能把车机 root 直接描述成任意 CAN 注入或远程控车。但 root 已经足以暴露车辆服务、诊断接口和厂商 IPC,安全影响不能按普通娱乐终端看待。
6. 持久化必须针对真实启动路径
看到 boot.img 里有 ramdisk,不代表正常启动会使用它。skip_initramfs + system-as-root 推翻了最初的 boot 补丁方案,也说明静态文件结构不能替代真实启动链证据。
7. 防砖能力比持久化本身更重要
没有独立恢复入口、真实备份和回滚证据,就算离线镜像已经通过检查,也不应该刷入没有实体恢复按键的车机。A/B 只是机制,不是已经得到验证的保险。
8. 只读调试也可能影响车辆功能
ptrace 导致的短暂停顿足以触发车辆功能告警。对实时车辆服务做研究时,“没有写入数据”不等于“没有改变系统行为”。
十八、总结
这次分析确认了一条存在于量产固件中的厂商调试后门:adbd 根据设备标识和固件内固定盐计算 token,验证通过后可以切换到 root。它没有服务端 challenge,没有不可导出的设备密钥,也没有一次性或时效性约束。所谓设备专用凭据,只是使用同一套公开于固件中的规则,为每台设备算出不同结果。
它的直接安全影响,是让能够接触 ADB 或工程通道的人绕过 Android 应用沙箱和正常授权流程,取得完整主机权限。root shell 可以读取受保护数据、控制系统服务、检查或调用厂商接口,并接近车辆服务与诊断通信链路。即使它尚未被证明能够远程触发,也足以让维修接触、二手流转、改装设备或非可信 USB 环境变成需要认真考虑的攻击场景。
更大的问题在于风险并不局限于一台设备。只要同平台继续复用固定盐和派生算法,针对一份固件完成的逆向工作就可能迁移到同系列设备。token 的值逐设备变化,但攻击方法可以批量复用;这会把一个隐藏维护入口变成平台级问题。
Bootloader 解锁、系统保留调试属性、SELinux Permissive 和非绿色 Verified Boot 又进一步放大了后门的价值。它们没有让所有安全机制彻底消失,AVB/dm-verity 仍然阻止直接修改受保护分区,独立 IOC 和网关也仍是车机之外的边界;但在攻击者已经取得 root 的前提下,这些宽松配置显著降低了继续分析系统、干预运行状态和尝试持久化的成本。
目前构造的持久化候选只完成了离线验证,不能直接刷写。它证明了利用 system-as-root 的 early-init 阶段恢复调试能力在技术上存在可行路径,同时也说明持久化会把一次临时提权升级为每次启动都存在的高权限入口。没有独立恢复、真实备份、A/B 回退和 vbmeta 接受策略的实机证据,这一步不应继续。
从防护角度看,最直接的修复不是更换哈希算法或把盐藏得更深,而是从量产版本中移除 root 调试分支。确有维护需求时,应使用带 challenge 的认证、不可导出的设备密钥、短时授权、显式用户或维修确认、操作审计,以及能够被撤销的权限设计。
这不是一个“知道口令就能方便调试”的小功能,而是一条能够改变整台车机信任边界的遗留入口。远程可达性目前没有证据,但本地提权和完整主机权限已经在实车上验证成立。仅这一点,就足以把它当作需要修复的安全问题。