汽车安全入门 03:ECU 与 UDS 诊断服务

导语:上回说到 OBD-II 那条 16 针线把诊断仪接进 CAN 总线。但 CAN 总线上跑的是帧,帧里塞什么、谁来解、按什么规则解,全是 UDS 在管。UDS 是 ISO 14229 定义的一套诊断协议栈,覆盖从读故障码、读传感器到刷写固件、跨域控制的全套操作。今天拆协议栈 + 十大核心服务 + Seed-Key 安全访问 + UDSim/UnlockECU 实战 + 红队攻击面,下一讲直接上攻击面建模。


一、ECU 不是”一台电脑”,而是一片域

现代汽车每辆搭载 70-150 个 ECU(Electronic Control Unit,电子控制单元),按域分布:

  • 动力域(Powertrain) — EMS(发动机控制单元)、TCU(变速箱控制单元)、BMS(电池管理系统,纯电/混动)
  • 底盘域(Chassis) — ABS(防抱死刹车)/ESC(车身稳定控制)/EPS(电动助力转向)
  • 车身域(Body) — BCM(车身控制单元,负责灯光/中控锁/雨刮)
  • ADAS 域(Advanced Driver Assistance Systems,高级驾驶辅助) — 毫米波雷达控制器、摄像头控制器、域控制器
  • 信息娱乐域(Infotainment) — IVI(In-Vehicle Infotainment,车载信息娱乐系统)、T-Box(远程信息处理盒)

UDS 是这些 ECU 都认的”普通话”。Tier 1(一级供应商)不管给哪家 OEM(整车厂)供货,UDS 接口几乎必须按 ISO 14229 实现。

ECU 与 UDS 协议栈

二、UDS 协议栈:跑在 OSI 第 5-7 层

UDS 本身定义在 OSI 7 层模型的会话/表示/应用层(第 5-7 层),不关心底层物理传输。实际部署时最常见的承载方式:

  • DoCAN(Diagnostic over CAN)— ISO 15765,主流方案
  • DoIP(Diagnostics over Internet Protocol)— ISO 13400,车载以太网 OBD 口的标配
  • DoLIN(Diagnostic over LIN)— ISO 17987,低速车身件
  • DoFlexRay — ISO 17458,奔驰/BMW 部分高端车

红队做渗透时,第一步永远先确认用的是 DoCAN 还是 DoIP。DoCAN 用 11/29 位 CAN ID 寻址(一般诊断 ID 落在 0x7DF-0x7EF 这块),DoIP 直接 UDP/TCP 走车载以太网,工具链完全不同。


三、十大核心服务(必须背熟)

UDS 的每个服务用 SID(Service Identifier,服务标识符)一个字节标识。请求 SID 是奇数偏移,响应 SID 是请求 +0x40。十大高频服务:

请求 SID响应 SID服务名用途
0x100x50DiagnosticSessionControl切换诊断会话(默认/扩展/编程)
0x110x51ECUResetECU 软重启/硬重启
0x140x54ClearDiagnosticInformation清故障码(DTC)
0x190x59ReadDTCInformation读故障码列表
0x220x62ReadDataByIdentifier按 DID 读数据
0x230x63ReadMemoryByAddress按地址读裸内存
0x270x67SecurityAccessSeed-Key 安全访问
0x2E0x6EWriteDataByIdentifier按 DID 写数据
0x290x69Authentication替代 0x27 的新机制(PKI 证书交换)
0x310x71RoutineControl跑 ECU 内部例程(自检/标定/特殊动作)

数 0x10 是”进门”,0x27 是”解锁”,0x2E 是”改值”。这三步连贯起来就是你远程调 ECU 的最短路径。


四、Negative Response Code:UDS 的错误字典

UDS 出错时 ECU 不回正常响应,而是回 7F 三字节格式,NRC(Negative Response Code,负响应码)一个字节标识错误类型。最常砍到的几个:

NRC名称实战含义
0x12SubFunctionNotSupported子功能不支持 — 目标 ECU 没实现你发的那个 sub-function
0x13IncorrectMessageLengthOrInvalidFormat报文长度错 — 漏带 sub-function 字节
0x14ResponseTooLong响应太长 — 触发 ISO-TP 多帧传输(下面讲)
0x22ConditionsNotCorrect条件不满足 — 比如车速不为 0 时拒绝刷写
0x24RequestSequenceError请求顺序错 — 没先 0x10 切到编程会话就发 0x27
0x31RequestOutOfRange参数越界 — DID 编号超范围
0x33SecurityAccessDenied安全访问拒绝 — 0x27 没解锁就调 0x2E
0x35InvalidKeySeed-Key 算错 — 算法或参数错了
0x36ExceededNumberOfAttempts超过最大尝试次数 — ECU 锁定一段时间
0x72GeneralProgrammingFailure刷写失败 — Flash 校验出错
0x78RequestCorrectlyReceivedResponsePending忙,等待中 — ECU 在算长 key,让你别急
0x7ESubFunctionNotSupportedInActiveSession当前会话不支持 — 没切到 Programming Session
0x7FServiceNotSupportedInActiveSession服务不支持 — 比如 Default Session 下禁止 0x2E

红队视角下这几个最常打交道的:0x22(车速/电压/P 挡条件)和 0x33(认证失败)几乎每个项目都会遇到。0x36 触发多了 ECU 会进锁定状态,严重的直接锁死,必须拆电池或者长按某键才解得开。


五、ISO-TP:UDS 在 CAN 上的多帧传输

