Skip to content

RENETOPR/Simos18_NVCrypt

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Simos18 NVRAM

Simos18 ECUs provide the Application Software with "NVRAM" which is emulated via the Tricore CPU's DFlash area.

This NVRAM is organized into 127 "channels" which can each store specific information - for example, adapted and learned values, variant coding, immobilizer data, and serial numbers.

The NVRAM system is built to minimize writes to the DFlash area - generally, changes are buffered and written out to the DFlash at a negative power latch phase (on ECU shutdown).

6 channels of NVRAM data are available to the "simple NVRAM" system in CBOOT - this is used to enable CBOOT to read critical data at boot, like the VIN and part number revisions.

Some NVRAM channels are intended to persist across a software flash, while others are intended to be discarded. Additionally, some channels are written twice to the second DFlash controller, while most are persisted only once.

Every stored record carries two CRC-16 checksums, both polynomial 0x8005:

  1. An inner content CRC, present depending on the channel's record_way. Init 0xABCD, computed over the channel's plaintext content and stored little-endian as a 2-byte prefix. On encrypted channels it lives inside the Hitag2 ciphertext. record_way == 0 has no prefix. This is computed at the application payload layer.

  2. An outer record CRC (the "trailing checksum"). Init 0xA55A, computed over the record payload followed by an 8-byte trailer (below) and appended to the record. This is computed at the EEPROM emulation / DFlash layer.

The immobilizer data uses CCITT checksums internally.

Optinally, some NVRAM channels can be encrypted with what appears to be the Hitag2 algorithm (attempted implementation here) with altered f4/f5 values.

The Hitag-encrypted channels are all encrypted with a shared key and IV generated using the Tricore Device ID. In this way, cloning the DFlash from one ECU to another is prohibited.

The layout of NVRAM in DFlash appears to be versioned. Searching for CH 46 seems to be the most reliable way to locate a record in NVRAM so far.

Some sample records are provided here:

42203131 31534338 46304838 30300000 00000000 00000004 0B239003 01464DC6

This record is Channel 1 due to: 0146

It contains 0x3 * 0x8 bytes of data due to the prior byte being 03:

42203131 31534338 46304838 30300000 00000000 00000004

Because the record_way is 1, the first two data bytes are the inner content CRC16: poly 0x8005, init 0xABCD, stored little-endian. It covers the channel's content only, not the whole 22-byte block:

CRC16 (LE prefix 4220 = 0x2042): crc16(init=0xABCD, "3131 31534338 46304838 3030 0000") = 0x2042

The trailing checksum: polynomial 0x8005, init 0xA55A, over the channel data followed by an 8-byte trailer.

7E 05 00 00  | 0D    | 00 | 46     | 06
<gen:4LE>      <len:1> <00> <marker> <channel>

<gen:4LE> is the flash generation counter (see below), little-endian.

The firmware CRCs the channel's logical data size, not the full record bytes; if there are leftover bytes, it's padded up to the 8 byte alignment used by the underlying flash emulation driver.

There are three ways the record can end up padded into the flash emulation:

  • left-pad data[0:data_size]
  • right-pad data[len*8-data_size:]
  • split : the record straddles a block boundary. In this case, a block-management byte is interleaved in the dump but not in the CRC stream; for example, channel 6's 103 logical bytes are stored as 72 + (1 marker) + 31. The split offset depends on underlying page alignment rather than record type.

Let's look at a simple record:

00600C00 55550000 0AF51001 2A4650AE

This record is record number 2A , as we know from the record specifier 2A 46 . It is 8 bytes long, as specified by length 01 . It has checksum 50AE, which matches when we calculate the CRC16 init A55A poly 8005 over 600c00555500007e0500000100462a .

The flash generation counter (7E05)

The value in the trailer is the flash generation counter. Records live in a page, and when a page fills the live records are copied into a freshly-erased page (garbage collection). The firmware keeps a 32-bit counter, which is incremented on each page switch.

The counter is written into every record's 0xA55A trailer as part of the CRCed material. We can recover it either from the FFFE page header or by brute-forcing it from any valid channel CRC. dflash.py does both and cross-checks them.

The 0AF510 field next to the trailer CRC is just the CRC (50AE) XOR FFFF, shifted left 4 bits within a 24-bit value.

