abdukir/hi3515-dvr-fpv

GitHub: abdukir/hi3515-dvr-fpv

将2011年报废的海思Hi3515模拟监控DVR通过逆向工程和自定义固件改造为现代化的低延迟FPV视频地面站。

Stars: 0 | Forks: 0

# hi3515-dvr-fpv **将一台报废的 2011 年 CCTV DVR 变成现代化的 FPV 地面站——通过用从零开始编写的固件替换原有固件。** 这是一个针对 **NetDVR RR104P** 的逆向工程项目:一台基于海思 **Hi3515** SoC(ARM926EJ-S,200 MHz,43 MB 可用 RAM,Linux 2.6.24,BusyBox 1.1.2)构建的 4 通道模拟 安全录像机。它唯一的客户端是一个用于 Internet Explorer 的 ActiveX 插件,因此出厂状态下这个设备纯属电子垃圾。 它现在运行着**我们自己的固件**——一个约 75 KB 的独立 C 程序,没有 libc,也没有厂商 SDK——它可以捕获模拟视频,进行硬件 H.264 编码,录制到 SATA, 通过网络进行实时流传输,并驱动其专属的屏幕 UI。基于浏览器的控制台通过我们设计的协议与之通信。 ## 它的实际功能 | | 原厂固件 | 本项目 | |---|---|---| | 实时预览 | 仅支持 IE 中的 ActiveX | 任何现代浏览器,基于 WebSocket + MSE 的 H.264 | | 录制 | 专有 `.ifv` 环形缓冲区 | 包含真实 PTS 的 MPEG-TS 文件,可在任何地方播放 | | 视频质量 | ~CIF,低码率 | 704×240(全宽 2CIF),FIXQP QP10 ≈ 17-23 Mbps | | 控制 | 基于 TLV 协议的厂商 CMS | TCP 8090 端口上的纯文本行协议,文档齐全 | | 屏幕 UI | 厂商菜单 | 我们自己的 OSD:HUD、菜单、鼠标/键盘、回放 | | 端到端延迟 | — | VGA 输出上约 130 ms(主要是显示器自身的延迟) | ### 为什么要折腾 专门针对 **FPV** 而言:将模拟视频接收器插入复合输入接口,你就能得到一台真正擅长这一唯一关键任务的录像机——以高码率捕获视频链路,同时以最小延迟在显示器上呈现。这台硬件的二手价值约等于零,却拥有一个真正的 H.264 编码器、四个复合输入、一个 SATA 端口和一个 VGA 输出。就其成本而言,这是一个功能非常强大的地面站。 更广泛地说,这是一个如何接管一台锁定且过时的嵌入式 Linux 设备并使其为你所用的完整实例:Dump flash、对无文档的媒体流水线进行逆向工程、编写替代的 init/userland,并带有恢复路径地进行安全刷机。 ## 硬件 - **SoC** 海思 Hi3515 v100(ARMv5TE,仅限 OABI 内核) - **捕获** Techwell TW2866,4× 复合接口,PAL/NTSC - **编码器/解码器** 硬件 H.264 (VEDU),在编码和回放之间共享 - **RAM** 128 MB,内核限制为 `mem=43M` - **Flash** 8 MB NOR:`mtd0` boot (1 MB) · `mtd1` kernel (2 MB) · `mtd2` rootfs (5 MB) - **存储** SATA,4 × vfat 分区挂载于 `/root/rec/a1..a4` - **输出** VGA(+ 复合接口),USB host,100M 以太网,RS-485 PTZ,蜂鸣器 - **Rootfs** gzip 压缩的 ext2 **RAM disk** —— `/` 是易失性的,这使得无 flash 开发成为可能 ## 两种运行方式 ### A. 无需刷机(推荐——本项目就是以此方式构建的) 不会产生任何永久性更改。`/` 是一个 RAM disk,因此循环加电(重启)即可完全恢复原厂固件。你需要将 DVR 连接到局域网并拥有 telnet 访问权限(原厂固件开启了 telnetd,root 密码为空)。 ``` # 1. 构建 firmware (WSL/Linux,bootlin armv5 uclibc toolchain — 见 docs/BUILD.md) bash device/dvr/build.sh # 2. 停止 vendor app,以便我们可以独占 MPP 硬件(仅限单一所有者) # serial console:`closewd` 然后 `exit` (停用 watchdog,结束 app.out) # 或通过 telnet:killall mydaemon.out; killall app.out # 3. 将我们的 binary 推送到 SATA 磁盘并运行它 py -3 tools/dvr.py push device/dvr/dvr /root/rec/a1/dvr 755 py -3 tools/dvr.py shell "/root/rec/a1/dvr 0 0 r 30 9 1 &" # 4. 检查它是否已启动 py -3 tools/dvr.py status ``` 断电重启即可撤销所有更改。 ### B. 刷机(持久化——设备可自行引导) Flash 的更改仅需修改**一个文件**:`/root/run_app.sh` 增加了一个钩子,用于挂载 SATA 并在存在 `/root/rec/a1/boot.sh` 时 `exec` 它,否则退回到执行原厂的 `app.out`。 随后我们的所有内容都存放在 **SATA 磁盘**上,该磁盘可通过网络进行写入——因此这是一次*一次性*的刷机,此后你再也无需触碰 NOR 芯片。 ``` power on └ U-Boot (mtd0) → kernel (mtd1) → ext2 RAM disk (mtd2) → /etc/init.d/rcS └ /root/run_app.sh net.sh (eth0 + telnetd) ←── always runs first, so telnet is up no matter what init.sh (drivers) ├ /root/rec/a1/boot.sh exists ──→ exec it ──→ our DVR ← the new path └ otherwise ──────────────────→ mydaemon.out + app.out ← stock, untouched ``` **请先阅读 [`docs/FLASH.md`](docs/FLASH.md)。** 简要说明如下: ``` bash flash/build.sh # build the rootfs image (needs your own mtd2 dump, see below) bash flash/verify.sh # 40+ offline checks; refuses to bless a bad image py -3 tools/dvr.py deploy # stage boot.sh + dvr + config on SATA, and test them by hand # on-device preflight:使用设备自身的 kernel 和 gunzip 对 image 进行循环检查 py -3 tools/dvr.py push flash/device/preflight.sh /root/rec/a1/preflight.sh 755 py -3 tools/dvr.py shell "sh /root/rec/a1/preflight.sh /root/rec/a1/mtd2_new.bin" py -3 tools/dvr.py flash flash/out/mtd2_new.bin --reboot ``` `tools/dvr.py flash` 会将当前的 `mtd2` 备份到 SATA,解除硬件看门狗,执行写入,然后在**允许你重启之前读回 flash 并比对 md5**。 #### 你必须提供自己的固件 dump 本仓库刻意**不包含任何厂商固件二进制文件**(参见[法律声明](#legal-and-scope))。`flash/build.sh` 需要*你的*设备中的 `dump/mtd2_rootfs.bin`——无论如何这都是正确的工作流程,因为每台设备的 config blob 和 MAC 都不同: ``` py -3 tools/dvr.py backup dump/ # pulls mtd0/mtd1/mtd2 off the device, md5-verified ``` ## 连接到设备 三个独立通道,在刷机前你希望三者都就绪: | 通道 | 提供的功能 | 备注 | |---|---|---| | **网络** | telnet(root,空密码),我们的控制端口 8090,媒体端口 8091,busybox httpd 8081 | 默认 `192.168.1.108`;我们的 `boot.sh` 会设置它,可通过 `/root/rec/a1/ip` 配置 | | **串口控制台** | root shell **以及** U-Boot —— 完全无需网络即可工作 | USB-TTL,115200 8N1。**这是恢复路径。** | | **采集卡** | 从你的 PC 查看 DVR 的实际 VGA 输出 | 任何 HDMI/VGA 采集器;用于验证 UI 工作 | 一切操作均由一个 CLI 驱动: ``` py -3 tools/dvr.py status # alive? what's it running? py -3 tools/dvr.py shell "ps | head" # telnet py -3 tools/dvr.py ctl INFO # control protocol py -3 tools/dvr.py push FILE /root/rec/a1/x # chunked, md5-verified upload py -3 tools/dvr.py screen shot.png # grab the DVR's screen py -3 tools/dvr.py serial send "id" # root shell over the console py -3 tools/dvr.py backup dump/ # pull all three flash partitions py -3 tools/dvr.py flash IMG --reboot # write mtd2, verified before reboot py -3 tools/dvr.py restore --reboot # put the stock firmware back py -3 tools/dvr.py recover --test # rehearse U-Boot + TFTP recovery ``` `tools/dvrlib.py` 将传输陷阱编码为具体行为而不是经验之谈——上传暂存在 SATA 上(`/` 只有约 1.9 MB 可用空间)并以 1 MB 的分块进行(busybox `nc` 在大约 2.5 MB 左右会静默停止),`nc` 监听器会获得其专属的 telnet 会话(否则它会吞掉下一条命令),telnet `exec` 超时是致命的(部分回复会导致会话永久失步),并且 Git-Bash 的 POSIX 路径篡改会被自动还原。 ## Web 控制台 (`webapp2/`) ``` cd webapp2 && npm install && node server.js # → http://localhost:8092 ``` 兼容 Node v14+。需要 `ffmpeg` 位于 `webapp/bin/ffmpeg.exe` 以进行片段重混流(参见[`SETUP.md`](SETUP.md));它**不**参与实时视频路径。 - **实时预览** —— H.264 直接从设备通过 WebSocket 传输到 jMuxer/MSE。无转码。 - **片段** —— 从设备列出录像,在浏览器中播放(无损 TS→MP4 重混流,基于范围服务)或在 **DVR 自身的显示器上**播放,以及下载和删除。 - **监控** —— DVR VGA 输出的远程控制:通道,OSD 方向键,实时画面控制(TW2866 寄存器),回放传输。 - **文件 / Shell** —— 文件管理器以及通过 telnet 的 root shell。 - **配置** —— 设备读数,时钟同步,PAL/NTSC,蜂鸣器开关,以及 `dvr.conf` 编辑器。 后端保持**一个**持久控制连接(设备仅接受四个),并为所有浏览器标签页轮询一次 `INFO`。 ## 工作原理 ``` TW2866 (4× composite) │ VI (only the channels something drains — see below) ├──────────────► VO / VGA ── the DVR's own monitor, codec-free, ~130 ms │ plus an fb0 OSD layer + fb4 cursor overlay │ └─► VENC ────► pump_encode() ─┬─► MPEG-TS file on SATA (recording) │ └─► TCP 8091 fan-out (live) VDEC ◄────┘ (on-screen playback, shares the VEDU with VENC) control: TCP 8090 + /dev/ttyAMA1 PC: webapp2 (8090 · 8091 → WS · telnet · 8081) ``` 重要的架构决策:**捕获和编码只发生一次,而磁盘 / 网络 / 显示是相互独立的消费者。** 添加一个查看器只需消耗一次 `write()`。这就是为什么 200 MHz 的 ARM9 能够在 704×240 分辨率和约 17 Mbps 码率下同时维持这三项任务。 MPP 的启动是对厂商 `app.out` 提取的 trace 进行**逐字节的 ioctl 重放**,因为唯一能获取到的海思 SDK 与此芯片不匹配(`VI_SetPubAttr` → `SYS_NOTREADY`)。每个结构体都是一个带有偏移量注释的魔数 byte array。看起来很糟糕;但这不可避免。`docs/MPP_INIT_SEQUENCE.md` 是其规范说明。 ## 逆向工程的发现 重点内容——完整记录见 [`docs/`](docs/): - **[`PROTOCOL.md`](PROTOCOL.md)** —— 厂商的 8670 TLV 协议,从 ActiveX DLL 和数据包捕获中解码得出。仅第一代设备需要;保留它是因为这是唯一的记录。 - **[`docs/MPP_INIT_SEQUENCE.md`](docs/MPP_INIT_SEQUENCE.md)** —— 使视频流转起来的确切 ioctl 序列,包括那五项让状态在“无画面”和“正常工作”之间产生本质区别的修正。 - **[`docs/IFV_FORMAT.md`](docs/IFV_FORMAT.md)** —— 厂商录像容器格式及其时间→偏移量索引。 - **[`docs/DISPLAY_PATH.md`](docs/DISPLAY_PATH.md)** —— VO/VGA 启动、fb0 OSD,以及位于专用 fb4 overlay 上的无 flash 光标(app.out 自身的技巧)。 - **[`docs/FRONT_PANEL.md`](docs/FRONT_PANEL.md)** —— 18 针面板接口:一个 5×5 GPIO 按键矩阵、LED 阵列和蜂鸣器(`0x20150000` 第 7 位,一个被动换能器,因此它能播放实际音调)。*子板本身未被使用——它的开关已经老化,会自动误触——但逆向工程的研究成果被保留了下来。* - **[`docs/REVIEW.md`](docs/REVIEW.md)** —— 架构审查、发现的缺陷以及按优先级排列的待办事项。包括**实时视频卡死**问题:空闲的 VI 通道捕获到共享的 VB 池中,但没有任何程序去排空它,导致关键通道资源耗尽。通过针对硬件的 A/B 测试进行了诊断,并且该部分刻意保留了早期错误的分析记录。 - **[`docs/FLASH.md`](docs/FLASH.md)** —— 持久化工作,包括发现厂商自身的 `mtd2` 是一个**为适应分区而被截断的 gzip 流**,且带有错误的数据 CRC(它之所以能启动,仅仅是因为 U-Boot 设置了 `verify=n`)。 ## 仓库布局 ``` device/dvr/ our firmware — dvr.c + oabi.h/net.h/ts.h/fb.h/ui.h/... , build.sh tools/ dvr.py + dvrlib.py — one CLI for telnet/control/serial/U-Boot/capture/flash flash/ the persistence kit: build.sh, verify.sh, mkuimage.py, boot hook, SATA payload webapp2/ the current web console (Node + browser) webapp/ gen-1 console that spoke the vendor protocol — kept for the 8670 RE docs/ protocol specs, RE writeups, build/flash procedures, the review *.py RE tooling: unpack.py, ext2extract.py, deframe.py, tftp_server.py, ... ``` 请从 [`CLAUDE.md`](CLAUDE.md) 开始阅读——这是一份导读文档,它准确说明了哪些部分是当前在用的,哪些是历史遗留的。 ## 状态 工作正常并从 flash 运行:冷启动到网络就绪约需 20 秒,向浏览器提供实时 H.264,手动进行每通道 MPEG-TS 录制,带有逐帧步进和拖动进度条的屏幕回放,由鼠标/键盘/网络驱动的 OSD 菜单,带有旋律的蜂鸣器反馈。 **录制是通过命令手动进行的,这是刻意设计的**——仅有 `REC`/`STOP`。没有自动录制,也没有循环覆盖保留机制。编写此固件的意义就在于*不*去重新构建一台 CCTV DVR。 已知待解决的事项已如实记录在 [`docs/REVIEW.md`](docs/REVIEW.md) 第 3 节中,包括 U-Boot TFTP 恢复路径*可用但未经排练*(它需要在 PC 上允许入站 UDP 69 端口)。 ## 法律与范围 - **此处不分发任何厂商固件。** 本项目构建所基于的 dump 文件——U-Boot、内核、rootfs、`app.out`、海思 SDK、ActiveX DLL、数据手册——均为制造商的版权财产,**不**包含在本仓库中。请 dump 你自己的设备(`tools/dvr.py backup`)。*发布*的内容仅为我们自己的代码以及对硬件工作原理的描述。 - 声明一个例外情况:`flash/rootfs_overlay/root/run_app.sh` 是设备的原厂启动脚本,其中插入了我们的交接钩子,将其完整保留是为了便于审计与原厂的差异。它大约有 40 行 `mount`/`tar`/`exec`,纯粹是为了实现互操作性。 - 文档中引用的原厂设备凭据(无密码的 `root`,`admin` / `000000`)是此设备系列的**出厂默认设置**,并非任何人的机密。 - 请仅在你自有的硬件上运行此项目。 - 与海思、Techwell 或该 DVR 的制造商没有任何隶属关系,也未得到其认可。 ## 许可证 本仓库中的代码采用 MIT 许可证。参见 [`LICENSELICENSE)。这不会也不可能授予对其互操作的第三方固件的任何权利。
标签:FPV地面站, MITM代理, 云资产清单, 客户端加密, 嵌入式固件, 嵌入式开发, 数据可视化, 硬件黑客, 逆向工具, 逆向工程, 音视频处理