CAN 单帧最多 8 字节载荷(CAN FD 最多 64),UDS 一条诊断消息动辄几十上百字节。ISO 15765-2(即 ISO-TP)定义了四种帧类型解决这个:

  • 单帧 SF(Single Frame)— 第一字节低 4 位表示后续数据字节数(0-7)
  • 首帧 FF(First Frame)— 数据超过 7 字节时用,前两字节标识总长度(最大 4095 字节)
  • 连续帧 CF(Consecutive Frame)— 按 0-15 序号循环发送剩余数据
  • 流控帧 FC(Flow Control)— 接收方告诉发送方块大小(BS)和最小间隔时间(STmin)

红队做 fuzz(模糊测试)时常见坑:FF 没收完就发完 CF,ECU 直接 NRC 0x14(ResponseTooLong)把你踢出去。下一篇讲攻击面建模时会专门拆这块。


六、SecurityAccess 0x27:Seed-Key 流程拆解

UDS 的安全访问用挑战-应答(Challenge-Response)模式,规则简单:

Tester → ECU:  27 01              # requestSeed (sub-function = 0x01)
ECU   → Tester: 67 01 AA BB CC DD  # positiveResponse + 4 字节 seed
Tester → ECU:  27 02 XX XX XX XX  # sendKey (sub-function = 0x02) + 计算出的 key
ECU   → Tester: 67 02              # 解锁成功

算法藏在 ECU 里,Tester 必须算出匹配的 key 才能解锁。算法各家不同:

  • Bosch(博世) PowertrainBoschContiSecurityAlgo1
  • Conti(大陆) ContiSecurityAlgo
  • Delphi(德尔福) DelphiAlgo
  • 日系 OEM 自研

Seed 长度通常 2-8 字节,Key 长度一般等于 Seed 长度。

SecurityAccess Seed-Key 流程

七、UDSim 实战:图形化 ECU 模拟器

UDSim(github.com/zombieCraig/UDSim)是 Craig Smith 写的 UDS 模拟器 + 模糊测试器,三种模式:

  • Learning 模式 — 监听 CAN 总线,自动学习 ECU 的 UDS 行为并保存
  • Simulation 模式 — 用学习好的配置模拟 ECU,对诊断仪做应答
  • Attack 模式 — 对目标 ECU 做主动 UDS 模糊测试

配置用纯文本 key=value,比如定义一个 0x7E0 的服务端:

[7e0]
pos = 300,131
responder = 1
positiveID = 7e8
negativeID = 7e8
{Packets}
7e8#0650030096177000

经典组合是 ICSim(Instrument Cluster Simulator,仪表盘模拟器) + UDSim + SocketCAN(Linux 内核原生 CAN 协议栈) 一起跑,搞个虚拟仪表盘 + 虚拟 ECU 出来,练手写 UDS 注入时不用上真车。下一篇讲攻击面建模时会用到这套环境。


八、UnlockECU:Seed-Key 不再神秘

seed-key 算法一般藏在车厂的私有 DLL 里,逆向门槛高。UnlockECU(github.com/jglim/UnlockECU)做的事很关键:把 30+ 家供应商的安全访问算法逆向实现成 C#,配上 db.json 数据库免去额外 DLL 依赖。

用法直接:

1. 从实车抓 seed(UDS 0x27 01 应答)
2. 在 UnlockECU 选 ECU 型号(如 ME97)
3. 工具自动选算法 → 算 key → 给你 UDS 0x27 02 应答帧
4. 直接用 key 解锁 ECU,进入编程会话刷写

实战链:caringcaribou 枚举 UDS 服务 → 抓到支持 0x27 → UnlockECU 算 key → 进入编程会话 → 刷写恶意固件。整个链路开源 + 自动化,红队视角下这是教科书级别。


九、红队攻击面:UDS 常见被砍姿势

按被砍次数排:

  1. 默认开放编程会话 — 不少车的 0x10 0x02(Programming Session)默认无认证,进入后 0x34/0x36/0x37 直接刷固件
  2. Seed-Key 算法被泄露 — 供应商 DLL 反编译后 key 算法全公开,等于 0x27 形同虚设
  3. DID 误用 — 0x22 读 VIN、里程、密钥等敏感 DID 没设访问控制
  4. 0x23 裸读内存 — 给个内存地址就能 dump 整个 Flash(闪存),密钥、私钥全暴露
  5. 0x31 RoutineControl 滥用 — 标定/工厂测试 Routine 在售后仍开放,可改写关键参数
  6. DoIP 远程访问 — 现代车 OBD 口背后就是 DoIP 设备,攻击者远端进 CAN 就是这一步

十、思考题

  • Q1:拿到一台车的 OBD 口,怎么在不接真实 ECU 的情况下练 UDS 注入?(提示:上文的 UDSim)
  • Q2:如果某 ECU 的 0x27 安全访问只用 OEM 私钥签名证书,但固件可以从公开渠道下载,会有什么后果?
  • Q3:0x31 RoutineControl 的 sub-function 0x01(Start Routine)能不能跑出 0x10 之外的会话切换?

十一、素材出处

  • awesome-vehicle-security #146 UDSim:https://github.com/zombieCraig/UDSim
  • awesome-vehicle-security #167 UnlockECU:https://github.com/jglim/UnlockECU
  • TR22 UDS Fuzzing:https://www.youtube.com/watch?v=c_DqxHmH7kc
  • Wikipedia ISO 14229 UDS:https://en.wikipedia.org/wiki/Unified_Diagnostic_Services
  • PortSwigger 风格 WebSocket 测试思路延伸到 UDS

© 版权声明
THE END
喜欢就支持一下吧
点赞6 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容