Part 3 — CAN Bus Reverse Engineering With SocketCAN

David Evans
The Startup
Published in
6 min readSep 11, 2020

Introduction

The objective of this article is to introduce some practical examples and approaches for reverse engineering a vehicle CAN bus network, using low cost tools and open source software.

In Part 1 we created a Virtual CAN interface (vcan0) in our Linux enviornment and used that interface to send and receive a CAN frame, using candump and cansend.

In Part 2 we developed a very small C application which provides minimum code for using SocketCAN to send a CAN frame.

In this part (Part 3) we will extend the application in Part 2 to send a CAN frame at a specific time interval, along with some data in the CAN frame.

simple_can_tx

I’ve created a github project called simple_can_tx which is a bit more structured than super_easy_can_tx.

Choose a favourite location for your development and download the zip, or alternatively clone the repository.

$ cd ~/MyDevlopmentFolder
$ git clone https://github.com/davidevansg/simple_can_tx.git

Let’s look at the contents of simple_can_tx using the tree commend

$ cd simple_can_tx
$ tree

Comparing to super_easy_can_tx we have three more files (can.c, can.h and main.h).

can.c

It’s worth looking at can.c during this bit.

We have covered setting up the CAN interface in Part 2 in super_easy_can_tx, what I want to focus on is using a thread in our application. If you are not familiar with Threads, I would start here.

I have made a function (CANThrInit) which is responsibe for creating a thread. A pthread variable (thr_tick) is created along with a pointer to a start routine (function) ‘Thr_Tick’ which will execute this functionality under a new process. A timer is created (line 14 below) and configured to run at 100,000,000 nanoseconds (100 milliseconds) (line 19 below). This will execute the functionality ‘tick_tx’ (line 34 below) every 100 milliseconds up until there is either an issue with the timer, or if there is a ‘reason_to_quit’ (line 25 below), which will then stop and quit the application.

What happens every 100 milliseconds?

Every 100 milliseconds the function tick_tx is called (line 1 below), which declares can_frame variable called ‘frame’ (line 4 below), this variable will be used both by the function ‘AssembleFrame’ (line 7 below) to populate the contents of the can_frame (can_id, can_dlc and data) which was introduced in Part 2 and the function ‘SendFrame’ (line 11 below) sends the frame to the socket, and to the Virtual CAN (vcan0) interface.

Assemble Frame

In this application, we are going to put a sequence counter value in one of the data bytes of the CAN frame.

A sequence counter can used as a means of ensuring data integrity. As an example, if you are expecting to receive this value every 100 milliseconds (0.1 seconds) and the previous value was 1, you can expect that in another 100 milliseconds time you can expect to receive a value or 2.

A sequence counter variable (seq_ctr) is declared as static (see line 3 below), this means that the value for seq_ctr is retained after this function has finished executing. We expect to call this function many-many times and by declaring seq_ctr as static we can retain the value within the function AssembleFrame and will not be accessible from other functions, an alternative would be to declare this as a global variable, but as seq_ctr is only intended to be used within this function (AssembleFrame), this would be considered good practice.

The AssembleFrame function receives a pointer to can_frame variable (line 4 above) and checks if the value is a null pointer (read more here). This is very unlikely to happen, but is good practice to check.

As we are dealing with a pointer to a value, instead of the value (like we were in super_easy_can_tx), we must de-reference the fra pointer to access the values within the structure, this involves using the (*) operator (see line 7 and 8 below).

We are still setting the can_id and can_dlc in a similar way shown in Part 2, the only difference is that we are dealing with a pointer in this version.

I also mentioned in Part 2 that there was some undefined/uninitialized values in the data array, an example can be seen in the image below. In the interests of always knowing what is in our data array, it’s best to set some default values or ensure that the data bytes are set.

Undefined / uninitialised data values

The function memset (see line 10 below) is used here to effectively initialise this memory with some value. The man(ual) for memset explains this function will fill the first ’n’ bytes ((*fra).can_dlc) of an area of memory, which is the start of the data array for the frame (&(*fra).data[0]), with a value of 0x00 (0). This is pretty much the same as running the loop (see line 13–15 below). This will result in all of the bytes in the data array being set to 0x00, as seen in the image below.

Setting data array to an initial / default value

The final action will be to put the sequence counter value (seq_ctr) into the first byte of the CAN frame data array, and then increment the value (for use next time around).

The function finishes and then sends the CAN frame, which we have covered already in Part 2.

Building simple_can_tx

To build this application, navigate to the directory and run the make command

cd simple_can_tx
make

Running the tree commend again we should now see an additional file name ‘simple_can_tx’ (see red in image below)

This is our executable created from the source files (.c and .h)

I would recommend starting candump with the following command:

$ (Terminal 1) candump -t d vcan0

Referencing the man(ual) for candump, the ‘-t’ (timestamp) flag with a value ‘d’ (delta) will print the difference in time between the current and previous CAN frame received.

// $ (Terminal 2) cd simple_can_tx
$ (Terminal 2) ./simple_can_tx

Running the application for a few seconds, and pressing ‘ctrl+c’ to terminate the application will provide the following.

If you note the timestamps in the candump output, we are aiming for 100 millisecond (0.1 second) accuracy in our application. We are ‘mostly’ achieving this timing, and depending on your use case may be ‘good enough’, certainly for my activities it was more than suffucient for what I wanted to do. A simpler example of this output can be seen below.

You will also note that the first byte of the CAN frame has a value of 0x00, followed by 0x01, 0x02 … 0x0F. This is the sequence counter incremeting each time. Additionally, you will note that all remaining bytes of the data array is set to 0x00 (0).

In the next part, we will encode a signal (vehicle_speed) into a CAN frame and use another application to decode and interpret that signal.

--

--

David Evans
The Startup

Irish | Living in Cambridge | Enjoys Car Hacking & Retro Games