UDS 服务仿真实验
👨💻 最近在 Github 上找到一个比较符合学习需求的 UDS 模拟器,拿来补充了一下,顺带搞个仿真实验教程。
UDS 服务仿真实验
参考资料:
- 写本文的时候这一系列还是都能免费看的,才过了没几天全变 VIP 文章了 😓【2024.07.23】
📌 本文中的理论知识部分均摘自上述参考资料
UDS:Unified Diagnostic Services,中文名称统一诊断服务,简称UDS。UDS协议是当前使用最为广泛的车载控制器诊断协议。UDS 既可应用于CAN总线,又可应用于以太网、LIN总线等。
环境搭建
本文测试使用系统:Ubuntu 22.04
本文实验所用模拟器:uds-server-simulator
- 模拟应用于 CAN 总线上的 UDS 诊断协议
1 | 安装依赖环境 |
CAN 数据帧
首先了解一下 CAN 数据帧结构:
简写 | 全称 | 中文含义 |
---|---|---|
N_PCIType | Network Protocol Control Information Type | 网络层协议控制信息类型 |
SF_DL | Single Frame Data Length | 单帧数据长度 |
N_Data | Network Layer Data | 网络层数据 |
FF_DL | First Frame Data Length | 首帧数据长度 |
SN | Sequence Number | 序号 |
FS | Flow Status | 流控帧状态 |
BS | Block Size | 块大小(允许一次最大可发送连续帧数) |
STmin | Separation Time Minium | 最小间隔时间 |
CF | Consecutive Frame | 连续帧、后续帧 |
- N_PCIType = 0 时,表示当前帧为单帧,首字节的低 4bit 代表当前帧后续的数据长度
- N_PCIType = 1 时,表示将要发送的数据不足以在单个帧中发送完毕,需要拆分,当前帧为首帧
- N_PCIType = 2 时,表示当前帧为拆分发送的数据,当前帧为连续帧,SN表示当前的帧序号
- N_PCIType = 3 时,表示当前帧为流控帧,能够调整 CF N_PDUs 发送速率
0x10 Diagnostic Session Control
服务简介
诊断会话控制服务,SID: 0x10
该服务内容详解:《UDS协议从入门到精通》系列——图解0x10:诊断会话控制
对应 ISO-14229-1 页码:36
UDS 协议中定义了三种会话模式,同一时间只能处于一种会话模式下,10 服务则用于切换会话模式:
- 默认会话:上电默认进入的会话,在切换到任意非默认会话时,默认会话中的诊断功能同样可用
- 编程会话
- 扩展会话
推荐的UDS诊断服务配置方式见下表(摘自canfd.net):
SID(0X) | Service | 默认会话 | 编程会话 | 扩展会话 |
---|---|---|---|---|
10 | Diagnostic Session Control | Y | Y | Y |
11 | ECU Reset | Y | Y | Y |
27 | Security Access | - | Y | Y |
28 | Communication Control | - | - | Y |
3E | Tester Present | Y | Y | Y |
85 | Control DTC Setting | - | - | Y |
22 | Read Data By Identifier | Y | Y | Y |
2E | Write Data By Identifier | - | Y* | Y* |
14 | Clear Diagnostic Information | Y | - | Y |
19 | Read DTC Information | Y | - | Y |
2F | Input Output Control By Identifier | - | - | Y* |
31 | Routine Control | - | - | Y* |
34 | Request Download | - | Y* | - |
36 | Transfer Data | - | Y* | - |
37 | Request Transfer Exit | - | Y* | - |
- $x^a$:在默认会话模式中是否也允许0x86服务是特定于实现的,即不一定支持该服务;
- $x^b$:如果是访问安全相关的DID则需基于安全访问服务,因此如果是该情况下要进非默认会话;
- $x^c$:如果是访问安全相关的内存区域则需要安全访问服务,因此如果是该情况下要进非默认会话;
- $x^d$:可以在默认和非默认会话中动态定义DID,因此非默认会话也支持这个服务;
- $x^e$:如果是安全相关例程需安全访问服务,因此需要非默认会话模式;需要客户端主动停止的例程也需要非默认会话模式。
请求 / 响应数据包分析
使用 cansend 发送诊断会话控制请求,请求更改会话模式为扩展会话模式:
1 | cansend vcan0 733#0210030000000000 |
查看 candump 捕获到 vcan0 接口上的 can 报文数据:
1 | vcan0 733 [8] 02 10 03 00 00 00 00 00 |
0x10 服务请求数据帧:
Sub-Function 的 1 Byte 数据代表 SessionType:
SessionType | 含义 |
---|---|
0x00 | 保留未使用 |
0x01 | 默认会话模式(Default Session),ECU上电后默认进入的模式,该模式无需 0x3E 服务进行维持 |
0x02 | 编程会话模式(Programming Session),主要用于 ECU 软件升级刷写 |
0x03 | 扩展会话模式(Extended Diagnostic Session),用于解锁需要高权限的诊断服务,基本覆盖各类服务(最常见的是读写 DID 前先进入扩展会话模式) |
0x03 | 安全模式(Safety System Diagnostic Session),使能所有跟车载系统安全相关的服务(如安全气囊) |
当前使用的 UDS 模拟器暂不支持 0x03 安全模式
0x10 服务肯定响应数据帧:
- P2Server_max:表示 ECU 在收到请求与给出响应的时间间隔为 50 ms (0x32)
- P2* Server_max:表示 ECU 发送 NRC 0x78 之后继续发送下帧诊断响应报文的时间间隔为 5000ms (0x01F4 * 10)
0x10 服务否定响应数据帧:
- 使用 cansend 发送一个长度为 3 Byte 的请求,得到否定响应:
1 | vcan0 733 [8] 03 10 03 00 00 00 00 00 |
可能出现的 NRC(否定响应码):
NRC | 描述 | 含义 |
---|---|---|
0x12 | sub-functionNotSupported | 子功能参数不受支持 |
0x13 | incorrectMessageLengthOrInvalidFormat | 消息长度错误 |
0x22 | conditionsNotCorrect | 不满足请求标准/条件 |
0x22 ReadDataByIdentifier
服务简介
通过 ID 读数据,SID: 0x22
该服务内容详解:《UDS协议从入门到精通》系列——图解0x22:通过ID读数据
对应 ISO-14229-1 页码:106
DID:Data Identifier,数据标识符,能够用来标识数据的代号
22 服务能够查询指定 DID 对应的数据
ISO 14229-1 中 337 页的附录中定义了一部分 DID 含义,其中一些 常见的DID:
DID | 描述 | 含义 |
---|---|---|
0xF180 | BootSoftwareIdentificationDataIdentifier | ECU 引导软件标识 |
0xF187 | vehicleManufacturerSparePartNumberDataIdentifier | 备件编号 |
0xF18B | ECUManufacturingDateDataIdentifier | ECU 生产日期 |
0xF190 | VINDataIdentifier | 车辆 VIN 码 |
请求 / 响应数据包分析
使用 cansend 发送请求读取 DID 为 0xF190 (VIN)的数据:
1 | !/bin/bash |
查看捕获到 的 can 报文数据:
1 | 请求切换到扩展会话模式 |
0x22 服务请求数据帧:
0x22 服务可以请求一个或多个DID(但实验所使用的 uds 模拟器暂不支持请求多个DID)
0x22 服务不支持 sub-function
0x22 服务肯定响应数据帧(单个DID):
由于数据超出一个 CAN 帧能够携带的数据长度,需要分段发送,接收到一个分段数据的首帧:
发送流控帧,请求后续的数据:
接收到后续的连续帧数据:
拼接即得到 VIN(0xF190) 数据:LUAU2AUB3GE383467
0x22 服务否定响应数据帧:
使用 cansend 发送一个请求不存在的 DID 的数据:
1 | vcan0 733 [8] 03 22 DE AD 00 00 00 00 |
0x22 服务支持的否定响应代码:
NRC | 描述 | 含义 |
---|---|---|
0x13 | incorrectMessageLengthOrInvalidFormat | 消息长度错误 |
0x14 | responseTooLong | 响应消息太长,比如一个请求包含多个DID,超过传输协议允许最大长度 |
0x22 | conditionsNotCorrect | 当前条件不满足 |
0x31 | requestOutOfRange | 请求的DID对当前设备或在当前会话不受支持,参数错误 |
0x33 | securityAccessDenied | 安全访问错误,比如访问的DID数据是安全数据,但当前等级未解锁 |
0x22 服务 NRC 处理流程:
0x27 SecurityAccess
服务简介
安全访问,SID:0x27
该服务内容详解:《UDS协议从入门到精通》系列——图解0x27:安全访问
对应 ISO-14229-1 页码:47
- ECU 中某些重要数据会受到安全保护,对其进行读或写操作时需要先申请安全访问,解锁对应的安全等级后才能进行后续操作
- 例如在通过 0x2E 服务对指定 DID 进行写操作时,或通过0x34 服务进行请求下载操作时通常需要安全解锁
工作流程(摘自👆详解):
该服务支持 sub-function
请求 / 响应数据包分析
本实验采用的模拟器自行定义了 03, 19, 21 三个 安全等级,以下是各等级的限制(这几个安全等级均为自行定义):
- 安全等级 03:读数据无需安全访问,写数据需要安全访问(fake key,固定key:
deadbeef
)- 安全等级 19:读数据无需安全访问,写数据需要安全访问
- 安全等级 21:读写数据均需要安全访问
所使用的 key 生成算法如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 uint8_t *security_algorithm(uint8_t *seed_ptr) {
uint8_t *key_ptr = (uint8_t *)malloc(sizeof(uint8_t)*4);
uint8_t Seed[4];
uint8_t Const[4];
uint8_t Key[4];
uint32_t wConst = 0xdeadbeef;
Seed[0] = *seed_ptr;
Seed[1] = *(seed_ptr+1);
Seed[2] = *(seed_ptr+2);
Seed[3] = *(seed_ptr+3);
Const[3] = (uint8_t)((wConst & 0xff000000) >> 24);
Const[2] = (uint8_t)((wConst & 0x00ff0000) >> 16);
Const[1] = (uint8_t)((wConst & 0x0000ff00) >> 8);
Const[0] = (uint8_t)(wConst & 0x000000ff);
Key[0] = Const[0] * (Seed[0] * Seed[0]) + Const[1] * (Seed[1] * Seed[1]) + Const[2] * (Seed[0] * Seed[1]);
Key[1] = Const[0] * (Seed[0]) + Const[1] * (Seed[1]) + Const[3] * (Seed[0] * Seed[1]);
Key[2] = Const[0] * (Seed[2] * Seed[3]) + Const[1] * (Seed[3] * Seed[3]) + Const[2] * (Seed[2] * Seed[3]);
Key[3] = Const[0] * (Seed[2] * Seed[3]) + Const[1] * (Seed[3]) + Const[3] * (Seed[2] * Seed[3]);
key_ptr = Key;
return key_ptr;
}
接下来使用安全等级 21 进行演示,捕获到的 can 报文如下:
1 | 请求切换到扩展会话模式 |
0x27 服务请求数据帧:
请求种子(seedRequest)请求中,SID 后一字节数据为奇数,代表对应的安全等级,securityAccessDataRecord 一般不使用
发送密钥(sendKey)请求中,SID 后一字节数据为偶数,其值为目标的安全等级+1
0x27 服务肯定响应数据帧:
大多情况下肯定响应帧只有 0x67 和 Sub-Function,只有在请求种子的响应中会携带 securitySeed 数据
0x27 服务否定响应数据帧:
发送一个错误的密钥进行否定响应演示,捕获到的 CAN 数据如下:
1 | [TX] 733 [8] 0210030000000000 |
0x27 服务支持的否定响应代码:
NRC | 描述 | 含义 |
---|---|---|
0x12 | sub-functionNotSupported | 子功能参数不受支持 |
0x13 | incorrectMessageLengthOrInvalidFormat | 消息长度错误 |
0x22 | conditionsNotCorrect | 不满足请求标准/条件 |
0x24 | requestSequenceError | 请求顺序错误,比如应该先发送请求种子,而不是先发送密钥数据 |
0x31 | requestOutOfRange | 请求中携带的数据是无效的 |
0x35 | invalidKey | 密钥不匹配,即Tester计算出来的key和目标ECU计算出来的不一样:若始终不匹配还不断尝试,ECU会回复下面的NRC=36,告诉你已经超过失败的次数了,不能再请求安全解锁了 |
0x36 | exceededNumberOfAttempts | 超过最大试错次数,已达到解锁最大错误次数,若你执意再请求,ECU就会回复你下面的NRC=37,意思是ECU现在不接受安全访问,这就是ECU锁死的现象,需等待一定时间后才可继续请求安全访问 |
0x37 | requiredTimeDelayNotExpired | 当前服务器处于延时状态,超时时间未到 |
未额外声明时,当前服务的上述 NRC 在 UDS 模拟器中均可支持
0x2E WriteDatabyIdentifier
服务简介
通过 ID 写数据,SID:0x2E
该服务内容详解:《UDS协议从入门到精通》系列——图解0x2E:通过ID写数据
对应 ISO-14229-1 页码:162
本服务不支持 sub-function,且不支持一次性写多个 DID
请求 / 响应数据包分析
UDS 模拟器的配置文件中声明了可用的 DID,接下来使用默认 TBOX 配置中的 DID 0xF190(VIN码) 作为目标 DID 进行演示(安全等级 19)
发送请求:请求读取 F190 数据,然后请求写 F190 数据(写入 Hello world
),最后请求读 F190 数据,捕获到的 CAN 数据包如下:
1 | 请求切换到扩展会话模式 |
0x2E 服务请求数据帧:
0x2E 服务肯定响应数据帧:
0x2E 服务否定响应数据帧:
尝试请求写一个安全等级尚未解锁的目标 DID:
1 | [TX] 733 [8] 0210030000000000 |
0x2E 服务支持的否定响应代码:
NRC | 描述 | 含义 |
---|---|---|
0x13 | incorrectMessageLengthOrInvalidFormat | 消息长度错误 |
0x22 | conditionsNotCorrect | 当前条件不满足 |
0x31 | requestOutOfRange | 请求参数不受支持,参数错误 |
0x33 | securityAccessDenied | 安全访问错误,比如访问的DID数据是安全数据,但当前等级未解锁 |
0x72 | generalProgrammingFailure | 通用编程错误,一般写入内存出错就报这个NRC |
UDS 模拟器暂不支持 NRC 0x72
0x2E 服务 NRC 处理流程:
TODO
0x34, 0x35, 0x36, 0x37 服务