朱海生 星舆车联网安全实验室
0x00 前言
大家好, 我是星舆车联网实验室的 Krypt0n,星舆取 “星辰大海, 舆载万物”之意,实验室是专注于车联网技术研究、漏洞挖掘和工具研发的安全团队。团队成员在漏洞挖掘,硬件逆向与AI大数据方面有着丰富经验,连续在GeekPwn等破解赛事中斩获奖项,并获众厂商致谢。团队研究成果多次发表于DEFCON等国内外顶级安全会议。
0x01 背景
当前,汽车领域正在向着智能化,网联化,电动化,共享化的四化方向发展,给出行驾驶体验带来方便的同时,也带来了很多的新的安全风险,暴露的攻击面也越来越多,而作为汽车电子系统通信链路上最重要的也是应用最多的总线 CAN ,针对 CAN 的逆向和攻击利用也越来越多,本文从 CAN 协议基础讲起,逐步让大家了解 CAN 协议的基础知识,后续文章在从 CAN 仿真,逆向,和真车实战攻防的角度对 CAN 进行深入的研究研讨。
0x02 Classic CAN 总线及协议介绍
历史
1983年,Robert Bosch GmbH 开发了CAN总线技术,并于 1986 年在SAE 汽车工程学会大会上将其作为全新串行总线系统发布,目前CAN总线在汽车的动力系统、底盘系统和舒适系统等 ECU 网络中发挥着重要作用,同时CAN总线具有数据传输可靠的特点,可以满足应用领域的实时要求;自从引入CAN总线之后,汽车中的复杂线束得到了大大的简化,有效的降低了车内线束布线重量和空间。
Classic CAN OSI-1/2层数据帧格式
CAN 总线是一种反逻辑电平,物理上的高电平对应逻辑上的 ‘0’,即显性电平,帧格式有数据帧、遥控帧、错误帧、过载帧、帧间隔5 种,只有数据帧和遥控帧有标准格式和扩展格式之分,并且只有数据帧和遥控帧有 SOF 和 EOF 段。
标准数据帧构成:帧起始[1 bit]、仲裁段[11+1 bit]、控制段[2+4 bit]、数据段[0-64 bit]、CRC段[15+1 bit]、ACK段[1+1 bit]、帧结束[7 bi],共7段;
- 仲裁段[11+1]:11位的ID比特位,指示报文内容和优先级,1位RTR位,显性表示数据帧,隐性表示遥控帧
- 控制段[2+4]:IDE[标识符扩展位],显性电平表示该帧是标准数据帧,IDE后1位保留,默认显性电平,然后是4位的数据长度码 DLC
- CRC段[15+1]:15位 CRC 序列[CRC15 多项式算法]加上1位隐性的CRC界定符组成
- ACK段[1+1]:1位应答段,显性电平表示有应答,1位ACK占位符
- 结束段[7]:7位隐性电平
- 在两个帧之间至少需要3个隐性电平[ 即帧间隔 ],若没有节点发送帧,则总线一直保持隐性电平的空闲状态


总结:标准帧格式中,SOF 位显性电平,数据帧的 RTR[远程发送请求] 位显性电平,控制帧的保留位 IDE/R0 显性电平,CRC 界定符位隐性电平,ACK 槽比特位显性电平表示接收,隐性电平表示发送,ACK 界定符是隐性电平,EOF 是7位隐性电平。
总线访问及优先级
总线节点在发送数据时,同时也会监听总线,CAN 总线仲裁字段连续出现显性电平最多的单元获得总线控制权;从软件角度看 ID 值越小,优先级越高,因为是反逻辑电平,逻辑 0 表示显性电平即物理高电平;总线仲裁失败的节点会继续监听总线,当总线再次处于空闲状态时,会第一时间继续发送仲裁帧。


1.节点发送显性电平[逻辑0],监测到总线此时处于显性电平[逻辑0],此时该节点继续发送,继续进行仲裁
2.节点发送显性电平[逻辑0],监测到总线此时处于隐性电平[逻辑1],此时发生总线错误
3.节点发送隐性电平[逻辑1],监测到总线此时处于显性电平[逻辑0],此时竞争失败,此节点转为接收方
4.节点发送隐性电平[逻辑1],监测到总线此时处于隐性电平[逻辑1],此时该节点继续发送,继续进行仲裁

数据帧和数据帧之间的优先级判定:根据 ID 值的大小进行仲裁判定;
数据帧和遥控帧之间的优先级判定:具有相同 ID 的数据帧和遥控帧,仲裁最后一位 RTR 为显性位的数据帧具有优先级;
标准格式和扩展格式的优先级判定:标准格式ID和具有相同ID的遥控帧或者具有扩展格式的数据帧在总线上竞争时,标准格式的 RTR 位为显性电平的具有优先权,可继续发送;
错误分类及协议处理机制
错误的种类:位错误、格式错误、填充错误、CRC 错误、ACK 错误;
填充位:为防止突发错误而设定的功能,由于 CAN 使用 NRZ 编码,为了确保所有总线节点都能够重新同步,使用填充的方法,在协议解析的时候,收发器物理上在接收端会去掉填充的比特位,如果示波器在物理层测量 CAN 协议数据时,若示波器本身没有 CAN 功能的话,需要人工手动的去掉填充位信息;
发送单元的填充机制:在发送数据帧和遥控帧时,SOF~CRC 段间的数据,相同电平如果持续 5 位,在下一个位(第六位)要插入 1 位与前 5 位相反的电平;
接收单元的填充机制:在接收数据帧和遥控帧时,SOF~CRC 段间的数据,相同电平如果持续 5 位,需要删除下一个位(第六位)在接收,如果这个第 6 位的电平与前 5 位相同,将视为错误并发送错误帧;
位时序及时序同步
基本概念
位速率:发送单元在非同步情况下发送的每秒钟的位数称为位速率,即波特率;
位时间:指一个(二进制逻辑)位在物理总线传输所需要的时间,位时间是位速率的倒数,即 Tbit;
位时序:每一帧数据(即一个完整的数据帧)有很多位 [Tbit] 组成,每位 [Tbit] 一般分为四段,同步段[SS]、传播时间段[PTS]、相位缓冲段1[PBS1]、相位缓冲段2[PBS2],每个段又由若干个 Tq 构成,这称为位时序;还有一个再同步补偿宽度段[SJW];
1 位 [Tbit] 分为四个段,每个段又由多个 Tq 构成,Tq [ Time Quantum ] 时间量子叫做最小时间单位;
通过位时序设定,可设置 1 位 [Tbit] 由多 Tq 构成,每个段又由多个 Tq 构成;可做到让多个单元同时采样,也可任意设置采样点;
以下是 1位[Tbit]的图示



CAN 控制器为了适应各种波特率,对四个段的时间长度,不是使用纳秒(ns)或微秒(us)来度量,而是使用节拍来度量,这个节拍称为时间量子 Tq [ Time Quantum ]。例如 500k 的波特率[位速率],每个 Tbit 位时间是 2000ns,如果分为 10[NBT] 个节拍,则每个 Tq 为 200ns;
一个位[Tbit]时间由波特率的倒数即位速率决定,Tbit = 1 / BaudRate;
Tq 由 位时间除以名义位时间 NBR [Nominal Bit Time] 计算,即 NBR * Tq = Tbit;
CAN节点 MCU 的时钟频率 F,则MCU的最快工作时间 T = 1 / F,分频计算方式 Tq = (BRP + 1)/ F 或 Tq = 8 *(BRP + 1)/ F ,BRP[Baud Rate Prescaler]是位速率分频值;根据此公式可对CAN控制器的BRP寄存器进行设置;
采样点位于 PBS1 末和 PBS2 头 的位置;
时序同步意义
CAN协议的通信方法为NRZ[Non-Return to Zero]方式,各个位的开头或者结尾都没有附加同步信号;发送单元以位时序同步的方式开始发送数据。另外,接收单元根据总线上电平的变化进行同步并进行接收工作。但是,发送单元和接收单元存在的时钟频率误差及传输路径上的(电缆/驱动器等)相位延迟会引起同步偏差,因此接收单元会通过硬件同步或者再同步的方法调整时序进行接收;
硬同步是指在总线空闲状态,接收节点检测出帧起始位(SOF)时,会调整当前位的同步段(SS)与发送节点的帧起始位SS段一样,每帧只可能出现一次硬同步;
再同步是指接收节点检测出除SOF位以外的其他位时进行的同步调整。再同步会通过加长PBS1段,或缩短PBS2段来调整同步,以保证采样点的准确;这里加长或缩短(即跳转)操作涉及到一个概念—同步跳转宽度 [SJW],SJW必须小于PBS1和PBS2的最小值,SJW最大值不能超过4;
Classic CAN 的物理电路设计

0x03 与CAN有关的协议
ISO 11898-1
ISO 11898-1 定义了控制器[数据链路层和 PLS子层]的规范;

ISO 11898-2
ISO 11898-2 定义了物理层高速CAN 的收发器的规范

ISO 11898-3
ISO 11898-3 定义了物理层低速CAN 的收发器的规范,注意此协议下的显性和隐形电平物理值与 ISO 11519-2 不同

ISO 11898 和 ISO 11519-2

0x04 UDSonCAN
车载诊断协议定义了诊断请求/响应的报文格式,ECU 如何处理请求的行为,请求和响应的消息内容等。
车载诊断协议的应用有很多,今天主要介绍UDS;UDS 可基于 CAN、LIN、车载以太网、蓝牙、WI-FI 等底层协议,UDS 本质上是一系列服务的集合,UDS的服务包含6大类,共26种;每种服务都有自己独立的ID,即服务标识符SID。
又由于CAN总线目前在车载总线中应用很广泛,所以下面我们主要介绍在OSI 7层协议中涉及 UDSonCAN 的相关层协议,如下图所示的红色标注部分

ISO 11891-1/2 已经在上一节中做了简单介绍,下面着重介绍其余层的协议
应用层 ISO 14229-1
UDS由 ISO-14229 系列标准定义,适用于汽车诊断服务,ISO 14229-1 定义了应用层服务诊断服务,不涉及网络及实现,不基于任何底层标准,它是诊断服务的规范化标准,如读取故障码应该向 ECU 发的指令,读数据流发的指令等;而ISO 14229-3则定义了UDS在CAN总线上的实现。UDS 14229-1 规定了6类,26种应用层服务。
- 诊断和通信管理功能单元
- 数据传输功能单元
- 存储数据传输功能单元
- 输入输出控制功能单元
- 例行程序功能单元
- 上传下载功能单元

SID 定义
SID 即服务标识符,是一个字节无符号整数,取值范围是 [0x00~0xFF],符合ISO 14229-1 规范的ID取值范围如下图所示,其中0x10-0x3E代表请求服务表示符,0x50-0x7E代表肯定响应标识符, 0x7F 代表所有的否定响应标识符,可以看出相应的肯定响应SID 等于请求服务标识符加上0x40;

下图展示了其它范围的SID和所定义的协议之间的关系,限于篇幅原因,这里不做铺开解读,读者可根据定义文档自己检索学习了解

[不同SID及其带有子服务SF的具体含义读者可参考 UDS 14229-1 协议]
UDS 的三种会话模式
- 默认诊断会话
- 编程会话
- 扩展诊断会话
TP层 ISO 15765-2
UDS网络层,又称为 TP 层[Transport Protocol Layer],其存在的目的是为了解决ISO 11898 协议中定义的经典CAN数据链路层与ISO 14229协议中定义的应用层,彼此之间数据长度不统一的问题。经典CAN数据链路层最大能够支持8个字节,但ISO 14229并不仅仅是为了CAN总线设计的,最大容量达到4095个字节。比如VIN码是17个字节,CAN总线必然需要传递3帧才能传完VIN码,如何将多个字节通过经典CAN来进行传输,就成了一个需要解决的问题;ISO 15765-2 协议由此诞生;ISO15765-2 可以但不限于与 ISO 14229-1 和 ISO 15031-5 一起使用,它与车载网络的大多数其他通信需求相兼容;ISO 15765-2 协议将CAN的8字节数据域腾出一到两个字节的做法,来体现网络层的控制信息[N_PCI];
几个概念
- A_PDU:应用层协议数据单元
- A_SDU:应用层服务数据单元
- N_PDU:网络层协议数据单元
- PCI:层协议控制信息
- DID:数据标识符
- A_PDU = A_PCI[SID or NR_SID,SID] + A_SDU
- A_PDU = A_AI + A_data
- A_AI = Mtype,SA,TA,TA,TA_Type,[RA,],[Length]
- A_Data = A_PCI + [parameter1,…]
- [N_PDU = N_PCI + N_SDU]
- SA 源地址,DA 目标地址,TA_Type:描述是物理寻址还是功能寻址
- 应用层协议数据单元中的 A_PCI 指的是服务标识符 SID,注意区分与 ISO 15765-2 的 N_PDU 中的 N_PCI 字段
下图简单展示了各层协议间的封装关系

N_PCI 帧信息
网络层PDU的PCI格式如下表所示
帧类型 | bit7-4 | bit3-0 | byte2 | byte3 |
---|---|---|---|---|
单帧 | PCItype=0 | SF_DL | N/A | N/A |
多帧首帧 | PCItype=1 | FF_DL | FF_DL | N/A |
连续帧 | PCItype=2 | SN | N/A | N/A |
流控帧 | PCItype=3 | FS | BS | ST_min |
- 0:单帧[SF],表示该帧包含整个载荷[payload],后 4 个 bit [SF_DL]表示数据段包含数据字节数
- 1:首帧[FF],表示多数据帧的第一帧,后面的 12 bit [FF_DL]表示载荷的数据大小
- 2:连续帧[CF],即多数据帧剩余部分,后 4 bit [SN]作为接收到数据包的排序索引[从1开始计数],如果传输内容的长度超过 112 字节,该索引可以作为包裹
- 3:流控制帧[FC],作为首帧数据帧的回馈信息,即诸如分发速率这样额外传输数据包的特定参数;FS 有三种状态:继续发送0、保持等待1、数据溢出2BS 规定发送端允许持续传输连续帧数目的最大值[0~255]STmin 限定连续帧相互之间所允许的最小时间间隔
会话举例
以 SID=2E的写数据服务为例;
请求服务帧:2E + DID + Data
响应服务帧:6E + DID
方向 | DLC | DATA |
---|---|---|
Tx | 8 | 10 14 2E F1 90 01 02 03 |
Rx | 8 | 30 00 0A AA AA AA AA |
Tx | 8 | 21 04 05 06 07 08 09 0A |
Tx | 8 | 22 0B 0C 0D 0E 0F 10 11 |
Rx | 8 | 03 6E F1 90 AA AA AA AA |
第一行 10 表示请求帧的首帧, 0 14 表示一共20个字节需要发送,F190表示子服务号,此处表示车架号VIN,01 02 03 表示具体写入数据[VIN一共17个字节];
第二行 30 表示响应的流控帧,0 00 0A 中的第一个0表示允许继续发送,0A表示连续帧最短的间隔时间是 10ms;
第三行表示连续帧,21 中的1表示连续帧的序号,后面是数据;
第四行表示连续帧,22 中的2表示连续帧的序号,后面是数据;
第五行表示ECU的响应,03 表示单帧,含有3个字节的数据,6E 是 对应着请求的 2E 的 SID,F190 是子服务号,代表着车架号VIN码;
0x05 仿真环境搭建
使用icsim 和 uds-server 及 can-utlis工具进行测试(默认ID为0x710)
环境 kali 5.6.0-kali1-amd64
ICSim 安装及使用
ICSim是一个ECU的模拟工具
# GITHUB 源地址
git clone https://github.com/zombieCraig/ICSim.git
# 依赖组件的安装
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install libsdl2-dev libsdl2-image-dev can-utils
# 编译,生成icsim和controls两个可执行文件
cd ICSim
make
# 编译,生成icsim和controls两个可执行文件
cd ICSim
make
# 建立一个虚拟 CAN 接口,vcan0 是可变参数,可以是 vcan1 等其它名字;
sudo modprobe can
# 加载虚拟vcan模块
sudo modprobe vcan
# 添加vcan0网卡
sudo ip link add dev vcan0 type vcan bitrate 500000
sudo ip link set up vcan0
# 其它相关命令
sudo ip link add
sudo ip link show
sudo ip link delete
# 关闭 vcan0 线路
sudo ip link set down vcan0
# 此时通过 ifconfig 可以看见 vcan0 接口
./setup_vcan.sh
# 打开仪表盘
./icsim vcan0
# 打开车联模拟控制器,使用手柄控制更方便
./controls vcan0
uds-server 安装及使用
统一诊断服务服务器,提供 UDS 支持的 ECU 模拟器,程序最初是为了与 [ICSim] 一起训练而编写的;目前也可作为安全研究测试的工具;
git clone https://github.com/zombieCraig/uds-server.git
make
# 启动
./uds-server
caringcaribou安装及使用
git clone https://github.com/CaringCaribou/caringcaribou
sudo apt-get install python3-pip -y
# 我的python 环境是 Python 3.8.6
# 安装 python can 包
pip3 install python-can
# 切换到 tools 目录
cd tool/libs
# 修改相关参数,否则会启动失败
sudo vim can_actions.py
self.bus = can.Bus(bustype='socketcan',channel=DEFAULT_INTERFACE)
sudo vim io15765_.py
self.bus = can.Bus(bustype='socketcan',channel=DEFAULT_INTERFACE)
# 启动
python3 cc.py -i vcan0 uds discovery
# 进入 tool 目录
# 列出帮助信息及所有可获取使用的模块
python3 cc.py -h
# 查找某一模块的帮助信息及使用举例,如 send
python3 cc.py send -h
# 若某一模块还有子功能,也可以进一步列出子功能的帮助信息及使用距离
python3 cc.py send message -h
python3 cc.py send file -h
软件的组合使用
# 在 ICSim 目录下,启动 ICSim
sudo modprobe can
sudo modprobe vcan
sudo ip link add dev vcan0 type vcan bitrate 500000
sudo ip link set up vcan0
# 切换 uds-server 目录,启动 uds-server,为模拟车辆搭建 uds 服务
./uds-server vcan0
# 切换 caringcaribou 目录,开启 uds 服务扫描
python3 cc.py -i vcan0 uds discovery

0x06 参考
- https://www.bilibili.com/video/BV1aP4y1p7Vo?spm_id_from=333.999.0.0
- https://zhuanlan.zhihu.com/p/37310388
- https://zhuanlan.zhihu.com/p/44857562
- ISO 14229-1/2/3
- https://github.com/zombieCraig/ICSim
- https://github.com/CaringCaribou/caringcaribou

未完待续,想看 Tesla Model 3 的 CAN 逆向么?后续文章即将揭晓