High Level Overview
The ecu in any car is designed to control how the engine operates. Car manufacturers want their engines to produce power, while still being fuel efficient, reliable, quiet, etc. The way they accomplish this is by defining "maps" which contain modifiable parameters that the ecu will use to make decisions on how the engine should be working. In the case of the Simos18, there are both measured values and modeled values in these maps.
Primary Control Strategy
The accelerator pedal tells the ecu how much torque the driver wants the engine to produce. That driver input is referenced against a table that defines the maximum torque available at any given RPM. That target torque value is referenced against other maps which define how much airflow, followed fuel and timing, the ecu should target to achieve the driver request torque value.
The way the ECU chooses to achieve different levels of performance are called "combustion modes". Refer to the FR page 1673 for more detailed combustion mode definitions.
Simos Information
Physical info
If you open up the 18.1 ecu casing and look at the number written on the processor, this is what you’ll find:
SAK-TC1791S-384 F200EP 3MB total flash 200 MHz Processor
Physically it contains 3MB of flash in total, spread over two flash units inside the chip (PMU0 – 2MB and PMU1 - 1MB) Other variants of the chip can have different configurations, for example the 18.10 ECU uses a SAK-TC1791S-512 F240EP chip with 4MB (PMU0 – 2MB and PMU1 – 2MB). All of this is in the free Infineon datasheets. So when the tools read out the contents, sometimes they read past the end of the physical chip! In which case anything after the physical end is just lots of zeroes. The 18.1 only has 3MB physically there, if you get a read bigger than 3MB then is all going to be zeroes and you can ignore it anyway.
The flash blocks
There are six blocks:
- PMU0 (2MB)
- 1. SBOOT – supplier boot (0x0000 0000 -> 0x0001 BFFF)
- 2. CBOOT – customer boot (0x0001 C000 - > 0x0003 FFFF)
- 3. ASW1 – Application SW part #1 (0x0004 0000 -> 0x0013 FFFF)
- 4. ASW2 – Application SW part #2 (0x0014 0000 -> 0x001F FFFF)
- PMU1 (1MB)
- 5. CAL – Calibration (0x0020 0000 -> 0x0027 FFFF)
- 6. ASW3 – Application SW part #3 (0x0028 0000 -> 0x002F FFFF)
Each block's purpose
SBOOT
SBOOT is not a field replaceable unit, so can’t be flashed over with OBD and you can ignore it for all intents and purposes since you can’t really modify it. The other five can be replaced via OBD and are subject to modifying.
CBOOT
CBOOT does all the flashing. Basically when you want to reflash via OBD, the ECU reboots into CBOOT and runs all the re-flashing procedures etc out of CBOOT. Once complete, it reboots again into ASW. ASW, the application software, is what controls the combustion process in your petrol engine. CBOOT has nothing to do with ASW. You can’t flash from ASW, you have to go into CBOOT for that. Both CBOOT and ASW have their own UDS comm stack, so when CBOOT is active it uses its own comm stack for CAN messages and similarly when ASW is active its using its own separate comm stack as well
ASW
ASW is the "Application Software" and contains all of the engine management software itself. This is the software that reads sensor data, and makes decisions.
CAL
This is the calibration data. The "maps" and "tables" that affect the way the car runs.
Calibration changes
It's important to understand that while making changes to the ECU, there's literally hundreds of different tables that may be referenced at any given time or situation. The process of identifying what tables should be used to achieve a given result includes reverse engineering the process that the ECU uses to make certain decisions. One example of this fact is that while anybody familiar with tuning this ECU knows (by know) about the Max clutch torque tables, they probably don't realize that there's more than than one (15?). These tables are (likely) used in different situations/combustions modes. Some tools will link all of them together as a single abstract table so that they all get updated simultaneously, since that's probably the goal of the tuner anyway.
Definitions files
Definitions files are essentially a "map" to where the maps are located within the calibration data. They give software the ability to display maps in a human readable way. They're proprietary (in the case of factory definitions files), or they're built by hand using the factory definitions as a model.
Flashing the ECU
Technically, you could retune the ECU by just changing the maps that are contained in the calibration block - but each data block in the ECU must be validated before the ECU will use it. The calibration block must be checksummed by the ECU to make sure it hasn't been modified.
The first flashing procedure includes a process which overrides the restrictions that exist in the factory ASW and effectively unlocks the ECU. This allows the tuner to modify the calibration block and flash it to the ECU without triggering the ASW to reject anything. This process is proprietary. Tools like Flashtec, Alientech, bFlash etc have their own way of accomplishing this. Once the ECU is unlocked, any table located in the calibration block can be modified and the entire calibration block reflashed to the ECU as a single action. If you've ever been through the process of flashing calibration changes - they go much faster than the initial flashing process (as they're only changing 1/5th of the overall data).
Unlocking the ECU
This is the factory OEM flash routine (aka FRF)
The numbers with the $xy suffix are UDS Service Routines, occasionally referred to in hex notation (0x31, 0x37, 0x2C, etc)
picture_1.pdf 123 kB PDF123 kB — Click to view
- $31 eraseMemory
- What is says. Deletes the entire CAL area so the whole block is grey to represent this.
- $34 requestDownload
- Ask to initiate a data transfer
- $36 requestDownload
- Actually move the bytes from the flash tool to the ECU
- $37 requestTransferExit
- Terminate the transfer after all bytes have been copied over. If you look the picture now, you’ll see that the blue data transferred doesn’t quite fill up the flash block. There is still a little bit of grey space remaining. That’s intentional, the flash block really is bigger than the blue CAL data. That little bit of spare space is used in the next steps.
- $31 checkMemory
- “Performs checks to write security keys for reprogrammed components. Security keys in flash memory to secure the reprogramming session if checksum and coherence are fitting. They are tested at start-up of the ECU. If one of the Security Keys is not valid, the applicative software is inhibited and there is no system operation”. So that’s the OKAY bytes written in green. NB – it’s a routine in the ECU that writes these OKAY bytes onto the flash after all checks are successful (checkMemory). Once the block has got the OKAY bytes, the block is allowed to be used. The OKAY bytes are written into the flash just after the blue data. If you look at a full 4MB file, you can see the OKAY bytes yourself just after the end of the CAL data. If there’s something invalid about the CAL data, then the check won’t write the OKAY bytes and the block is invalid…
And this is the initial exploit (this is just one step of several in what the tools do. There are some subsequent steps as well. This just gets some non-OEM code onto the ECU. In subsequent steps that non-OEM code will do further stuff)
The normal sequence is Erase, Write, Check.
picture_2.pdf
Because the Erase wipes both the Data AND the Okay bytes, once you’ve erased and sent the new data across you have to run the check to rewrite the Okay bytes and make the entire thing valid again. But now if you look very carefully, when the tool sends some patched ASW immediately again, it DOESN’T erase first. And because it doesn’t erase first, the Okay bytes are still there… But there are some restrictions if you are going to write WITHOUT erasing. I found this in the Infineon documentation: “The erased state of a flash cell is ZERO. After programming a ONE to a cell, only an erase can bring it back to ZERO. Writing a second time to the same cell with a ZERO does not change the ONE. But a ZERO can always be programmed to a ONE”
That means that you can only put additional stuff in a blank area, you can’t change existing areas. I think like this:
Flash after "Erase, Write, Check" Write this to Flash again without "Erase" Flash ends up being a combination of the two writes t h i s i s t h e o r i g i n a l s o u r c e c o d e 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 a p a t c h 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 = t h i s i s t h e o r i g i n a l s o u r c e c o d e 0 0 0 0 0 0 0 0 0 0 a p a t c h 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Okay, the next thing is CBOOT. The CBOOT handles the flashing but there are actually two CBOOT used in the process (CBOOT_temp and CBOOT_actual)
image.png image.png
Whilst you can directly update CAL, ASWx and CBOOT_temp via OBD flash routines, you can’t reach CBOOT_actual. You can only reach it indirectly. This is a safety thing. The flash routines are executing out of CBOOT_actual. It doesn’t matter how many times you mess up the blocks on the left, you just simply reboot, start execution out of CBOOT_actual and try again until you get it correct. But if you messed up CBOOT actual, then you’ve got nothing that works to reboot into. It’s bricked.
Thus the indirect way to update CBOOT is to OBD write to CBOOT_temp. Then CBOOT_temp gets checked over very carefully to ensure it is totally intact, no corruption, checksums okay and crypto signature all valid etc. And only then does an internal routine move CBOOT_temp to CBOOT_actual
You can read about this in detail in the FR – it’s explained in Section 21.2 ECRP-ECU Customer reprogramming (Appl. Inc.)
image.png image.png
So now building up the story, step by step. The first step talked about writing without erasing. Second was the concept of CBOOT_temp and CBOOT_actual. Thirdly is below.
image.png image.png
You can directly reach ASW1 via OBD. You can inject some extra code into ASW1 by write without erase. Limitation is you can only add, not delete or modify… Then when the ECU reboots into ASW1, the extra code in ASW1 does a low-level flash write to the flash memory location of CBOOT_actual (0x8000xxxx). It’s also subject to the same limitations because it’s not doing a delete, it’s writing on top of what’s already present. Basically it’s trying to change just a few bytes in CBOOT_actual.
No point in changing them in CBOOT_temp – the _temp to _actual checks will catch any suspicious changes before _temp is allowed to be copied over to _actual. You have to change _actual itself.
So eventually after all this you end up with some changes in CBOOT_actual, having gone via ASW1.
What do the few changed bytes in CBOOT_actual do? They switch off crypto signature checking, allowing modified files to be flashed via the OEM process :blush:
Just a few notes at this point in the flashing process
The first initial software blocks sent from tool to ecu are most likely to be stock blocks, just returning the ecu to the stock software. If the tool is going to be trying to crack the ecu and exploit stuff, it doesn’t want to be colliding with weird code that a previous tool / another tool vendor has potentially already left behind… No, you want to start greenfield, with known software and utterly predictable behaviour. Set it back to stock and start from there, the OEM software will always go back on because it’s always signed legitimately and check summed correctly.
When transferring from tool to ecu, the software blocks (CAL, ASWx, CBOOT) are compressed one-at-a-time by a LZSS algo and then the compressed files are encrypted with an AES key. Finally these AES-encrypted, compressed files are sent from the tool to the ECU with the ISO-TP multiblock packet size set to the biggest size possible (4095 bytes at a time). Get it across as fast as possible, small file in the biggest chunks possible (obviously inside the ecu it then decrypts and un-compresses them).
But what I’ve seen when the tools do the WriteWithoutErase exploit, the block is NOT compressed. It’s only AES encrypted. And the ISO-TP packet size is stepped down from 4000 to just a 100-or-so bytes per packet. So you have a big (uncompressed) file being sent a little bit at a time. And to make it worse, it’s like an old fashioned reel of tape! You can’t specify just an address in the middle. It always starts at the beginning and plods along to the very end, even if it was just 100 bytes in the middle you were trying to write. So that’s the file mostly filled with zeroes. It’s gonna start at the beginning, plod along through all the zeroes which won’t change anything, get to a few bytes and write them, then carry on with more zeroes until it reaches the end. That makes it really slow and I think this slowness is deliberate. I believe that when you are trying to write data to a flash substrate that hasn’t been freshly erased, it can take time for the bytes to ‘soak’ into the flash. It’s not freshly erased and just sitting there waiting. Instead it’s already got some contents and now you’re writing to it again. Maybe it takes longer to sink in and for the binary state to change… So that is your thirty to forty-five minutes waiting around time when the crack first happens. Those checks I mentioned that set the green OKAY bytes in the pictures I drew. Those checks only happen when you ‘step through the front door’. They don’t happen again. Just a once-off when the file is first pushed onto the ECU through the OBD front door. After it’s written to the flash, no point in checking it again, it already passed once and caused the OKAY bytes to be written. Simply the presence of the OKAY bytes suffices to say it was checked. That’s why if you can somehow change the code and leave the OKAY bytes intact, the code will not be checked again and your changes will go undetected!
Now you’ve reached the point, after a lot of effort, that you can write modified stuff down to the ecu. Great. But what if this is the second or third or nth time you want to flash. Clearly you don’t need to patch the ecu every single time, just the first time. No one want to sit around for 45 minutes unnecessarily. So wouldn’t it be nice to somehow check if the ECU has already been patched and we can just skip straight to sending modified files. Or discover if it was never patched and we have to start from scratch. Maybe a friend brings their ecu along and you don’t know the history. You need a way to talk to the ECU…
So now I must digress back to UDS again.
You’ve see services $2C, $22 and $23 for logging. There’s another service $3E TesterPresent. It is simply a heartbeat sent from any OBD tool to the ECU periodically every few 100ms and reminds the ECU that the tool is still there. Just a little tap on the shoulder. Useful for things like elevated security levels – if the tool had requested an elevated privilege level (Service $27 SeedKey) and the ECU then went into that privilege mode, but then the tool disappeared for some reason (flat battery/disconnected cable) then the ECU would notice the missing heartbeat, tear down the elevated privilege session and go back to a basic diagnostic mode waiting for the next connect.
A regular ISO/SAE UDS hearbeat is really simple. The format is always the same as well:
0x02 0x3E 0x00:
2 – it’s 2 bytes in length. Mandated by the UDS standards. Anything other than 2 in this field returns an error
3E – the first byte, it’s service $3E
00 – the second byte, it’s allowed to be 00 or 80 (acknowledgement required/not required)
So many third party tools re-task the UDS heartbeat message for their own purposes. What they’ve done is to slightly ‘corrupt’ the heartbeat request/reply, to use it beyond the original intention:
0x03 0x3E 0x00 0x01
3 - it’s 3 bytes in length.
3E - the first byte, it’s service $3E
00 - the second byte, it’s allowed to be 00 or 80 (acknowledgement required/not required)
01 – an extra byte, tacked upon the end. This could represent Command #1
So now you send this modified heartbeat. Firstly, the length is different now, 3 bytes instead of 2 bytes. So an unpatched stock ECU would reply with an Error 13 – incorrectMessageLengthOrInvalidFor
mat because the ISO/SAE standard is just 2 bytes always . But a patched ECU (with suitable patching in the UDS stack) would accept this and reply with a positive response instead. And there you have it, the tool can learn whether the ECU is previously patched or stock by simply send a little heartbeat message and seeing if the reply is positive or negative.
In addition, you can use that extra byte at the end to trigger something inside the ECU as well (as long as there is corresponding patching to accept it). In the example above, the extra byte being a value of 0x01 could mean invoke activity #1. A value of 0x02 might mean invoke activity #2. All driven by the tool. The clever bit here is that it’s a combination of external tool and ecu patches. It’s not enough just to have the patches inside the ECU, you also need the external tool to trigger those patches inside the ECU otherwise they just sit there waiting.
Next chapter of the story So now the ECU is wide open. After several steps (and going around the houses via ASW), finally a couple of bytes (yes, it really is just two) have been changed in CBOOT_actual which bypass the RSA signature checks. The ECU will now accept any modified file (i.e. signature not matching contents) that you throw at it, moreover you can just use the factory flash sequence of UDS commands to send the modified files. You don’t need a fancy tool costing £££ thousands either, anything that can send the UDS commands will do the job. So the pendulum has swung too far if you are a tool vendor! And now they dial it back a bit and make sure that it’s their tool in control again and you have to use their tool. So whilst the ECU is wide open with RSA signature checks bypassed, the flash tools send down a CBOOT with a number of changes: A patched heartbeat Remember UDS command $31 02 02 checkMemory, described much earlier? The one that does a bunch of checks and write the green OKAY bytes? Well, there’s a patch that does the same thing but it doesn’t do any checks beforehand, it just blindly writes the OKAY bytes And the RSA signature checking is re-activated (the two bytes are reverted)
The ECU reboots, comes up running this modified CBOOT (and although it’s modified it runs quite happily because no checks are done after the fact. The checks are only done once when it passes through the front door on the way in via OBD). The active RSA signature checks prevent you from sending down any more modified files. Err - but then how do you send a modified CAL? Remember again that modified UDS heartbeat $3E? Tacking a byte onto the end? And using that byte to represent some command? Well, you send over the modified CAL using the same UDS flash sequence commands Erase, then Write, all legitimately ($34, $36 etc). The CAL ends up on the flash inside the ECU and the final step is to get the OKAY bytes. At this point, instead of running the OEM $31 02 02 checkMemory which will fail because the file is modified and not write the OKAY bytes, the tools send a modified heartbeat. A patch inside the ECU recognises the modified heartbeat and blindly writes the OKAY bytes for CAL. Then as far as the ECU is concerned, all is fine, the OKAY bytes are there and everything is perfect. And the tool vendor is happy because you’ve had to use their tool to do this and their modified heartbeat comms channel and whatever command byte sequence they put in :blush:
Adding features
Since the ASW blocks can be flashed, it is possible to not only recalibrate tables in the calibration but also add or modify functions and features in the ASW. Unencrypted application bins include the machine code that's actually run by the ECU. The scope of what's possible is only limited to the amount of free space available (and whether or not you can decipher the instructions).
Logging
One example of an added feature is the Eurodyne High Speed Logger (HSL). There's a couple of different ways that the operation of the engine can be logged (usually for tuning/optimization purposes).
- Reading data available on the CANbus
- Reading memory address directly from the ECU
- Direct readByAddress queries
- Defining a dynamic identifier
CAN
The CANbus is essentially an open bus that can be sniffed. However, not every data point is available on the CANbus (since the ECU doesn't need to broadcast everything). RPM might be there (since the ECU communicates with the cluster), but the ecu won't broadcast knock statistics over the CAN network.
Reading memory by address
Tools that have the capability of connecting to the ECU can also request the values that the ECU has stored in various memory addresses. These values are stored in a non-human readable way with scaling factors that might only make sense to the ECU itself. The memory locations and their scaling factors are available in A2L/Definitions files, so assuming you have access to one of those for the ECU in question, you can find the location and it's scaling factor and you can query the ECU for it. The downside to this is that it has the potential to slow down dramatically as the number of logged parameters increases. If you have the capability of requesting 30 memory addresses per second, but you want to request the value of 10 different parameters, you'll only get 3 responses per second for each one. This has the potential to be inaccurate.
Dynamic Identifier
A dynamic identifier is (essentially) an identifier in the ECU that "maps" to multiple memory addresses. What that means is that you could, in theory, create a single dynamic identifier that maps to 5 different memory addresses. When you make a single request to the ECU for the value of that identifier, the ECU responds with the values in each of the memory addresses at once. This has the potentially to be many times faster than directly reading memory by address, though it's not available in every ECU (and when it is, it requires special [security level] access to be granted by the ECU).
Eurodyne actually installs a full dynamic identifier routine patched into the ECU for use with the high speed logger.
Map Switching
While this isn't specific to the Simos ECU (or even directly applicable), it outlines a procedure which should, in theory, achieve the same results if copied. Again, write the new feature, find free space (00's) in memory where it can be placed, and then find out what you want to do to trigger it.
https://www.tangentmotorsport.com/?p=47
ROM Consolidation
ROM Consolidation is something that tuners do, though Cobb seems to the be only one that advertises it. In short, this technique is used by tuners to broadly support a common family of ECUs by modifying them all with the same box code/application software. Since (mostly?) all of the Simos 18.1 ECUs use the same hardware code, the only difference from one to another is what software is installed on them. From the factory, there's a portion of the code which (like the checksums in the calibration block) verify the ASW that the ECU has installed. By bypassing this verification it allows the tuner to load the same code (and thus, the same calibration blocks) onto any ECU that shares/controls the same hardware. If they didn't do this, they would need definitions for each different calibration block since tables can be shifted slightly or exist in different places all together.