Lithiumee

Lithiumee

Go wild

雲溪盒 3.0 Pro 破解:從受限到獲取根權限

背景#

image
手裡有台閒置多年的雲犀 Box 3.0 Pro 觸屏導播一體機,這台設備支持雙路 1080P  HDMI 輸入、內置抖音、淘寶、快手直播及 RTMP 推流直播功能。但我們之前的使用場景比較特殊,有些平台以前沒法方便地進行 RTMP 推流,更多還是採用電腦連接多張 USB 采集卡和 OBS Studio 虛擬攝像頭的方案,因此這個設備閒置多年無人問津。

最近,我正好在開發類似定位的硬體產品,於是開始重新審視這台設備。一方面,希望學習和了解其軟硬體方案;另一方面,也期望通過軟體改造,讓其獨特的硬體能在其他場景中發揮作用。

硬體#

擰下藏在背面防滑墊下的螺絲,就能輕鬆打開後蓋。主板的設計相對簡潔,元器件只佈局在一面,並且採用了核心模組 + 底板的形式。

對於這種 PCB 空間充裕的項目來說,使用核心板或核心模組確實有很多優勢,一方面高頻的 RF 部分、高速的 DDR 記憶體、存儲等最複雜的設計都集成在核心模組上,大大減少了硬體設計和調試的工作量,有助於產品更快推向市場;另一方面底板可以採用更低的層數和更簡單的工藝,將高多層板限制在最小面積,有效控制成本。

從拆解圖中可以看到雲犀 Box 3.0 Pro 的主要芯片方案:

  • 核心模組:移遠 (Quectel) SC60 模組,搭載高通驍龍 625 (MSM8953) SoC,2GB RAM 和 16GB eMMC。
  • 以太網:亞信 (ASIX) AX88179 USB 3.0 to Gigabit Ethernet
  • MIPI-DSI to HDMI Tx 桥接:龍訊 (Lontium) LT8912B
  • HDMI Rx to MIPI-CSI 桥接:東芝 (Toshiba) TC358840XBG *2
  • 電源管理:德州儀器 (TI) BQ25890
  • USB HUB:創惟 (Genesys Logic) GL3523
    IMG_20250330_142827
    IMG_20250330_144149
    起初,我猜測它採用了最常見的瑞芯微 RK3399 方案,但拆機後才發現是高通方案。仔細想想,考慮到直播設備可能需要通過移動網路進行推流,具有基帶優勢的高通平台就有了很大的吸引力,選擇高通方案也就合情合理了。
    image 1
    對於其核心功能 HDMI 采集,雲犀 Box 使用了兩顆東芝 TC358840XBG 桥接芯片來實現兩路信號采集。TC358840XBG 雖然經典,但也有些局限,它需要 Dual Link MIPI-CSI-2 一共 8 Lane 才能實現 4K 30fps 的視頻采集(兩個 Link 分別采集 4K 畫面的左右兩半),像雲犀 Box 這樣使用 Single Link CSI-2 則只能支持到 1080p 采集。相比之下,現在更新的芯片,例如龍訊的 LT6911UXE,其 MIPI 每條 Lane 的最高速率可達 2.5Gbps,僅用一個 4-Lane 的 MIPI 端口就能支持 4K 60fps 采集,若要進行 4K 采集方案會簡潔不少。不過 TC358840XBG 有開源驅動可用,對個人開發者友好,而 LT6911UXE 的公開資料目前非常稀少。
    image 2
    在硬體設計上,可以感受到 MSM8953 的外設限制給設計帶來了一些挑戰。由於 MSM8953 沒有內置以太網 MAC,也沒有 PCIe 接口,要實現有線網路接入,基本只能依賴 USB 網卡。然而,MSM8953 只有一個 USB 3.0 接口。這就帶來了一系列連鎖反應:一方面,為了同時支持一個 USB 網卡和一個外部 USB 3.0 Type-A 接口,必須引入 USB HUB 芯片 (GL3523)。另一方面,主要作 Device 用的 Type-C 接口和作 Host 用的 USB HUB 需要使用 USB 開關芯片進行信號物理連接的切換,對應主板上的 PI3USB302 和 TS3USB221A。這意味著一旦系統切換到 Host 模式(連接網卡和 Type-A 口),Type-C 接口就完全被斷開了,依賴這個接口的 ADB、Fastboot 等功能也就無法使用了。
    image 3

