用scapy进行汽车渗透测试

注解

所有与汽车相关的功能在Linux系统上工作得最好。scapy中的cansockets和isopspockets基于Linux内核模块。除了Linux之外,python can项目还用于支持其他系统上的can和cansockets。本指南介绍了BeagleboneBlack的硬件设置。选择BeagleboneBlack是因为它在主处理器上有两个CAN接口。在一个设备中存在两个CAN接口,可能会导致CAN MITM攻击和会话劫持。cannelloni框架将beaglebone black转换为can-to-udp接口,让您可以在更强大的机器上随意运行scapy。

协议

下表应简要概述SCAPY的所有汽车功能。大多数应用层协议都有许多专门的 Packet 类。这些特殊用途类不属于本概述的一部分。使用 explore() 函数获取一个特定协议的所有信息。

应用层

协议

欺诈性实施

应用层

UDS(ISO 14229)

UDS,UDS*

GMLAN

GMLAN,GMLLAN**

一些/知识产权

索米普

宝马ENET

网络,网络插座

OBD

OBD公司

CCP

CCP、DTO、CRO

运输层

ISO-TP(ISO 15765-2)

等电位插座,等电位插座,等电位插座

等速硝化剂,等速消息生成器

等峰面,等峰面,

等温线,等温线,等温线,等温线,等温线

数据链路层

CAN(ISO 11898)

罐、罐插座、RDC泵

手上

通过Linux socketcan发送消息:

load_layer('can')
load_contrib('cansocket')
socket = CANSocket(iface='can0')
packet = CAN(identifier=0x123, data=b'01020304')

socket.sr1(packet, timeout=1)

srcan(packet, 'can0', timeout=1)

通过矢量CAN接口发送消息:

import can
load_layer('can')
conf.contribs['CANSocket'] = {'use-python-can' : True}
load_contrib('cansocket')
from can.interfaces.vector import VectorBus
socket = CANSocket(iface=VectorBus(0, bitrate=1000000))
packet = CAN(identifier=0x123, data=b'01020304')
socket.sr1(packet)

srcan(packet, VectorBus(0, bitrate=1000000))

系统兼容性

根据您的设置,必须使用不同的实现。

Python操作系统

Linux与can ou isopp

Linux wo can ou isopp

Windows/OSX

Python 3

ISOTPNativeSocket

ISOTPSoftSocket

ISOTPSoftSocket

conf.contribs['CANSocket'] = {'use-python-can': True}

conf.contribs['CANSocket'] = {'use-python-can': False}

Python 2

ISOTPSoftSocket

conf.contribs['CANSocket'] = {'use-python-can': True}

ISOTPSoftSocket

conf.contribs['CANSocket'] = {'use-python-can': True}

班级 ISOTPSocket 可以设置为 ISOTPNativeSocket 或A ISOTPSoftSocket . 决定取决于配置 conf.contribs['ISOTP'] = {{'use-can-isotp-kernel-module': True}} (选择 ISOTPNativeSocketconf.contribs['ISOTP'] = {{'use-can-isotp-kernel-module': False}} (选择 ISOTPSoftSocket )这将允许您编写与平台无关的代码。在加载各向同性层之前应用此配置 load_contrib("isotp") .

关于等粒子相容性的另一个评论。总是与一起用于套接字创建。例子::

with ISOTPSocket("vcan0", did=0x241, sid=0x641) as sock:
    sock.send(...)

罐头层

安装程序

这些命令在Linux机器上启用虚拟CAN接口:

from scapy.layers.can import *
import os

bashCommand = "/bin/bash -c 'sudo modprobe vcan; sudo ip link add name vcan0 type vcan; sudo ip link set dev vcan0 up'"
os.system(bashCommand)

如果需要,可以将CAN接口设置为 listen-onlyloopback 模式与 ip link set 命令:

ip link set vcan0 type can help  # shows additional information

这个例子展示了linux can-utils的基本功能。这些实用程序便于快速检查或记录。

../_images/animation-cansend.svg

CAN帧

创建标准罐架:

frame = CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')

创建扩展的罐架:

frame = CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')
../_images/animation-scapy-canframe.svg

写入和读取PCAP文件:

x = CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08')
wrpcap('/tmp/scapyPcapTest.pcap', x, append=False)
y = rdpcap('/tmp/scapyPcapTest.pcap', 1)
../_images/animation-scapy-rdpcap.svg../_images/animation-scapy-rdcandump.svg

cansocket本机

创建简单的本机cansocket::

conf.contribs['CANSocket'] = {'use-python-can': False} #(default)
load_contrib('cansocket')

# Simple Socket
socket = CANSocket(iface="vcan0")

创建本机cansocket只侦听id==0x200:的消息:

socket = CANSocket(iface="vcan0", can_filters=[{'can_id': 0x200, 'can_mask': 0x7FF}])

创建本机cansocket仅侦听ID大于等于0x200且ID小于等于0x2ff:的消息:

socket = CANSocket(iface="vcan0", can_filters=[{'can_id': 0x200, 'can_mask': 0x700}])

创建本机cansocket只侦听ID为的消息!= 0x200:

socket = CANSocket(iface="vcan0", can_filters=[{'can_id': 0x200 | CAN_INV_FILTER, 'can_mask': 0x7FF}])

使用多个can_筛选器创建本机cansocket::

socket = CANSocket(iface='vcan0', can_filters=[{'can_id': 0x200, 'can_mask': 0x7ff},
                                               {'can_id': 0x400, 'can_mask': 0x7ff},
                                               {'can_id': 0x600, 'can_mask': 0x7ff},
                                               {'can_id': 0x7ff, 'can_mask': 0x7ff}])

创建本机cansocket,它还接收自己的消息:

socket = CANSocket(iface="vcan0", receive_own_messages=True)
../_images/animation-scapy-native-cansocket.svg

在CanSocket上嗅探:

../_images/animation-scapy-cansockets-sniff.svg

CanSocket python罐

在Windows、OSX或Linux上,需要使用各种CAN接口。python can库通过cansocket对象使用。要创建python can cansocket对象,python可以 Bus 对象必须用作接口。这个 timeout 参数可用于提高python can cansocket对象的接收性能。 recv 在python can中,cansocket对象是通过busy wait实现的,因为没有 select 在Windows或某些专有的CAN接口(如矢量接口)上的功能。小的 timeout 可能需要,如果 sniffbridge_and_sniff 在多个接口上执行。

创建python can cansocket的方法:

conf.contribs['CANSocket'] = {'use-python-can': True}
load_contrib('cansocket')
import can

创建一个简单的python can cansocket::

socket = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000))

