当前位置: 首页 > news >正文

OBD-II/UDS 渗透测试(一):协议基础 — CAN 总线与 ISO-TP

OBD-II/UDS 渗透测试(一):协议基础 — CAN 总线与 ISO-TP

    • 第 1 章 车载诊断的"为什么"和"是什么"
      • 1.1 一句话起源
      • 1.2 OBD-II 是什么、UDS 是什么、它们的关系
        • 1.2.1 SID(UDS)vs Mode(OBD-II)—— 容易混淆的概念
      • 1.3 为什么诊断协议是"攻击金矿"
    • 第 2 章 CAN 总线层基础
      • 2.1 帧结构(11-bit 标准帧)
      • 2.2 11-bit 还是 29-bit?
      • 2.3 OBD-II 11-bit 寻址规则(关键!)
      • 2.4 实战采样:一帧完整 CAN
    • 第 3 章 ISO-TP 传输层
      • 3.1 为什么需要 ISO-TP
      • 3.2 四种 PCI(Protocol Control Information)帧
      • 3.3 PCI 字节详解
      • 3.4 多帧重组的完整交互(实战)
      • 3.5 多帧重组的关键代码

[AI 整理声明]本文内容来源于作者在汽车网络安全实战项目中积累的测试笔记,由 AI(Claude)整理归纳输出;行文措辞、内容组织和格式均由 AI 完成,原始测试发现与知识积累来自作者本人。

本文是「OBD-II/UDS 汽车网络安全实战手记」系列第一篇,内容基于真实渗透测试项目。每个知识点配有实际采集到的总线报文作佐证,由浅入深,从"什么是 CAN 总线"一路到"如何识别 ISO-TP 多帧"。文中 F-XXX 为测试发现编号。

系列文章(一)协议基础 — CAN 总线与 ISO-TP(本文) · (二)UDS 服务字典 · (三)安全机制 — NRC + Session + SecAccess · (四)实战攻防与方法论


第 1 章 车载诊断的"为什么"和"是什么"

1.1 一句话起源

20 世纪 80 年代,加州空气委员会(CARB)要求所有上路汽车都必须能被一台外部设备读出排放相关故障——这就是 OBD(On-Board Diagnostics)。1996 年起 OBD-II 在美国成为法规要求,欧洲叫 EOBD(2001 年起),中国是 GB18352 系列法规(2007 年起强制)。

OBD-II 的最初目的非常窄:让维修工和环保部门读"我的发动机控制器排放有没有故障"。当年没人想过攻击者会用同一个接口去刷固件、关闭电池保护、抹掉攻击痕迹。

1.2 OBD-II 是什么、UDS 是什么、它们的关系

OBD-II法规层面的最小诊断协议。物理上规定了一个 16 针的方梯形插座(J1962 接口),逻辑上规定了 10 个 Mode(0x01-0x0A)的命令:

Mode名称含义
0x01Show current data读实时排放参数(车速、转速、O₂ 浓度、冷却液温度等)
0x02Show freeze frame读冻结帧(DTC 触发瞬间的现场快照)
0x03Show stored DTCs读已存储的排放相关 DTC
0x04Clear DTCs清除 DTC 和相关排放数据
0x05Test results, O₂ sensor氧传感器测试结果(仅非 CAN 总线用)
0x06Test results, other其他排放部件/系统测试结果
0x07Show pending DTCs读 pending DTC
0x08Control on-board component控制排放相关部件(EVAP 系统泄漏测试等)
0x09Request vehicle info读车辆信息(VIN、校准 ID、CVN 等)
0x0APermanent DTCs读永久 DTC(不能被 Mode 0x04 清掉

OBD-II 只关心"排放相关",连"开/锁车门"都没有。Mode 0x0A PermanentDTC 是排放反取证的法规防线——见第 8 章。

UDS(Unified Diagnostic Services,ISO 14229)OEM 自定义诊断的统一标准。它定义了一套服务标识 SID(0x10-0x3E + 0x83-0xBF),覆盖整车所有 ECU 的所有诊断需求——读写 ECU 参数、刷固件、控制执行器、读故障码、控制安全访问……

UDS 的服务可以复用OBD-II 已有的功能(比如 UDS 0x19 ReadDTC 几乎涵盖了 OBD-II Mode 0x03)。所以现代车上 ECU 通常同时支持 OBD-II 和 UDS:OBD-II 接口用最少功能合规,UDS 是真正干活的接口。

1.2.1 SID(UDS)vs Mode(OBD-II)—— 容易混淆的概念

两者都是 CAN data 第一字节后面的"命令码"。区别如下:

对比项OBD-II ModeUDS SID
取值范围0x01-0x0A(固定 10 个)0x10-0x3E+0x83-0xBF(~30 个)
关心的事只关心排放相关ECU全部诊断功能
请求 ID通常用0x7DF广播或0x7E0-0x7E7OEM 自定义
正响应规则Mode + 0x40(0x01 → 0x41SID + 0x40(0x10 → 0x50
负响应格式7F + Mode + NRC7F + SID + NRC(完全一样)

关键:两者取值范围不重叠(0x01-0x0A vs 0x10+),所以一个 ECU 收到0x03就知道这是 OBD(读排放 DTC),收到0x19就知道这是 UDS(也是读 DTC,但功能丰富得多)。两套命令复用同一条 CAN 总线

部分服务功能等价:

OBD-II Mode 0x03 读 DTC ≈ UDS SID 0x19 ReadDTC(UDS 多 20+ sub-function) OBD-II Mode 0x04 清 DTC ≈ UDS SID 0x14 ClearDTC OBD-II Mode 0x09 读 VIN ≈ UDS SID 0x22 DID 0xF190 OBD-II Mode 0x0A 永久 DTC ≈ UDS SID 0x19 sub 0x15

协议栈关系:

┌──────────────────┐ │ CAN 总线物理层 │ (ISO 11898) └──────────────────┘ │ ┌──────────────────┐ │ ISO-TP 传输层 │ (ISO 15765-2, 解决 > 8 字节的多帧重组) └──────────────────┘ │ ┌──────────┴──────────┐ │ │ ┌───────────────┐ ┌───────────────┐ │ OBD-II │ │ UDS │ │ (ISO 15031) │ │ (ISO 14229) │ │ 10 个 Mode │ │ ~30 个 SID │ └───────────────┘ └───────────────┘

1.3 为什么诊断协议是"攻击金矿"

从渗透角度看,诊断协议有三个让人馋的属性:

  1. 诊断协议直接命中 ECU 内部状态机——绕过应用层,直接和 ECU 固件对话
  2. 接口物理可达——OBD-II 插座就在驾驶位下方,任何人都能插
  3. 协议明文 + 无加密——除非 OEM 主动加密(极少数高端车有),所有报文裸跑在 CAN 上

OBD-II 设计时假设"接到诊断口的是维修工/合规检测设备",完全没有威胁模型。直到 2010 年 Karl Koscher、Stefan Savage 等人发表的《Experimental Security Analysis of a Modern Automobile》、2015 年 Charlie Miller 与 Chris Valasek 远程刷写 Jeep 才把这个洞撕开。


第 2 章 CAN 总线层基础

2.1 帧结构(11-bit 标准帧)

CAN 是个广播总线——一帧报文,全总线所有节点都能听到。一帧标准 CAN 数据帧(500 kbit/s 是车上 OBD 常见波特率)的关键字段:

SOF | ID(11) | RTR | IDE | r0 | DLC(4) | Data(0-8 bytes) | CRC(15) | ACK | EOF 1bit | 11 bits | 1 | 1 | 1 | 4 bits | 0-64 bits | 15 bits | 2 | 7

我们最关心的就两件事:

  • ID:11 bit,决定总线优先级(数值越小优先级越高)和目标(OBD-II 用 ID 来路由请求/响应)
  • Data:最多 8 字节负载

2.2 11-bit 还是 29-bit?

CAN 帧的 IDE 位决定是 11-bit (Classic) 还是 29-bit (Extended)。OBD-II 法规允许两种:

  • 11-bit:诊断请求广播0x7DF,响应来自各 ECU0x7E8-0x7EF
  • 29-bit:诊断请求广播0x18DB33F1,响应0x18DA F1 xx

商用车(重卡)多用 29-bit(J1939 协议族要求);乘用车多用 11-bit。具体到诊断侧,11-bit 在 OBD 中更常见,本文示例均以 11-bit 为基础。

2.3 OBD-II 11-bit 寻址规则(关键!)

OBD-II 规范的请求/响应 ID 有配对规则,标准 OBD-II 响应 ID = 请求 ID + 8:

请求 (tester → ECU) 响应 (ECU → tester) 广播 0x7DF 各 ECU 用自己的 phy ID phy 0x7E0 0x7E8 ← 标准 OBD ECU 用 0x7E0-0x7E7 / 0x7E8-0x7EF phy 0x7E1 0x7E9 ... phy 0x7E7 0x7EF

2.4 实战采样:一帧完整 CAN

最常见的诊断请求是0x10 0x03(进 extended session)。完整 CAN 帧:

CAN ID = 0x777 DLC = 8 Data = 02 10 03 00 00 00 00 00 │ │ │ └──────────────────► padding (5 字节 0x00) │ │ └──► UDS sub-function = 0x03 (extendedDiagnostic) │ └──► UDS SID = 0x10 (DiagnosticSessionControl) └──► ISO-TP PCI byte = 0x02 (SingleFrame, length=2)

这是后面所有协议层的基础——CAN 帧只是个容器,真正的诊断信息在 Data 区里,Data 的第 1 字节是 ISO-TP 的 PCI 头。


第 3 章 ISO-TP 传输层

3.1 为什么需要 ISO-TP

CAN 一帧最多 8 字节。但 UDS 读个 VIN 就 17 字节,读个 SupportedDTC 列表可能几百字节。怎么办?

答案是ISO-TP(ISO 15765-2)——在 CAN 之上加了一层分段重组协议,类似 IP 之上的 TCP(但简单得多)。

3.2 四种 PCI(Protocol Control Information)帧

ISO-TP 用 Data 的第 1 字节高 4 位区分帧类型:

高 4 位名称含义
0SF (SingleFrame)数据 ≤7 字节,一帧打完
1FF (FirstFrame)多帧的第一帧
2CF (ConsecutiveFrame)多帧的后续帧
3FC (FlowControl)接收方控制发送速率

3.3 PCI 字节详解

SingleFrame (SF)

PCI = 0x0L, L = 数据长度 (1-7) 后 7 字节 = 实际数据 (不足补 padding)

FirstFrame (FF)

PCI = 0x1L LL, LL...L = 12-bit 总数据长度 (1-4095) 后 6 字节 = 数据的前 6 字节

ConsecutiveFrame (CF)

PCI = 0x2N, N = 序号 (0-F, 循环) 后 7 字节 = 后续数据

FlowControl (FC):接收方发回,告诉发送方"我准备好了,继续发"

PCI byte 0 = 0x3F, F = Flag (0=continue, 1=wait, 2=overflow) byte 1 = BS (Block Size, 0=一次性收完, N=每 N 个 CF 后再 FC 一次) byte 2 = STmin (相邻 CF 间隔最小毫秒)

3.4 多帧重组的完整交互(实战)

在读 0x759 的 SupportedDTC 列表时收到了 173 字节响应。完整流程:

方向 CAN ID Data 说明 ────── ────── ──────────────────────────────── ──────────────────────── T → E 0x751 03 19 0A 00 00 00 00 00 请求: SID 19 sub 0A param 00 │ │ │ │ │ └──► sub-function 0x0A (SupportedDTC) │ └──► SID 0x19 (ReadDTC) └──► PCI: SF, length=3 E → T 0x759 10 AD 59 0A 09 D1 00 00 FF: 总长 0x0AD = 173 字节 │ │ │ │ └──────────────────► 数据前 5 字节 │ │ └──► SID + 0x40 = 0x59 (正响应) + sub byte │ └──► 总长低 8 位 = 0xAD └──► PCI 高 4 位 = 1 (FF), 低 4 位 = 总长高 4 位 = 0 T → E 0x751 30 00 00 00 00 00 00 00 FC: 0x30 = continue, │ │ │ BS=0 (一次发完), STmin=0 │ │ └──► STmin (相邻 CF 间隔) │ └──► BS (块大小) └──► PCI = 0x30 (FC, flag=continue) E → T 0x759 21 D1 01 00 91 01 00 91 CF #1 (序号=1) │ └──────────────────────────► 后续 7 字节数据 └──► PCI: hi=2 (CF), lo=1 (seq=1) E → T 0x759 22 02 00 91 03 00 91 04 CF #2 (序号=2) ...继续直到收到 173 字节

收齐 173 字节后,重组的完整 UDS 响应是:

59 0A 09 D1 00 00 D1 01 00 91 01 00 91 02 ... (171 字节 DTC 列表)

3.5 多帧重组的关键代码

// 简化版多帧接收while(true){TCANFD f=receive_can_frame();if(f.FIdentifier!=expected_resp_id)continue;u8 pci=f.FData[0];u8 hi=pci>>4;// PCI 高 4 位决定帧类型if(hi==0){// SingleFrames32 len=pci&0x0F;memcpy(out,&f.FData[1],len);returnDONE;}if(hi==1){// FirstFrametotal_len=((pci&0x0F)<<8)|f.FData[1];// 12-bit 长度memcpy(out,&f.FData[2],6);// 前 6 字节collected=6;send_flow_control(req_id);// 关键!必须发 FCexpected_seq=1;}if(hi==2){// ConsecutiveFrameu8 seq=pci&0x0F;if(seq!=expected_seq)continue;// 序号不对就丢expected_seq=(expected_seq+1)&0x0F;// 0-F 循环memcpy(out+collected,&f.FData[1],7);collected+=7;if(collected>=total_len)returnDONE;}}
http://www.cnnetsun.cn/news/3273520.html

相关文章:

  • VMware安装CentOS7教程:从下载镜像到网络设置_一步不落_新手零失败
  • 机器人行业面试全攻略:从0到1拿下心仪Offer,看这一篇就够了
  • 医用远程会诊系统:同屏会诊模块的设计与实现
  • Claude 复制内容带乱码太头疼?AI 导出鸭一键解决复制导出难题,程序员高效必备工具
  • 自定义图表:选用Highcharts能否支持动态定制图表
  • 抖音批量下载助手:三步轻松收藏心仪视频内容
  • JetBrains IDE试用期重置完整指南:高效管理开发工具的生命周期
  • Python 3.12 环境迁移:解决 venv 虚拟环境 pip 报错的 2 种可靠方法
  • Dify 工作流编排实战:用可视化 DAG 替代手写 Prompt 链的工程经验
  • AI短剧告别开盲盒!万兴3D导演台让创作者像搭积木一样精准控片
  • 【2013-11-10】Android应用开发笔记:分享至微博及友盟的基本使用
  • 2026 年 EDI 许可证办理服务商筛选指南:评测维度与机构能力分层对比
  • WVP-GB28181-Pro语音对讲功能深度解析与部署优化实践
  • 第一章Netty, NIO基础
  • 给 AI 长出一件新本事——插件不用敲,是聊出来的
  • SpringCloud服务治理实践
  • 拿来即用!C#上位机实现PLC远程控制:Modbus TCP协议+网口通信全解析
  • Istio EnvoyFilter 的高级用法:不该改的地方别手贱
  • 《唤醒你的AI同事:WorkBuddy从零上手》007:初始配置向导
  • 生猪接产智能产房监控 QT信创完整工程
  • 2000-2025年上市公司、地级市国家级“东数西算”工程政策试点DID数据
  • UVa 642 Word Amalgamation
  • Linux之进程(六)--环境变量
  • 单 RGB 摄像头实现复杂环境导航!Robostral Navigate 成功率达 76.6%
  • 【LeetCode 手撕算法】(细节知识点总结)
  • 如何构建自进化企业级 Agent 平台
  • 浏览器自动化_agent-browser
  • OpenClaw基础学习
  • 2026年房地产动画行业观察
  • 前端 CDN 缓存策略调优:静态资源、HTML 与 API 分层设计