既然已经通过 hackrf
做到了车辆位置信息的动态修改。而车企仍然会检测车辆里程,对于当前品牌车辆来说,行驶5K km后将被认定为二手车,不再受企业平台防止出口的监督。
而车辆里程并非通过行驶gps计算,而是由 tbox
接收到 CAN
总线发来的数据(应该是轮胎转速吧)计算而来,同时由 tbox
发送给服务器,具体内容参考关于Tbox分析的博客
那么如果要伪造里程,最简单的办法就是进行中间人攻击,在CAN总线和Tbox中插入一个中间人设备,其负责读取CAN总线传输的内容,改变特定内容(如这里的转速),再打包并发送给 Tbox
让其发送,从而欺骗服务端来伪造里程
由于没有DBC文件无法对CAN总线内容进行解码,所以需要抓取信息并进行分析猜测,这块是最麻烦的内容
而中间人设备使用的是树莓派+CAN模块(MCP2515)
数据读取实验
现在电脑上模拟一下读取数据,采用的是
Linux
的can-utils
,借助USB-CAN
向外输出,同时CAN模块连接到树莓派的SPI接口,以及CAN_H
和CAN_L
上等设备到再写树莓派连接基于
MCP2515
的CAN
总线模块对于MCP2515 SPI转CAN的控制器通信模块,其基本针脚如下:
与树莓派的接线如下:
1
2
3
4
5
6
7
8RPi Pin RPi Label CAN Module
01---------3.3V----------VCC
14---------GND-----------GND
19---------GPIO10--------MOSI (SI)
21---------GPIO9---------MISO (SO)
22---------GPIO25--------INT
23---------GPIO11--------SCK
24---------GPIO8---------CS在树莓派上(我的内核是6.2):
在
boot/firmware/config.txt
中增加:1
2dtparam=spi=on
dtoverlay=mcp2515-can0,oscillator=8000000,interrupt=25波特率对应的是
MCP2515
上控制器的晶振频率8MHz
,后面的中断引脚对应的是上面接线接的22---INT
,即GPIO25然后重启,
ls /sys/bus/spi/devices/spi0.0/net
查看mcp2515
是否成功加载,可以看到can0
或者用
dmesg | grep mcp
来看是否成功初始化1
2sudo ip link set can0 type can bitrate 500000 loopback on #设置can0网卡波特率250k,回环模式
sudo ip link set can0 up #开启can0网卡环回模式允许器件内部的发送缓冲器和接收缓冲器之间进行报文的自发自收,而无需通过CAN 总线。此模式可用于系统开发和测试。
现在再查看
ifconfig
可以看到can0
的状态为UP,RUNNING
测试
1
apt install can-utils
一个终端运行
candump can0
开始监听另一个运行
cansend can0 123#1122334455667788
,can ID
为0x123
,数据为1122334455667788
的报文
电脑USB转CAN总线发送数据
随便买的USB-TO-CAN的工具,CANH和CANL对应连接到
MCP2515
的H和L,GND接到树莓派上的GND来保证与MCP2515
共地在树莓派上开启监听:
1
2ip link set can0 up type can bitrate 500000
candump can0在
linux
上:1
ip link set can0 up type can bitrate 500000
保证两者波特率相同,此时
ifconfig
能看到can0
然后跟之前一样
1
cansend can0 123#114514
就能在树莓派上收到了
连接两个MCP2515
知乎上那篇文章讲的问题挺大的,建议看[这篇](https://www.jianshu.com/p/b0
1
2
3
4
5
6
7
8RPi Pin RPi Label CAN Module
17---------3.3V----------VCC
06---------GND-----------GND
38---------GPIO20--------MOSI (SI)
35---------GPIO19--------MISO (SO)
37---------GPIO26--------INT
40---------GPIO11--------SCK
12---------GPIO18---------CS配置为
1
2
3
4
5dtparam=spi=on
dtoverlay=spi1-1cs
dtoverlay=spi0-1cs
dtoverlay=mcp2515,spi0-0,oscillator=8000000,interrupt=25
dtoverlay=mcp2515,spi1-0,oscillator=8000000,interrupt=26可以在
/boot/overlay/README
中看到具体信息,简单来说就是can0
连接到了spi0
总线上,使用第0个片选信号(CS),can1
连接到了spi1
总线上,使用第0个片选信号。而这两个片选信号默认是
GPIO8
/GPIO18
,即上面接线接到的,当然也可以手动配置,比如:1
dtoverlay=spi1-1cs,cs0_pin=18
电脑发送并读取
跟连一个的时候同理:
1
2sudo ip link set can0 up type can bitrate 500000
sudo ip link set can1 up type can bitrate 500000在树莓派上运行以下脚本:
1
2
3
4
5
6
7
8
9
10
11
12
13import can
# 打开 can0 和 can1 接口
can0 = can.interface.Bus(channel='can0', bustype='socketcan')
can1 = can.interface.Bus(channel='can1', bustype='socketcan')
print("开始 CAN 转发: can0 -> can1")
while True:
msg = can0.recv() # 接收 can0 消息
if msg is not None:
print(f"收到来自 can0 的消息: {msg}")
can1.send(msg) # 发送到 can1
print(f"已转发到 can1: {msg}")唯一的问题就是linux那边是反着的,也是就树莓派的
can0
在那边是can1
啊嗯至于更改就随便写一个试一下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30import can
# 打开 can0 和 can1 接口
can0 = can.interface.Bus(channel='can0', bustype='socketcan')
can1 = can.interface.Bus(channel='can1', bustype='socketcan')
print("开始 CAN 转发: can0 -> can1")
while True:
msg = can0.recv()
if msg is None:
continue
print(f"收到 can0 消息: ID={hex(msg.arbitration_id)}, Data={msg.data.hex()}")
# 判断是否为指定条件
if msg.arbitration_id == 0x119 and msg.data[:3] == bytes([0x11, 0x45, 0x14]):
# 构造新消息
new_data = bytes([0x01, 0x91, 0x98, 0x10])
new_msg = can.Message(
arbitration_id=msg.arbitration_id,
data=new_data,
is_extended_id=False
)
print(f"匹配到目标消息,已替换为: Data={new_data.hex()}")
can1.send(new_msg)
else:
# 原样转发
print(f"原样转发")
can1.send(msg)附一张实物图
然后再配一个自动配置脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
set -e
echo "==> 加载 mcp251x 驱动模块..."
sudo modprobe mcp251x
sudo modprobe can_dev
sudo modprobe can_raw
echo "==> 关闭可能存在的旧接口..."
sudo ip link set can0 down 2>/dev/null || true
sudo ip link set can1 down 2>/dev/null || true
echo "==> 配置 CAN 接口速率并启用..."
# 配置 can0(挂载在 SPI0)
if ip link show can0 &>/dev/null; then
sudo ip link set can0 type can bitrate 500000
sudo ip link set can0 up
echo "✅ can0 启动成功"
else
echo "❌ can0 不存在"
fi
# 配置 can1(挂载在 SPI1)
if ip link show can1 &>/dev/null; then
sudo ip link set can1 type can bitrate 500000
sudo ip link set can1 up
echo "✅ can1 启动成功"
else
echo "❌ can1 不存在"
fi
echo ""
echo "📡 当前 CAN 接口状态:"
ip -br link | grep can两端相互传数据的情况下做监听:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42import can
import datetime0
# 打开 can0 和 can1 接口
can0 = can.interface.Bus(channel='can0', bustype='socketcan')
can1 = can.interface.Bus(channel='can1', bustype='socketcan')
# 打开日志文件
log_file = open("can_relay_log.txt", "a")
print("开始双向 CAN 转发: can0 <-> can1")
try:
while True:
# 检查 can0 是否有消息
msg0 = can0.recv(timeout=0.01)
if msg0 is not None:
try:
can1.send(msg0)
print(f"[{datetime.datetime.now()}] can0 → can1: {msg0}")
log_file.write(f"[{datetime.datetime.now()}] can0 → can1: {msg0}\n")
log_file.flush()
except can.CanError as e:
print(f"发送到 can1 失败: {e}")
# 检查 can1 是否有消息
msg1 = can1.recv(timeout=0.01)
if msg1 is not None:
try:
can0.send(msg1)
print(f"[{datetime.datetime.now()}] can1 → can0: {msg1}")
log_file.write(f"[{datetime.datetime.now()}] can1 → can0: {msg1}\n")
log_file.flush()
except can.CanError as e:
print(f"发送到 can0 失败: {e}")
except KeyboardInterrupt:
print("退出转发程序")
finally:
log_file.close()
can0.shutdown()
can1.shutdown()
OBD
调试接口数据读取OBD
和Tbox
的 CAN总线并不是同一个,并且OBD
是询问应答机制,需要先给其发送要查询的数据,其才会返回相关结果询问gpt得到遵循CAN协议的OBDⅡ指令集,其请求帧如下:
CAN ID: 0x7DF(通用广播 ID,发送到所有 ECU)
数据字节:
Byte 0: 长度(如 0x02)
Byte 1: 模式(如 0x01)
Byte 2: PID(如 0x0C)
Byte 3-7: 填充 0x55 或 0x00
例如请求车速:
1
2CAN ID: 0x7DF
DATA: 02 01 0D 00 00 00 00 00第一个是回路接收到的发送命令
2-4是来自多个
ECU
的响应,都表示车速0D
的值是000
最后两个是异常响应,表示不支持mode01服务? 不用在意那个
然后偷了个专用的OBD调试机器来试,找了一圈,比较重要的数据就是车速和累计里程,二者都是整数
CAN
总线数据尝试找资料能确定时速在P-CAN上,累计里程的话看他们调表的时候拆的芯片在仪表盘上,可能在I-CAN上传输
只找到了奇瑞星途 (Chery Exeed) 的很小一部分dbc
下下来是这个b公司加密过的
ref
格式,还好就在两周前有好人写了转换脚本转化一下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71VERSION ""
NS_ :
CM_
BA_DEF_
BA_
VAL_
CAT_DEF_
CAT_
FILTER
BA_DEF_DEF_
EV_DATA_
ENVVAR_DATA_
SGTYPE_
SGTYPE_VAL_
BA_DEF_SGTYPE_
BA_SGTYPE_
SIG_TYPE_REF_
VAL_TABLE_
SIG_GROUP_
SIG_VALTYPE_
SIGTYPE_VALTYPE_
BO_TX_BU_
BA_DEF_REL_
BA_REL_
BA_DEF_DEF_REL_
BU_SG_REL_
BU_EV_REL_
BU_BO_REL_
SG_MUL_VAL_
BS_:
BU_: Vector__XXX
BO_ 165 CAN_MSG_165: 6 VECTOR__XXX
SG_ Engine_Speed : 16|16@0+ (0.25,0) [0|16383.75] "rpm" Vector__XXX
BO_ 167 CAN_MSG_167: 6 VECTOR__XXX
SG_ Accelerator_Pedal_Position : 40|8@0+ (0.396825,0) [0|101.190476190476] "%" Vector__XXX
BO_ 168 CAN_MSG_168: 8 VECTOR__XXX
SG_ Brake_Position : 40|8@0+ (0.5,0) [0|127.5] "%" Vector__XXX
BO_ 296 CAN_MSG_296: 8 VECTOR__XXX
SG_ Gear_Requested : 20|4@0+ (1,0) [0|15] "" Vector__XXX
SG_ Gear : 16|4@0+ (1,0) [0|15] "" Vector__XXX
BO_ 299 CAN_MSG_299: 6 VECTOR__XXX
SG_ Steering_Angle : 24|14@0+ (-0.1,720) [-918.3|720] "°" Vector__XXX
BO_ 315 CAN_MSG_315: 6 VECTOR__XXX
SG_ Wheel_Speed_FL_kph : 32|16@0+ (0.0088,0) [0|576.708] "km/h" Vector__XXX
SG_ Wheel_Speed_FR_kph : 16|16@0+ (0.0088,0) [0|576.708] "km/h" Vector__XXX
SG_ Wheel_Speed_FL_mph : 32|16@0+ (0.0054648,0) [0|358.135668] "mph" Vector__XXX
SG_ Wheel_Speed_FR_mph : 16|16@0+ (0.0054648,0) [0|358.135668] "mph" Vector__XXX
BO_ 318 CAN_MSG_318: 6 VECTOR__XXX
SG_ Wheel_Speed_RL_kph : 32|16@0+ (0.0088,0) [0|576.708] "km/h" Vector__XXX
SG_ Wheel_Speed_RR_kph : 16|16@0+ (0.0088,0) [0|576.708] "km/h" Vector__XXX
SG_ Wheel_Speed_RL_mph : 32|16@0+ (0.0054648,0) [0|358.135668] "mph" Vector__XXX
SG_ Wheel_Speed_RR_mph : 16|16@0+ (0.0054648,0) [0|358.135668] "mph" Vector__XXX
BO_ 338 CAN_MSG_338: 8 VECTOR__XXX
SG_ Side_Lights : 24|1@0+ (1,0) [0|0] "on/off" Vector__XXX
SG_ Headlights : 28|1@0+ (1,0) [0|0] "on/off" Vector__XXX
SG_ Full_Beam : 30|1@0+ (1,0) [0|0] "on/off" Vector__XXX
SG_ Indicator_Left : 18|1@0+ (1,0) [0|0] "on/off" Vector__XXX
SG_ Indicator_Right : 20|1@0+ (1,0) [0|0] "on/off" Vector__XXX
BO_ 456 CAN_MSG_456: 5 VECTOR__XXX
SG_ Gear_Switch : 26|3@0+ (1,0) [0|7] "" Vector__XXX处理一下:
| CAN ID | 信号名 | 起始位 | 长度 | 字节序 | 缩放因子 (Factor) | 偏移量 | 单位 |
| ------ | ---------------------------- | --- | -- | ----- | ------------- | --- | ------ |
| 0x0A5 | Engine\_Speed | 16 | 16 | Intel | 0.25 | 0 | rpm |
| 0x0A7 | Accelerator\_Pedal\_Position | 40 | 8 | Intel | 0.396825 | 0 | % |
| 0x0A8 | Brake\_Position | 40 | 8 | Intel | 0.5 | 0 | % |
| 0x0128 | Gear\_Requested | 20 | 4 | Intel | 1 | 0 | — |
| 0x0128 | Gear | 16 | 4 | Intel | 1 | 0 | — |
| 0x012B | Steering\_Angle | 24 | 14 | Intel | -0.1 | 720 | ° |
| 0x013B | Wheel\_Speed\_FL\_kph | 32 | 16 | Intel | 0.0088 | 0 | km/h |
| 0x013B | Wheel\_Speed\_FR\_kph | 16 | 16 | Intel | 0.0088 | 0 | km/h |
| 0x013B | Wheel\_Speed\_FL\_mph | 32 | 16 | Intel | 0.0054648 | 0 | mph |
| 0x013B | Wheel\_Speed\_FR\_mph | 16 | 16 | Intel | 0.0054648 | 0 | mph |
| 0x013E | Wheel\_Speed\_RL\_kph | 32 | 16 | Intel | 0.0088 | 0 | km/h |
| 0x013E | Wheel\_Speed\_RR\_kph | 16 | 16 | Intel | 0.0088 | 0 | km/h |
| 0x013E | Wheel\_Speed\_RL\_mph | 32 | 16 | Intel | 0.0054648 | 0 | mph |
| 0x013E | Wheel\_Speed\_RR\_mph | 16 | 16 | Intel | 0.0054648 | 0 | mph |
| 0x0152 | Side\_Lights | 24 | 1 | Intel | 1 | 0 | on/off |
| 0x0152 | Headlights | 28 | 1 | Intel | 1 | 0 | on/off |
| 0x0152 | Full\_Beam | 30 | 1 | Intel | 1 | 0 | on/off |
| 0x0152 | Indicator\_Left | 18 | 1 | Intel | 1 | 0 | on/off |
| 0x0152 | Indicator\_Right | 20 | 1 | Intel | 1 | 0 | on/off |
| 0x01C8 | Gear\_Switch | 26 | 3 | Intel | 1 | 0 | — |
以左轮胎速度为例:
1
2
3
BO_ 315 CAN_MSG_315: 6 VECTOR__XXX
SG_ Wheel_Speed_FL_kph : 32|16@0+ (0.0088,0) [0|576.708] "km/h" Vector__XXX
SG_ Wheel_Speed_FR_kph : 16|16@0+ (0.0088,0) [0|576.708] "km/h" Vector__XXX
| 字段 | 含义 |
| -------------------- | ------------------------------ |
| `BO_ 315` | CAN ID 为 **0x13B** 的帧(十进制 315) |
| `Wheel_Speed_FL_kph` | 左前轮速度,单位为 km/h |
| `起始位 = 32` | 在第 4 个字节起始 |
| `长度 = 16` | 占 2 字节 |
| `@0+` | Intel(小端)字节序,正数 |
| `Factor = 0.0088` | 缩放因子 |
| `Offset = 0` | 无偏移 |
| `长度 = 6` | 该 CAN 帧的 Payload 长度为 6 字节 |
比如抓到了
1
13B#6B 00 52 00 91 01
则 `Wheel_Speed_FL_kph` 是 `byte[4:6]`,即 $91 01$(小端) = $0x0191$ = $401$
实际速度 = $401 * 0.0088 = 3.528 km/h$
待测试区
测比特率
1
ip -details -statistics link show can0
显示这个:
RX packets: 0
→ 没收到任何帧,说明总线上无数据或波特率不对抓汽车点火的时候的情况?是否只在每次点火的时候才上传数据
没有
挂挡的时候?
没有
抓Tbox的包,据维修手册可能存在响应应答
没有
悲报
不知道为什么一直读不出数据,暂时转到Tbox攻击上了