VIN 只校验 17 位格式:车型识别、ECU 固件匹配与启动边界

作者:

在

结论:车型识别收集的是识别证据,不是车辆身份认证。is_valid_vin() 只检查 17 位字符格式,既不校验它与实车绑定,也不证明响应来源可信;CAN 指纹只是报文地址与长度的消元,不是对内容或 ECU 身份的认证。更关键的是,FirmwareQueryDone 只表示「固件查询这一步写完了」,此时 CarParams 尚未写入,pandad 还要等 ControlsReady 才读取它——它不等于车型可信,也不等于允许执行。以下基于源码快照 c8fb906 的静态阅读,未做诊断总线隔离、VIN 欺骗或双证据冲突验证。

启动时序:等 CAN、识别、两个握手门

Car.__init__() 先等首个非空 CAN、解析缓存,再调用 get_car()。can_comm_callbacks() 把 can/sendcan 消息转成识别函数需要的收发接口;obd_callback() 写参数并等待 pandad 确认,而这个握手没有显式超时。识别完成后,FirmwareQueryDone 在车型接口创建后写入,但此时 CarParams 还没写;pandad 的 fetchCarParams() 必须再等 ControlsReady 才读取它。随后 card 依据控制器是否存在、用户开关和 dashcamOnly 决定 passive,被动时改成 noOutput。

阶段 静态行为 含义与边界
缓存 非 mock、含固件、VIN 非未知且未设 DISABLE_FW_CACHE 时复用 CarParamsCache;仍重做 CAN 指纹 缓存是加速来源,不是当前车辆身份认证;使用时没有比对本次 VIN
主动诊断 无合格缓存且未设 SKIP_FW_QUERY 时打开 OBD 复用,查询 VIN、ECU 地址和固件 会发诊断 CAN;结果可能因超时或局部失败不完整
CAN FPv1 维护 bus 0/1 候选,对 11-bit 非排除地址按长度消元;frame>100 且唯一则成功,frame>200 终止 frame 是收到的 CAN 包计数,不是严格墙钟两秒
固件结果 固件候选恰好一个时覆盖 CAN 结果;多个或零个不覆盖 exact 与 fuzzy 都可形成单一候选;不要求两类证据一致
强制覆盖 非空 FINGERPRINT 最后覆盖所选车型,来源记为 fixed 开发便利入口,不适合作为实车执行许可
未知车型 get_car() 将无候选回退到 MOCK 仍由后续参数/安全配置决定输出,回退不等于具备控制能力

还有一个容易被误读的细节:FINGERPRINT 覆盖时只改车型和来源,exact_match 保留此前固件匹配阶段的值。因此 fuzzyFingerprint 也不能单独解释为「固定车型的可信等级」——来源与模糊标记是追踪元数据,不是交叉验证结果。

VIN 与固件查询细节

get_vin() 会在 bus 0/1 与 UDS、OBD 及品牌特例请求之间重试,找不到则返回 VIN_UNKNOWN。固件侧的查询则分几步:

  1. get_present_ecus() 按 OBD 复用模式分组,普通地址并行、子地址逐个探测;get_brand_ecu_matches() 统计可能品牌,但注释明确尚未检查响应总线。
  2. get_fw_versions_ordered() 按响应 ECU 数量及匹配比例排序;对某品牌查询后,只要该品牌固件给出单一候选就提前停止,因此查询结果未必覆盖所有品牌。
  3. get_fw_versions() 依据品牌的 FwQueryConfig 形成地址、总线、请求/响应前缀及 ECU 白名单;异常记录后继续,返回已取得的局部 CarFw。被 logging 或 extra_ecus 标记的响应不用于后续匹配。
  4. IsoTpParallelQuery.get_data() 并行发送请求,多帧接收会延长单地址超时,负响应 0x78 使用更长的 pending 超时;其余坏响应/异常/超时只影响相应地址,返回字典只含完成的响应。
  5. match_fw_to_car_exact() 要求数据库列出的必要 ECU 存在且命中版本;非必要 ECU 可缺失,但一旦出现就必须匹配。Ecu.debug 特殊跳过。
  6. 通用 fuzzy 排除摄像头、雷达、EPS 与 debug,只在至少两个不同 ECU 地址唯一指向同一车型时给出匹配;match_fw_to_car() 先 exact 后 fuzzy,最终可能多个候选,只有唯一候选才在外层覆盖 CAN。
