Electronics > Microcontrollers

RD60xx MCU controlled power units

(1/3) > >>

It's my usual thing to have a look into any firmware I'am running on devices in my workshop, My RD6012 is no exception.
After being burned before when updating a device with a locked down bootloader only to get a brick at the end with no OEM support. This time around i dumped the bootloader first.
Sadly the first method used only gives me insight into how the bootloader works , pretty standard, uses the same decrypt at write method the MKS robin and OM127 devices use that I have documented elsewhere.
Once i fully understand the crypt function I will post a open source bootloader that can support factory firmware, I have already done so for other devices so this is clean room code so to speak for all but the key (crypt function).

I have posted this separate to the actual rd60xx firmware page as thats all based around factory function and is not open source.

when looking at the file please understand the dump method does not yet dump all the data , you will see 0xffff in many places, only when i can encrypt a dump routine into the f/w update system can i then dump all the code complete.

Looking at the code so far i have identified two methods to do this, a buffer overflow attack on the UART update system or reversing the encryption/decryption routine  and creating a trojan f/w to just dump the flash.


Update file format
AES128 encrypted file :-
0x0-0x3f = Header info - file size - version number
0x40-End of file  = Encrypted bin file

Header break down
0x0-0x3  file size (little endian)  (eg 0x0c 0xab 0x01 0x00 = 0x1abc0 file size)  - bootloader does fail if receives incorrect amount then will revert to bootloader loop, reset will jump to broken f/w
0x4 Version number in hex (eg. 0x88 = 133dec)
0x5-0x3f ???????

Key 1 location 0x3f008 
Key 2 location 0x3f808
NB: last 4 bytes are incorrect due to extraction method in use atm.

[0x3f008 =   57 11 16 00 67 5B B8 00 C8 23 CB 15 07 00 00 20
0x3f808 =   65 F3 F6 0B 04 6A D7 C2 92 0E 88 9B 07 00 00 20

AES256 key located at 0x52EC   +32


--- Code: ---
static const uint8_t sbox[256] = {
  //0     1    2      3     4    5     6     7      8    9     A      B    C     D     E     F
  0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
  0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
  0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
  0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
  0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
  0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
  0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
  0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
  0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
  0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
  0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
  0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
  0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
  0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
  0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
  0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };

--- End code ---

I've brute forced the missing 4 bytes of both keys (took 16 bytes at+0x90 (the repeating vector table piece) from custom fw release, match criteria: bytes at 3, 7, B, F are 08) - no matches. Either the keys are wrong or AES is tweaked somehow.
Btw, the dump method you are using works a bit better (reading initial SP and PC) when run from addr 0 (where the 08000000 is aliased).

Update: the algo is AES256 indeed and the correct key is at +52EC in the dump (38 12 55 64...).

And bingo was his name-O.
You hit the nail on the head, I think the other sector of data is maybe factory settings or similar.
image below shows I managed to encrypt 0x00 by 16 and it matches known data. now i can encrypt my dumping code and dump the entire bootloader without issue.
many thanks for your input there, I think i was not seeing the wood for the tree's and overlooking that call, now that i've gone through it with fresh eyes (i had my covid jab and a reaction so forced few days away from my screen) I can see it's the aes 256 decrypter with default SBOX (thats what I used to encrypt)

I will have some tools up soon.


That particular AES implementation in the bootloader is not obvious, it is optimized for flash size and generates tables in RAM. The decrypt function does CBC xors, but they zeroize IVs for each decrypt_block() call so it is plain ECB at the end. Python decryptor (requires pycryptodomex package):

--- Code: ---from sys import argv, exit
from Cryptodome.Cipher import AES

fwkey = b'\x38\x12\x55\x64...\xF7\x3A\x63\x8C'

if len(argv)!=3:
exit("Usage: "+argv[0]+" <infile> <outfile>")

open(argv[2], 'wb').write(AES.new(fwkey, AES.MODE_ECB).decrypt(open(argv[1], 'rb').read()))

--- End code ---


[0] Message Index

[#] Next page

There was an error while thanking
Go to full version