创建具有多个过滤器的python can cansocket::

socket = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000,
                can_filters=[{'can_id': 0x200, 'can_mask': 0x7ff},
                            {'can_id': 0x400, 'can_mask': 0x7ff},
                            {'can_id': 0x600, 'can_mask': 0x7ff},
                            {'can_id': 0x7ff, 'can_mask': 0x7ff}]))
../_images/animation-scapy-python-can-cansocket.svg

有关python的更多详细信息,请查看:https://python-can.readthedocs.io/en/2.2.0/

用桥接器和嗅探来攻击MITM

这个例子展示了如何在虚拟CAN接口上使用桥接和嗅探。对于真实世界的应用程序,使用真实的CAN接口。在Linux终端上设置两个VCAN::

sudo modprobe vcan
sudo ip link add name vcan0 type vcan
sudo ip link add name vcan1 type vcan
sudo ip link set dev vcan0 up
sudo ip link set dev vcan1 up

导入模块:

import threading
load_contrib('cansocket')
load_layer("can")

创建用于攻击的CAN套接字::

socket0 = CANSocket(iface='vcan0')
socket1 = CANSocket(iface='vcan1')

创建一个函数来发送带线程的数据包:

def sendPacket():
    sleep(0.2)
    socket0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))

创建用于转发或更改数据包的函数::

def forwarding(pkt):
    return pkt

创建一个在两个套接字之间桥接和嗅探的函数:

def bridge():
    bSocket0 = CANSocket(iface='vcan0')
    bSocket1 = CANSocket(iface='vcan1')
    bridge_and_sniff(if1=bSocket0, if2=bSocket1, xfrm12=forwarding, xfrm21=forwarding, timeout=1)
    bSocket0.close()
    bSocket1.close()

创建用于发送数据包和桥接和嗅探的线程::

threadBridge = threading.Thread(target=bridge)
threadSender = threading.Thread(target=sendMessage)

启动线程:

threadBridge.start()
threadSender.start()

嗅探数据包:

packets = socket1.sniff(timeout=0.3)

关闭插座:

socket0.close()
socket1.close()
../_images/animation-scapy-cansockets-mitm.svg../_images/animation-scapy-cansockets-mitm2.svg

DBC文件格式和CAN信号

为了支持DBC文件格式, SignalFields 以及 SignalPacket 类被添加到scapy中。 SignalFields 只能在 SignalPacket . 多路复用器字段(mux)可以通过 ConditionalFields . 下面的示例演示了用法:

DBC Example:

