UDS诊断学习
UDS诊断学习
UDS(统一诊断服务)是车上很重要的一个诊断协议,但是网上并没有针对这一块的练习板,纯讲理论没啥意思
接下来通过 STM32 开发板进行 UDS 诊断练习,使用 CAN 调试仪实际与目标进行诊断交互,配套固件也会传上来,师傅们可以自己买板子烧进去练习
附一篇很不错的文章,简单明了:UDS诊断入门
本文不做理论知识讲解,复现之前自己先看理论知识嗷
环境配置
固件
更新时间:2023.12.28
下载后改后缀为 zip 嗷,语雀连 zip 都不让传...
Github 也放一份 https://github.com/yichen115/STM32_UDS_Demo/releases
硬件
淘宝店只是随便搜了几个,实际大部分我都是从闲鱼买的
名称 | 链接 | 图片 |
---|---|---|
STM32F103ZE 开发板 | https://item.taobao.com/item.htm?spm=a21n57.1.0.0.38ba523cLpQHl8&id=662393747123&ns=1&abbucket=10#detail | |
TJA1050 CAN 控制器接口模块(这个没有排针,要是自己有电烙铁可以自己焊上排针方便接插杜邦线) | https://item.taobao.com/item.htm?spm=a1z09.2.0.0.47042e8ddx2KAs&id=522575628213&_u=h2hmqj03b128 | |
CAN 调试仪(随便一个 USB 转CAN 的设备,用惯 PCAN 了) | https://item.taobao.com/item.htm?spm=a21n57.1.0.0.423a523cJjV592&id=666301886723&ns=1&abbucket=10#detail | |
杜邦线若干(公对公、公对母) | https://item.taobao.com/item.htm?spm=a1z10.3-c-s.w4002-24706531953.29.12c96a4bWUsdlg&id=558182761958 | |
Jlink 或 STlink(主要是烧写固件的,一定要买带这个排线的,不然自己去接杜邦线去嗷) | https://item.taobao.com/item.htm?spm=a21n57.1.0.0.7994523cNdeWR4&id=680701967721&ns=1&abbucket=10#detail |
软件
Jflash:About J-Flash 用来配合 Jlink 刷写固件的
TSMaster:Releases · TOSUN-Shanghai/TSMaster 用来进行 CAN 通信的
硬件连接
Jlink 直接通过排线与 STM32 开发板相连即可,STM32 右边的 USB 接口是个串口可以看 UART 日志
TJA1050 的 RX 接 STM32 的 PA11,TX 接 STM32 的 PA12,VCC 接STM32 的 5V,GND 接 STM32 的 GND
TJA1050 的 CANH 接 CAN 分析仪的 CAN_H、CANL 接 CAN 分析仪的 CAN_L( 这里以 PCAN 为例)
固件刷写
安装好 Jflash 之后打开,选择新建项目
点击三个点,在输入框输入 STM32F103ZE 过滤出来,选择下面那个短的,然后 ok
把两个固件都拖到右边的数据文件窗口,然后点击 Target -> Production Programming 烧写固件(hex 文件都是记录着地址信息的,直接烧录即可)
然后打开串口调试工具,波特率设置为 115200,按下复位键看看是不是有输出了,如下输出说明正常
UDS通信
这里有一本加了目录的14229协议的 PDF 可以自取
ISO14229 UDS中文翻译版-542页-加目录.pdf
UDS 定义了一系列的服务,每个服务都有自己的 ID 即 SID(Service Identifier),接下来通过开发板实际进行 UDS 诊断通信体验一下
22 通过ID读数据
22 服务通过 ID 读取数据,例如读取当前会话状态的 ID 是F1 86
,那么可以使用7DF # 03 22 F1 86
来读取当前会话,F1 86
后面跟的01
就是当前会话状态
在 14229 标准里面还有很多 ID,比如F1 90
读取 VIN 码等(开发板暂未实现),以及厂商也会自定义 ID
10 诊断会话控制
使用7DF # 03 22 F1 86
读取当前会话
切换到扩展会话7DF # 02 10 03
然后 0x22 读取会话确认一下7DF # 03 22 F1 86
,一般在扩展会话进行一些高权限的操作,比如读写数据
切换到编程会话7DF # 02 10 02
此时观察串口可以看到进入到了 bootloader 的代码中,一般在这个会话状态进行刷写烧录相关操作
当进入非默认会话后如果不及时发送 3E 维持会话,过一阵就会退回默认会话
3E 会话维持
前面 10 服务提到,如果不及时发送会话维持,过一阵就会退回到默认会话,会话维持的服务是 3E
有两种子功能,00 和 80
7DF # 02 3E 00
表示需要诊断服务端响应
7DF # 02 3E 80
表示不需要诊断服务端响应,具体表现为你发送之后并不会收到回应
27 安全访问
这时候就得注意区分一下物理寻址和功能寻址了,前面通过 7DF 进行功能寻址,所有 ECU 都能收到的,虽然我们的实验只有 STM32 这一块板子,但实际在车上肯定不是,而且可能一堆不同厂商的 ECU,那解锁安全访问的算法必然也要不同,所以 27 服务的时候要使用物理寻址,指定哪个 ECU
但这玩意都是代码里定义的呀,我们咋知道呢,可以使用 CaringCaribou 这个工具去探测嗷,要通过这个工具在 linux 下调用 pcan 需要安装 pcan 的 linux 驱动,具体流程如下:
在这里下载 pcan 的 linux 驱动:https://www.peak-system.com/Drivers.523.0.html 并解压出来
apt update
apt-get -y install linux-headers-$(uname -r) # 这一步可能需要
apt-get install libpopt-dev
make clean
make NET=NETDEV_SUPPORT
sudo make install
modporbe pcan # 这里有时候 modporbe can 即可
ip link set can0 up type can bitrate 500000
比如我这里探测的结果是:0x721
那接下来就可以请求 seed 了,发送721 # 02 27 01
发现报错了
查阅 NRC 响应,是因为当前会话状态不支持此操作
那么切换到扩展会话7DF # 02 10 03
,然后发送维持会话7DF # 02 3E 80
再次请求便可得到种子,但是我们并不知道怎么从种子算出密钥呀,这时候就要反编译固件分析逻辑了
可以把 app.hex 拖到 IDA 里面以 ARM 小端格式打开,搜索字符串,我把 SeedToKey 字符串加在了代码里方便定位😋
转成伪代码可以看到具体逻辑,a1 就是传进来的 seed,v2 就是计算出来的 key
让 chatGPT 写个 Python 脚本计算一下
def factory_security_seed_to_key(seed):
seed = int.from_bytes(seed.to_bytes(4, 'little'), 'big') #切换大小端序
xor = 0x4368656e # ASCII "Chen"
key = seed ^ xor
print(f"Key after xor: 0x{key:08X}")
for i in range(32):
if key & 0x80000000:
key = (key << 1) ^ xor
else:
key = key << 1
return key & 0xFFFFFFFF # 限制为32位整数
seed_value = 0x34023105 # 用你实际的种子值替换这里的数值
result = factory_security_seed_to_key(seed_value)
print(f"Seed: 0x{seed_value:08X}, Key: 0x{result:08X}")
请求种子
计算
返回密钥,成功通过安全访问
如果你多次发错密钥,则会得到一个否定响应,表示尝试解锁次数已经达到了设定的上限
11 复位功能
先进拓展会话,再发送7DF # 02 11 01
可以在串口中观察到设备重启,与按下复位按键效果是一样的
23 通过地址读数据(暂未实现)
操作地址相比上面编码要复杂一些,我们先来看一下 14229 中给出的例子,从内存地址 0x20481392 读取 259 字节数据,则构造 UDS 诊断命令如下
数据 | 含义 |
---|---|
0x23 | 0x23服务 |
0x24 | 地址和长度定义 |
0x20 | 地址 |
0x48 | |
0x13 | |
0x92 | |
0x01 | 读取长度 |
0x03 |
其中地址和长度定义指的是:0x24 换成二进制,低四位是地址的长度 0100 = 0x4 字节,高四位是读取长度的长度 0010 = 0x2 字节