KhanLabs/surfsight-ai12-reverse-engineering
GitHub: KhanLabs/surfsight-ai12-reverse-engineering
一份商用车队行车记录仪的深度逆向工程日志,记录了从云端绑定设备到本地自主改造的完整过程,包括启动链分析、EDL 救砖、LTE modem 诊断和固件后门发现。
Stars: 1 | Forks: 0
# 重生 Surfsight / Lytx / GEO AI-12 车队行车记录仪
**从电子垃圾到无云本地记录仪:逆向工程日志**
## 📖 故事背景
我从电子垃圾堆里捡到了一台 Surfsight AI-12(也以 Lytx 或 GEO AI-12 的名义销售)。这是一台商用双摄像头车队远程信息处理行车记录仪,硬件配置极其丰富:搭载 SDM450 SoC、LTE、GNSS、双摄像头以及端侧 AI。问题是什么?它生来就是一个受制于云端的 Corporate spyglass。
我的目标是将其改造为一台自主拥有的、无需云端的本地记录仪,并搞清楚为什么它的 LTE modem 完全罢工了。这份文档就是我的核心逆向工程日志。如果你正试图越狱、获取 Root 权限或复活这类设备,你来对地方了。
📸 **想看看电路板?** SoC、PMIC、RAM 和天线/接口布局的微距照片在 [HARDWARE_PHOTOS.md](HARDWARE_PHOTOS.md) 中。如需完整的十六进制转储级别的笔记(分区映射、死胡同、Sahara/EDL 取证分析),请参阅 [TECHNICAL_DEEP_DIVE.md](TECHNICAL_DEEP_DIVE.md)。
## 证据分类
本文档中的每一项技术声明都带有以下标签之一:
- **[CONFIRMED]**(已确认) — 在我们持有的硬件上直接观察并通过实验复现。
- **[STRONG]**(强有力) — 有多个独立的观察结果支持,但缺乏单一的、决定性的测试。
- **[THEORY]**(理论) — 与证据相符的工作假设,尚未证实。
- **[REJECTED]**(已否决) — 经过测试并被推翻的假设。
- **[UNKNOWN]**(未知) — 明确未予确定;文本说明了已知的信息、未知的信息,以及无法确定的原因。
这些标签仅适用于它们所附加到的句子或行,而不适用于整个章节。
## ⚠️ 电源架构与“隐藏的”电池
如果你从这个仓库里只能记住一件事,那就是这个。**在尝试进入 EDL 时,绝对不要相信标准的 Android 重启命令。**
我花了几个小时撞墙,拼命想让这玩意儿进入 EDL 模式,直到我拆解了它并发现了它的电源架构。该设备拥有**四**个独立的电源域:
1. **5V/2A Pin**:为 SoC 和完整的 Android 启动供电。
2. **USB-C**:*仅*为 EDL/PBL USB ROM 逻辑(9008 枚举)供电。它**不能**为主 Android 处理器供电。
3. **内部 LiPo(约 300 mAh)**:主 PCB 背面的这颗微小的白色 3 针 JST 电池用于维持 IMEM/SMEM 备份不断电。
4. **Supercap**:在外部电源切断时提供约 4 秒钟的电力保持。
**陷阱:** 当你运行 `adb reboot edl` 时,SBL 会向 IMEM 写入一个 EDL cookie 并重启。因为那个内部 LiPo 会让 IMEM 保持供电,所以该 cookie 在拔掉数据线或进行标准重启后依然存在。**为了进行真正的冷启动并清除 EDL cookie,你必须物理断开内部 LiPo 电池。**
*(此外,设备没有 RTC 纽扣电池。每次完全断电时,时钟都会重置为 2009-01-01。稍后我会详细说明为什么这是一场噩梦)。*
## 🛠️ 硬件与软件概览
### 规格 `[CONFIRMED]`
* **SoC**:Qualcomm SDM450 (MSM8953) / PMIC PMI632
* **RAM/存储**:2GB LPDDR3 + **16GB eMMC** `[CONFIRMED]`(Samsung KMQE60013B eMCP,自报为 `QE63BB`/manfid `0x15`=Samsung —— 已通过 `blockdev --getsize64 /dev/block/mmcblk0` = 15,634,268,160 字节在线确认)。录像存放在一张**可拆卸的 microSD 卡上,容量最高 128GB**(通过 `mmcblk1` = 127,865,454,592 字节确认)——零售标签/SKU 上的“128GB”实际上来源于此,而不是内部的 eMMC。
* **操作系统**:Android 9 (Pie),AOSP(无 GMS)。Build `3.9.209`。
* **摄像头**:通过标准 Android Camera2 HAL 提供 2 个逻辑视频源(道路与车内)。应用 (`surfdash2`) 中包含 RTSP/ONVIF 客户端代码,但车内摄像头只是一个标准的 CSI 传感器,而不是 IP 摄像头。
* **Bootloader**:已解锁 (`ro.boot.verifiedbootstate=orange`),但 `fastboot` 是个谎言。运行 `adb reboot bootloader` 只会让你进入一个 Android-kernel UI。这里根本不存在 Fastboot;**EDL 是你唯一的高级底层通道。**
### Root 与 System-as-Root
该设备使用 Legacy System-as-Root (SAR)。System 分区直接挂载为 `/`,并且 kernel 已将 `skip_initramfs` 编译进去。我使用 **Magisk (LEGACYSAR 方法)** 对 Unit 2 进行了 Root。
## 📡 LTE / Modem 探秘黑洞
我的两台设备表现出了完全相同的症状:已插入 SIM 卡,飞行模式已关闭,但 modem 状态为 `OUT_OF_SERVICE`。它会进行无休止的搜索,在任何 RAT(2G/3G/4G)上都找不到半个基站,并且信号死死卡在 `-120 dBm` 的底噪上。
为了弄清楚这到底是软件配置问题还是硬件损坏,我深陷于一个巨大的调查黑洞中。以下是我已经排除的因素:
### 1. Carrier Config / Band Lock `[REJECTED]`
我原以为它可能被锁定到了美国运营商的 Band Mask。并非如此。当前激活的 `mcfg_sw` 是 `ROW_Commercial`(通用全球版)。再说了,Band Mask 也不可能同时屏蔽掉 2G 和 3G。
### 2. 损坏的 NV / Calibration `[REJECTED]`
### 3. RFC 扫描(大型实验) `[STRONG]`
* 我的 NV 1878 被设置为 **87**。
* 我 Dump 了 `modem.b13`,发现固件中只编译了 8 张 RFC 卡:`{75, 87, 157, 191, 218, 229, 232, 249}`。
**实验:** 我写了一个脚本,循环遍历这 8 张卡,将每张卡写入 NV 1878 并重启。
* **结果:** 卡 75、157、191 等均导致 radio 状态保持为 `POWER_OFF`。**RFC 87 是*唯一*允许 radio 开启并进入搜索状态的卡。**
* **结论:** Modem 的配置并没有出错。RFC 87 是这块板子正确的卡,这意味着预期的 RF 硬件在总线(bus)上*确实*是物理存在的。故障纯粹是模拟层面的(RF 前端硅片损坏或天线单元失谐/损坏)。软件诊断到此为止。
## 🕵️♂️ DIAG、FTM 以及为什么你无法嗅探 Radio
我想提取实时的 RxAGC/RSSI 日志,以证明 radio 确实什么都没听到。这引发了我极大的挫败感。
1. **流式日志被阻断 `[CONFIRMED]`**:此 Build 的 kernel 驱动已被剥离了日志记录 IOCTL。Qualcomm 官方的 `diag_mdlog` 会以 `errno: 22` 报错失败。QCSuper 能够连接,但不会产生任何 GSMTAP 帧。不要将“零日志数据包”作为 RF 沉默的证据;捕获管道本身就是坏的。
2. **工厂工具是假的 `[CONFIRMED]`**:设备内置了 `FactoryKit.apk` 和 `fastmmi`。我反编译了它们,本希望能找到工程 RF 菜单。结果“RF_Cal”界面只是读取了表示通过/失败的 NV 标志位。`ftm_test_config` 文件仅包含音频回环测试。此固件中根本没有内置任何实时的蜂窝网络测量路径。
3. **FTM Opcodes 缺失 `[UNKNOWN]`**:FTM(Factory Test Mode)的命令/响应*确实*可以通过 DIAG 正常工作,但用于 RF 测量的实际 payload 布局被死死锁在 Qualcomm 专有的 modem 源码中。胡乱猜测 opcodes 极有可能导致你不小心触发了发射器或擦除了校准数据,所以我到此为止。
**如何与 DIAG 通信:**
原生的 USB diag 接口仅在 FFBM(Factory Boot Mode)下才会绑定。在正常的 Android 启动中,你必须通过 `adb shell` + `su` 来使用 **QCSuper**。
*实用提示:QCSuper 中的 Windows USB 自动检测功能会崩溃或抓取到错误的设备。用 Python 把它 stub 掉,这样它就会回退到 ADB 传输:*
```
from qcsuper.inputs import usb_modem_pyusb_devfinder as devfinder
class _NF: not_found_reason = devfinder.PyusbDevNotFoundReason.auto_criteria_did_not_match
devfinder.PyusbDevInterface.auto_find = staticmethod(lambda: _NF())
```
## 🧱 RIP Unit 1:变砖与 EDL 取证
我在尝试刷入 `aboot` 时把 Unit 1 变砖了。它现在在 Qualcomm EDL 状态(9008 和 900E)之间不断循环。如果你的设备也变砖了,以下是你需要了解的信息:
* **PID 9008 (Firehose)**:USB 能够正常枚举,但 Sahara 完全没有响应。Bulk endpoints 超时。SBL 初始化了 USB,但在 Sahara 处理程序启动之前就挂起了。
* **PID 900E (Memory Debug / Ramdump)**:Sahara 有响应!你可以 Dump 出 2GB 的 DDR ramdump(这就是我重构启动链并证明它在 ABOOT secure-boot 阶段失败的方法)。**然而**,它断然拒绝接受 Firehose programmer loader(在传输哪怕一个字节之前就返回 `0x09 INVALID_IMAGE_TYPE`)。
* **“深度刷机线”技巧无效 `[REJECTED]`**:标准的 Qualcomm 恢复操作涉及将 USB D+ 短接至 GND。我对此进行了测试。因为在这块板子上 USB-C *不为 AP 供电*,PBL 做出启动决策的过程完全独立于 USB 数据线。短接 D+ 除了向主机隐藏该设备之外毫无作用。你需要找到内部 eMMC 的 DAT0 测试点,才能强制进入纯净的 9008 EDL(我还没有找到该触点)。
## 🐛 奇癖、Bug 与警告
### 休眠/唤醒导致录像清空 Bug `[CONFIRMED]`
这是 `surfdash2` 应用与硬件结合产生的一个极其严重的缺陷。
当汽车停放时,设备会进入休眠(由 Bosch BMI160 加速度传感器门控)。当它被唤醒时,它会执行一次**完全重启**,而不是轻量级的挂起。
因为没有 RTC 电池,时钟会重置到 2009 年。当 `surfdash2` 启动并看到错误的日期时,它会重新初始化其 `Movies` 文件夹,并**静默删除 SD 卡上所有未同步的录像**。我眼睁睁看着大约 4GB 的录像在一次唤醒循环后消失得只剩约 400MB。
*变通方案:在进行任何重启/休眠实验之前,请拔出你的 SD 卡,或者通过 busybox httpd 服务器 dump 出你的录像。*
### 最终镜像中内置的出厂 Root 启用后门 `[CONFIRMED]`
在调查一个眼生的中文标签系统应用(“关机界面”/ `fun.qucii.com.quciipowerkey`,一个无害的电源键/关机界面处理程序)时发现。紧挨着它的是 `com.example.logtest` —— 这是一个来自同一家“Qucii”组件供应商的工厂 QC/工程工具,它依然存在于出厂的 `/system/app` 镜像中,并且带有 `sharedUserId="android.uid.system"`(真正的系统级权限,而非应用沙盒)。
它包含三个部分:
* **`RootActivity`**:显示一个“Enable Root”的 `AlertDialog`。点击确认后会调用 `SystemProperties.set("persist.sys.force.root", "1")` —— 这是 Qualcomm 公版设计中用于在 `user` 类型 Build 上授予 Root 权限的标准机制。由于没有设置 `android:exported="false"` 且 `targetSdkVersion=23`,此 Activity 默认处于导出状态 —— 它可以被任何其他应用启动,或者通过 `adb shell am start -a android.intent.action.QUCII_ROOT_ENABLE` 呼起,而不仅仅是从 Launcher 图标启动。
* **`LogTestService`**:一个完整的工程日志记录面板 —— 包含 main/system/radio/event/kmsg/camera/dumpsys/PMIC 日志的开关,外加 AP/modem/USB 调试开关,以及对原生二进制文件 `/system/bin/quciilog` 和 `/vendor/bin/diag_mdlog` 的启动/停止控制。
* **`LogBootCompletedReceiver`**:开机自启动(`RECEIVE_BOOT_COMPLETED`它在 manifest 中声明的唯一权限 —— 没有 `INTERNET` 权限,因此它无法向外发送任何数据)。
从数据窃取的角度来看它不是间谍软件,但它确确实实是一个本地权限提升的攻击面,在消费级固件出货前理应被剥离。它与 KhanLabs 品牌重塑工作中发现并封杀的 `surfdash2` 级后门有着本质区别(那个存在于 OEM 应用本身之中;而这个是更底层的 ODM 工厂工具残留物)。我在 Unit 2 上通过 `pm disable-user com.example.logtest` 禁用了它(可逆操作,未卸载),而没有让它继续存活。
## 🗺️ 分区映射与重要路径
为方便编写脚本或制作备份的人,这里是单槽位布局(`mmcblk0`):
* `modem`=p1, `sbl1`=p4, `tz`=p8, `aboot`=p19, `boot`=p25, `system`=p28, `vendor`=p29, `userdata`=p54。
* **警告**:`/vendor` 受 dm-verity 保护 (`dm-0`)。不要直接对其写入。
* **Modem 固件**:`/vendor/firmware_mnt/image/`(只读 vfat)。
* **Modem EFS**:*只能*通过 DIAG 子系统 19 (`DIAG_SUBSYS_FS`) 访问,无法通过 Android 文件系统访问。
## 🔮 后续步骤 / 你可以如何帮忙
如果你正准备接着我的进度继续 hack,以下是还需要完成的工作:
1. **天线测试**:软件没问题,是 RF 前端坏了/聋了。终极测试是买一根 5 美元的 U.FL LTE 软天线,将其直接插到板子的蜂窝网络端口上,看看是否会出现信号。如果有,说明内部软排线天线坏了。如果没有,说明板子的 RF 芯片烧毁了。
2. **寻找 EDL 测试点**:我需要在 PCB 上找到 eMMC DAT0 触点,以强制让 Unit 1 进入纯净的 9008 状态,从而尝试通过 Firehose 进行恢复。
*如果你成功绘制出了 EDL 测试点,或者找到了 FTM RxAGC opcodes,欢迎提交 Issue 或 PR!*
*最后更新:2026 年 7 月。保持 hack,记住在刷机前一定要断开内部电池!*
标签:Android系统, 嵌入式系统, 无线电通信诊断, 物联网设备, 电子设备维修, 硬件逆向工程, 逆向工具, 高通骁龙