The goal of this project was to inject steering commands onto the FlexRay bus of an Audi as a proof of concept for adding openpilot support for a FlexRay vehicle.
FlexRay is a different communications protocol developed by a group of companies including Daimler, BMW, Motorola and Philips. It’s supposed to be faster and more reliable than CAN. It’s mostly used on more recent cars from European brands like Audi, BMW and Mercedes.
Compared to CAN, the FlexRay protocol is a lot stricter on timing. For example, the messages from all ECUs are sent on a fixed schedule: each ECU gets assigned time slots where they can send a message. On the Q8, the FlexRay bus has cycle time of 5 ms, so each ECU can send messages at 200 Hz.
Because the bus uses Time-Division Multiple Acces (TDMA), the scheduling doesn’t allow receiving a message, changing certain bytes in the message, and then sending it out again at a random time.
To allow changing arbitrary data in a packet we have decided to modify individual bits in messages while they are transmitted over the bus.
For this project we used an Audi Q8 with Driver Assistance package. This includes adaptive cruise control and LKAS. The Q8 is very similar to other Audi and Volkswagen vehicles with FlexRay, except for the main difference which is that the Driver Assist ECU is not located together with the camera on the windshield. The Q8 has multiple cameras that are all routed to a Driver Assist ECU under the driver’s seat.
For this project, we decided to interrupt the FlexRay bus at the EPS side and place our proxy hardware between the EPS and the rest of the car. This way, we could make sure we could control everything that went into the EPS.
We built a FlexRay development board which utilizes an FPGA, four FlexRay transceivers and four CAN transceivers. For the FPGA we used a CYC1000, a dev board with an Intel Cyclone 10 LP. The FlexRay transceivers are an Infineon TLE9222.
Conventional microcontrollers usually have custom silicon to handle transmission and reception of CAN and FlexRay messages. They receive the whole message, check the CRC and put the result in some registers ready for the end-user to use them. However, this does not allow manipulation on the bit level as required for this attack. Therefore, we decided to use an FPGA and implement the PHY (Physical Layer) for FlexRay ourselves.
The FPGA acts as a gateway or bridge between FlexRay and CAN. You send CAN messages to the FPGA, which stores the data received and overwrites the appropriate FlexRay bits when the message is transmitted on the bus. The FlexRay transceivers are used to split the FlexRay bus, and then proxy the messages between the two sides (allowing modification of messages on the fly).
The first step is to record the FlexRay messages from the stock system in order for us to analyze the messages and find which bytes control the steering.
Recording the messages on the FlexRay bus does not yet require the FPGA. We simply hook the outputs of the FlexRay transceivers on the FlexRay harness to a logic analyzer. We used the Saleae Logic Pro 16 analyzer which can do 500 MS/s, but any logic analyzer that can do 100 MS/s should be more than sufficient.
Decoding FlexRay messages by hand would be a lot of work. Unfortunately, the Saleae software does not include a FlexRay decoder. You can, however, write your own decoder, so we authored and open sourced a FlexRay decoder for Saleae logic analyzers.
We went on several drives recording the vehicle in different states (manual steering, LKAS engaged vs. not engaged, LKAS steering left and right, etc…) and then combed through the data looking for the corresponding patterns. We found a frame with ID 65 that contained the LKAS steering control commands sent to the EPS. The steering data was in every fourth cycle and the last byte seems to be something like a sub-address. Interestingly, the data that we cared about very closely matched the CAN steering message HCA_01 already defined in opendbc for Volkswagen MQB vehicles.
First the bus is split electrically and we try to proxy every message unchanged. In the FPGA we implemented a simple circuit that looks if there is data coming in from either side. Once the first bit comes in, we enable the transmitter on the other side and connect it to the receiver. After a frame is finished, there is a little bit of idle time on the bus and we can turn off the transmitter. Then this process starts from the beginning.
The following logic analyzer dump shows a packet coming from the car being forwarded to the EPS, then a packet from the EPS to the car, and then another packet from the car to the EPS.
The next step was to build a FlexRay packet decoder on the FPGA. Unfortunately there was no open source code available for this, so we had to write the VHDL code from scratch. We wrote a block that handles the reception of a single packet. Since most of the traffic is just forwarded we don’t need to handle any of the wake up and other types of special messages.
When the receiver matches a header with a certain frame ID and cycle counter, we change a couple bytes in the packet while forwarding. We also recompute the 24 bit FlexRay CRC on the fly and replace that too.
In the screenshot below you can see that we replace a few bytes in the message with Frame ID 65:
We built a python script to read a joystick and used a panda to send the CAN messages to the FPGA, which overrides the appropriate bits on the FlexRay bus to control the EPS. By combining the results from the previous steps, we were able to control the steering with a joystick.
This project resulted in a two open source repositories which could be useful for anyone doing FlexRay research: