UDS in Scapy#

Overview#

../../_images/ScanpySWArchitecture1.png

Fig. 66 Overview of UDS packets and socket types on Linux.#

Preparations#

from scapy.all import *   # If you launch `scapy` directly, this import isn't necessary

conf.contribs['CANSocket'] = {'use-python-can': False}
conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': True}
load_contrib("isotp")
load_contrib("automotive.uds")

Examples#

  • Create a socket. basecls has to be set as UDS.

sock = ISOTPNativeSocket("vcan0", tx_id=0x6f1, rx_id=0x610, ext_address=0x10, rx_ext_address=0xf1, basecls=UDS)
---------------------------------------------------------------------------
Scapy_Exception                           Traceback (most recent call last)
Cell In[2], line 1
----> 1 sock = ISOTPNativeSocket("vcan0", tx_id=0x6f1, rx_id=0x610, ext_address=0x10, rx_ext_address=0xf1, basecls=UDS)

File /usr/local/lib/python3.13/site-packages/scapy/contrib/isotp/isotp_native_socket.py:336, in ISOTPNativeSocket.__init__(self, iface, tx_id, rx_id, ext_address, rx_ext_address, bs, stmin, padding, listen_only, frame_txtime, fd, basecls)
    334     log_isotp.warning('Provide a basecls ')
    335 self.basecls = basecls
--> 336 self._init_socket()

File /usr/local/lib/python3.13/site-packages/scapy/contrib/isotp/isotp_native_socket.py:365, in ISOTPNativeSocket._init_socket(self)
    352 can_socket.setsockopt(SOL_CAN_ISOTP,
    353                       CAN_ISOTP_LL_OPTS,
    354                       self.__build_can_isotp_ll_options(
   (...)
    357                           tx_dl=CAN_FD_ISOTP_DEFAULT_LL_TX_DL if self.fd
    358                           else CAN_ISOTP_DEFAULT_LL_TX_DL))
    359 can_socket.setsockopt(
    360     socket.SOL_SOCKET,
    361     SO_TIMESTAMPNS,
    362     1
    363 )
--> 365 self.__bind_socket(can_socket, self.iface, self.tx_id, self.rx_id)
    366 # make sure existing sockets are closed,
    367 # required in case of a reconnect.
    368 if getattr(self, "outs", None):

File /usr/local/lib/python3.13/site-packages/scapy/contrib/isotp/isotp_native_socket.py:238, in ISOTPNativeSocket.__bind_socket(self, sock, iface, tx_id, rx_id)
    235 def __bind_socket(self, sock, iface, tx_id, rx_id):
    236     # type: (socket.socket, str, int, int) -> None
    237     socket_id = ctypes.c_int(sock.fileno())
--> 238     ifr = self.__get_sock_ifreq(sock, iface)
    240     if tx_id > 0x7ff:
    241         tx_id = tx_id | socket.CAN_EFF_FLAG

File /usr/local/lib/python3.13/site-packages/scapy/contrib/isotp/isotp_native_socket.py:232, in ISOTPNativeSocket.__get_sock_ifreq(self, sock, iface)
    229 if ret < 0:
    230     m = u'Failure while getting "{}" interface index.'.format(
    231         iface)
--> 232     raise Scapy_Exception(m)
    233 return ifr

Scapy_Exception: Failure while getting "vcan0" interface index.
  • Create a packet and send it

rdbi_pkt = UDS()/UDS_RDBI(identifiers=[0x172a])
rx = sock.sr1(rdbi_pkt, timeout=1)
rx.show()
Begin emission:
Finished sending 1 packets.
*
Received 1 packets, got 1 answers, remaining 0 packets
###[ UDS ]### 
  service   = ReadDataByIdentifierPositiveResponse
###[ ReadDataByIdentifierPositiveResponse ]### 
     dataIdentifier= GatewayIP
###[ Raw ]### 
        load      = '\x00\\xc0\\xa8\x11\\x97\\xff\\xff\\xff\x00\\xc0\\xa8\x11\x01'
  • Alternative way to send and receive a packet

rdbi_pkt = UDS()/UDS_RDBI(identifiers=[0x172a])
rx = sock.sniff(timeout=1, count=1, started_callback=lambda: sock.send(rdbi_pkt))
rx[0].show()
Begin emission:
Finished sending 1 packets.
*
Received 1 packets, got 1 answers, remaining 0 packets
###[ UDS ]### 
  service   = ReadDataByIdentifierPositiveResponse
###[ ReadDataByIdentifierPositiveResponse ]### 
     dataIdentifier= GatewayIP
###[ Raw ]### 
        load      = '\x00\\xc0\\xa8\x11\\x97\\xff\\xff\\xff\x00\\xc0\\xa8\x11\x01'
  • Basecls is UDS(Packet)

  • UDS implements fake layers to auto-fill fields

  • Identifiers can be customized

  • OEM specific packets can be added to the parser, easily

Customization of Packets#

  • Define identifier 0x172a as GatewayIP

rdbi_pkt = UDS()/UDS_RDBI(identifiers=[0x172a])
print(repr(rdbi_pkt))
UDS_RDBI.dataIdentifiers[0x172a] = 'GatewayIP'
print(repr(rdbi_pkt))
<UDS  service=ReadDataByIdentifier |<UDS_RDBI  identifiers=[GatewayIP] |>>
<UDS  service=ReadDataByIdentifier |<UDS_RDBI  identifiers=[GatewayIP] |>>
  • Also the received packet is affected