軟體#

從設備界面可以看出雲犀 Box 運行的是安卓系統。相比於純 Linux 方案,採用安卓系統能利用其對硬體的高度抽象簡化開發,並且可以借助基於 Java 的成熟應用開發生態大幅提升應用開發效率。

不過這個安卓系統被深度限制了,只能運行官方的雲犀 APP,無法直接啟動任意應用,沒有地方可以打開 ADB,下拉通知欄被鎖定,系統設置等標準安卓界面也都被隱藏了。看來想通過簡單的系統界面操作來解除限制並不可行。
IMG_20250406_150942_1
於是制定了下一步計劃:利用高通平台的 9008 緊急下載模式 (EDL) 來提取設備 eMMC 存儲中的 system 分區鏡像,然後掛載鏡像進行修改,替換內置 App,最後重新燒錄修改後的鏡像。

嘗試後發現,主板上預留的按鍵 S2 正好連接到 MSM8953 的 force_boot_from_usb 引腳 (GPIO37)。在上電時按住此鍵,設備就能進入 9008 模式。利用 Qualcomm Premium Tool 工具導出 system 分區鏡像,之後就可以在 PC 上自由地掛載和修改這個鏡像了。
image 4
由於雲犀 APP 可以啟動抖音、快手、淘寶直播這幾個特定的第三方應用,我便將淘寶直播對應的 APK 文件替換成了修改了包名的 QuickShortcutMaker APK,進行偷梁換柱。將修改後的 system.img 通過 9008 模式刷回設備,重啟後看到桌面上原 “淘寶直播” 的圖標都變成了 QuickShortcutMaker 的圖標。打開 QuickShortcutMaker,就可以自由地啟動系統內安裝的任何應用和系統組件了,包括被隱藏的安卓原生設置。
為了獲得完整的系統控制權,Root 必不可少。使用 Magisk 對設備的 boot 分區鏡像進行修補,為系統加入 su 命令,刷入後這個設備就順利獲得了 Root 權限。
IMG_20250406_163334
之前擔心的 USB 問題還是出現了。雖然在設置中成功開啟了 USB 調試選項,但將 Type-C 口連接到電腦後仍然不能看到 ADB 設備。說明此時 USB 控制器正處於 Host 模式,用於內置的 USB 網卡和 Type-A 口。

但是想要自己強制切換模式就比較棘手了,一條可能可行但未經驗證的路徑是一方面在硬體上,割斷 USB 切換芯片的切換控制信號線,使得 USB 信號物理上始終通向 Type-C 接口;另一方面在軟體上通過將 /sys/kernel/debug/msm_otg/mode 設置為 peripheral ,使 USB 控制器工作在 Device 模式。

不過偶然發現按下主板上另一個預留按鍵 S3 後,USB 調試就意外地出現了,應該是廠商預留的調試手段。這下省去了我們自己進行複雜軟硬體修改的麻煩。
image 5
後來為了梳理切換 USB 模式的過程,從 boot.img 中提取並反編譯了設備樹。在設備樹的 usb_detect 節點中,發現 qcom,gpio-usbdetect 相比 Example ,還額外定義了一個 key-gpio,其 GPIO 編號與 vol_up 鍵對應,板上的 S3 按鍵正是對應此 vol_up 。因此廠商應該是修改了內核中的 gpio-usbdetect 驅動,使得按下 S3 按鍵也能觸發中斷,並執行 USB 開關切換以及 USB Role (Host/Peripheral) 的切換邏輯。