BO_ 4 muxTestFrame: 7 TEST_ECU
 SG_ myMuxer M : 53|3@1+ (1,0) [0|0] ""  CCL_TEST
 SG_ muxSig4 m0 : 25|7@1- (1,0) [0|0] ""  CCL_TEST
 SG_ muxSig3 m0 : 16|9@1+ (1,0) [0|0] ""  CCL_TEST
 SG_ muxSig2 m0 : 15|8@0- (1,0) [0|0] ""  CCL_TEST
 SG_ muxSig1 m0 : 0|8@1- (1,0) [0|0] ""  CCL_TEST
 SG_ muxSig5 m1 : 22|7@1- (0.01,0) [0|0] ""  CCL_TEST
 SG_ muxSig6 m1 : 32|9@1+ (2,10) [0|0] "mV"  CCL_TEST
 SG_ muxSig7 m1 : 2|8@0- (0.5,0) [0|0] ""  CCL_TEST
 SG_ muxSig8 m1 : 0|6@1- (10,0) [0|0] ""  CCL_TEST
 SG_ muxSig9 : 40|8@1- (100,-5) [0|0] "V"  CCL_TEST

BO_ 3 testFrameFloat: 8 TEST_ECU
 SG_ floatSignal2 : 32|32@1- (1,0) [0|0] ""  CCL_TEST
 SG_ floatSignal1 : 7|32@0- (1,0) [0|0] ""  CCL_TEST

此DBC描述的scapy实现:

class muxTestFrame(SignalPacket):
     fields_desc = [
         LEUnsignedSignalField("myMuxer", default=0, start=53, size=3),
         ConditionalField(LESignedSignalField("muxSig4", default=0, start=25, size=7), lambda p: p.myMuxer == 0),
         ConditionalField(LEUnsignedSignalField("muxSig3", default=0, start=16, size=9), lambda p: p.myMuxer == 0),
         ConditionalField(BESignedSignalField("muxSig2", default=0, start=15, size=8), lambda p: p.myMuxer == 0),
         ConditionalField(LESignedSignalField("muxSig1", default=0, start=0, size=8), lambda p: p.myMuxer == 0),
         ConditionalField(LESignedSignalField("muxSig5", default=0, start=22, size=7, scaling=0.01), lambda p: p.myMuxer == 1),
         ConditionalField(LEUnsignedSignalField("muxSig6", default=0, start=32, size=9, scaling=2, offset=10, unit="mV"), lambda p: p.myMuxer == 1),
         ConditionalField(BESignedSignalField("muxSig7", default=0, start=2, size=8, scaling=0.5), lambda p: p.myMuxer == 1),
         ConditionalField(LESignedSignalField("muxSig8", default=0, start=3, size=3, scaling=10), lambda p: p.myMuxer == 1),
         LESignedSignalField("muxSig9", default=0, start=41, size=7, scaling=100, offset=-5, unit="V"),
     ]

class testFrameFloat(SignalPacket):
     fields_desc = [
         LEFloatSignalField("floatSignal2", default=0, start=32),
         BEFloatSignalField("floatSignal1", default=0, start=7)
     ]

CAN校准协议(CCP)

CCP源自CAN。CAN头是CCP框架的一部分。CCP有两种类型的消息对象。一个称为命令接收对象(CRO),另一个称为数据传输对象(DTO)。通常,CRO发送到一个ECU,DTO从一个ECU接收。如果一个DTO回答CRO,则信息通过计数器字段(ctr)实现。如果两个对象都具有相同的计数器值,则可以通过相关CRO对象的命令来解释DTO对象的有效负载。

创建CRO消息::

CCP(identifier=0x700)/CRO(ctr=1)/CONNECT(station_address=0x02)
CCP(identifier=0x711)/CRO(ctr=2)/GET_SEED(resource=2)
CCP(identifier=0x711)/CRO(ctr=3)/UNLOCK(key=b"123456")

如果我们对电子控制单元的DTO不感兴趣,我们可以这样发送一条CRO消息:发送一条CRO消息:

pkt = CCP(identifier=0x700)/CRO(ctr=1)/CONNECT(station_address=0x02)
sock = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000))
sock.send(pkt)

如果我们对ECU的DTO感兴趣,我们需要将cansocket的basecls参数设置为ccp,我们需要使用sr1:发送cro消息:

cro = CCP(identifier=0x700)/CRO(ctr=0x53)/PROGRAM_6(data=b"\x10\x11\x12\x10\x11\x12")
sock = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000), basecls=CCP)
dto = sock.sr1(cro)
dto.show()
###[ CAN Calibration Protocol ]###
  flags=
  identifier= 0x700
  length= 8
  reserved= 0
###[ DTO ]###
     packet_id= 0xff
     return_code= acknowledge / no error
     ctr= 83
###[ PROGRAM_6_DTO ]###
        MTA0_extension= 2
        MTA0_address= 0x34002006

由于sr1调用answers函数,我们的dto对象的有效负载通过cro对象的命令得到解释。

ISOTP

同位素信息

创建一条等幅线消息:

load_contrib('isotp')
ISOTP(src=0x241, dst=0x641, data=b"\x3eabc")

使用扩展寻址创建一个ISOTP消息:

ISOTP(src=0x241, dst=0x641, exdst=0x41, data=b"\x3eabc")

使用扩展寻址创建一个ISOTP消息:

