Advertisement

SCAPY官方教程十一

阅读量:

基于CAN总线的网络安全协议分析框架
安装必要的依赖项
首先确保安装了Scapy库及其相关模块:
bash pip install scapy can isotp someip udsb xmmsd
配置虚拟CAN接口
创建虚拟CAN接口以模拟被测设备:
bash 创建两个虚拟CAN接口(供不同的设备使用) ip link add name can0 type can up ip link add name can1 type can up 设置IP地址(根据实际情况修改) ip addr set can0 192.168.0.5/255.255.255.0 ip addr set can1 192.168.0.6/255.255.255.0 设置广播模式 ip link set can0 broadcast up ip link set can1 broadcast up 关闭环路检测(防止不必要的流量监控) ip link set can0 dotx off ip link set can1 dotx off
安装内核模块(可选)
如果需要更强大的功能(如UDS套接点),可以安装ISO-TP内核模块:
bash sudo apt-get install libiso-kern-mode-canspi-xml libiso-canlp-xml libiso-canlp-xmu -o /etc/modules.so -R /etc/modules.d/modules.so \ iso-kern-mode-canspi-xml iso-kern-mode-canspi-xmu iso-kern-mode-canspi-xmu \ iso-kern-mode-canlp-xmu iso-kern-mode-canlp-xmu iso-kern-mode-canlp-xmu \ iso-kern-mode-canlp-xu iso-kern-mode-canlp-xu iso-kern-mode-canlp-xu \ iso-kern-mode-canlp-xl iso-kern-mode-canlp-xl iso-kern-mode-canlp-xl \ iso-kern-mode-isotp-xmu iso-kern-mode-isotp-xmu iso-kern-mode-isotp-xmu \ iso-kern-mode-isotp-xl iso-kern-mode-isotp-xl iso-kern-mod e-isotp-xl \ /etc/modules.d/modules.so -o /lib/modules.so -liso-tp内核模式加载成功.
编写基于CAN的网络安全分析框架
以下是一个简单的基于CAN的安全性测试框架示例:
`python
import scapy.all as scapy
def basic CAN 包抓取():

使用ISO-TP套接点捕获数据包

sock = scapy.CAN(isotp=True, channel='can0', basecls=Isotpsocket('vcan'))

捕获事件并打印被捕获的数据包类型和详细信息

for p in sock.sr(ethernet=True, macMAC=True, count=3):
print(f"Captured {p.class.name} packet: {p}")
if p[0].type == 'request':
print(f"Request ID: {p[7].data[requestidoffset:]}

各协议Layer使用说明:

一、汽车专用文档

所有与汽车相关的功能均在Linux系统中实现最佳状态。CANSockets与ISOTPSockets均为基于Linux核心内核模块的开发项目。Python-CAN开源项目旨在全面支持CAN协议及兼容多种硬件接口。

概述

建议制作一个简洁的表格来概述Scapy的所有汽车相关功能。多数协议层协议都包含多种特定类型的Packet实例。这些具有特殊用途的Packets不在本概述范围内。通过调用该函数 explore() 可以快速获取关于特定协议的所有相关信息。

OSI 层 协议 Scapy 实现
应用层 UDS (ISO 14229) UDS、UDS_*、UDS_TesterPresentSender
GMLAN GMLAN, GMLAN_*, GMLAN_[Utilities]
SOME/IP SOMEIP, SD
BMW HSFZ HSFZ、HSFZSocket、UDS_HSFZSocket
OBD OBD, OBD_S0[0-9A]
CCP CCP、DTO、CRO
XCP XCPOnCAN、XCPOnUDP、XCPOnTCP、CTORequest、CTOResponse、DTO
运输层 ISO-TP (ISO 15765-2) ISOTPSocket、ISOTPNativeSocket、ISOTPSoftSocket ISOTPSniffer、ISOTPMessageBuilder、ISOTPSession ISOTPHeader、ISOTPHeaderEA、isotp_scan ISOTP、ISOTP_SF、ISOTP_FF、ISOTP_CF、ISOTP_FC
数据链路层 CAN (ISO 11898) CAN、CANSocket、rdcandump、CandumpReader

1 物理协议

车辆内部采用了20多种不同的通信协议。大部分车辆会采用五至十种不同的协议来进行内部信息传递工作。在决定哪一种具体的技术时, 原始设备制造商通常会综合考虑沟通技术和最终汽车价格之间的平衡因素。在ECU之间, 主要使用的四种先进传输技术分别是控制器局域网(CAN)、FlexRay、本地互连网络(LIN)以及汽车以太网.这些技术之所以能在安全性方面得到广泛应用, 是因为它们能够满足现代汽车无线网络的基本需求.

LIN

LIN 是一种专设用于低速数据传输的单线通信协议。车辆中的执行器和传感器通过与充当 LIN 主机的ECU进行通信来交换信息。LIN 可以实现软件升级。然而,在LIN系统中负责处理日常操作的端设备一般无需升级软件,因其功能受限。

CAN

CAN 是目前应用最为广泛的 ECU 间通信协议。在成本较低或较老旧的车型中,CAN 协议依然是车载网络的基础传输介质。该协议凭借其可靠性与兼容性,能够支持车辆运行过程中的关键信息安全、诊断数据采集以及系统软件更新工作。然而,CAN 协议由于缺乏内置的安全机制,同时具有普遍性,因此在网络安全风险评估领域发挥着核心作用

FlexRay

FlexRay联盟定义了 FlexRay 作为 CAN 技术的后续版本,并旨在满足更高阶的通信需求。随着汽车行业的智能化发展,对通信带宽的需求日益增长。按照设计规范, FlexRay 是一种专为 ECU 之间实现高效可靠通信而设计的协议,并且在性能上远超同类方案。相较于 CAN 组件,在成本上 FlexRay 成套设备更具优势,并促使整车厂在选型时优先考虑其性价比优势

汽车以太网

现代上层汽车系统已普遍采用汽车以太网技术作为一种新型核心通信技术。随着带宽需求的急剧增加,这一趋势主要源于驾驶员辅助系统与自动驾驶技术的发展需求。在OSI模型中,物理层(第1层)通过采用IEEE 802.3标准实现了对汽车以太网BroadR-Reach的有效区分。这种架构选择带来了显著的优势。例如,在不修改操作系统或路由、过滤及防火墙机制的前提下,操作系统的通信堆栈仍可正常运行。当前所采用的汽车以太网组件价格相对较低,并将导致CAN总线和汽车以太网成为应用中最常见的两种通信协议。

2 拓扑

线路总线

线路总线网络拓扑

中央网关

具有中央 GW ECU 的网络拓扑

中央网关 (GW) 拓扑可以在这一市场环境中发现价格较高的老旧车辆与中价至新车型之间存在的潜在连接机会。通过集中式GW ECU实施域分离机制隔离特定功能区域,从而允许 OEM能够将所有具备远程攻击面的ECU集中配置在一个专用子网络内。具有安全核心功能的关键型ECU则独立部署于专用CAN网络上,而FlexRay也可作为单一域内的本地通信协议使用。这种拓扑结构的安全性直接由中央GW ECU的安全性能所决定,其架构优势在于实现了对各个功能区域的有效隔离保护,从而增强了整体防护能力。值得注意的是,即使在攻击者通过任意入口点利用ECU实现越权访问的情况下,也需要依赖额外的漏洞或逻辑错误才能影响其他隔离域中的关键系统。

中央网关和域控制器

具有汽车以太网骨干网和 DC 的网络拓扑

在当前高端汽车中发现了一种新型架构,其核心组件包括中央GW节点和域控制器(DC)等关键部分.随着现代汽车配备了先进的自动驾驶和驾驶员辅助系统,它们对带宽的需求日益增长.以太网作为汽车内部的主要通信网络,在整个架构中扮演着关键角色.由多个DC节点与中央GW节点连接形成的区域组成了车体的关键干线路.每个DC节点负责协调本区域内与其他区域的数据传输.该架构通过各DC节点与其所属区域之间强大的隔离机制,在确保数据安全的同时实现了极高的防护等级.基于按需服务(FoD)理念的设计优势使得OEM能够提供灵活高效的信息路由解决方案.

3 汽车通信协议

本节阐述了用于汽车网络安全评估的相关通信协议。相较于"物理协议"章节, 本节着重探讨数据通信特性。

CAN

CAN 通信技术开创于1983年,被视为一种基于消息传输的信息可靠车辆总线系统。德国罗伯特·鲍斯集团在CAN标准中制定了多种功能,旨在开发出适合控制器局域网的安全高效协议。这种通信机制的一个显著特点是其内部状态机,该状态机通过执行故障静默操作来保护关键安全网络,从而避免遭受低层次节点干扰的问题。当发生接收错误或传输错误时,CAN协议会将节点的状态从主动错误转为被动错误,最终导致总线关闭以防止冲突。

传输错误时的 CAN 总线状态。接收错误计数器 (REC)、发送错误计数器 (TEC)

近年来,该协议标准已被广泛滥用,涉及针对车辆CAN网络实施拒绝服务(DoS)攻击以及信息收集攻击等恶意行为。等研究人员通过利用ECU总线关闭状态这一特性,展示了针对CAN网络实施DoS攻击的具体方法:在特定节点发送通信报文时若引入数据包误码,将导致受攻击节点报告丢失数据,从而迫使这些节点也进入总线关闭状态。研究发现,在特定节点发送通信错误即可导致大量数据丢失;而当ECU遭受DoS攻击后,其后续收到的所有丢失帧均能被相关ECU识别并记录;这种方法可实现对CAN总线异常行为的高度定位能力.2019年,Kulandaivel等研究者首次将统计分析技术与上述方法相结合,实现了对CAN网络中的故障定位.他们通过分析ECU遭受攻击前后的CAN通信流量特征,成功构建了一套完整的故障定位框架.这一方法不仅能够有效识别受损帧源ECU,还能够快速定位出所有异常数据包来源; Ken Tindell于2019年就对CAN总线低级攻击进行了系统性总结

完整的CAN数据帧结构

该图展示了通过网络传输的...其包含多个关键字段如仲裁控制和数据等这些部分均为普通应用软件能够访问的对象而剩余部分则主要由硬件层进行评估并通常不会转发至应用程序层需要注意的是...在此示例中引入的重要变体包括具有扩展仲裁字段的...以及基于灵活数据速率(CAN FD)...协议值得注意的是在Linux系统中每个接收到来自...的数据帧都会被传递给SocketCAN接口借助操作系统提供的网络套接字功能SocketCAN能够有效地处理来自不同设备的数据传输当用户级应用程序从特定类型的...套接字接收数据时会发生怎样的编码行为

SocketCAN定义的CAN帧

如图所示,在处理CAN帧期间,物理层驱动程序明显存在信息丢失现象。绝大多数CAN驱动程序运行模式基本一致,在微控制器和Linux内核环境下均表现相同。这表明标准的应用程序无法访问CRC字段、确认位或帧结束字段。

基于车辆或本地域中的CAN通信系统,ECU会传递传感器数据和控制指令;这些信息存在安全隐患,容易被acker修改.acker能够轻易地蒙骗CAN总线上传感器的数值,从而导致其他ECU产生恶意反应.这种欺骗性攻击是Miller和Valasek在其对汽车网络的研究中所描述的.为了防范通过CAN传输的关键信息安全威胁,AUTOSAR组织发布了相应的安全通信规范,旨在保障关键信息系统的安全性.

ISO-TP (ISO 15765-2)

该协议仅限于处理最多8字节的数据量。
这些应用如故障诊断或ECU编程等对数据处理能力的要求远高于CAN协议所能提供的基础能力。
为此,
汽车行业已标准化采用了一个位于CAN之上并作为其扩展功能而被引入的传输层协议7
在使用CAN帧分段的情况下,在两个ISO-TP端点之间能够传递的最大有效数据量为4095字节。
在该协议中定义了四种特定类型的帧格式用于数据传输管理。

ISO-TP 碎片化通信

多种类型ISO-TP帧通过上图进行展示。CAN总线的有效负载容量被四个不同的ISO-TP帧中的一个所替代。每个ISO-TP帧都有其特定用途。单一帧能够承载从1到7个字节范围内的ISO-TP消息数据。其中Single Frame或First Frame的len字段标识ISO-TP消息长度值。对于那些包含超过7个有效载荷字节的消息数据而言,在发送前必须将其分割为一个First Frame随后接多个连续Frame以实现高效传输。上图展示了这种通信机制的工作原理:当发送方发送完First Frame后接收方必须通过Flow Control Frame将自身的信息反馈给发送方以便后续的操作仅在接收到该Flow Control Frame之后才允许发送方根据接收方的实际处理能力来发送后续连续Frame

ISO-TP 帧类型

ISO-TP 被用作传输协议,在借助寻址机制实现定向通信的基础上,在车辆领域中主要充当诊断通信的传输协议。极少情况下,则用于在车辆内的ECU之间传递较大的数据信息。为了确保信息安全,在必要的情况下应应用到所有通过 ISO-TP 传递的应用层协议上;由于 ISO-TP 无法保护其传输的数据内容。

DoIP

IP 诊断 (DoIP) 最初是在集中式网关拓扑的汽车网络中实施的。集中式网关 GW 被指定为 DoIP 的端点节点,负责将诊断消息路由至目标网络区域,并允许多台ECU的同时配置和故障检测功能得以实现。值得注意的是,在维修店环境中,GW 和测试设备之间的 IP 通信速度显著快于通过CAN总线连接到目标ECU的过程。这种速度差异使得剩余带宽资源得以专门用于建立DoIP与其他不同CAN域中的ECU之间的新连接关系。此外,该协议体系作为AUTOSAR架构和ISO标准之一被引入到该协议体系中,并与ISO-TP类似地未预先指定特殊的安全机制。安全通信责任最终由应用层协议负责划分。

诊断协议

基于现有技术文档中的信息可知,在诊断协议领域有两种典型实例:通用汽车局域网(GMLAN)以及统一诊断服务(UDS),后者依据ISO 14229-2标准构建。该方案由通用汽车公司采用;而德国OEM厂商普遍采用UDS这一方案。就规范而言,这两种协议具有高度相似性,并且均采用了ISO-TP或DoIP消息作为目标ECU的信息传输手段。值得注意的是,在尽管不同厂商普遍采用了UDS这一方案;但为了满足自身特定需求,在标准中各厂商均会增添各自特有的附加功能项;同时在ECU间实现独立化的ISO-TP网络配置也是一个较为关键的技术要点。相比UDS方案,在GMLAN中对ECU的寻址地址及内部运行机制有更为精确的规定。

UDS 和 GMLAN 采用树状式消息架构,在其中的第一个字节标记出服务信息。每个服务都通过反馈机制来完成回应过程。标准规范了两种类型的反馈类型:一种是以特定编码方式发送;另一种则是通过附加 0x40 到请求中的服务ID来表示。

汽车诊断协议栈

传输层和应用层之间的明确分离允许为两个网络堆栈创建应用层工具。上图提供了相关协议和相应层的概览。UDS 定义了应用程序层和传输层之间的清晰分离。在基于 CAN 的网络上,ISO-TP 用于此目的。CAN协议可以看作是网络访问协议。这允许用 DoIP 或 HSFZ 和以太网替换 ISO-TP 和 CAN。GMLAN 协议结合了与 ISO-TP 和 UDS 非常相似的传输层和应用层规范。由于这种相似性,可以应用相同的应用层特定扫描技术。为了克服 CAN 的带宽限制,最新的车辆架构使用基于以太网的诊断协议(DoIP、HSFZ) 与中央网关 ECU 通信。中央网关 ECU 将应用层数据包从基于以太网的网络路由到基于 CAN 的车辆内部网络。通常,车辆中所有 ECU 的诊断功能都可以通过 UDSonCAN 或 UDSonIP 从 OBD 连接器访问。

SOME/IP

SOME/IP 作为一种可扩展的服务级IP中间件,在汽车网络数据通信领域引入了创新性理念。该协议特别适用于 latest automotive networks 中域控制器之间的数据交换。通过订阅与通知机制实现动态的数据订阅功能,在车辆正常运行期间与域控制器相关的网关之间传递通信数据。其应用场景与CAN总线通信方案相似。该协议的主要目标是实现ECU间传感器和执行器数据的有效交互。同时,在网络安全层面,该协议将某些/I P通信视为潜在攻击的目标进行监控与防护。

CCP/XCP

通用测量与校准协议 (XCP) 被视为汽车系统校准领域的重要规范之一,它是基于CAN总线 (CCP) 发展而来的衍生标准,由ASAM eV于2003年正式规范化制定.作为关键组件,XCP的主要功能体现在汽车电子控制单元 (ECU) 及车辆开发测试与校准环节.专为CAN总线设计,CCT具有8字节传输限制的特点,而为了避免这一限制,XCP设计初衷是为了兼容多种数据传输协议.该规范方案不仅支持CAN总线(CAN)、CAN快速数据传输协议(CAN FD)、串行外设接口(SPI)、以太网(ETN)、通用串行总线(USB)以及FlexRay等多种通信介质,还特别强调了其广泛的适用性和灵活性.相较于CCT,CXP在功能覆盖范围上更为宽广,同时也对数据处理效率进行了显著优化.

两种协议各自采用基于会话式的通信流程,并通过主节点与多个从节点间的种子及密钥机制实现身份验证功能。其中主节点通常指代工程型个人计算机(PC),而从节点则主要用于系统参数配置。XCP系统不仅提供仿真能力还具备强大的调试功能:车辆工程师可通过XCP平台调试MATLAB Simulink模型,并利用该工具读写ECU内存中的关键数据。另一个显著特点在于其支持的数据采集机制:两种协议均允许工程师指定感兴趣内存地址以构建数据采集列表:该列表中的所有内存地址将定期被读取并在选中通信通道上发送相应的CCP或XCP数据包(DAQ)。命令传输对象(CTO)区域内所有通信均遵循由XCP主站发起的统一请求响应流程:接收方可能收到命令响应包(RES)、错误(ERR)、事件包(EV)或服务请求包(SERV)作为处理结果。完成配置后从机能够实时监听刺激(STIM)数据包并定期输出预设的DAQ数据包。系统资源部分列出了潜在的安全威胁包括程序代码文件(PGM)、校准操作文件(CAL)、DAQ端口及刺激接口(STIM)等区域:这些区域若被不当利用将可能导致严重的安全隐患和漏洞风险。因此确保该协议能够有效保障车辆运行的安全性和稳定性至关重要

位于XCP Master和XCP Slave之间运行的XCP通信架构。该架构揭示了CTO/数据传输实体(DOE)包8的信息传递路径。

4 Layers

① CAN

通过 Linux SocketCAN 发送和接收消息:

复制代码
 load_layer("can")

    
 load_contrib('cansocket')
    
  
    
 socket = CANSocket(channel='can0')
    
 packet = CAN(identifier=0x123, data=b'01020304')
    
  
    
 socket.send(packet)
    
 rx_packet = socket.recv()
    
  
    
 socket.sr1(packet, timeout=1)
    
    
    
    
    AI助手

通过 Vector CAN 接口发送和接收消息:

复制代码
 load_layer("can")

    
 conf.contribs['CANSocket'] = {'use-python-can' : True}
    
 load_contrib('cansocket')
    
  
    
 socket = CANSocket(bustype='vector', channel=0, bitrate=1000000)
    
 packet = CAN(identifier=0x123, data=b'01020304')
    
  
    
 socket.send(packet)
    
 rx_packet = socket.recv()
    
  
    
 socket.sr1(packet)
    
    
    
    
    AI助手

CAN 帧

可查阅关于CAN的基础信息可通过访问该网页链接获取到

在Scapy会话中进行测试的前提是CAN层已加载。如果测试环境中的CAN层尚未加载,则可以在Scapy会话中执行相应的配置以实现CAN层的激活。

复制代码
    >>> load_layer("can")
    
    AI助手

创建标准 CAN 帧:

复制代码
    >>> frame = CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')
    
    AI助手

创建扩展 CAN 帧:

复制代码
 frame = CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08')

    
 >>> frame.show()
    
 ###[ CAN ]###
    
   flags= extended
    
   identifier= 0x10010000
    
   length= 8
    
   reserved= 0
    
   data= '\x01\x02\x03\x04\x05\x06\x07\x08'
    
    
    
    
    AI助手

CAN 帧输入和输出

CAN 帧可以从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)
    
    
    
    
    AI助手

此外,在candump输出的基础上可与日志文件结合导入CAN帧。该类类似于socket接口的设计模式,并支持通过调用Scapy API进行其他操作。

复制代码
 with CandumpReader("candump.log") as sock:

    
     can_msgs = sniff(count=50, opened_socket=sock)
    
    
    
    
    AI助手

DBC 文件格式和 CAN 信号

为了支持 DBC 文件格式(如DBC),Scapy 中引入了 SignalFields 类。该模块中的 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
    
    
    
    
    AI助手

此 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)
    
     ]
    
  
    
 bind_layers(SignalHeader, muxTestFrame, identifier=0x123)
    
 bind_layers(SignalHeader, testFrameFloat, identifier=0x321)
    
  
    
 dbc_sock = CANSocket("can0", basecls=SignalHeader)
    
  
    
 pkt = SignalHeader()/testFrameFloat(floatSignal2=3.4)
    
  
    
 dbc_sock.send(pkt)
    
    
    
    
    AI助手

此示例使用类 SignalHeader 用作标题。
有效负载由 individual 指定为 SignalPackets。
bind_layers 负责将标头与取决于 CAN 标识符的有效载荷进行组合。
如果希望直接从您的接收获取 SignalPackets, 请确保在 init 函数中传递 CANSocket 参数。

Canmatrix 提供从 DBC 或 AUTOSAR XML 文件生成 Scapy 文件的能力,并通过 GitHub 链接提供了相应的转换工具和文档。

② CANSockets

Linux SocketCAN

本小节介绍了与 Linux SocketCAN 相关的基本知识。您可以在以下位置访问 Oliver Hartkopp 的精彩概述:https ://wiki.automotivelinux.org/_media/agl-distro/agl2017-socketcan-print.pdf

虚拟 CAN 设置

Linux上的SocketCAN实现了虚拟化的CAN接口。这些接口作为在CAN总线上的基础操作而存在,并不依赖于额外的硬件配置即可运行。值得注意的是,在Scapy的单元测试环境中,这类虚拟接口被广泛使用,并成为开发汽车相关功能的重要工具。

实现虚拟化CAN套接字功能需要一个特定的Linux内核模块配置。在命令行界面环境下运行以下命令以加载所需模块:

说明

复制代码
    sudo modprobe vcan
    
    AI助手

为了实现配置虚拟 CAN 接口的目的,在此代码段中将指定vcan0作为虚拟 CAN 接口的名称;具体来说,在此处可设置任意名称:

复制代码
 sudo ip link add name vcan0 type vcan

    
 sudo ip link set dev vcan0 up
    
    
    
    
    AI助手

可以从 Scapy 执行相同的命令,如下所示:

复制代码
 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)
    
    
    
    
    AI助手

当有必要时,请通过执行命令配置CAN接口使其处于 listen-only 或 loopback 模式;具体操作如下:ip link set

复制代码
    ip link set vcan0 type can help  # shows additional information
    
    AI助手

Linux can-utils

作为Linux-Can的一部分,Oliver Hartkopp 开发了一系列高度实用的命令行工具:https://github.com/linux-can/can-utils

以下实例突显了 Linux can-utils 的基础功能。这些工具在快速检查、转储、发送或记录 CAN 消息方面表现尤为出色。

Scapy CANSocket

在Scapy中被实现为两种不同的CANSocket类型。其中一种被称为 Native CANSocket ,另一种则被命名为 Python-can CANSocket 。

基于Python 3语言的集成,《本地CANSockets》技术可以在运行于Linux生态系统的任意版本(包括但不限于)操作系统上成功部署与应用。这些高性能通信接口特征由可调用函数实现(如select),其特性对这些场景的应用产生显著影响。

出于兼容性原因, Scapy 中添加了Python-can CANSockets。在 Windows 或 OSX 以及所有没有 Python 3 的系统上,可以通过python-can. python-can需要在系统上安装:https ://github.com/hardbyte/python-can/ Python-can CANSockets 是 Scapy 的 python-can 接口对象的包装器。两种 CANSocket 都提供相同的 API,这使得它们在大多数情况下都可以交换。然而,必须尊重每种 CANSocket 类型的一些独特行为。某些 CAN 接口,例如 Vector 硬件,仅在 Windows 上受支持。这些接口可以通过Python-can CANSockets 使用。

原生 CANSocket

创建一个简单的原生 CANSocket:

复制代码
 conf.contribs['CANSocket'] = {'use-python-can': False} #(default)

    
 load_contrib('cansocket')
    
  
    
 # Simple Socket
    
 socket = CANSocket(channel="vcan0")
    
    
    
    
    AI助手

创建本机 CANSocket 仅侦听 Id == 0x200 的消息:

复制代码
    socket = CANSocket(channel="vcan0", can_filters=[{'can_id': 0x200, 'can_mask': 0x7FF}])
    
    AI助手

创建本机 CANSocket 仅侦听 Id >= 0x200 和 Id <= 0x2ff 的消息:

复制代码
    socket = CANSocket(channel="vcan0", can_filters=[{'can_id': 0x200, 'can_mask': 0x700}])
    
    AI助手

创建本机 CANSocket 仅侦听 Id != 0x200 的消息:

复制代码
    socket = CANSocket(channel="vcan0", can_filters=[{'can_id': 0x200 | CAN_INV_FILTER, 'can_mask': 0x7FF}])
    
    AI助手

创建具有多个 can_filters 的本机 CANSocket:

复制代码
 socket = CANSocket(channel='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}])
    
    
    
    
    AI助手

创建一个本机 CANSocket,它也接收自己的消息:

复制代码
    socket = CANSocket(channel="vcan0", receive_own_messages=True)
    
    AI助手

嗅探 CANSocket:

CANSocket python-can

建议采用Python-Can库在Windows、macOS和Linux系统上实现CAN接口通信。该库可借助名为can_socket的对象进行操作。创建Python-Can库中的can_socket对象时,请确保所有参数均来自该对象以正确初始化设备地址配置项。

创建python-can CANSocket的方法:

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

    
 load_contrib('cansocket')
    
    
    
    
    AI助手

创建一个简单的python-can CANSocket:

复制代码
    socket = CANSocket(bustype='socketcan', channel='vcan0', bitrate=250000)
    
    AI助手

创建一个带有多个过滤器的 python-can CANSocket:

复制代码
 socket = CANSocket(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}])
    
    
    
    
    AI助手

关于 Python-Can 的更多信息,请访问:https ://python-can.readthedocs.io/

CANSocket MITM 攻击与桥接和嗅探

该示例具体说明了在虚拟CAN接口上实现桥接和嗅探的方法。针对实际应用场景,建议采用真正的CAN接口。在Linux终端环境中进行vcan的配置时,请确保设置至少两个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
    
    
    
    
    AI助手

导入模块:

复制代码
 import threading

    
 load_contrib('cansocket')
    
 load_layer("can")
    
    
    
    
    AI助手

创建可以攻击的套接字:

复制代码
 socket0 = CANSocket(channel='vcan0')

    
 socket1 = CANSocket(channel='vcan1')
    
    
    
    
    AI助手

创建一个函数以使用线程发送数据包:

复制代码
 def sendPacket():

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

创建转发或更改数据包的功能:

复制代码
 def forwarding(pkt):

    
     return pkt
    
    
    
    
    AI助手

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

复制代码
 def bridge():

    
     bSocket0 = CANSocket(channel='vcan0')
    
     bSocket1 = CANSocket(channel='vcan1')
    
     bridge_and_sniff(if1=bSocket0, if2=bSocket1, xfrm12=forwarding, xfrm21=forwarding, timeout=1)
    
     bSocket0.close()
    
     bSocket1.close()
    
    
    
    
    AI助手

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

复制代码
 threadBridge = threading.Thread(target=bridge)

    
 threadSender = threading.Thread(target=sendMessage)
    
    
    
    
    AI助手

启动线程:

复制代码
 threadBridge.start()

    
 threadSender.start()
    
    
    
    
    AI助手

嗅探数据包:

复制代码
    packets = socket1.sniff(timeout=0.3)
    
    AI助手

关闭套接字:

复制代码
 socket0.close()

    
 socket1.close()
    
    
    
    
    AI助手

③ CAN 校准协议 (CCP)

起源于CAN协议体系中的一种通信机制。作为CAN总线协议的一部分,在其架构中CCP承担着协调节点间信息传递的重要职责。该协议按照功能特点可划分为两类的消息实体:一类为指令接收型(CRO),另一类为数据传输型(DTO)。其中将主要负责将指令发送至ECU;而DTO则主要负责从ECU接收数据信息。当一个DTO接收到来自相关CRO的信息时,则相关信息通过计数值字段 (ctr) 实现;若两个对象拥有相同的计数值,则可以根据相关联的CRO指令解析出DTO所负载的实际数据量。

创建 CRO 消息:

复制代码
 load_contrib('automotive.ccp')

    
 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")
    
    
    
    
    AI助手

如果对 Ecu 的 DTO 不感兴趣的话,则可以按照以下方式操作:接下来的操作步骤如下:

复制代码
 pkt = CCP(identifier=0x700)/CRO(ctr=1)/CONNECT(station_address=0x02)

    
 sock = CANSocket(bustype='socketcan', channel='vcan0')
    
 sock.send(pkt)
    
    
    
    
    AI助手

当我们关注 Ecu 的 DTO 时

复制代码
 cro = CCP(identifier=0x700)/CRO(ctr=0x53)/PROGRAM_6(data=b"\x10\x11\x12\x10\x11\x12")

    
 sock = CANSocket(bustype='socketcan', channel='vcan0', 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
    
    
    
    
    AI助手

因为 sr1 调用answers函数而导致我们的DTO对象的负载能力被CRO对象进行解析。

④ 通用校准和测量协议 (XCP)

XCP 可被视为 CCP 的延续,并广泛应用于多种通信场景中。Scapy 软件包整合了多种基础通信协议功能。
具体而言,
它支持包括 CAN、UDP 和 TCP 等通信协议。
该协议系统分为两种消息类型:
一种是用于发送指令的目标信息,
另一种则用于接收数据。
当发送 CTO 时,
ECU 将收到相关请求信息,
并根据结果进行响应或错误反馈。
除了上述功能外,
在某些情况下,
当 ECU 发送 CTO 时可以触发主设备的异步事件或启动特定的服务。
接收方将接收到的数据包称为 DAQ 数据块,
这些数据块包含了测量值或其他相关信息。
ECU 接收的数据包则用于周期性地触发特定的动作序列,
这些动作序列被称为 STIM 序列。

创建 CTO 消息:

复制代码
 CTORequest() / Connect()

    
 CTORequest() / GetDaqResolutionInfo()
    
 CTORequest() / GetSeed(mode=0x01, resource=0x00)
    
    
    
    
    AI助手

要通过 CAN 发送消息,必须添加标头:

复制代码
 pkt = XCPOnCAN(identifier=0x700) / CTORequest() / Connect()

    
 sock = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0'))
    
 sock.send(pkt)
    
    
    
    
    AI助手

当关注 Ecu 的响应时

复制代码
 sock = CANSocket(bustype='socketcan', channel='vcan0', basecls=XCPonCAN)

    
 dto = sock.sr1(pkt)
    
    
    
    
    AI助手

因为调用answers函数, 我们的XCP响应对象的有效负载将由CTO对象通过相应的命令进行解释, 否则将无法完成这种解析. 第一条消息始终应为"CONNECT"消息, Ecu的响应将决定消息读取的方式. 例如: 字节顺序. 否则, 必须在contrib配置中设置地址粒度以及DTO和CTO的最大大小:

复制代码
 conf.contribs['XCP']['Address_Granularity_Byte'] = 1  # Can be 1, 2 or 4

    
 conf.contribs['XCP']['MAX_CTO'] = 8
    
 conf.contribs['XCP']['MAX_DTO'] = 8
    
    
    
    
    AI助手

如果您不希望在收到消息后设置此项,您也可以禁用该功能:

复制代码
 conf.contribs['XCP']['allow_byte_order_change'] = False

    
 conf.contribs['XCP']['allow_ag_change'] = False
    
 conf.contribs['XCP']['allow_cto_and_dto_change'] = False
    
    
    
    
    AI助手

要通过 TCP 或 UDP 发送 pkt,必须使用另一个标头。TCP:

复制代码
 prt1, prt2 = 12345, 54321

    
 XCPOnTCP(sport=prt1, dport=prt2) / CTORequest() / Connect()
    
    
    
    
    AI助手

UDP:

复制代码
    XCPOnUDP(sport=prt1, dport=prt2) / CTORequest() / Connect()
    
    AI助手

XCP扫描仪系统 官方文档

XCPScanner 是一个实用程序,用于查找支持 XCP 的 ECU 的 CAN 标识符。

命令行使用示例:

复制代码
 python -m scapy.tools.automotive.xcpscanner -h

    
 Finds XCP slaves using the "GetSlaveId"-message(Broadcast) or the "Connect"-message.
    
  
    
 positional arguments:
    
   channel               Linux SocketCAN interface name, e.g.: vcan0
    
  
    
 optional arguments:
    
   -h, --help            show this help message and exit
    
   --start START, -s START
    
                     Start identifier CAN (in hex).
    
                     The scan will test ids between --start and --end (inclusive)
    
                     Default: 0x00
    
   --end END, -e END     End identifier CAN (in hex).
    
                     The scan will test ids between --start and --end (inclusive)
    
                     Default: 0x7ff
    
   --sniff_time', '-t'   Duration in milliseconds a sniff is waiting for a response.
    
                     Default: 100
    
   --broadcast, -b       Use Broadcast-message GetSlaveId instead of default "Connect"
    
                     (GetSlaveId is an optional Message that is not always implemented)
    
   --verbose VERBOSE, -v
    
                     Display information during scan
    
   23.     Examples:
    
     python3.6 -m scapy.tools.automotive.xcpscanner can0
    
     python3.6 -m scapy.tools.automotive.xcpscanner can0 -b 500
    
     python3.6 -m scapy.tools.automotive.xcpscanner can0 -s 50 -e 100
    
     python3.6 -m scapy.tools.automotive.xcpscanner can0 -b 500 -v
    
    
    
    
    AI助手

交互式 shell 使用示例::

复制代码
 >>> conf.contribs['CANSocket'] = {'use-python-can': False}

    
 >>> load_layer("can")
    
 >>> load_contrib("automotive.xcp.xcp")
    
 >>> sock = CANSocket("vcan0")
    
 >>> sock.basecls = XCPOnCAN
    
 >>> scanner = XCPOnCANScanner(sock)
    
 >>> result = scanner.start_scan()
    
    
    
    
    AI助手

结果字段包含 slave_id(用于接收 XCP 消息的 Ecu 的唯一标识符)和 response_id(用于发送 XCP 消息的 Ecu 的唯一标识符)。

⑤ ISOTP

创建 ISOTP 消息:

复制代码
 load_contrib('isotp')

    
 ISOTP(tx_id=0x241, rx_id=0x641, data=b"\x3eabc")
    
    
    
    
    AI助手

创建具有扩展寻址的 ISOTP 消息:

复制代码
    ISOTP(tx_id=0x241, rx_id=0x641, rx_ext_address=0x41, data=b"\x3eabc")
    
    AI助手

创建具有扩展寻址的 ISOTP 消息:

复制代码
    ISOTP(tx_id=0x241, rx_id=0x641, rx_ext_address=0x41, ext_address=0x41, data=b"\x3eabc")
    
    AI助手

从 ISOTP 消息创建 CAN 帧:

复制代码
    ISOTP(tx_id=0x241, rx_id=0x641, rx_ext_address=0x41, ext_address=0x55, data=b"\x3eabc" * 10).fragment()
    
    AI助手

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

复制代码
 isoTpSocket = ISOTPSocket('vcan0', sid=0x241, did=0x641)

    
 isoTpMessage = ISOTP('Message')
    
 isoTpSocket.send(isoTpMessage)
    
    
    
    
    AI助手

嗅探 ISOTP 消息:

复制代码
 isoTpSocket = ISOTPSocket('vcan0', sid=0x641, did=0x241)

    
 packets = isoTpSocket.sniff(timeout=0.5)
    
    
    
    
    AI助手

ISOTP Sockets

Scapy 支持了 ISOTP-Sockets 的两种实现方式。其中一种基于 ISOTPNativeSocket 实现的方式采用了 Hartkopp 开发的 Linux 核心模块;另一种基于 ISOTPSoftSocket 的实现则完全由 Python 语言编写;这些方案均可应用于 Linux、Windows 以及 macOS 系统上。

不将 tx_id, rx_id, rx_ext_address, ext_address 作为 ISOTP 的关注对象

系统兼容性

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

蟒蛇操作系统 带有 can_isotp 的 Linux Linux wo can_isotp 视窗/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}

ISOTPSocket可以设置为 a ISOTPNativeSocket或 a ISOTPSoftSocket。该决定取决于配置(选择)或 (选择)。这将允许您编写独立于平台的代码。在加载 ISOTP 层之前应用此配置。conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True}``ISOTPNativeSocket``conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': False}``ISOTPSoftSocket``load_contrib('isotp')

就 ISOTPSocket 的兼容性另述。通常用 with 语句来创建套接字。确保 ISOTPSoftSocket 对象正确关闭。例如,在开发过程中,通常使用 with 语句来创建套接字。

复制代码
 with ISOTPSocket("vcan0", did=0x241, sid=0x641) as sock:

    
     sock.send(...)
    
    
    
    
    AI助手

ISOTPNativeSocket

要求:

Python3

Linux

Hartkopp的Linux内核模块已成功并入主线下一代版本

渗透测试环境中, ISOTPNativeSockets 常见地展现出卓越的性能与稳定性. 建议您若是在运行Linux系统, 请参考以下建议:

复制代码
 conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True}

    
 load_contrib('isotp')
    
 sock = ISOTPSocket("can0", sid=0x641, did=0x241)
    
    
    
    
    AI助手

该方案基于标准Linux内核提供的套接字接口设计,并利用这些内建功能实现了对Scapy库的支持。sniff, sr, sr1, bridge_and_sniff

ISOTPSoftSocket

ISOTPSoftSockets 支持任何类型的 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(...)
    
    
    
    
    AI助手

与 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(bustype='socketcan', channel="can0"), sid=0x641, did=0x241) as sock:
    
     sock.send(...)
    
    
    
    
    AI助手

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

提示:ISOTPSoftSockets的核心实现依赖于独立的后台进程。确保该线程能够被正确关闭,推荐采用Python with语句进行操作。

使用网桥和嗅探的 ISOTP 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
    
    
    
    
    AI助手

导入模块:

复制代码
 import threading

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

创建 ISOTP 套接字以进行攻击:

复制代码
 isoTpSocketVCan0 = ISOTPSocket('vcan0', sid=0x241, did=0x641)

    
 isoTpSocketVCan1 = ISOTPSocket('vcan1', sid=0x641, did=0x241)
    
    
    
    
    AI助手

创建函数以使用线程在 vcan0 上发送数据包:

复制代码
 def sendPacketWithISOTPSocket():

    
     sleep(0.2)
    
     packet = ISOTP('Request')
    
     isoTpSocketVCan0.send(packet)
    
    
    
    
    AI助手

创建转发数据包的函数:

复制代码
 def forwarding(pkt):

    
     return pkt
    
    
    
    
    AI助手

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

复制代码
 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()
    
    
    
    
    AI助手

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

复制代码
 threadBridge = threading.Thread(target=bridge)

    
 threadSender = threading.Thread(target=sendPacketWithISOTPSocket)
    
    
    
    
    AI助手

启动线程:

复制代码
 threadBridge.start()

    
 threadSender.start()
    
    
    
    
    AI助手

嗅探 vcan1:

复制代码
    receive = isoTpSocketVCan1.sniff(timeout=1)
    
    AI助手

关闭套接字:

复制代码
 isoTpSocketVCan0.close()

    
 isoTpSocketVCan1.close()
    
    
    
    
    AI助手

isotp_scan 和 ISOTPScanner

该函数被设计为高效地定位ISOTP端点,在工业自动化领域具有重要应用价值。其是一个专为实现相同功能而设计的命令行工具

命令行使用示例:

复制代码
 python -m scapy.tools.automotive.isotpscanner -h

    
 usage:      isotpscanner [-i interface] [-c channel] [-b bitrate]
    
             [-n NOISE_LISTEN_TIME] [-t SNIFF_TIME] [-x|--extended]
    
             [-C|--piso] [-v|--verbose] [-h|--help] [-s start] [-e end]
    
  
    
     Scan for open ISOTP-Sockets.
    
  
    
     required arguments:
    
     -c, --channel         python-can channel or Linux SocketCAN interface name
    
     -s, --start           Start scan at this identifier (hex)
    
     -e, --end             End scan at this identifier (hex)
    
  
    
     additional required arguments for WINDOWS or Python 2:
    
     -i, --interface       python-can interface for the scan.
    
                       Depends on used interpreter and system,
    
                       see examples below. Any python-can interface can
    
                       be provided. Please see:
    
                       https://python-can.readthedocs.io for
    
                       further interface examples.
    
     -b, --bitrate         python-can bitrate.
    
  
    
     optional arguments:
    
     -h, --help            show this help message and exit
    
     -n NOISE_LISTEN_TIME, --noise_listen_time NOISE_LISTEN_TIME
    
                       Seconds listening for noise before scan.
    
     -t SNIFF_TIME, --sniff_time SNIFF_TIME
    
                       Duration in milliseconds a sniff is waiting for a
    
                       flow-control response.
    
     -x, --extended        Scan with ISOTP extended addressing.
    
     -C, --piso            Print 'Copy&Paste'-ready ISOTPSockets.
    
     -v, --verbose         Display information during scan.
    
  
    
     Example of use:
    
  
    
     Python2 or Windows:
    
     python2 -m scapy.tools.automotive.isotpscanner --interface=pcan --channel=PCAN_USBBUS1 --bitrate=250000 --start 0 --end 100
    
     python2 -m scapy.tools.automotive.isotpscanner --interface vector --channel 0 --bitrate 250000 --start 0 --end 100
    
     python2 -m scapy.tools.automotive.isotpscanner --interface socketcan --channel=can0 --bitrate=250000 --start 0 --end 100
    
  
    
     Python3 on Linux:
    
     python3 -m scapy.tools.automotive.isotpscanner --channel can0 --start 0 --end 100
    
    
    
    
    AI助手

交互式 shell 使用示例:

复制代码
 >>> conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True}

    
 >>> conf.contribs['CANSocket'] = {'use-python-can': False}
    
 >>> load_contrib('cansocket')
    
 >>> load_contrib('isotp')
    
 >>> socks = isotp_scan(CANSocket("vcan0"), range(0x700, 0x800), can_interface="vcan0")
    
 >>> socks
    
 [<<ISOTPNativeSocket: read/write packets at a given CAN interface using CAN_ISOTP socket > at 0x7f98e27c8210>,
    
  <<ISOTPNativeSocket: read/write packets at a given CAN interface using CAN_ISOTP socket > at 0x7f98f9079cd0>,
    
  <<ISOTPNativeSocket: read/write packets at a given CAN interface using CAN_ISOTP socket > at 0x7f98f90cd490>,
    
  <<ISOTPNativeSocket: read/write packets at a given CAN interface using CAN_ISOTP socket > at 0x7f98f912ec50>,
    
  <<ISOTPNativeSocket: read/write packets at a given CAN interface using CAN_ISOTP socket > at 0x7f98f912e950>,
    
  <<ISOTPNativeSocket: read/write packets at a given CAN interface using CAN_ISOTP socket > at 0x7f98f906c0d0>]
    
    
    
    
    AI助手

⑥ UDS

UDS 的主要功能是用于ECU的闪烁和诊断操作。作为应用层的通信协议,UDS不仅支持DoIP或HSFZ的有效数据传输,还能够直接通过ISOTPSocket发送UDS数据包。不同厂商可能根据自身需求定制UDS版本以满足特定要求,在这种情况下开发通用应用程序会面临较大挑战,并且渗透测试工作还需要厂商具备专业知识才能顺利开展。这些自动化操作通常包括RouteControl作业以及ReadDataByIdentifier/WriteDataByIdentifier等服务功能,它们都具有高度定制性以适应不同系统需求。

使用ISOTPSocket 函数的basecls=UDS参数。init

以下是两个使用示例:

UDS_RDBI、UDS_WDBI的自定义

在实际应用场景中,UDS层通常会根据具体需求进行高度定制。其中ReadDataByIdentifier或WriteDataByIdentifier等数据包具有高度定制化的子结构设计。因此,在field_desc中通常不会包含aStrField及其相关记录。这些特定文件旨在通过扩展Scapy的通用UDS层功能来实现对ECU或OEM设备的高效通信。

自定义示例:

复制代码
 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'
    
    
    
    
    AI助手

如果想使用这些自定义添加,可以在运行时将这些加载到 Scapy 解释器:

复制代码
 >>> load_contrib('automotive.uds')

    
 >>> load_contrib('automotive.OEM-XYZ.car-model-xyz')
    
   4. >>> pkt = UDS()/UDS_WDBI()/DBI_IP(IP='192.168.2.1', SUBNETMASK='255.255.255.0', DEFAULT_GATEWAY='192.168.2.1')
    
   6. >>> 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  ..+.............
    
    
    
    
    AI助手

⑦ 通用局域网

GMLAN与UDS极为相似。这一协议被用作GM的应用层协议,在他们的汽车上实现闪烁、校准和诊断功能。使用ISOTPSocket函数中的basecls=GMLAN参数进行操作。初始化指令为init

使用示例:

⑧ Ecu 实用程序示例

Ecu 实用程序用于评估被调查的 Ecu 内部状态。此实用程序主要基于所使用的协议的支持。UDS支持。

记录应用于 Ecu 的所有命令

通过此示例可以看出 Ecu 对象的日志记录机制。Ecu 的日志记录系统基于应用程序的 UDS 命令字典进行管理。该字典中的键对应于各个 UDS 服务名称。每个条目由一个元组构成,其中包含时间戳和对应的日志数据.

使用示例:

复制代码
 ecu = Ecu(verbose=False, store_supported_responses=False)

    
 ecu.update(PacketList(msgs))
    
 print(ecu.log)
    
 timestamp, value = ecu.log["DiagnosticSessionControl"][0]
    
    
    
    
    AI助手

跟踪应用于 Ecu 的所有命令

本案例突显了Ecu对象的追踪机制。
该案例表明,在标准输出上记录了Ecu对象的状态信息以及接收的消息。
遵循协议规定,在特定情况下可能会导致Ecu对象内部状态的变化。

使用示例:

复制代码
 ecu = Ecu(verbose=True, logging=False, store_supported_responses=False)

    
 ecu.update(PacketList(msgs))
    
 print(ecu.current_session)
    
    
    
    
    AI助手

生成支持的 Ecu 响应

此示例显示了一种通过分析数据包列表来克隆真实世界 Ecu 的机制。

使用示例:

复制代码
 ecu = Ecu(verbose=False, logging=False, store_supported_responses=True)

    
 ecu.update(PacketList(msgs))
    
 supported_responses = ecu.supported_responses
    
 unanswered_packets = ecu.unanswered_packets
    
 print(supported_responses)
    
 print(unanswered_packets)
    
    
    
    
    AI助手

分析多个 UDS 消息

此示例阐述了如何利用UDS工具从包含消息的 pcap 文件中 解析 CAN 消息。通过一个 pcapreader 对象实现对通信端口(即套接字)的解析功能,并将接收的帧(如 isotpsession)中的 can 数据解析并转换为符合 isotp 标准的帧结构。随后, ud s 通过指定 basecls 参数的方式将其转换为目标数据对象。

使用示例:

复制代码
 with PcapReader("test/contrib/automotive/ecu_trace.pcap") as sock:

    
     udsmsgs = sniff(session=ISOTPSession, session_kwargs={"use_ext_addr":False, "basecls":UDS}, count=50, opened_socket=sock)
    
  
    
  
    
 ecu = Ecu()
    
 ecu.update(udsmsgs)
    
 print(ecu.log)
    
 print(ecu.supported_responses)
    
 assert len(ecu.log["TransferData"]) == 2
    
    
    
    
    AI助手

使用 EcuSession 即时分析

此示例阐述了 ECU 框架中 Session 对象在嗅探功能中的应用方式。可供选择的主要工具有 ISOTP Socket 以及所有能够提供准确通信协议支持并传输完整消息的各种类型套接字工具。AnECU会话(EcuSession)作为替代方案使用以代替传统的 ISOTP 案例(ISOTPSession)。为了通过明确指定位置来获取 ECU 对象(EcuSession),必须确保该对象在嗅探操作之外的位置进行创建和配置。

使用示例:

复制代码
 session = EcuSession()

    
  
    
 with PcapReader("test/contrib/automotive/ecu_trace.pcap") as sock:
    
     udsmsgs = sniff(session=ISOTPSession, session_kwargs={"supersession": session, "use_ext_addr":False, "basecls":UDS}, count=50, opened_socket=sock)
    
  
    
 ecu = session.ecu
    
 print(ecu.log)
    
 print(ecu.supported_responses)
    
    
    
    
    AI助手

⑧ SOME/IP 和 SOME/IP SD 消息

创建 SOME/IP 消息

此示例展示了SOME/IP消息,并采用方法 0x421 来请求服务 0x1234;这些不同类型的SOME/IP消息都遵循相同的流程,请查看此处获取详细规范:http://www.some-ip.com/papers/cache/AUTOSAR_TR_SomeIpExample_4.2.1.pdf

加载contribution:

复制代码
    load_contrib('automotive.someip')
    
    AI助手

创建 UDP 包:

复制代码
    u = UDP(sport=30509, dport=30509)
    
    AI助手

创建IP包:

复制代码
    i = IP(src="192.168.0.13", dst="192.168.0.10")
    
    AI助手

创建 SOME/IP 包:

复制代码
 sip = SOMEIP()

    
 sip.iface_ver = 0
    
 sip.proto_ver = 1
    
 sip.msg_type = "REQUEST"
    
 sip.retcode = "E_OK"
    
 sip.srv_id = 0x1234
    
 sip.method_id = 0x421
    
    
    
    
    AI助手

添加有效载荷:

复制代码
    sip.add_payload(Raw ("Hello"))
    
    AI助手

堆叠并发送:

复制代码
 p = i/u/sip

    
 send(p)
    
    
    
    
    AI助手

创建 SOME/IP SD 消息

在此示例中展示了带有 IPv4 端点的 SOME/IP SD 提供服务消息。采用与本处相同的流程处理不同条目和选项,在文档中可进一步了解相关细节 https://www.autosar.org/fileadmin/user_upload/standards/classic/4-3/AUTOSAR_SWS_ServiceDiscovery.pdf

加载contribution:

复制代码
    load_contrib('automotive.someip')
    
    AI助手

创建 UDP 包:

复制代码
    u = UDP(sport=30490, dport=30490)
    
    AI助手

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

创建IP包:

复制代码
    i = IP(src="192.168.0.13", dst="224.224.224.245")
    
    AI助手

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

创建条目数组输入:

复制代码
 ea = SDEntry_Service()

    
  
    
 ea.type = 0x01
    
 ea.srv_id = 0x1234
    
 ea.inst_id = 0x5678
    
 ea.major_ver = 0x00
    
 ea.ttl = 3
    
  
    
 ​
    
    
    
    
    AI助手

创建选项数组输入:

复制代码
 oa = SDOption_IP4_EndPoint()

    
 oa.addr = "192.168.0.13"
    
 oa.l4_proto = 0x11
    
 oa.port = 30509
    
    
    
    
    AI助手

l4_proto 定义了与端点通信的协议,在这种情况下是 UDP。

创建 SD 包并放入输入:

复制代码
 sd = SD()

    
 sd.set_entryArray(ea)
    
 sd.set_optionArray(oa)
    
    
    
    
    AI助手

堆叠并发送:

复制代码
 p = i/u/sd

    
 send(p)
    
    
    
    
    AI助手

⑨ OBD

该方法在ISOTP之上运行,并通过ISOTPSocket与ECU进行通信。建议在您创建的ISOTPSocket初始化过程中,请设置参数basecls为OBD以及padding为True。

OBD 分为不同的服务组,以下是一些示例请求:

请求服务 0x01 支持的 PID:

复制代码
    req = OBD()/OBD_S01(pid=[0x00])
    
    AI助手

response将包含一个 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
    
    
    
    
    AI助手

假设我们的被测 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 %
    
    
    
    
    AI助手

不同服务在 OBD 系统中分别承担着不同的数据处理任务。其中, 服务 01 和 02 的主要功能是管理参数标识符 (pid),而那些涉及诊断功能的部分(如 03、07 和 0A)则专注于获取并解析故障代码信息 (dtc)。需要注意的是, 服务 04 被设计为无需额外负载配置, 这使得其在资源受限的设备上也能正常运行。此外, 关于诊断功能的具体实现, 目前仍停留在理论阶段(service 05)。相比之下, 其他辅助类功能(如监控标识符、测试标识符以及信息标识符)则在当前系统架构中得到了较为完善的支持(services 06、08 和 09)。

例子:

请求支持的信息标识符:

复制代码
    req = OBD()/OBD_S09(iid=[0x00])
    
    AI助手

索取车辆识别号 (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']
    
    
    
    
    AI助手

⑩ 测试设置教程

ISO-TP 内核模块安装

Linux内核中的ISO-TP组件可通过访问该网站获取: https://github.com/hartkopp/can-isotp.git. 该存储库中的README.isotp文件提供了获取此内核组件以及构建它的所有所需信息和必要步骤. 建议将此ISO-TP内核组件添加至/etc/modules目录中以实现其在系统启动时自动加载的功能.

CAN接口设置

在为应用准备使用CAN总线接口时,在前一步骤中必须配置这些接口配置参数。系统会根据被测对象总线的实际波特率需求自动选择适当的波特率参数。

如何:

复制代码
 ip link set can0 up type can bitrate 500000

    
 ip link set can1 up type can bitrate 500000
    
    
    
    
    AI助手

Raspberry Pi的SOME/IP配置过程可参考官方文档:https://scapy.readthedocs.io/en/latest/layers/automotive.html#raspberry-pi-some-ip-setup

为了建立一个小型测试环境,在其中您可以通过向服务器实例发送相关数据包的方式进行通信;或者您可以模拟成一个服务器角色来参与测试。这些硬件设备包括Raspberry Pi、您的笔记本电脑以及必要的vsomeip库就可以满足需求。

下载图片

获取最新版本的Raspbian操作系统映像...然后按照指导文档将该映像文件复制到Raspberry Pi设备上完成安装。

Vsomeip 设置

从 Rapsberry 平台上获取 vsomeip 库资源,并将其与 boost 库集成以实现兼容性支持;完成配置后即可运行。

复制代码
        1. git clone https://github.com/GENIVI/vsomeip.git

    
        2. cd vsomeip
    
        3. wget -O 0001-Support-boost-v1.66.patch.zip \
    
        4. https://github.com/GENIVI/vsomeip/files/2244890/0001-Support-boost-v1.66.patch.zip
    
        5. unzip 0001-Support-boost-v1.66.patch.zip
    
        6. git apply 0001-Support-boost-v1.66.patch
    
        7. mkdir build
    
        8. cd build
    
        9. cmake -DENABLE_SIGNAL_HANDLING=1 ..
    
        10. make
    
        11. make install
    
    AI助手

制作应用程序

开发一些作为服务端或客户端的小型应用程序工具,并采用 Scapy 的 SOME/IP 功能模块构建与客户端或服务器之间的通信机制。相关 vsomeip 应用程序实例可在 GitHub Wiki 页面中找到(https://github.com/GENIVI/vsomeip/wiki/vsomeip-in-10-minutes)。

CANNELLI 框架

Cannelloni 框架是一个基于 C++ 编写的轻量级应用程序,在 UDP 协议下实现对 CAN 总线数据的传输功能。该框架采用了一种独特的设计思路,在研究人员的工作站中即可配置远程设备间的总线通信,并支持在同一台主机上集成多台远程设备以实现复杂的通信网络构建。官方可用下载链接如下:https://github.com/mguentner/cannelloni.git. 其附带的README.md文件则全面介绍了安装和使用方法。Cannelloni 依赖于操作员计算机上的虚拟总线接口配置。以下清单详细列出了虚拟总线接口的配置步骤。

如何:

复制代码
 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 &
    
    
    
    
    AI助手

全部评论 (0)

还没有任何评论哟~