vol_up {
	label = "volume_up";
	gpios = <0xbe 0x55 0x01>;
	linux,input-type = <0x01>;
	linux,code = <0x73>;
	debounce-interval = <0x0f>;
};

usb_detect {
	compatible = "qcom,gpio-usbdetect";
	interrupt-parent = <0x11e>;
	interrupts = <0x00 0xc6 0x00>;
	interrupt-names = "vbus_det_irq";
	key-gpio = <0xbe 0x55 0x01>;
	usb-switch-gpio = <0xbe 0x1c 0x00>;
};

通過觀察內核日誌 (dmesg),可以看到按下 S3 前後 usb_role_switch 的相關 log:

[ 1089.480345] [yunxi_bq25890]: vbus_detect_delay_work: key_level: 0
[ 1089.480374] [yunxi_bq25890]: usb_role_switch , is_host: 1

[ 1329.977896] --== bq25890_irq_handler
[ 1339.940379] [yunxi_bq25890]: vbus_detect_delay_work: key_level: 0
[ 1339.940410] [yunxi_bq25890]: usb_role_switch , is_host: 0

HDMI 采集#

HDMI 采集是這個設備最核心且獨特的功能了。我的目標是能夠繞過限制重重的官方雲犀 APP,直接利用這一硬體能力。

TC358843XBG 芯片通過 MIPI CSI-2 接口輸出視頻數據,這與一般 Sensor 接入 SoC 的方式一致。不同之處在於,TC358843XBG 直接輸出 YUV 或 RGB 格式的數據,而非攝像頭常見的 Bayer RAW 格式。

安裝開源的 Open Camera 應用後,就可以直接識別並預覽到兩路 HDMI 輸入的畫面,兩路 HDMI 輸入即為兩個攝像頭。這樣一來通過 Open Camera 便直接可以進行 HDMI 輸入的實時監看和視頻錄製。再次讓人感嘆還得是安卓,良好的硬體抽象帶來了強大的兼容性,讓很多功能無需為每個特定平台重複開發,並且在龐大生態加持下,可以直接利用現成的甚至是開源的海量應用。

通過查看 Open Camera 的調試信息,並結合對雲犀官方 APP 的反編譯分析,還可以確認雲犀 Box 使用的是較舊的 android.hardware.Camera API (Camera1 API),並不支持功能更強大、更靈活的 Camera2 API。

image 6

view

其他#

啟動流程#

由於在分區備份寫入過程中出過一些差錯,我在排查故障時對 MSM8953 平台的啟動流程進行了大致的了解:

  1. 上電與 Boot ROM (PBL):
    1. 按下電源鍵後,處理器從內部 ROM (Boot ROM) 的預定義地址開始執行,運行主引導加載程序 (PBL - Primary Boot Loader)。
    2. PBL 讀取 eMMC 等啟動介質上的分區表 (GPT),找到 sbl1 (Secondary Boot Loader 1) 或備份 sbl1bak 分區,將其加載到片上內存 (IMEM) 並跳轉執行。
  2. SBL1:
    1. SBL1 首先初始化 DDR 記憶體,然後加載並驗證後續啟動階段所需的組件:
      • tz.mbn: TrustZone 相關組件 (QSEE, HYP, TEE 等)。
      • devcfg.mbn: 設備配置數據。
      • rpm.mbn: 資源功耗管理器 (RPM) 固件,加載到 RPM 核心的內部內存 (TCM)。
      • aboot.mbn: 應用引導加載程序 (APPSBL),通常是 Little Kernel (LK)。
    2. 向 RPM 發送啟動信號並解除其復位狀態。
    3. 通過寫 RMR_EL3 請求 Warm Reset,CPU 以 AArch64 EL3 特權級重新啟動,進入 TrustZone 執行環境。
  3. QSEE/ TrustZone:
    1. 在 EL3 環境下進行安全相關的初始化。
    2. 將 CPU 切換回 AArch32 EL1,跳轉到 aboot.mbn (LK) 的入口點。
  4. Aboot (LK):
    1. LK (little kernel),提供 Fastboot 刷機模式
    2. 初始化必要的硬體,如 MIPI-DSI 控制器和顯示面板,從 splash 分區讀取 Logo 文件並顯示開機畫面。
    3. 加載 boot.img (正常啟動) 或 recovery.img (恢復模式),並對其進行驗證。
    4. 準備好內核運行環境,切換到 AArch64 EL1,跳轉到內核的入口地址。
  5. Kernel:
    1. 識別掛載 systemvendoruserdatapersist 等分區
    2. 加載並啟動其他協處理器固件,如 Modem (基帶)、WCNSS (Wi-Fi/Bluetooth) 。
    3. 進入 userspace,啟動用戶空間的第一個進程 init,安卓啟動。