ISOTP(src=0x241, dst=0x641, exdst=0x41, exsrc=0x41, data=b"\x3eabc")

从ISOTP消息创建CAN帧::

ISOTP(src=0x241, dst=0x641, exdst=0x41, exsrc=0x55, data=b"\x3eabc" * 10).fragment()

通过isopp套接字发送isopp消息::

isoTpSocket = ISOTPSocket('vcan0', sid=0x241, did=0x641)
isoTpMessage = ISOTP('Message')
isoTpSocket.send(isoTpMessage)

嗅探ISOTP消息:

isoTpSocket = ISOTPSocket('vcan0', sid=0x641, did=0x241)
packets = isoTpSocket.sniff(timeout=0.5)

用桥接和嗅探等电位MITM攻击

在Linux终端上设置两个VCAN::

sudo modprobe vcan
sudo ip link add name vcan0 type vcan
sudo ip link add name vcan1 type vcan
sudo ip link set dev vcan0 up
sudo ip link set dev vcan1 up

设置同位素::

.. note::

首先确保构建了一个iso tp内核模块。

当VCAN核心模块加载“sudo modprobe vcan”时,ISO TP模块可以加载到内核。

因此,导航到isopp目录,并使用“sudo insmod./net/can/can isopp.ko”加载模块。(在内核4.9.135-1-manjaro上测试)

详细说明请参见https://github.com/hartkopp/can-isopp。

导入模块:

import threading
load_contrib('cansocket')
conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True}
load_contrib('isotp')

创建到isopp套接字进行攻击::

isoTpSocketVCan0 = ISOTPSocket('vcan0', sid=0x241, did=0x641)
isoTpSocketVCan1 = ISOTPSocket('vcan1', sid=0x641, did=0x241)

创建用于在VCAN0上发送数据包的函数,线程为:

def sendPacketWithISOTPSocket():
    sleep(0.2)
    packet = ISOTP('Request')
    isoTpSocketVCan0.send(packet)

创建转发数据包的函数:

def forwarding(pkt):
    return pkt

创建在两条总线之间桥接和嗅探的函数:

def bridge():
    bSocket0 = ISOTPSocket('vcan0', sid=0x641, did=0x241)
    bSocket1 = ISOTPSocket('vcan1', sid=0x241, did=0x641)
    bridge_and_sniff(if1=bSocket0, if2=bSocket1, xfrm12=forwarding, xfrm21=forwarding, timeout=1)
    bSocket0.close()
    bSocket1.close()

创建用于发送数据包和桥接和嗅探的线程::

threadBridge = threading.Thread(target=bridge)
threadSender = threading.Thread(target=sendPacketWithISOTPSocket)

启动线程基于Linux内核模块。除了Linux之外,python can项目还用于支持其他系统上的can和cansockets。本指南介绍了BeagleboneBlack的硬件设置。选择BeagleboneBlack是因为它在主处理器上有两个CAN接口。在一个设备中存在两个CAN接口,可能会导致CAN MITM攻击和会话劫持。cannelloni框架将beaglebone black转换为can-to-udp接口,使您可以在更强大的机器上自由运行scapy。::

threadBridge.start()
threadSender.start()

在VCAN1上嗅探:

receive = isoTpSocketVCan1.sniff(timeout=1)

关闭套接字:

isoTpSocketVCan0.close()
isoTpSocketVCan1.close()

一个等速球不会尊重 src, dst, exdst, exsrc 一个isopp消息对象。

同位素插座

scapy提供了两种isopp套接字。其中一个实现是使用Hartkopp的Linux内核模块。另一个实现,isoppsOftsocket在python中完全实现。这个实现可以在Linux、Windows和OSX上使用。

ISOTPNativeSocket

要求:

  • Python 3

  • Linux系统

  • Hartkopp的Linux内核模块: https://github.com/hartkopp/can-isotp.git

在Pentest中,等电位插座通常具有更好的性能和可靠性。如果您正在使用Linux,请考虑以下实现:

conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True}
load_contrib('isotp')
sock = ISOTPSocket("can0", sid=0x641, did=0x241)

因为这个实现使用的是标准的Linux套接字,所以所有的垃圾函数都像 sniff, sr, sr1, bridge_and_sniff 开箱即用。

ISOTPSoftSocket

isopsuftsockets可以使用任何cansocket。这使得可以灵活地使用所有的python can接口。此外,这些插座可用于python2和python3。在带有本机cansockets的Linux上的用法:

conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': False}
load_contrib('isotp')
with ISOTPSocket("can0", sid=0x641, did=0x241) as sock:
    sock.send(...)

使用python can cansockets::

conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': False}
conf.contribs['CANSocket'] = {'use-python-can': True}
load_contrib('isotp')
with ISOTPSocket(CANSocket(iface=python_can.interface.Bus(bustype='socketcan', channel="can0", bitrate=250000)), sid=0x641, did=0x241) as sock:
    sock.send(...)

第二个示例允许使用 python_can.interface 对象。

注意: isopsOffsockets的内部实现需要一个后台线程。为了能正确地闭合这根线,我们建议使用 Python 。 with 语句。

UDS

UDS的主要用途是闪烁和诊断ECU。UDS是一种应用层协议,可以用作DOIP或ENET负载,也可以通过一个ISOPSOCKET直接发送UDS包。每个OEM都有自己的自定义UDS。这增加了一般应用程序的难度,渗透测试需要原始设备制造商的专门知识。routineControl作业和readDataByIdentifier/writeDataByIdentifier服务都是高度定制的。

使用参数 basecls=UDSinit 等速粒子的函数。

以下是两个用法示例:

../_images/animation-scapy-uds.svg../_images/animation-scapy-uds2.svg

自定义自定义项

在实际的用例中,UDS层是高度定制的。原始设备制造商定义了自己的包的子结构。尤其是readdatabyidentifier或writedatabyidentifier的数据包具有非常OEM或甚至是特定于ECU的子结构。因此,A StrField dataRecord 未添加到 field_desc . 预期用途是创建特定于ECU或OEM的描述文件,通过进一步的协议实现扩展scapy的通用UDS层。

自定义示例:

cat scapy/contrib/automotive/OEM-XYZ/car-model-xyz.py
#! /usr/bin/env python

# Protocol customization for car model xyz of OEM XYZ
# This file contains further OEM car model specific UDS additions.

from scapy.packet import Packet
from scapy.contrib.automotive.uds import *

# Define a new packet substructure

class DBI_IP(Packet):
name = 'DataByIdentifier_IP_Packet'
fields_desc = [
    ByteField('ADDRESS_FORMAT_ID', 0),
    IPField('IP', ''),
    IPField('SUBNETMASK', ''),
    IPField('DEFAULT_GATEWAY', '')
]

# Bind the new substructure onto the existing UDS packets

bind_layers(UDS_RDBIPR, DBI_IP, dataIdentifier=0x172b)
bind_layers(UDS_WDBI, DBI_IP, dataIdentifier=0x172b)

# Give add a nice name to dataIdentifiers enum

UDS_RDBI.dataIdentifiers[0x172b] = 'GatewayIP'

如果要使用此自定义添加,可以在运行时将其加载到scapy解释器:

>>> load_contrib("automotive.uds")
>>> load_contrib("automotive.OEM-XYZ.car-model-xyz")

>>> pkt = UDS()/UDS_WDBI()/DBI_IP(IP='192.168.2.1', SUBNETMASK='255.255.255.0', DEFAULT_GATEWAY='192.168.2.1')

>>> pkt.show()
###[ UDS ]###
  service= WriteDataByIdentifier
###[ WriteDataByIdentifier ]###
     dataIdentifier= GatewayIP
     dataRecord= 0
###[ DataByIdentifier_IP_Packet ]###
        ADDRESS_FORMAT_ID= 0
        IP= 192.168.2.1
        SUBNETMASK= 255.255.255.0
        DEFAULT_GATEWAY= 192.168.2.1

>>> hexdump(pkt)
0000  2E 17 2B 00 C0 A8 02 01 FF FF FF 00 C0 A8 02 01  ..+.............
../_images/animation-scapy-uds3.svg

GMLAN

GMLAN与UDS非常相似。这是GMS应用层协议,用于对他们的汽车进行闪烁、校准和诊断。使用参数 basecls=GMLANinit 等速粒子的函数。

用法示例:

../_images/animation-scapy-gmlan.svg

一些/ip和一些/ip sd消息

创建some/ip消息

此示例显示了一个some/ip消息,该消息使用方法0x421请求服务0x1234。不同类型的某些/IP消息遵循相同的过程,它们的规范可以在这里看到。 http://www.some-ip.com/papers/cache/AUTOSAR_TR_SomeIpExample_4.2.1.pdf .

加载贡献:

load_contrib("automotive.someip")

创建UDP包::

u = UDP(sport=30509, dport=30509)

创建IP包:

i = IP(src="192.168.0.13", dst="192.168.0.10")

创建一些/IP包::

sip = SOMEIP()
sip.iface_ver = 0
sip.proto_ver = 1
sip.msg_type = "REQUEST"
sip.retcode = "E_OK"
sip.msg_id.srv_id = 0x1234
sip.msg_id.method_id = 0x421

添加有效载荷:

sip.add_payload(Raw ("Hello"))

堆叠并发送:

p = i/u/sip
send(p)

创建some/ip sd消息

在此示例中,显示了一些/ip sd provide服务消息和一个IPv4端点。不同的条目和选项基本上遵循与此处所示相同的过程,可以在中看到 https://www.autosar.org/fileadmin/user_upload/standards/classic/4-3/AUTOSAR_SWS_ServiceDiscovery.pdf .

加载贡献:

load_contrib("automotive.someip_sd")

创建UDP包::

u = UDP(sport=30490, dport=30490)

UDP端口必须是为某些/IP SD传输选择的端口。

创建IP包:

i = IP(src="192.168.0.13", dst="224.224.224.245")

IP源必须来自服务,目标地址必须是所选的多播地址。

创建条目数组输入::

ea = SDEntry_Service()

ea.type = 0x01
ea.srv_id = 0x1234
ea.inst_id = 0x5678
ea.major_ver = 0x00
ea.ttl = 3

创建选项数组输入::

oa = SDOption_IP4_Endpoint()
oa.addr = "192.168.0.13"
oa.l4_proto = 0x11
oa.port = 30509

在本例中,l4_proto定义了与端点udp通信的协议。

创建sd包并输入:

sd = SD()
sd.set_entryArray(ea)
sd.set_optionArray(oa)
spsd = sd.get_someip(True)

get-some ip方法将some ip/sd消息叠加在some/ip消息之上,该消息具有为some/ip sd包传输预先填充的所需some/ip值。

堆叠并发送:

p = i/u/spsd
send(p)

OBD消息

OBD是在ISOTP之上实现的。使用ISOPSOCKET与电子控制单元通信。你应该设置参数 basecls=OBDpadding=True 在您的isotpsocket init调用中。

车载诊断系统分为不同的服务组。以下是一些示例请求:

请求支持的服务PIDS 0x01::

req = OBD()/OBD_S01(pid=[0x00])

响应将包含packetlistfield,调用 data_records . 此字段包含实际响应:

resp = OBD()/OBD_S01_PR(data_records=[OBD_S01_PR_Record()/OBD_PID00(supported_pids=3196041235)])
resp.show()
###[ On-board diagnostics ]###
  service= CurrentPowertrainDiagnosticDataResponse
###[ Parameter IDs ]###
     \data_records\
      |###[ OBD_S01_PR_Record ]###
      |  pid= 0x0
      |###[ PID_00_PIDsSupported ]###
      |     supported_pids= PID20+PID1F+PID1C+PID15+PID14+PID13+PID11+PID10+PID0F+PID0E+PID0D+PID0C+PID0B+PID0A+PID07+PID06+PID05+PID04+PID03+PID01

假设我们测试的ECU支持PID 0x15:

req = OBD()/OBD_S01(pid=[0x15])
resp = sock.sr1(req)
resp.show()
###[ On-board diagnostics ]###
  service= CurrentPowertrainDiagnosticDataResponse
###[ Parameter IDs ]###
     \data_records\
      |###[ OBD_S01_PR_Record ]###
      |  pid= 0x15
      |###[ PID_15_OxygenSensor2 ]###
      |     outputVoltage= 1.275 V
      |     trim= 0 %

车载诊断系统中的不同服务支持不同类型的数据。服务01和服务02支持参数标识符(PID)。维修03、07和0A支持故障诊断码(DTC)。服务04不需要有效载荷。服务05不是通过CAN在OBD上实现的。服务06支持监控标识符(MID)。服务08支持测试标识符(tid)。服务09支持信息标识符(IID)。

实例:

请求支持的信息标识符::

req = OBD()/OBD_S09(iid=[0x00])

请求车辆识别号(VIN)::

req = OBD()/OBD_S09(iid=0x02)
resp = sock.sr1(req)
resp.show()
###[ On-board diagnostics ]###
  service= VehicleInformationResponse
###[ Infotype IDs ]###
     \data_records\
      |###[ OBD_S09_PR_Record ]###
      |  iid= 0x2
      |###[ IID_02_VehicleIdentificationNumber ]###
      |     count= 1
      |     vehicle_identification_numbers= ['W0L000051T2123456']
../_images/animation-scapy-obd.svg

测试设置教程

硬件设置

Beagle Bone Black操作系统设置

  1. 下载图像
    最新的Debian Linux映像可以在网站上找到。
    https://beagleboard.org/latest-images . 选择Beagleboon黑色物联网版本并下载。
    wget https://debian.beagleboard.org/images/bone-debian-8.7\
    -iot-armhf-2017-03-19-4gb.img.xz
    

    下载后,将其复制到至少4 GB存储空间的SD卡。

    xzcat bone-debian-8.7-iot-armhf-2017-03-19-4gb.img.xz | \
    sudo dd of=/dev/xvdj
    
  2. 启用WiFi
    Debian Linux支持USB WiFi软件狗。通过bbb上的ssh登录并将WiFi网络凭据添加到文件中 /var/lib/connman/wifi.config . 如果USB WiFi加密狗不可用,也可以与通过USB模拟的BBB以太网连接共享主机的互联网连接。共享主机网络连接的教程可在此页面上找到:
    https://elementztechblog.wordpress.com/2014/12/22/sharing-internet -using-network-over-usb-in-beaglebone-black/ .
    以根用户身份登录bbb:
    ssh debian@192.168.7.2
    sudo su
    

    向Connman提供WiFi登录凭据:

    echo "[service_home]
    Type = wifi
    Name = ssid
    Security = wpa
    Passphrase = xxxxxxxxxxxxx" \
    > /var/lib/connman/wifi.config
    

    重新启动connman服务:

    systemctl restart connman.service
    

