I am P1kachu

Security, cars, playing with fire.

A tour of automotive systems from 20 years ago

276hp at 1953 bauds, communicating with the elderly

I moved to Japan mid-2017 and bought an old MY97 Subaru Impreza STi. While old, this car became my daily driver, as well as my track and tōge ride. It definitely is a nice car to play with. At that time, I was still working on my custom IVI and decided to add the support for this car. The idea for me was to be able to recover basic engine info in real time, and send them to the IVI. Of course, devices and softwares already existed, but I wanted to do it myself, as I knew I would learn (useful ?) stuff.

hello u

Step one: reconnaissance

The car features an OBD-II port under the steering wheel, but I knew that chances of it supporting CAN were very slim. Digging through google and twitter raised an acronym: SSM, which stands for Subaru Select Monitor. To this day, it seems that Subaru vehicles still support different versions of this protocol (the latest one being SSM4), in addition to CAN. The one that my car supported was either the first or second version.

To find out which version I was dealing with, I just had to check whether the OBD-II K-Line pin (pin 7) was connected or not. I could have used an oscilloscope, but removing the panel and checking directly was quicker:

OBD-II connector, no CAN, no K-Line

We can see that the K-Line pin is not connected. This means that this is not SSM2. However, we can see that the power pin is connected (orange), and that two Vendor Option pins are connected too (top two wires, which are usually used for constructor specific protocols). The two bottom lines are Signal ground and Chassis ground.

No K-Line, SSM1 it is. I thus began looking for information on how to interface with it. The SSM1 protocol is a simple serial protocol running at 1953 bauds at 5V TTL. TTL means Transistor-Transistor Logic, meaning it relies on circuits built from bipolar transistors to achieve switching and maintain logic states (TTL gates define a below 0.5V as 0, and a voltage of 4–5V as 1).

Summary, it shouldn’t be too hard (if you don’t have a defective level shifter).

Step two: communication

The protocol itself was pretty well documented on Alcyone, but I still needed the hardware to handle such communication. I took a simple Teensy 3.2 and a custom made OBD-II connector, and started from there. Since the Teensy’s serial pins are only dealing with 3.3V signals, I added a level shifter (also called Logic Level Converter) to convert the Teensy’s 3.3v to the car’s 5v. I could have directly soldered the level shifter’s pins to the wires behind the panel, but I wanted to avoid any destructive modification if possible (spoiler: not anymore). Hardware devices are still sold for this protocol (like BlueSSM for example), but I had more liberty querying values by writing my own software, instead of having to rely on an interface. Following is a picture of the early setup (the PiCAN2 shield is there because I still used it for other projects, but is useless here).

Early communication setup

SSM1’s protocol is well described here, but as an overview, here are the commands I used for this project:

00 46  48  49 -> Get ROM ID
78 msb lsb 00 -> Read data from ECU address
12 00  00  00 -> Stop

Querying data was then possible using the information recovered from obscure Subaru forums. I finally found which address was related to which data, and how to interpret them (almost, I still have doubts about how to read the MAP value).

// Used addresses
#define BATTERY_VOLTAGE_ADDR      0x0007
#define SPEED_ADDR                0x0008
#define RPM_ADDR                  0x0009
#define COOLANT_ADDR              0x000A
#define AIRFLOW_SENSOR_ADDR       0x000C
#define THROTTLE_ADDR             0x000F
#define MANIFOLD_PRESSURE_ADDR    0x0020
#define BOOST_SOLENOID_ADDR       0x0022

// Not yet used ones
#define IGNITION_ADVANCE_ADDR     0x000B
#define ENGINE_LOAD_ADDR          0x000D
#define ISU_DUTY_VALVE_ADDR       0x0011
#define O2_AVERAGE_ADDR           0x0012
#define O2_MINIMUM_ADDR           0x0013
#define O2_MAXIMUM_ADDR           0x0014
#define KNOCK_CORRECTION_ADDR     0x0015
#define AF_CORRECTION_ADDR        0x001C

I neatly tucked the teensy circuit in one of the glove box, wired everything behind the IVI, and sent data over USB to the tinkerboard for reading and displaying.

Teensy 3.2 querying data