設備樹提取#

設備樹描述了硬體信息,內核啟動時會讀取它來配置相應的驅動程序。在高通平台上,DTB (Device Tree Blob) 通常附加在內核鏡像 (zImage) 的末尾。

使用 unpackbootimg 工具從 boot.img 中分離出 zImage,然後使用從moetayuko/split-appended-dtb獲得的 split-appended-dtb 工具從 boot.img 中提取出所有附加的 DTB 文件,一共提取出 69 個 DTB 文件。

$ file boot.img 
boot.img: Android bootimg, kernel, ramdisk, page size: 2048, cmdline (console=ttyHSL0,115200,n8 androidboot.console=ttyHSL0 androidboot.hardware=qcom msm_rtb.filter=0x237 ehci-hcd.park=3 lpm_levels)
$ ./unpackbootimg -i boot.img -o ./
BOARD_KERNEL_CMDLINE console=ttyHSL0,115200,n8 androidboot.console=ttyHSL0 androidboot.hardware=qcom msm_rtb.filter=0x237 ehci-hcd.park=3 lpm_levels.sleep_disabled=1 androidboot.bootdevice=7824900.sdhci earlycon=msm_hsl_uart,0x78af000 buildvariant=user
BOARD_KERNEL_BASE 80000000
BOARD_PAGE_SIZE 2048
$ ls
boot.img  boot.img-base  boot.img-cmdline  boot.img-pagesize  boot.img-ramdisk.gz  boot.img-zImage
$ ./split-appended-dtb boot.img-zImage 
Found 69 appended dtbs, please check the output.

隨後通過 dtc (Device Tree Compiler) 工具將二進制的 DTB 文件反編譯為人類可讀的文本格式 DTS (Device Tree Source)。

#!/bin/bash
        for i in $( ls *.dtb ); do
            echo decompile $i
            dtc -I dtb -O dts $i -o output/$i.dts
        done

設備樹數量眾多但只會使用一個,LK 會根據 SBL 傳入的硬體 ID (如 qcom,msm-id, qcom,board-id) 來選擇最匹配的一个加載。通過查看 LK 在啟動過程中通過串口輸出的日誌信息可以發現匹配到了 ID 為 <293 8 0 0x0> 的設備樹

[2110] Found an appended flattened device tree (Qualcomm Technologies, Inc. MSM8953 + PMI8950 MTP - 293 8 0 0x0)
[2120] Add DTB entry 293/00000008/0x00000000/0/10016/0/0/0/a11c281f/3e740
[2120] Device tree exact match the board: <293 8 0 0x0> == <293 8 0 0x10001>

據此找到對應的設備樹 DTS 文件即為:

/ {
	model = "Qualcomm Technologies, Inc. MSM8953 + PMI8950 MTP";
	compatible = "qcom,msm8953-mtp\0qcom,msm8953\0qcom,mtp";
	qcom,msm-id = <0x125 0x00>;
	interrupt-parent = <0x01>;
	qcom,board-id = <0x08 0x00>;
	qcom,pmic-id = <0x10016 0x00 0x00 0x00>;
	...
};