print(repr(rx))
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Input In [1], in <module>
----> 1 print(repr(rx))

NameError: name 'rx' is not defined
  • Define a payload class

class DBI_IP(Packet):
    name = 'DataByIdentifier_IP_Packet'
    fields_desc = [
        ByteField('ADDRESS_FORMAT_ID', 0),
        IPField('IP', 0),
        IPField('SUBNETMASK', 0),
        IPField('DEFAULT_GATEWAY', 0)]
  • Connect RDBIPR with new payload class

bind_layers(UDS_RDBIPR, DBI_IP, dataIdentifier=0x172a)
  • Enjoy your new packet parser (show2() is called to rebuild the packet)

rx[0].show2()
###[ UDS ]### 
  service   = ReadDataByIdentifierPositiveResponse
###[ ReadDataByIdentifierPositiveResponse ]### 
     dataIdentifier= GatewayIP
###[ DataByIdentifier_IP_Packet ]### 
        ADDRESS_FORMAT_ID= 0
        IP        = 192.168.17.151
        SUBNETMASK= 255.255.255.0
        DEFAULT_GATEWAY= 192.168.17.1
  • Get all possible packets of a layer in Scapy

explore("scapy.contrib.automotive.uds")
Packets contained in scapy.contrib.automotive.uds:
Class      |Name                                           
-----------|-----------------------------------------------
UDS        |UDS                                            
UDS_ATP    |AccessTimingParameter                          
UDS_ATPPR  |AccessTimingParameterPositiveResponse          
UDS_AUTH   |Authentication                                 
UDS_AUTHPR |AuthenticationPositiveResponse                 
UDS_CC     |CommunicationControl                           
UDS_CCPR   |CommunicationControlPositiveResponse           
UDS_CDTCI  |ClearDiagnosticInformation                     
UDS_CDTCIPR|ClearDiagnosticInformationPositiveResponse     
UDS_CDTCS  |ControlDTCSetting                              
UDS_CDTCSPR|ControlDTCSettingPositiveResponse              
UDS_DDDI   |DynamicallyDefineDataIdentifier                
UDS_DDDIPR |DynamicallyDefineDataIdentifierPositiveResponse
UDS_DSC    |DiagnosticSessionControl                       
UDS_DSCPR  |DiagnosticSessionControlPositiveResponse       
UDS_ER     |ECUReset                                       
UDS_ERPR   |ECUResetPositiveResponse                       
UDS_IOCBI  |InputOutputControlByIdentifier                 
UDS_IOCBIPR|InputOutputControlByIdentifierPositiveResponse 
UDS_LC     |LinkControl                                    
UDS_LCPR   |LinkControlPositiveResponse                    
UDS_NR     |NegativeResponse                               
UDS_RC     |RoutineControl                                 
UDS_RCPR   |RoutineControlPositiveResponse                 
UDS_RD     |RequestDownload                                
UDS_RDBI   |ReadDataByIdentifier                           
UDS_RDBIPR |ReadDataByIdentifierPositiveResponse           
UDS_RDBPI  |ReadDataByPeriodicIdentifier                   
UDS_RDBPIPR|ReadDataByPeriodicIdentifierPositiveResponse   
UDS_RDPR   |RequestDownloadPositiveResponse                
UDS_RDTCI  |ReadDTCInformation                             
UDS_RDTCIPR|ReadDTCInformationPositiveResponse             
UDS_RFT    |RequestFileTransfer                            
UDS_RFTPR  |RequestFileTransferPositiveResponse            
UDS_RMBA   |ReadMemoryByAddress                            
UDS_RMBAPR |ReadMemoryByAddressPositiveResponse            
UDS_ROE    |ResponseOnEvent                                
UDS_ROEPR  |ResponseOnEventPositiveResponse                
UDS_RSDBI  |ReadScalingDataByIdentifier                    
UDS_RSDBIPR|ReadScalingDataByIdentifierPositiveResponse    
UDS_RTE    |RequestTransferExit                            
UDS_RTEPR  |RequestTransferExitPositiveResponse            
UDS_RU     |RequestUpload                                  
UDS_RUPR   |RequestUploadPositiveResponse                  
UDS_SA     |SecurityAccess                                 
UDS_SAPR   |SecurityAccessPositiveResponse                 
UDS_SDT    |SecuredDataTransmission                        
UDS_SDTPR  |SecuredDataTransmissionPositiveResponse        
UDS_TD     |TransferData                                   
UDS_TDPR   |TransferDataPositiveResponse                   
UDS_TP     |TesterPresent                                  
UDS_TPPR   |TesterPresentPositiveResponse                  
UDS_WDBI   |WriteDataByIdentifier                          
UDS_WDBIPR |WriteDataByIdentifierPositiveResponse          
UDS_WMBA   |WriteMemoryByAddress                           
UDS_WMBAPR |WriteMemoryByAddressPositiveResponse           
  • Get all fields of a packet

ls(UDS_RC)
  • Tester present sender

tps = UDS_TesterPresentSender(sock)
tps.start()
print("Do whatever you need to do")
tps.stop()
  • Security Access Seed

../../_images/scapy_sa_tx.svg
../../_images/scapy_sa_rx.svg
  • Security Access Key

../../_images/scapy_sa_tx2.svg
../../_images/scapy_sa_rx2.svg