Friday, December 31, 2010

Reverse engineering strategies

OK, so now you have this massive assembly language listing staring you in the face.  How do you go about reverse engineering it?  This reminds me of a saying/joke a friend of mine told me:  How do you eat an elephant?  One bite at a time.  So grab your knife and fork and get ready for a really big meal, because this is going to take a long time.  All of the automated analysis has been done by your disassembler, so now it's up to you to figure out what this code actually does.  But that's the fun of it, right? :)

Still, you have to start somewhere.  Should you just start from the first instruction and go from there?  That's pretty much what I did the first time.  Don't make the same mistake.  Reverse-engineering code is kind of like putting together a puzzle, except you don't know what the final picture will look like, at least not exactly.  But in the case of an ECU, you do know some things that have to be there.  You know what sensors and actuators it's talking to (O2 sensor, MAP and/or MAF sensor, coolant temperature sensor, injectors, etc).  For OBDII cars, you also know that it's got to support certain standard Modes and PIDs, as well as Diagnostic Trouble Codes.  And, you should know something about the design of your microcontroller, such as where the reset vector and interrupt vectors are located, and what special function registers control the ADC and serial ports.

In the case of the NB Miata, I originally thought that it would be much harder to reverse-engineer, since OBDII compliance meant that it had to support a lot more diagnostic functions than earlier cars, which I assumed would make the code much more difficult to figure out.  In fact, the opposite is true.  The reason is that, since so many diagnostic functions are mandated by law to work a certain way, all you need to do is to find the code that is responsible for processing and responding to OBD commands to figure out how this information is stored in the ECU.  For example, take Mode $01, PID $05, Engine Coolant Temperature.  SAE J1979 requires that the response contains a byte that corresponds to the car's engine coolant temperature in degrees Celsius, plus 40.  So if you find the code that generates the response, you now know which memory location holds the engine coolant temperature.

So what would be the most likely place to find code that allows the microcontroller to communicate with the outside world?  Perhaps through a serial port?  That's a very logical guess.  And, in the case of the Miata, it's also correct.  By searching the code for any references to serial ports, and looking at the functions pointed to by the serial port interrupt vectors, it took very little time to find the code that generated responses to not only Mode 1 PID 5 but all OBD commands (including the non-standard, proprietary ones).

Once you know which memory locations hold important information (such as coolant temperature, RPM, ignition timing, etc), start looking at all the functions that reference those variables.  For example, if you examine the functions that deal with ignition timing, you are likely to find that one or more of them refer to a lookup table (LUT) that is used to determine the timing.  These are the tables that most "performance chips" modify, along with the fuel LUTs.

So, if you're reverse-engineering firmware from any OBDII compliant car, I would say that the absolute best place to start is to try to locate the code that generates the reply messages.  Once you find it and figure out how each response is generated, you will have established a pretty significant beachhead from which you can venture out further in your understanding of the inner workings of your ECU.  Happy trails!

2 comments:

  1. Really great reading! Props to you. As I own a 00` Miata, many of these codes will be of great use to me. Since there are no ADs here, I can`t click them. And paypal account for me to give a few bucks?

    ReplyDelete
  2. Hmm, yeah, can't really figure out this whole ad thing. Oh, well.

    ReplyDelete