Step three: Dumping and reverse engineering

In the end, I decided to dump the whole ECU (64kb long) by issuing read commands at every single address. It took around 9 hours to get the whole ECU, since I was reading each address at least twice (to be sure). The ECU sends one result each time it calls the communication routine, so changing the baud rate only wouldn’t really affect how many I could query per second. To avoid running out of battery, I plugged an external charger to the car’s battery.

This allowed me to get my hands on a 0x10000 bytes long binary blob. I still needed some information about the processor, so I decided to inspect the ECU itself which was hidden under the passenger floor mat.

Locating the culprit...

The ECU is a ~20cmx20cmx5cm metallic box with 3 connectors (identified as B135, B136 and B137) for a total of 64 pins. The whole ECU pinout is documented on multiple forums and ESL, which helped determining which pin was responsible for which sensor (useful for later).

ECU connectors ECU pinout from ESL

Opening it reveals a very well conserved board: the engine’s brain. Here is a short description of what can be found on the ECU:

ECU board

  • 1: Slot for an immobiliser chip (Not present on Japanese ECUs like this one, more infos here)
  • 2: Main CPU + embedded firmware. Custom JECS IC.
  • 3: Ignition circuitry
  • 4: Lower power transistors
  • 5: Heat sink with heat clamps (High power transistors)
  • 6: Power management circuitry
  • 7: Diode to prevent destruction if the positive charge and negative charge are inverted
  • 8: Secondary custom IC
  • 9: Contact strip

The contact strip can be used to add a daughterboard to the ECU, which is for example what Enduring Solutions Limited do when they want to prepare Subaru cars.

Some more research revealed that this IC was based on the Mitsubishi M37791 CPU (7700 family). One of the difficulties of disassembling m7700 code is that there is an “M flag” that changes the instruction decoding at runtime, but IDA still does a fine job. Poking around a bit revealed the location of fuel, ignition timing and ignition advance maps.

Function using one of the maps

Maps are easily spotted in the hex view

Following is a 3D representation of said maps using python and matplotlib (scripts available in the repo).

First map extractions

Step four: Bypassing the speed limiter

Another thing I realised when taking the car for testing on the track is the presence of a speed limiter. When reaching a certain speed (around 186 km/h), the ECU would just cut off fuel injection, which would make the engine stall suddenly for a short moment, before reactivating the injection when a fixed, lower speed was reached. After digging a bit in the code, I found the function responsible for that. While doing multiple things (the function is more than 0x300 bytes long), the interesting part can be seen in the following screenshot.

Speed limiter function

The exact speed at which the limiter kicks in is 188km/h. When this speed is reached, the speed limiter is activated and the fuel injection is cut (nsl_bitvector_1, bit 2). When the car’s speed decreases, checks are made to know when to reactivate the fuel injection. If the speed is 186, nothing happens, and the ECU waits for either 184km/h (reactivate fuel injection) or 188km/h (maintain fuel cut) to be reached to take a decision.

Three options were considered to bypass this speed limiter:

  • Modify the ECU’s firmware (requires a daughterboard on top of the motherboard)
  • Disconnect the vehicle speed sensor (or VSS)
  • Fool the speed sensor signal

The first one presented a lot of difficulties (creating a daughterboard to reflash the firmware is not something I know how to do at the time of writing, to be honest). The second one had a major downside, which was that no speed information would be available to the engine and while it could run without, this can causes issues described below.

Interlude: Maps and speed sensor

While the engine could run without a speed sensor, some issues can arise depending on what this signal is used for. What I will explain here does not necessarily hold true for my car, but is more of a “this can be found in some, so let’s not take any risk”. You may want skip this part is you have no interest in engines and why this option is not recommended.

Engine ECUs (with the arrival of electronic fuel injection) use maps to determine different variables that make the engine run. The quantity of fuel to mix with air in the cylinders (fuel maps), the ignition advance and base timing (advance maps/timing maps, when to fire the sparks plug in function of the position of the piston), and such. These maps usually depend on the current RPM and the engine load (how much “power” is requested by the driver from the engine; this can either be the throttle pressure percentage for NA engines, or the MAF value for forced induction ones). Examples of such maps can be seen above. As the speed does not affect these maps, a VSS is not mandatory.