Inspecting a dump

dflash.py renders a dump as a block with ASCII boxes per record.

uv run python dflash.py PMU0_DFlash.bin            # full ASCII block view
uv run python dflash.py PMU0_DFlash.bin --summary  # one line per channel
uv run python dflash.py PMU0_DFlash.bin --channel=9 # just one record
uv run python dflash.py PMU0_DFlash.bin 4480051118a04829020c0020  # decrypt enc. channels (incl. immo)

A record box looks like:

+ ch 0x09 (9)  @0x03748  len=1 (8B) -----------------------------------------+
| raw          : de 83 00 ff 80 55 00 00                                     |
|                                                                            |
| DATA  ([T] covers data[0:])                                                |
|   [P] crc16  : de 83  = 0x83DE  init ABCD over 4 B  [OK]                   |
|   content    : 00 ff 80 55                         |...U                   |
|   pad        : 00 00                                                       |
| TRAILER                                                                    |
|   inv_crc      : 0F95C0   ((crc^FFFF)<<4)                                  |
|   len/id       : 01 / 46 09   x8=8B, marker/ch                             |
|   [T] crc16    : 06A3  init A55A, seed 7e05 0000 01 00 46 09  [OK]         |
+----------------------------------------------------------------------------+

[P] is the inner content CRC (init 0xABCD), [T] the outer record CRC (init 0xA55A).

Immo

These are records 6, 7, and 8.

Here's a sample:

AAC98552 8F0000D4 3A0000F8 8B005967 F8FBF7AF 634F17CF 7865F183 24C33156 57415437 41333146 43303232 39313501 6AAA2735 00000000 A5050000 03000000 BF800000 00000000 00000000 00000000 00000031 56574154 37413331 46433032 32393135 9738D4B9

Each 6, 7, and 8 record contains 3 sub-records: datStat, datDat, and the VIN written again, I think for use by CBOOT.

datStat and datDat need to be identical across all 3 records.

[0:13]    datStat
[13:66]   datDat
[66:100]  third field = [17 reserved/zero][17 ASCII VIN copy]
[100:104] padding to dflash block size

The structure of each is as follows:

datStat:

AAC985528F0000D43A0000F88B

`AA` or `55` depending on if authPreVld was set, should be AA
Next 4 bytes C985528F are the first 4 values of imoRand
Next 2 bytes 0000 are just 0.
Next 4 bytes D43A0000 are some kind of counter incremented when the data task is called.
Next 2 bytes F88B are CRC16-CCITT of this subrecord.

datDat:

005967F8FBF7AF634F17CF7865F18324C33156574154374133314643303232393135016AAA27350000000A505000003000000BF80

00 is static
Next 16 bytes are the AES key CS/noKeySecu: 5967F8FBF7AF634F17CF7865F18324C3
Next 17 bytes are the VIN noVeh: 31565741 54374133 31464330 32323931 35 -> 1VWAT7A31FC022915

Now we have a bunch of data:

016AAA27 35000000 00A50500 00030000 00BF80

01 -> ctDatBasFazit , prefix means it is a counter of some kind, always 1 in files I have seen
6A -> PClass byte.
AA -> Internal state, same across everything I have seen. A5 and 5A are also values here, not sure exactly what they do here yet.
2735 -> noKeyMst , This is the PIN for the immobilizer master participant (instrument cluster).
00 -> tiDlyDown, this one is a diff, I think it's the time delay for download requests
00 -> noDlyDown, download counter
00 -> tiAccDown, another Download time interval
00 -> noAccDown, another Download counter
A5 -> bInhAcsMem == true, same across all immos I have seen
05 -> bLock == false, same across all immos I have seen
00 -> bTrigFctDi == false, no idea what this means
00 -> seems to never be written, always 0
03 00 00 00 -> this is a counter of some kind and it's the same across most immos I have seen. 
BF80 -> CRC16-CCITT

Third field (VIN plaintext copy), bytes [66:100]

[66:83]  17 bytes, reserved / zero in every dump seen so far
[83:100] 17-byte ASCII VIN in plaintext.

About

Forked from https://github.com/bri3d/Simos18_NVCrypt for investigation purposes

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • Python 100.0%