DBC#

  • DBC is a file format to describe datafields in CAN frames

  • OEM specified to encode information into can frames

  • Information is compressed very space efficient (example 13 Bit Speed, 4 bits gear etc.) since original CAN has only 8 byte payload

  • DBC files are used to automatically generate c files that send the messages

  • Example:

    • B0_ defines a CAN-frame of length 8 with the identifier 341 as data object BMS_1.

    • This data object contains three different signals, SG_climit, SG_current, and SG_soc.

    • The signal SG_climit contains the following definitions:

      • 7|8: This signal starts at bit position 7 and has a length of 8 bits.

      • @0+: The endianess of the signal is big-endian and the transferred value is unsigned.

      • (5,0): The transferred value has a scaling factor of 5 and the offset value 0.

      • [0|35]: The transferred value has a value range from 0 to 35.

      • “A”: The unit of the transferred value is labeled with “A”.

  • Signal fields can be located at any position in a CAN frame

BO_341 BMS_1: 8 XXX
  SG_climit :  7|8@0+  (5,0)       [0|35]      "A" XXX
  SG_current: 11|12@0+ (-0.25,500) [-500|1000] "A" XXX
  SG_soc    : 39|16@0+ (0.0025,0)  [0|100]     "%" XXX
  • Future versions are part of ARXML (AUTOSAR XML) (*. arxml)

DBC tools#

  • SavvyCAN has a graphical editor for DBC files and can analyze CAN frames in real time https://www.savvycan.com/

  • The cantools project has a commandline representation of CAN frames and allows the creation of graphs eerimoq/cantools

  • canmatrix is a useful tool to convert DBC files in various other formats, e.g. Wireshark dissectors ebroecker/canmatrix

  • dbcc is a project that allows the creation of C code for build and dissect howerj/dbcc

DBC support in Scapy#

  • SignalPacket objects can be used to define DBC data objects

  • SignalField and derivatives allow the definition of DBC signals

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
from scapy.all import *
from scapy.layers.can import *
from scapy.contrib.cansocket import *

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("vcan0", basecls=SignalHeader)

pkt = SignalHeader()/testFrameFloat(floatSignal2=3.4)

pkt.show2()

hexdump(pkt)

dbc_sock.send(pkt)
###[ SignalHeader ]###
  flags     = 
  identifier= 0x321
  length    = 8
  fd_flags  = 
  reserved  = 0
###[ testFrameFloat ]###
     floatSignal2= 3.4000000953674316 
     floatSignal1= 0.0 

0000  00 00 03 21 08 00 00 00 00 00 00 00 9A 99 59 40  ...!..........Y@
16

DBC reverse engineering#

  • The knowledge about DBC definitions is necessary for any attacks on vehicle functions

  • Reverse-Engineering methods:

    • Live-Monitoring:

      • Sniff traffic on the bus

      • Perform a defined action on the vehicle (e.g. Press a button)

      • Check for changes in the monitored traffic

      • New IDs? New CAN-Data?

    • Statistical Reverse Engineering [Sto18]

TAV (Transistion Aggregation Vector)#

Let the vector \(\mathbf{x}:=(b_{63}, b_{62}, \dots, b_0) \in \mathbb{F}_2^{64}\) define the data bits of a CAN-frame.

\[\begin{split} \mathbf{X} = \begin{pmatrix} b_{63,0} & b_{62,0} & \dots & b_{0,0} \\ b_{63,1} & b_{62,1} & \dots & b_{0,1} \\ \vdots & \vdots & \ddots & \vdots \\ b_{63,n} & b_{62,n} & \dots & b_{0,n} \end{pmatrix} \in \mathbb{F}_2^{64\times n} \end{split}\]

The matrix \(\mathbf{X}\) represents all CAN-data frames of an entire log file. The TAV of all data bits is computed by the following equation:

\[ y_j = \sum\limits_{i=1}^{n} (x_{j,i - 1} \oplus x_{j,i}) ~~~~~ y \in \mathbb{N}_0^{64} \]
../../_images/tav.png

Fig. 30 TAV of data bits from a group of CAN-frames with identifier 0x3d. The x-axis indicates the bit position inside the CAN-frame. The y-axis indicates the number of bit transitions (bit-flips) between two consecutive CAN-frames.#