双CAN设置

  1. 设备树设置
    只有当您想使用两个CAN接口(DCAN0和DCAN1)时,才需要遵循本节。这将禁止I2c2使用DCAN0所需的插脚P9.19和P9.20。您只需要执行本节中的步骤一次。
    警告:本节中的配置将禁用BBB Capes的工作。每个斗篷都有一个小的I2c EEPROM,它存储BBB需要知道的信息,以便与斗篷通信。禁用I2c2,BBB无法与Cape Eeproms通话。当然,如果你不使用资本支出,那么这不是问题。
    获取与内核版本匹配的DTS源。去 here 切换到表示内核版本的分支。将整个分支下载为zip文件。提取它并执行以下操作(4.1版如示例所示):
    # cd ~/src/linux-4.1/arch/arm/boot/dts/include/
    # rm dt-bindings
    # ln -s ../../../../../include/dt-bindings
    # cd ..
    Edit am335x-bone-common.dtsi and ensure the line with "//pinctrl-0 = <&i2c2_pins>;" is commented out.
    Remove the complete &ocp section at the end of this file
    # mv am335x-boneblack.dts am335x-boneblack.raw.dts
    # cpp -nostdinc -I include -undef -x assembler-with-cpp am335x-boneblack.raw.dts > am335x-boneblack.dts
    # dtc -W no-unit_address_vs_reg -O dtb -o am335x-boneblack.dtb -b 0 -@ am335x-boneblack.dts
    # cp /boot/dtbs/am335x-boneblack.dtb /boot/dtbs/am335x-boneblack.orig.dtb
    # cp am335x-boneblack.dtb /boot/dtbs/
    Reboot
    
  2. 覆盖设置
    本节介绍如何为两个CAN设备(DCAN0和DCAN1)构建设备覆盖。您只需要执行本节中的步骤一次。
    以两种方式之一获取BBB Cape Overlay…
    # apt-get install bb-cape-overlays
    https://github.com/beagleboard/bb.org-overlays/
    
    然后执行以下操作:
    # cd ~/src/bb.org-overlays-master/src/arm
    # ln -s ../../include
    # mv BB-CAN1-00A0.dts BB-CAN1-00A0.raw.dts
    # cp BB-CAN1-00A0.raw.dts BB-CAN0-00A0.raw.dts
    Edit BB-CAN0-00A0.raw.dts and make relevant to CAN0. Example is shown below.
    # cpp -nostdinc -I include -undef -x assembler-with-cpp BB-CAN0-00A0.raw.dts > BB-CAN0-00A0.dts
    # cpp -nostdinc -I include -undef -x assembler-with-cpp BB-CAN1-00A0.raw.dts > BB-CAN1-00A0.dts
    # dtc -W no-unit_address_vs_reg -O dtb -o BB-CAN0-00A0.dtbo -b 0 -@ BB-CAN0-00A0.dts
    # dtc -W no-unit_address_vs_reg -O dtb -o BB-CAN1-00A0.dtbo -b 0 -@ BB-CAN1-00A0.dts
    # cp *.dtbo /lib/firmware
    
  3. Can0覆盖示例
    在dts文件夹中,创建一个包含以下列表内容的文件。
    cd ~/bb.org-overlays/src/arm
    cat <<EOF > BB-CAN0-00A0.raw.dts
    
    /*
     * Copyright (C) 2015 Robert Nelson <robertcnelson@gmail.com>
     *
     * Virtual cape for CAN0 on connector pins P9.19 P9.20
     *
     * This program is free software; you can redistribute it and/or modify
     * it under the terms of the GNU General Public License version 2 as
     * published by the Free Software Foundation.
     */
    /dts-v1/;
    /plugin/;
    
    #include <dt-bindings/board/am335x-bbw-bbb-base.h>
    #include <dt-bindings/pinctrl/am33xx.h>
    
    / {
        compatible = "ti,beaglebone", "ti,beaglebone-black", "ti,beaglebone-green";
    
        /* identification */
        part-number = "BB-CAN0";
        version = "00A0";
    
        /* state the resources this cape uses */
        exclusive-use =
            /* the pin header uses */
            "P9.19",        /* can0_rx */
            "P9.20",        /* can0_tx */
            /* the hardware ip uses */
            "dcan0";
    
        fragment@0 {
            target = <&am33xx_pinmux>;
            __overlay__ {
                bb_dcan0_pins: pinmux_dcan0_pins {
                    pinctrl-single,pins = <
                        BONE_P9_19 (PIN_INPUT_PULLUP | MUX_MODE2) /* uart1_txd.d_can0_rx */
                        BONE_P9_20 (PIN_OUTPUT_PULLUP | MUX_MODE2) /* uart1_rxd.d_can0_tx */
                    >;
                };
            };
        };
    
        fragment@1 {
            target = <&dcan0>;
            __overlay__ {
                status = "okay";
                pinctrl-names = "default";
                pinctrl-0 = <&bb_dcan0_pins>;
            };
        };
    };
    EOF
    
  4. Test the Dual-CAN Setup
    每次需要时都可以执行以下操作,或者根据需要自动执行这些步骤。
    # echo BB-CAN0 > /sys/devices/platform/bone_capemgr/slots
    # echo BB-CAN1 > /sys/devices/platform/bone_capemgr/slots
    # modprobe can
    # modprobe can-dev
    # modprobe can-raw
    # ip link set can0 up type can bitrate 50000
    # ip link set can1 up type can bitrate 50000
    

    如果两个CAN接口都已加载,请检查CapeManager的输出。

    cat /sys/devices/platform/bone_capemgr/slots
    
    0: PF----  -1
    1: PF----  -1
    2: PF----  -1
    3: PF----  -1
    4: P-O-L-   0 Override Board Name,00A0,Override Manuf, BB-CAN0
    5: P-O-L-   1 Override Board Name,00A0,Override Manuf, BB-CAN1
    

    如果出了问题, dmesg 提供内核消息以分析失败的根源。

  5. 工具书类
  6. 确认
    多亏了汤姆·原森。本节的部分内容摘自他的指南:https://github.com/haramori/rhme3/blob/master/preparation/bbb_can_setup.md