匹配策略 成立条件 结果
exact 数据库列出的必要 ECU 存在且命中版本 形成候选;非必要 ECU 可缺失,但一旦出现就必须匹配
通用 fuzzy 至少两个不同 ECU 地址唯一指向同一车型 形成候选;品牌自定义回调也可能参与
外层覆盖 match_fw_to_car() 先 exact 后 fuzzy 后只剩一个候选 只有唯一候选才覆盖 CAN 结果

重新设计识别时的边界

以下为设计建议,不是现有代码已实现的保障。可以把 VIN、ECU、CAN 和硬件拓扑检测设计成只读的「证据插件」,结果携带采集时间、总线、来源、完整性及冲突信息;插件不得直接设置车型安全模式或发 sendcan。由受信任的启动策略统一裁决:缓存只作性能优化,实车执行前需本次车辆证据复核;fuzzy、环境强制车型、证据冲突或版本不明时默认只读或 noOutput。OBD 复用、诊断请求和 CAN 发送应视为能力而非普通插件 API,限定唯一所有者、允许的总线/地址/速率与生命周期。最后,要保留 panda MCU 的独立限幅、心跳与安全模式——车型识别成功不等于安全配置可执行。

验证缺口同样明确:现有 CAN 指纹测试用离线 FPv1 样本检查识别与包计数,固件指纹测试覆盖 exact、fuzzy 与数据库一致性;但本文未运行测试,也未验证诊断总线隔离、VIN 欺骗、缓存跨车误用、双证据冲突、OBD 握手阻塞与掉电恢复。此外 fingerprintSource/fuzzyFingerprint 在本快照中主要被写入 CarParams,未见 card 或 pandad 用这两个字段实施独立许可,因此不能把它们当成现成安全门。

常见问题

VIN 校验通过,能说明识别到了真车吗?

不能。is_valid_vin() 只检查 17 位字符格式,既不校验它与实车绑定,也不证明响应来源可信。识别结果应携带来源与完整性信息,由上层策略裁决。

FirmwareQueryDone 是不是代表车型已经可信?

不是。它只表示固件查询这一步写完,此时 CarParams 尚未写入;pandad 还要等 ControlsReady 才读取。它既不代表车型可信,也不代表允许执行。

CAN 指纹里的 frame>100 是两秒计时吗?

不是。frame 是收到的 CAN 包计数,不是严格墙钟两秒;frame>100 且唯一则成功,frame>200 终止。采集字典也不等于可信身份。

固件匹配唯一,就能直接覆盖 CAN 指纹结论吗?

只有唯一候选才会在外层覆盖 CAN 结果。exact 与 fuzzy 都能形成单一候选,当前不要求两类证据一致;多个或零个候选时不覆盖。

延伸阅读

参考资料

  • 项目 02 —— openpilot-research/CAR_FINGERPRINT_FW_MATCH_DEEP_DIVE.md(车型识别、VIN 与 ECU 固件匹配,含源码行号索引)
  • 项目 02 —— openpilot-research/ACTUATION_GATE_CHAIN_DEEP_DIVE.md(源文档指向的最终执行门专题,供识别与执行的边界延伸)
  • 项目 02 —— openpilot-research/SOFTWARE_MINDMAP.md(源文档指向的主系统结构)

说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。

更新记录

首次发布 2026-10-11 最近核对:2026-10-11

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注