跳转至

UDS诊断学习

UDS诊断学习

UDS(统一诊断服务)是车上很重要的一个诊断协议,但是网上并没有针对这一块的练习板,纯讲理论没啥意思

接下来通过 STM32 开发板进行 UDS 诊断练习,使用 CAN 调试仪实际与目标进行诊断交互,配套固件也会传上来,师傅们可以自己买板子烧进去练习

附一篇很不错的文章,简单明了:UDS诊断入门

本文不做理论知识讲解,复现之前自己先看理论知识嗷

环境配置

固件

更新时间:2023.12.28

下载后改后缀为 zip 嗷,语雀连 zip 都不让传...

hex.pdf

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 1702800069852-aa73fd85-1d7a-403b-890e-a5c64064a009.png
TJA1050 CAN 控制器接口模块(这个没有排针,要是自己有电烙铁可以自己焊上排针方便接插杜邦线) https://item.taobao.com/item.htm?spm=a1z09.2.0.0.47042e8ddx2KAs&id=522575628213&_u=h2hmqj03b128 1702798088723-91d3094e-7fab-419e-8646-4b92564e65cc.png
CAN 调试仪(随便一个 USB 转CAN 的设备,用惯 PCAN 了) https://item.taobao.com/item.htm?spm=a21n57.1.0.0.423a523cJjV592&id=666301886723&ns=1&abbucket=10#detail 1702798166509-d1c58492-3a89-4371-9329-23ca02479e6c.png
杜邦线若干(公对公、公对母) https://item.taobao.com/item.htm?spm=a1z10.3-c-s.w4002-24706531953.29.12c96a4bWUsdlg&id=558182761958 1702798272744-20551028-3bdd-463c-ae19-ae687a68c433.png
Jlink 或 STlink(主要是烧写固件的,一定要买带这个排线的,不然自己去接杜邦线去嗷) https://item.taobao.com/item.htm?spm=a21n57.1.0.0.7994523cNdeWR4&id=680701967721&ns=1&abbucket=10#detail 1702798590667-666d3ef3-036e-4ebb-b2d8-df683798ec82.png

软件

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 为例)

1702802017251-b2e910bd-d433-4ca2-a261-40f309b44602.png

固件刷写

安装好 Jflash 之后打开,选择新建项目

1702799315083-71cf1d93-d181-4e59-b5ea-62957c9bcd1b.png

点击三个点,在输入框输入 STM32F103ZE 过滤出来,选择下面那个短的,然后 ok

1702799406640-19de9dab-b305-4f47-b97a-5167ed6118b6.png

把两个固件都拖到右边的数据文件窗口,然后点击 Target -> Production Programming 烧写固件(hex 文件都是记录着地址信息的,直接烧录即可)

1702799774286-b3f20c25-6344-42ef-8d42-d6f2b27e8674.png

然后打开串口调试工具,波特率设置为 115200,按下复位键看看是不是有输出了,如下输出说明正常

1703724269784-971bc8d1-bcbf-4ac0-9fcc-26503e9badb7.png

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就是当前会话状态

1703724427129-e5152ba1-6d9e-4ece-adda-c15ba7c83d9a.png

在 14229 标准里面还有很多 ID,比如F1 90读取 VIN 码等(开发板暂未实现),以及厂商也会自定义 ID

10 诊断会话控制

使用7DF # 03 22 F1 86读取当前会话

1703724427129-e5152ba1-6d9e-4ece-adda-c15ba7c83d9a.png

切换到扩展会话7DF # 02 10 03然后 0x22 读取会话确认一下7DF # 03 22 F1 86,一般在扩展会话进行一些高权限的操作,比如读写数据

1703724811222-0064f227-341b-4c99-ac65-4a5463a1fa73.png

切换到编程会话7DF # 02 10 02此时观察串口可以看到进入到了 bootloader 的代码中,一般在这个会话状态进行刷写烧录相关操作

1703725781587-f245dd83-4c19-420e-bc79-92937b48d42f.png

当进入非默认会话后如果不及时发送 3E 维持会话,过一阵就会退回默认会话

1703724881555-c9756b06-d35c-4639-b38d-82d63d4ae4df.png

3E 会话维持

前面 10 服务提到,如果不及时发送会话维持,过一阵就会退回到默认会话,会话维持的服务是 3E

有两种子功能,00 和 80

7DF # 02 3E 00表示需要诊断服务端响应

1703728220631-e9e184d2-0123-4b36-807d-f0853bb74bd0.png

7DF # 02 3E 80表示不需要诊断服务端响应,具体表现为你发送之后并不会收到回应

1703728599343-f5068396-0aa9-4f7a-9bff-c8e90cf92e32.png

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

1703730234667-9c2c3145-aa26-464c-a5c5-683c8085c9d3.png

那接下来就可以请求 seed 了,发送721 # 02 27 01发现报错了

1703730862773-816000be-063f-4612-859e-e03df98dd3fc.png

查阅 NRC 响应,是因为当前会话状态不支持此操作

1703730959492-1536c5e5-74e1-4a80-8dff-25c8fa730037.png

那么切换到扩展会话7DF # 02 10 03,然后发送维持会话7DF # 02 3E 80

再次请求便可得到种子,但是我们并不知道怎么从种子算出密钥呀,这时候就要反编译固件分析逻辑了

可以把 app.hex 拖到 IDA 里面以 ARM 小端格式打开,搜索字符串,我把 SeedToKey 字符串加在了代码里方便定位😋

1703731723583-fbd17ca4-fbc9-44a1-9b03-fe7855b921d9.png

转成伪代码可以看到具体逻辑,a1 就是传进来的 seed,v2 就是计算出来的 key

1703731756591-bbb09e40-64b4-4b13-a126-43f796ac6f32.png

让 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}")

请求种子

1703732056529-f5dd1d01-8ffa-4207-936f-6c3484fa1d8c.png

计算

1703732139582-e78b509a-be17-47d1-8aa9-c764286e963d.png

返回密钥,成功通过安全访问

1703732089928-534dd467-635a-473f-8051-b622bfc606a2.png

如果你多次发错密钥,则会得到一个否定响应,表示尝试解锁次数已经达到了设定的上限

1703733167303-901bc70d-dcee-402d-8185-86d693f11089.png

1703733211667-5dff2f7b-6b3f-44fa-9c80-a6595be37a85.png

11 复位功能

先进拓展会话,再发送7DF # 02 11 01

1703735935869-73c4e6f4-ee46-4ce4-84dd-c99146ad3741.png

可以在串口中观察到设备重启,与按下复位按键效果是一样的

1703735993444-4a617b76-47dd-4f10-b299-b820c8113feb.png

23 通过地址读数据(暂未实现)

操作地址相比上面编码要复杂一些,我们先来看一下 14229 中给出的例子,从内存地址 0x20481392 读取 259 字节数据,则构造 UDS 诊断命令如下

数据 含义
0x23 0x23服务
0x24 地址和长度定义
0x20 地址
0x48
0x13
0x92
0x01 读取长度
0x03

其中地址和长度定义指的是:0x24 换成二进制,低四位是地址的长度 0100 = 0x4 字节,高四位是读取长度的长度 0010 = 0x2 字节

1703734297267-601b323e-363f-4eb5-8517-87b7445cb846.png

此处为语雀卡片,点击链接查看

原文: https://www.yuque.com/hxfqg9/iot/mawulsqfigf258x7