ISO-TP内核模块安装

Linux ISO-TP内核模块可从以下网站下载: https://github.com/hartkopp/can-isotp.git . 文件 README.isotp 在此存储库中,提供下载和构建此内核模块所需的所有信息和必要步骤。iso-tp内核模块也应该添加到 /etc/modules 文件,以便在BBB系统启动时自动加载此模块。

CAN接口设置

作为准备BBB的CAN接口以供使用的最后一步,必须通过一些终端命令设置这些接口。比特率可以选择来适应被测CAN总线的比特率。

ip link set can0 up type can bitrate 500000
ip link set can1 up type can bitrate 500000

树莓pi-some/ip设置

要构建一个小型测试环境,在该环境中,您可以向服务器实例发送一些/IP消息,也可以将自己伪装成服务器,一个Raspberry PI、您的笔记本电脑和vsomeip库就足够了。

  1. 下载图像

    下载最新的Raspbian图像 (https://www.raspberrypi.org/downloads/raspbian/ )安装在树莓上。

  2. VSOIMIP协议

    在rapsberry上下载vsomeip库,应用git补丁,这样它就可以与更新的boost库一起工作,然后安装它。

    git clone https://github.com/GENIVI/vsomeip.git
    cd vsomeip
    wget -O 0001-Support-boost-v1.66.patch.zip \
    https://github.com/GENIVI/vsomeip/files/2244890/0001-Support-boost-v1.66.patch.zip
    unzip 0001-Support-boost-v1.66.patch.zip
    git apply 0001-Support-boost-v1.66.patch
    mkdir build
    cd build
    cmake -DENABLE_SIGNAL_HANDLING=1 ..
    make
    make install
    
  3. 制作应用程序

    编写一些小应用程序,它们可以作为服务或客户机使用,并使用scapy-some/ip实现与客户机或服务器通信。vsomeip应用程序的示例可在vsomeip github wiki页面上找到。 (https://github.com/GENIVI/vsomeip/wiki/vsomeip-in-10-minutes

软件设置

Cannelloni框架安装

ChanelONI框架是一个用C++编写的用于在UDP上传输CAN数据的小应用程序。这样,研究人员可以将远程设备的CAN通信映射到其工作站,甚至可以在其机器上组合多个远程CAN设备。框架可从以下网站下载: https://github.com/mguentner/cannelloni.git . 这个 README.md 文件详细说明了安装和使用。Cannelloni需要操作员机器上的虚拟CAN接口。下一个列表显示虚拟CAN接口的设置。

modprobe vcan

ip link add name vcan0 type vcan
ip link add name vcan1 type vcan

ip link set dev vcan0 up
ip link set dev vcan1 up

tc qdisc add dev vcan0 root tbf rate 300kbit latency 100ms burst 1000
tc qdisc add dev vcan1 root tbf rate 300kbit latency 100ms burst 1000

cannelloni -I vcan0 -R <remote-IP> -r 20000 -l 20000 &
cannelloni -I vcan1 -R <remote-IP> -r 20001 -l 20001 &