However, disconnecting the speed sensor isn’t 100% without side effects, as some cars have slight engine mapping tweaks depending on different speeds. For example, BNR32 ECUs seem to have 3 maps: the idle map (when the car is in neutral), the transition map, and the full map (same as transition map, but with more timing advance). The full map is activated by the VSS and has things like closed loop O2 sensor feedback (correct the fuel injection depending on the combustion, by checking the O2 level in the exhaust gases) as well as fuel cut on deceleration. Power-wise, the worst that could happen is being stuck in the transition map, which would not make as much power as the full one, but engine-wise, the problem would be running lean (not enough fuel for the amount of air), which could damage it. Another example, BNR32s also use the speed sensor to trigger HICAS, Nissan’s rear wheel steering system.

Anyway, back to our signal

Fooling the speed sensor signal was worth a shot as it was pretty easy to implement and could still allow speed information to be sent to the engine. I could at the same time test the second option just as a PoC, to verify that only the speed was used in the speed limiting process.

The VSS can have multiple forms, but judging by its signal, its basic behavior can be represented as follow (source): VSS

The wire responsible for carrying the signal to the ECU is on the middle connector (B135, pin 3). This signal is an analog signal that switches between 0v and 5v following the speed of the car. The faster the gear turns, the higher the frequency. The number of “edges” is then recorded by the ECU in a specific time interval, and the speed is deduced from this. Cutting this wire, and adding a Teensy 3.2 in between (with another level-shifter, as the Teensy still only works with 3.3v) did the trick in successfully monitoring the signal. Having the teensy’s LED blink at each edge was the simplest way to get a direct visual representation of the speed.

From this I could receive and forward the signal. The next step would be to modify it when a speed of 180km/h or more is reached only. This would prevent strange behaviors of the engine from happening at lower speed (speed at which the car is 99% of the time). I did some tests, and managed to have my IVI print 354km/h while being stationnary, which triggered the speed limiter, cut the fuel injection, and stalled the engine. Meh, at least it works!

Determining the number of edges read in one kilometer was required, to properly convert a frequency to a speed, so I drove 100 meters and monitored the number of edges to get a rough value. The result seems to be around 200 edges/100m, so around 2 edges/m. Further testing showed that the value was not completely accurate, but fair enough for a PoC.

I was able to fool the ECU easily into thinking the car was going 140km/h while stationary just by sending a signal that edge’d once every 5 milliseconds approximately. The Teensy is able to keep track of the real speed at all time, and thus is able to send the real speed when it is less than 180, or just a delay(5) loop when it is over this speed, to avoid triggering the limiter.

Below is a simplified version of the code running on the Teensy:

while (speed >= 180) {
        if (previous_signal_value) {
                signal_value = 0;
        } else {
                signal_value = 1023;
        analogWrite(VSS_TX, signal_value);
        delay(5); // Speed recorded is around 140km/h
        previous_signal_value = signal_value;

The final version uses a loop instead of a delay, and edges every 290 iterations. This allows me to get a varying value between 176 and 182km/h, while still being able to do some “teensy multithreading” (I hate that). I added a mecanical switch next to the handbrake to engage or disengage the bypass circuit (in case something wrong happen, I can just fallback to the original one).

ON/OFF switch

And finally, the results:

Fuji Speedway’s straight, with speed limiter ON (max speed: ~186km/h) Speed limiter ON

Fuji Speedway’s straight, with speed limiter bypass (max speed: ~220km/h, but it rained so I needed to brake early to be sure I could stop :))) ) Speed limiter OFF

Video demonstration is available on Youtube.

Most of this is now available on Github, meaning the code that I use to query information for the IVI, the code used to dump the firwmare, and the code used to modify the VSS signal. I won’t publish the firmware dump for legal reasons (I’m not 100% sure about Japan’s position, but let’s be safe).

Thanks for reading, questions and comments are welcomed through Twitter. If you find anything invalid or that could be better explained, feel free to correct me :)

Thanks to Phil’s website Alcyone and zours for his help during the hardware phase!



Newer >>