4、诊断会话管理:默认会话、编程会话、扩展会话的切换逻辑与安全策略

各位同学,咱们今天聊聊诊断会话管理。说实话,这个主题在UDS协议里看着简单,但实际落地时坑特别多。我见过不少项目,因为会话切换没处理好,导致ECU在产线上刷写失败,或者售后诊断连不上。嗯,咱们今天就把这块彻底讲透。

4.1 三种会话的基本定位

UDS定义了三种标准会话:默认会话(Default Session)编程会话(Programming Session)扩展会话(Extended Session)。我个人习惯把它们比作ECU的三种工作模式:

  • 默认会话:ECU上电后的初始状态。说白了就是“日常模式”,只允许读取一些基本数据,比如VIN码、故障码。你想干点“出格”的事?不行,得先切换会话。
  • 编程会话:这是“刷写模式”。只有在这个会话下,才能执行34服务(请求下载)、36服务(传输数据)这些刷写相关的操作。安全等级最高,通常需要解锁。
  • 扩展会话:这是“调试模式”。你可以读写一些非易失性存储器、执行例程、做标定。我在项目中遇到过,很多OEM把扩展会话当作“高级诊断模式”,用来做产线终检或者售后深度排查。

核心要点:三种会话的权限是递增的。默认会话权限最低,扩展会话居中,编程会话最高。但注意,不是所有服务都能在任意会话下执行,这是OEM自定义的重点。

4.2 会话切换逻辑

切换会话用的是10服务(Diagnostic Session Control)。报文格式很简单:

请求:10 02    // 切换到编程会话(02)
响应:50 02 00 32 00 64  // 成功,P2_server_max=50ms,P2*_server_max=5000ms

这里有个细节:响应里会带两个时间参数——P2和P2*。P2是ECU处理单个请求的最大等待时间,P2*是当ECU需要更多时间处理时的扩展等待时间。我建议你们在设计时,把编程会话的P2*设得长一些,比如5秒甚至10秒。为什么?因为刷写时ECU要擦除Flash,这活儿很慢,你设个1秒的P2*,ECU根本来不及响应,直接超时。

切换逻辑其实不复杂,但有几个关键点:

  • 从默认会话可以切换到任何会话:这是最常用的路径。上电后默认会话,然后根据需求切到编程或扩展。
  • 从编程会话只能切回默认会话:不能直接从编程切到扩展。我猜这是协议设计者为了安全考虑——刷写过程中不允许你“分心”去做其他操作。
  • 从扩展会话可以切到编程会话:但通常需要先切回默认,再切到编程。有些OEM允许直接切,但我不推荐,容易出问题。

我的经验:在产线刷写场景中,我见过一个坑——ECU在编程会话下突然掉电,重新上电后默认会话,但刷写工具还认为它在编程会话,继续发36服务,结果ECU直接回复NRC 0x22(条件不满足)。解决方案是:工具端每次发请求前,先发一个10 01确认当前会话,或者干脆每次刷写前都重新切一次会话。

4.3 安全策略:会话与解锁的关系

会话切换不等于安全解锁。很多新手会混淆这两个概念。我举个例子:你从默认会话切到编程会话,只是告诉ECU“我要开始刷写了”,但ECU不会立刻让你刷——它还需要你提供安全访问密钥(27服务)。

安全策略的核心是:会话决定了你能做什么,解锁决定了你能不能做。两者是“与”的关系。

会话类型 允许的服务 是否需要解锁 典型场景
默认会话 22(读取数据)、19(读取故障码) 日常诊断、OBD检查
扩展会话 2E(写入数据)、31(例程控制) 是(通常需要) 产线标定、售后配置
编程会话 34、36、37(刷写相关) 是(必须) ECU刷写、软件升级

你看,默认会话下不需要解锁,因为只能读不能写。但扩展和编程会话下,几乎所有“写”操作都需要先解锁。我曾经在项目中遇到一个OEM,他们要求扩展会话下写某个DID(数据标识符)不需要解锁,结果被安全审计发现了,最后不得不打补丁。

避坑指南:千万不要在默认会话下开放任何写服务!哪怕只是写一个无关紧要的配置位。我曾经见过一个ECU,默认会话下允许写一个“生产日期”DID,结果被黑客利用,通过修改日期绕过了ECU的生命周期管理。嗯,从那以后,我设计规范时一律要求:默认会话只读,写操作必须切到扩展或编程会话,并且解锁。

4.4 会话超时与自动回退

ECU不会永远待在编程会话里。协议规定,如果ECU在编程会话下长时间没有收到任何诊断请求(包括非诊断的CAN报文),它会自动切回默认会话。这个超时时间通常是5秒左右,但OEM可以自定义。

我建议你们把超时时间设得合理一些:

  • 编程会话:5秒太短了。刷写时,ECU擦除Flash可能需要10秒甚至更久,这期间它不会处理诊断请求。如果超时设成5秒,ECU擦到一半就切回默认会话了,刷写直接失败。我一般设成30秒。
  • 扩展会话:10秒左右比较合适。售后诊断时,技师可能一边看数据流一边思考,动作慢一点,10秒够用。
  • 默认会话:不需要超时,因为这是ECU的“家”,永远不会自动离开。

另外,有个小技巧:ECU在编程会话下收到非诊断的CAN报文(比如网络管理报文),会不会重置超时计时器?答案是:看OEM怎么定义。我个人习惯是只重置诊断请求的计时器,非诊断报文不参与。这样能避免ECU因为网络繁忙而一直待在编程会话里。

4.5 OEM自定义的常见做法

UDS协议只定义了三种标准会话,但OEM可以自定义更多。比如:

  • 安全会话(Security Session):有些OEM会加一个专门用于安全网关诊断的会话,权限比扩展会话还高。
  • 产线会话(Production Session):用于产线终检,只允许执行特定的例程,比如“校准传感器”。
  • 休眠会话(Sleep Session):允许ECU进入低功耗模式,同时还能响应诊断请求。

但要注意,自定义会话不能与标准会话的ID冲突。标准会话的ID是:

  • 默认会话:0x01
  • 编程会话:0x02
  • 扩展会话:0x03

自定义会话从0x40开始用,比较安全。

我的建议:除非有非常明确的业务需求,否则不要轻易加自定义会话。每多一个会话,测试工作量翻一倍。我见过一个项目,OEM定义了7种会话,结果测试团队花了3个月才覆盖所有切换路径,最后还是漏了一个边界条件,导致ECU在特定场景下死锁。

4.6 实战中的常见问题

最后,我总结几个我在项目中踩过的坑:

  1. 会话切换失败后不重试:有些工具切一次会话失败就直接报错。其实ECU可能只是忙,重试一次就好了。我建议重试3次,间隔100ms。
  2. 忽略P2时间参数:ECU返回的P2和P2*,工具端一定要解析并应用。我见过一个工具,不管ECU说啥,都按1秒超时处理,结果在编程会话下频繁超时。
  3. 安全解锁状态与会话绑定:有些ECU设计成“解锁状态随会话切换而丢失”。也就是说,你在扩展会话下解锁了,切到编程会话后需要重新解锁。这个设计本身没问题,但文档里一定要写清楚,否则售后技师会骂娘。
  4. 会话切换时的NRC处理:ECU在切换会话时,如果当前有未完成的操作(比如正在执行例程),应该回复NRC 0x22(条件不满足)。我建议在规范里明确列出所有可能返回的NRC,以及对应的处理方式。

好了,关于诊断会话管理,今天就聊这么多。下一章咱们讲安全访问——27服务,那个更刺激,涉及到种子和密钥的算法设计。到时候我会分享一个我当年在项目中设计的“伪随机种子生成算法”,差点把安全团队气哭。嗯,敬请期待。