Files
go1_pro_sdk/docs/REVERSE_ENGINEERING.md
2026-06-20 20:02:36 +08:00

4.2 KiB

逆向工程过程精华

完整的探索过程见原项目笔记 free-dog-sdk/REVERSE_ENGINEERING_NOTES.md (1700+ 行)。这里是事后整理的精华。

起点

Unitree Go1 PRO 版机器人, 跑 sportMode 系统:

  • 树莓派 4 (192.168.123.161): MQTT broker + Legged_sport 控制循环 + appTransit
  • 3 台 Jetson Nano (192.168.123.13/14/15): 相机/AI/SLAM
  • MCU (192.168.123.10): 电机控制器 (UDP :8007)

PRO 跟 EDU 的关键区别 (Unitree 故意做的限制):

  • MCU 用 Blowfish 加密 LowState 回包 (EDU 无加密)
  • MCU 要求 LowCmd 也是 Blowfish 加密 (EDU 接受明文)
  • Legged_sport 二进制 UPX 压缩 + 符号剥离, 拿不到密钥

走过的弯路 (记录, 避免再踩)

弯路 1: 误判加密算法是 TEA

看到 Feistel 结构 + 32 字节常数, 第一反应是 TEA. 花了几天用 Z3 求解 128-bit key, 全部超时.

纠正: TEA 的特征是 lsl #4 + lsr #5 在同一函数. Blowfish 是 T-table 查表 + 加法 + 异或, 没有移位混合常数. 反汇编时区分这两点是关键.

弯路 2: 试图静态反推密钥

Blowfish 的 key schedule 是:

  1. P[i] = P_initial[i] ^ key_word(i % keylen)
  2. 用此 P 和初始 S-box 加密 (0,0), 用结果替换 P[0..1]
  3. 继续替换 P[2..3] ... 直到 P[16..17]
  4. 同样替换 S-box

纠正: 步骤 1 的中间值在步骤 2 中立刻被覆盖, 从最终 P-array 无法反推 key. 但有 state 就够了, 不需要原始 key. 运行时 state = 4168B = P[18] + S0..S3.

弯路 3: 误以为 LowCmd 不加密

heap 搜索没找到 feefff00 密文 → 错误结论 "LowCmd 是明文". 实际上加密发生在 sendto 缓冲区里, 加密后立刻被消费掉, 没留在 heap.

纠正: 用 strace -e sendto 抓到 Legged_sport 真实发送的字节, 解密后是 LowCmd 头, 确认必须加密.

弯路 4: 误以为 MCU 锁源端口

之前认为 MCU 锁 Legged_sport 的源端口 8008. 改 sin 测试用 :8008 还是不动.

纠正: MCU 实际是按"最近发包者的 IP:port"切换. 任意端口都行, 关键是要先停掉 Pi 上的抢占者.

弯路 5: 没杀干净 Legged_sport

killall -9 Legged_sport 杀完, 进程立刻又活. 看 ps 才发现父进程是 keep_sport_alive.sh (PID 970), 每 0.2 秒检查并重启 Legged_sport.

纠正: 必须先杀看门狗, 再杀 Legged_sport. 顺序错了等于没杀.

关键突破

突破 1: 内存提取 Blowfish state

# 在 Legged_sport 运行时 attach GDB (只读, 不设断点不写内存)
sudo bash extract_blowfish_key.sh   # < 30 秒, 不影响运动控制
# 拉回 Mac, 在 heap dump 里搜索 4168B 候选, 用已知明密文对验证
python analyze_blowfish_dump.py blowfish_dump.tar.gz
# 命中: rw_7f86a1f000.bin offset 0x232190

突破 2: PRO 格式跟 EDU 不一样

用 tcpdump 抓 Legged_sport 真实发送的 616B 包, 解密后跟 free-dog-sdk lowCmd.buildCmd() 输出做字节级 diff:

字段 EDU PRO
总长 614B 616B (多 2B 填充)
CRC 偏移 610..614 612..616
CRC 算法 encryptCrc(genCrc(cmd[:608])) XOR 0xedcab9de gen_crc(cmd[:612])
bandWidth 字节序 LE BE
SN/version 填实际值 全 0

突破 3: 内存里的 wirelessRemote 数据

LowState 的 759..799 是 40 字节遥控器数据, 跟 EDU unitree_legged_sdk/comm.h 公开的 xRockerBtnDataStruct 完全一致. 直接 parse 就能拿到按键 + 摇杆 + L2 模拟值.

最终性能 (Mac 直连)

指标 实测
控制频率 480 Hz (96% 目标)
单步 p50 1.74 ms
Blowfish 加密 1.02 ms
Blowfish 解密 0.67 ms
sendto 0.02 ms
Blowfish 解码率 100%

工具集

工具 用途
tools/extract_blowfish_key.sh 狗端内存 dump
tools/analyze_blowfish_dump.py Mac 端搜索 4168B state
tools/calibrate_joints.py 手动转动法测真实关节限位
tools/capture_lowcmd.sh 抓 Legged_sport 真实发送的包
tools/diff_real_lowcmd.sh 抓包 + 字段级 diff
tools/stop_sportmode.sh SSH 杀抢占源
tools/run_on_mac.sh Mac 端跑测试 (自动停抢占)
tools/run_on_pi.sh Pi 端跑测试 (源 IP 跟 Legged_sport 一致)