前言
搞车联网安全的肯定都会涉及到 UDS 诊断,但除非能接触到实际项目,否则目前没见过有开源的能进行真实 UDS 诊断的练习板,只能看网上的一些理论知识,不咋过瘾
我在闲鱼买了一套 UDS_bootloader 的源码,目前跑通了几个 UDS 服务的功能,水一篇文章,介绍一下如何通过一块 STM32 的开发板实际练习 UDS 诊断
鉴于公众号文章发完了改不了几个字了,所以后面的读者可以去语雀查看最新的文档:
https://www.yuque.com/hxfqg9/iot/mawulsqfigf258x7
固件可以去语雀下载,也可以去 github 下载
https://github.com/yichen115/STM32_UDS_Demo/releases
另外本文不涉及理论知识的讲解,需要自己网上找其他文章学习或者直接读标准文档
硬件设备
下面说一下怎么攒一套硬件设备
首先是主体,STM32F103ZE 开发板,用来跑我们的 UDS 代码的
然后是 TJA1050 CAN 控制器,用来转换 CAN 信号的,淘宝卖的默认是没有排针的,要是自己有电烙铁可以焊上排针,方便接杜邦线
CAN 调试仪,用来与 STM32 建立 CAN 通信的,什么品牌无所谓,我用 PCAN 习惯了
Jlink 或其他可以刷写 STM32 的编程器,用来给 STM32 刷写固件的,注意最好是买个带排线的,可以直接连接调试器与 STM32,否则自己去找丝印接杜邦线嗷
最后就是若干杜邦线了
硬件连线
硬件连接情况如下图
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通信
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 这个工具去探测嗷,比如我这里探测的结果是: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
可以在串口中观察到设备重启,与按下复位按键效果是一样的
TODO
因为原来那个卖家是卖 UDS_bootloader 的,也就是通过 UDS 刷写升级 app 的,因此很多功能并没有实现,也没有实现的必要。等把目前他实现的功能梳理出来之后试试能不能自己写点,有需要源码的师傅可以加我私聊嗷,人家卖钱的东西就不公开发出来了(有良心但是不多)
文中有理解错的地方师傅们给指点指点嗷