Hacking your Car Network - Part 3.
"Wrapped up in silence, all circuits are dead Cannot decode, my whole life spins into a frenzy"
I introduced the various networks found in vehicles in part 1 of this series and walked through how to hook up and start logging CAN packets with your Raspberry Pi in Part 2. While ODB2 is a well documented standard, details about the other protocols, especially the proprietary ones, running on the CAN bus are often not available to the general public. With some exceptions, most auto manufacturers keep them close to their chest.
There are some notable exceptions. General Motors has a number of documents detailing their standards. If you plan to hack on a platform that runs GMLAN you will need a copy of GMW8762 -Platform to Powertrain Electrical Interface Specification which describes in excruciating detail the On-Board Diagnostics, Electrical, and GMLAN Serial Data Signal Definitions and Framing Requirements. I will warn you, this one is not free, hunting around on the search engines there are some vendors who will sell you a PDF.
But what about those protocols that are not publicly available. Even if someone else has taken the time to reverse engineer and document it online, you will at the least have to verify that this works for your vehicle. Or you will have to reverse engineer it yourself.
Before we get started I want to suggest you get a copy of The Car Hacker’s Handbook by Craig Smith. There is an amazing amount of information there. I also suggest you take a second and check out this video Craig Smith did a few years back on CAN Bus Reverse Engineering. That said let’s get started.
Wake up, little Wrangler, Wake up.
In this example I am using the same setup I described in my last post, Raspberry Pi and the Waveshare CAN interface. I have the can0 interface attached to my Jeep Wrangler’s internal low speed bus (CAN-IH). The Jeep is off, with the key removed, but I am powering the Pi through a USB outlet that provides constant power.
The first thing I am going to do is to check for CAN activity and verify that my device is working.
$cansniffer -c can0
You might not see anything right away, but if you close a door, turn on the headlights, or click the key FOB you will see a short flurry of packets. This is the TIPM waking up, eventually the bus will settle down.
At this point you can start doing things and looking for packets that stick out. In the command for cansniffer I specified the -c option. This will mark the changed bytes for a recurring packet in red.
On the Wrangler, you will notice that there are a number of frames that repeat and can be distracting to our task. Among them are:
219 - The VIN number
3E6 - The current time
We can ask cansniffer to filter those out. The easiest thing is to wait till the bus settles down and specify a filter.
-219<enter>
-3e6<enter>
Incidentally I believe there is a bug in cansniffer where fails to handle user input when it’s updating. You can see the other options by running
$cansniffer -?
One of the other useful options is the ability to save a settings file. Once you have the filters setup like you want, wait till the bus settles down and type:
-wsomename<ENTER>
This will cause a file sniffset.somename to be created. You can later run cansniffer with these filters already setup/
$cansniffer -c -r somename can0
When one door closes, another opens.
For this example we are going to specifically look for is a CAN frame that indicates when a door is opened or closed. So I started the sniffer and I started to walk around the Jeep and opened and closed the doors a few time. And this pattern became immediately noticeable.
can0 244 [5] 00 00 43 04 80 <- All doors closed
can0 244 [5] 01 00 43 04 80 <- Driver door open
can0 244 [5] 00 00 43 04 80 <- All doors closed
can0 244 [5] 01 00 43 04 80
can0 244 [5] 00 00 43 04 80
can0 244 [5] 01 00 43 04 80
can0 244 [5] 00 00 43 04 80
can0 244 [5] 02 00 43 04 80 <- Passenger door open
can0 244 [5] 00 00 43 04 80
can0 244 [5] 02 00 43 04 80
can0 244 [5] 00 00 43 04 80
can0 244 [5] 02 00 43 04 80
can0 244 [5] 00 00 43 04 80
can0 244 [5] 04 00 43 04 80 <- Passenger rear door
can0 244 [5] 00 00 43 04 80
can0 244 [5] 04 00 43 04 80
can0 244 [5] 00 00 43 04 80
can0 244 [5] 04 00 43 04 80
can0 244 [5] 00 00 43 04 80
can0 244 [5] 08 00 43 04 80 <- Driver rear door
can0 244 [5] 00 00 43 04 80
can0 244 [5] 08 00 43 04 80
can0 244 [5] 00 00 43 04 80
can0 244 [5] 08 00 43 04 80
can0 244 [5] 00 00 43 04 80
can0 244 [5] 10 00 43 04 80 <- tailgate
can0 244 [5] 00 00 43 04 80
can0 244 [5] 10 00 43 04 80
can0 244 [5] 00 00 43 04 80
can0 244 [5] 10 00 43 04 80
can0 244 [5] 00 00 43 04 80
And there you have it. Frame 244 was being sent out periodically indicating the status of the doors in the first byte. I have no idea what the other 4 bytes indicate yet. But it was obvious that for byte 1
b7 - 0
b6 - 0
b5 - 0
b4 - Tailgate
b3 - Driver rear
b2 - Passenger rear
b1 - Passenger Front
b0 - Driver front
For miles and miles…
That example was easy, some other frames are not. For example, the milage from the odometer can be found in Frame 214.
214 [7] 2D BE A4 00 12 65 00
It is decoded as such
mm mm mm 00 dd dd 00
Where mm mm mm is the milage as a 24 bit hex number. If we convert 0x2dbea4 to decimal we get 2997924 Divide by 10 to get the tenths of kilometers 299792.4 km.
If you take a second to convert this into units applicable to countries that have walked on the moon and you get 186282.3 miles.
Wash, Rinse, Repeat
You pretty much have to do that for all the packets you observe. Some will be easy, some will not. One technique I found helpful was to record the packets and look at them later.
$candump -l can0
Which produces an ASCII file with a name similar to candump-2022-03-17_112314.log. Each line has a unix timestamp, the interface, the frame id and the data from the frame.
(1647541514.340372) can0 244#0000430480
(1647541514.341304) can0 21D#4111297562E52E81
(1647541514.342155) can0 2A8#000000000000
You can write some code to filter through these or if you wish to play them back, there is a trick. You can create a virtual CAN bus interface and use the canplayer command to replay this file.
First create a virtual network called vcan0.
$sudo ip link add dev vcan0 type vcan
$sudo ip link set up vcan0
The from another window run the cansniffer, specifying the virtual link
$cansniffer -c vcan0
and then playback the logfile through the link.
$canplayer -I candump-2022-03-17_112314.log vcan0=can0
What this is telling canplayer to do is to take input from the file and send the entries from can0 (which we recorded from) to vcan0 where the cansniffer is waiting.
Roll me another one.
Personally I find the tools cumbersome, but they are typical Unix utilities that have multiple purposes. I have considered writing a better tool for reverse engineering and displaying the data driven by a data definition file. I wouldn’t be surprised if someone has already done it though.
Which leads me into our next episode. I previously mentioned that the Linux kernel not only supports CAN drivers for the MCP2515 but also has SocketCAN drivers. SocketCAN is an interface that extends the familiar Berkeley sockets API. This makes it relatively east to write your own code in C, C++ and there are even wrapper available for Python.
In my next post I will show you how you can write code to access the CAN bus and maybe do some useful things.