從 DTS 文件中可以獲取非常豐富的信息,包括特殊的 GPIO 分配、螢幕初始化序列等。對於進行系統移植非常有價值,比如有了下面的 MIPI DSI 螢幕初始化信息,點亮這塊螢幕就變得容易了。

qcom,mdss_dsi_yunxi_1200p_video {
				qcom,mdss-dsi-panel-name = "yunxi 1200p video mode dsi panel";
				qcom,mdss-dsi-panel-controller = <0x1a4>;
				qcom,mdss-dsi-panel-type = "dsi_video_mode";
				qcom,mdss-dsi-panel-destination = "display_1";
				qcom,mdss-dsi-panel-framerate = <0x3c>;
				qcom,mdss-dsi-virtual-channel-id = <0x00>;
				qcom,mdss-dsi-stream = <0x00>;
				qcom,mdss-dsi-panel-width = <0x4b0>;
				qcom,mdss-dsi-panel-height = <0x780>;
				qcom,mdss-dsi-h-front-porch = <0x32>;
				qcom,mdss-dsi-h-back-porch = <0x3e>;
				qcom,mdss-dsi-h-pulse-width = <0x08>;
				qcom,mdss-dsi-h-sync-skew = <0x00>;
				qcom,mdss-dsi-v-back-porch = <0x08>;
				qcom,mdss-dsi-v-front-porch = <0x0a>;
				qcom,mdss-dsi-v-pulse-width = <0x06>;
				qcom,mdss-dsi-h-left-border = <0x00>;
				qcom,mdss-dsi-h-right-border = <0x00>;
				qcom,mdss-dsi-v-top-border = <0x00>;
				qcom,mdss-dsi-v-bottom-border = <0x00>;
				qcom,mdss-dsi-bpp = <0x18>;
				qcom,mdss-dsi-color-order = "rgb_swap_rgb";
				qcom,mdss-dsi-underflow-color = <0xff>;
				qcom,mdss-dsi-border-color = <0x00>;
				qcom,mdss-dsi-on-command = [39 01 00 00 01 00 02 b0 00 39 01 00 00 01 00 06 b3 14 08 00 22 00 39 01 00 00 01 00 02 b4 0c 39 01 00 00 01 00 03 b6 3a d3 39 01 00 00 01 00 02 b7 00 39 01 00 00 01 00 07 b8 07 90 1e 00 1e 32 39 01 00 00 01 00 07 b9 07 82 3c 00 3c 87 39 01 00 00 01 00 07 ba 07 9e 20 00 20 8f 39 01 00 00 01 00 19 ce 7d 40 43 49 55 62 71 82 94 a8 b9 cb db e9 f5 fc ff 01 38 02 02 44 24 01 39 01 00 00 01 00 02 c0 ff 39 01 00 00 01 00 25 c1 04 61 00 20 8c a4 d6 ff ff ff ff 7f 73 ef b9 f6 ff ff ff ff bf 54 22 02 00 00 00 00 00 00 62 03 00 22 00 01 39 01 00 00 01 00 0a c2 31 f7 80 00 09 00 04 00 00 39 01 00 00 01 00 04 c3 00 00 00 39 01 00 00 01 00 0d c4 70 00 00 00 00 00 00 00 00 05 04 00 39 01 00 00 01 00 15 c6 6f 73 75 0a 14 0b 16 00 00 00 00 00 00 00 00 00 00 1c 17 09 39 01 00 00 01 00 1f c7 08 0e 19 22 31 3f 49 58 3c 44 4e 5b 64 6c 76 04 0c 13 1e 2d 3d 47 58 3c 44 50 5d 66 6e 76 39 01 00 00 01 00 14 c8 01 00 00 00 00 fc 00 00 00 00 00 fc 00 00 00 00 00 fc 00 39 01 00 00 01 00 0d cb 3f c0 0f f0 03 00 03 00 03 00 00 c0 39 01 00 00 01 00 02 cc 11 39 01 00 00 01 00 06 d0 44 81 bb 15 94 39 01 00 00 01 00 1a d3 0b 33 bf bb b3 33 33 37 00 01 00 a0 98 a0 00 39 39 33 3b 37 72 07 3d bf 99 39 01 00 00 01 00 03 44 07 7f 39 01 00 00 01 00 06 2a 00 00 04 af 00 39 01 00 00 01 00 06 2b 00 00 07 7f 00 39 01 00 00 01 00 02 d6 01 39 01 00 00 01 00 02 3a 77 39 01 00 00 01 00 02 2c 00 39 01 00 00 01 00 02 51 e6 39 01 00 00 01 00 02 53 24 39 01 00 00 01 00 02 55 01 05 01 00 00 32 00 02 29 00 05 01 00 00 78 00 02 11 00 39 01 00 00 32 00 02 35 00];
				qcom,mdss-dsi-off-command = [05 01 00 00 32 00 02 28 00 05 01 00 00 78 00 02 10 00];
				qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
				qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
				qcom,mdss-dsi-h-sync-pulse = <0x01>;
				qcom,mdss-dsi-traffic-mode = "burst_mode";
				qcom,mdss-dsi-lane-map = "lane_map_0123";
				qcom,mdss-dsi-bllp-eof-power-mode;
				qcom,mdss-dsi-bllp-power-mode;
				qcom,mdss-dsi-lane-0-state;
				qcom,mdss-dsi-lane-1-state;
				qcom,mdss-dsi-lane-2-state;
				qcom,mdss-dsi-lane-3-state;
				qcom,mdss-dsi-panel-timings = <0xf23a2800 0x6c6e2c3e 0x2e030400>;
				qcom,mdss-dsi-t-clk-post = <0x02>;
				qcom,mdss-dsi-t-clk-pre = <0x2d>;
				qcom,mdss-dsi-bl-min-level = <0x01>;
				qcom,mdss-dsi-bl-max-level = <0xfff>;
				qcom,mdss-dsi-dma-trigger = "trigger_sw";
				qcom,mdss-dsi-mdp-trigger = "none";
				qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_pwm";
				qcom,mdss-dsi-reset-sequence = <0x01 0x14 0x00 0x02 0x01 0x14>;
				qcom,mdss-dsi-panel-timings-phy-v2 = <0x241f0809 0x50304a0 0x241f0809 0x50304a0 0x241f0809 0x50304a0 0x241f0809 0x50304a0 0x241c0809 0x50304a0>;
				qcom,mdss-dsi-bl-pmic-pwm-frequency = <0x64>;
				qcom,mdss-dsi-bl-pmic-bank-select = <0x00>;
				qcom,mdss-dsi-pwm-gpio = <0x1a5 0x04 0x00>;
				qcom,panel-supply-entries = <0x1a3>;
				linux,phandle = <0x1a9>;
				phandle = <0x1a9>;
			};
		};

參考#

雲犀 BOX - 視界,精彩一觸即播,國內領先觸屏直播硬體,讓直播導播更簡單 - 雲犀直播

高通平台 dtb 文件的加載過程_高通 dump dts 文件 - CSDN 博客

我的 4g 網卡運行著 GNU/Linux -- 某 4g 無線網卡的逆向工程與主線 Linux 移植 (一)_fatal error: blob has incorrect magic number-CSDN 博客

利用 9008 刷寫 / 備份單分區 - 哔哩哔哩

高通芯片啟動流程 - asges 林 - 博客園

設備刷機需要的軟體 | 寧寧's Blog

Android 設備開機日誌分析_掛載安卓開機 log-CSDN 博客

轉載 - Qualcomm MSM8953 啟動流程:PBL-SBL1-(bootloader) LK-Android - liangliangge - 博客園

Android 啟動加載器分析 —— Aboot - 知乎

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。