bri3d/VW_Flash

Error decrypting and unpacking .FRF file

reikred opened this issue · 17 comments

While waiting for a dongle to arrive, I am trying to decrypt and unpack an .frf file into a .bin file, but I fail.

Any thoughts on what might be going wrong here? I'm on ubuntu 22.04.3, but the process was also not working in win10 (it was hanging in the GUI, I did not try the cmdline in windows).

python3 VW_Flash.py --action prepare --frf ../FL_5C6915182___0601.frf --output_bin ../FL_5C6915182___0601.bin
cannot import name 'WINFUNCTYPE' from 'ctypes' (/usr/lib/python3.10/ctypes/__init__.py)
dashing module not loaded
2023-11-22 11:43:10 [INFO] VWFlash: Starting VW_Flash.py
Traceback (most recent call last):
  File "/tmp/VW_Flash-0.6.0/VW_Flash.py", line 288, in <module>
    input_blocks = input_blocks_from_frf(args.frf)
  File "/tmp/VW_Flash-0.6.0/VW_Flash.py", line 269, in input_blocks_from_frf
    input_blocks[filename] = BlockData(i, flash_data[filename])
KeyError: 'FD_0'
bri3d commented

The prepare feature is intended only for supported binaries: Simos ECU, DQ250, DQ381, and Haldex files, as it also performs checksumming and other operations.

The file FL_5C6915182___0601.frf is not one of these supported control modules (it's a BMS I guess?), so it crashes.

You can still use the tools in this repo to extract this file, though:

Use PYTHONPATH=. python3 frf/extractfrf.py --file ../FL_5C6915182___0601.frf to produce the ODX and then python3 extractodx.py --file FL_5C6915182___0601.odx --outdir . to produce the ODX segments.

You're in luck as this file doesn't look to be encrypted or compressed.

Yeah, it is the BCM/BMS file. Does the above mean I will not be able to load this .frf file into the BCM using VW_Flash? I did manage to create the .odx file, but is that enough?

Edit: I guess you answered that already, I need to write a module/SA2 thing.

Just a small correction. I think you meant decryptfrf.py not extractfrf.py in the command

PYTHONPATH=. python3 frf/extractfrf.py --file ../FL_5C6915182___0601.frf   # WRONG
PYTHONPATH=. python3 frf/decryptfrf.py --file ../FL_5C6915182___0601.frf  #RIGHT

The 2nd command worked for me. Thx.

For reference, here are the resulting image files for the whole process

% ls -l FD_*
-rw-r--r-- 1 reik reik   8192 Nov 23 13:51 FD_0DRIVE
-rw-r--r-- 1 reik reik 385024 Nov 23 13:51 FD_1DATA
-rw-r--r-- 1 reik reik   8192 Nov 23 13:51 FD_2DRIVE
-rw-r--r-- 1 reik reik   4096 Nov 23 13:51 FD_3DATA

I'm struggling trying to find out out the proper address at which to write each of the 4 segments FD_0DRIVE FD_1DATA FD_2DRIVE FD_3DATA. I don't know the base address of anything, not where the BOOT segment is, nothing. How can one get to this information? As I mentioned elsewhere, I have the metadata but I don't see any base-addresses in there. Can someone knowledgeable please point me in the right direction?

Metadata can be found here:

FL_5C6915182___0601.odx.metadata.txt

bri3d commented

But that is exactly the problem: I don't know how to generate a binary flash file that places these 4 code+data segments at the correct locations of the 512kB flash memory, where the boot code expects to find them, and without overwriting the boot code itself.

Am I explaining the problem the wrong way?

I tried to figure out where the 4 segments are supposed to be located by hooking up an Orange5 eeprom programmer and trying to read out the current flash content. Then I could compare it to the segments extracted from the .frf/odx and find the addresses using some spatial correlation/matching of the data. BUT the big but is that the uPD76F0192 MCU chip is write-only erase-only and I cannot read out the old content. So I have nothing. Am I making sense?

bri3d commented

Yes, but if you just want to flash over UDS, you don't need to know the locations, since the RequestDownload requests just use a block number (unless there is an address-referenced checksum you are trying to correct). The reason those addresses are in the module files in VW_Flash is to correct address-referenced checksums and to generate a flat binary file which matches the on-device flash layout. You don't need either one of those things to reflash the module with an FRF, only to tamper with the data later.

Okay, so they key point that I was missing was that the CANbus/UDS interface does not reveal the actual destination addresses to the outside world. It just asks for data segments because it already knows internally where to write it.

But I need the addresses because it turns out that the CANbus is no-comm after all, hence no UDS, and I have to flash the file using the Orange5 connected directly to the UART on the pcb. (I have wired it all up. and it talks to the chip okay). I thought I would get around not knowing the addresses by reading the flat data out first, and then splice in the new images, then write a flat file back. But with reading not allowed I can't do that. So I am stuck.

Wondering out loud: What avenues to pursue next (a) somehow get a debug-mode password that allows me read the entire flash (b) Find an exploit similar to the ones used in VW_Flash for the known MCUs (c) Do some kind of brute force search for the addresses based on known "address-referenced checksum" (I need to understand first how "address-referenced checksum" is calculated).

Why would you need an exploit? You're not writing modified data and I'm certain there's little to no chance of this having RSA even if you were.

You're best bet would be to just start looking at the extracted blocks in hex If you're lucky there will probably be a header at the start of each block containing the address ranges that need to be checksummed. The 2 driver blocks will likely not have an internal checksum but if they follow the same format as other ancillary modules it's likely they'll have direct references to addresses in the first 20 bytes or so.

Scavenging for what looks like an address is an interesting idea. Additional thought along those lines: For the code segments maybe running the bytes through an ARM disassembler could narrow down the possibilities of what bytes might be an address?

Update on what I have been up to: I tried to make an inversion/deduction attempt using crchack to find out what address value may have been prepadded or postpadded (cf. "address-referenced checksum") to each segment that would result in the known (from metadata) CRC32 values. But it failed: It looks like the CRC32 for all segments were calculated using the same prepadded fake address of 0x0001000 hex = 64k start address. I know the address should be at most 19b (512kB flash) of nonzero values, so could also determine that flipping the bits and also the postpad inversion gave no sensible addresses at all.

UPDATE: This was a waste of time. "address-referenced checksum" did not mean padding the segment before or after with the address before calculating the checksum crc32.

It's not ARM so an ARM disassembler won't be of any use. It's a V850 series MCU from Renesas, you can read the user manual for it here: https://www.renesas.com/us/en/document/mah/v850-familytm-architecture

The CRC32 in the ODX is quite literally just the CRC32 of the entire block. My advice is start looking at the blocks you've extracted in a hex editor. Also looks like there's a ghidra module for this instruction set (https://github.com/esaulenka/ghidra_v850) but your mileage may vary.

When we say "address referenced checksum" what we mean is that in the block header of some control units you have what is essentially a guide on how to perform the checksum. Here's an example from a Simos ECU:
image

Splitting this into chunks of 4 bytes and reading it as little endian uint32's you can deduce:

  • Initial value of the checksum (in this case 00000000)
  • Current checksum of the block (3BA9E0)
  • How many sections make up the checksum (2)
  • First range is A0810000 -> A08102FF
  • Second range is A0810400 -> A08BF9FF

This tells me the address of this block is at 0xA0810000

( posted before I saw your latest comment, will go back and read it) (have to sleep now, it is 0530 here, I've been up late)

Scavenging for possible addresses: Hmm, I cannot see anything obvious. The value 0001 5004 is within the 7FFFF range but it is the same in two of the files, so that seems like an unlikely candidate unless the 2nd file is just a backup copy intended to be loaded into the 1st destination 15004 if needed.

Any ideas? I could upload the files somewhere if that would help.

machine(user):6172 > hexdump -n 64 FL_5C6915182___0820/FD_0DRIVE
0000000 0001 5004 c014 ffff c054 ffff c076 ffff
0000010 c2aa ffff 0e40 0000 3761 cae1 6201 6746
0000020 0002 0746 0001 0746 0000 0746 0003 0766
0000030 0004 0766 0009 0766 000d 0766 0011 0766
0000040
machine(user):6173 > hexdump -n 64 FL_5C6915182___0820/FD_1DATA
0000000 0783 5810 0000 0000 0000 0000 0000 0000
0000010 0785 a4c8 0000 0000 0000 0000 0000 0000
0000020 0785 a4b8 0000 0000 0000 0000 0000 0000
0000030 0000 0000 0000 0000 0000 0000 0000 0000
0000040
machine(user):6174 > hexdump -n 64 FL_5C6915182___0820/FD_2DRIVE
0000000 0001 5004 c014 ffff c054 ffff c076 ffff
0000010 c2aa ffff 0e40 0000 3761 cae1 6201 6746
0000020 0002 0746 0001 0746 0000 0746 0003 0766
0000030 0004 0766 0009 0766 000d 0766 0011 0766
0000040
machine(user):6175 > hexdump -n 64 FL_5C6915182___0820/FD_3DATA
0000000 f398 fec2 3044 3130 0000 0000 000a 0000
0000010 0001 0000 0002 0032 0001 0000 e630 0002
0000020 0064 0000 0258 0000 0032 0000 028a 0000
0000030 00a0 0000 fed4 ffff 000a 0000 fed4 ffff

Okay, I have now read what Connor suggested. I'll look into ghidra tool and ghidra_v850.

Meanwhile, I will upload the segment files. I'm not able to see any obvious address patterns myself. Maybe someone can? It would be great someone could find something.

I used xxd -e -g4 to get the presumably little-endian segment files to display as 32b values in the big-endian way, which should be a more human-readable form.

I have experimented with using the following grep as filter to show values that look like 10000-7FFFF range addresses. It may be helpful.

cat FD*xxd-e-g4.txt | grep -P " 000[1-7]0000" | more

Here are the files:

FD_0DRIVE.xxd-e-g4.txt
FD_1DATA.xxd-e-g4.txt
FD_2DRIVE.xxd-e-g4.txt
FD_3DATA.xxd-e-g4.txt


Note to self: My idea of what "address-referenced checksum" constituted was completely wrong. So that whole inversion/deduction effort was a waste of time. Although I learned something about crchack. Also explains why every answer was 0x0001000: there was no prepadded or postpadded address in the first place.

I managed to get the ghidra tool going, and here is the result of disassembly of the FD_0DRIVE segment file. The analysis creates both assembly code and some C-like pseudo code, as enclosed below. Is there any address info embedded here that indicates the starting address?

FD_0DRIVE.c.txt

I used Ghidra to create a full set of disassembled and decompiled files. Here they are:

FD_0DRIVE.asm.txt
FD_0DRIVE.c.txt
FD_1DATA.asm.txt
FD_1DATA.c.txt
FD_2DRIVE.asm.txt
FD_2DRIVE.c.txt
FD_3DATA.asm.txt
FD_3DATA.c.txt

Hopefully there is an expert that can help me deduce the correct flash memory address at which to load each one of these ...

Still struggling with the above. One thing occurred to me from the ConnorHowell example above: It looks like the Flash is memory-mapped into the same address space as the RAM. So if one knew the starting address of the flash area (looks like it might be A08x_xxxx in his example), that could help narrow down the search for (non-relative) addresses in the segment/image files.

The chip I'm trying to program is a Renesas uPD76F0192GC, and is presumably a custom version for Sanyo, the supplier of the BCM box (no datasheet). The chip probably has 512kB or 576kB of flash, and an unknown amount of RAM. Maybe the Flash start address is the same for any chip in the V850 family? Knowing the size of the boot code (kernel?) also could be important knowledge. The BCM box was manufactured in 2013 circa.