some new findings. It seems that the new bootloader 5 really expects encrypted firmware image.
Here is a fragment of ProcessPacket_057b function (it handles flash write packet):
3d2: 4822 ldr r0, [pc, #136] @ (0x45c) @ r0 = (uint8_t*)0x20001124;
3d4: f7ff febe bl 0x154 @ memcpy(0x20001124, packet_ptr->flashData, 0x100);
3d8: 4920 ldr r1, [pc, #128] @ (0x45c) @ r1 = (uint8_t*)0x20001124;
3da: 2000 movs r0, #0 @ r0 = 0;
@ do {
3dc: 0082 lsls r2, r0, #2 @ r2 = r0 << 2;
3de: 588b ldr r3, [r1, r2] @ r3 = *(uint32_t*)&r1[r2];
3e0: 1c40 adds r0, r0, #1 @ r0 += 1;
3e2: ba1b rev r3, r3 @ r3 = bswap_32(r3);
3e4: b280 uxth r0, r0 @ r0 = (uint16_t)r0;
3e6: 508b str r3, [r1, r2] @ *(uint32_t*)&r1[r2] = r3
3e8: 2840 cmp r0, #64 @ 0x40
3ea: d3f7 bcc.n 0x3dc @ } while (r0 < 64);
3ec: 4f1c ldr r7, [pc, #112] @ (0x460) @ r7 = 0x20000F24;
3ee: 2600 movs r6, #0 @ r6 = 0;
@ do {
3f0: 0130 lsls r0, r6, #4 @ r0 = r6 << 4;
3f2: 4a1a ldr r2, [pc, #104] @ (0x45c) @ r2 = 0x20001124;
3f4: 19c1 adds r1, r0, r7 @ r1 = r0 + r7;
3f6: 1880 adds r0, r0, r2 @ r0 = r0 + r2;
3f8: f7ff ff10 bl 0x21c @ DecryptBlock128(0x20001124 + r6*16, 0x20000F24 + r6*16);
3fc: 1c76 adds r6, r6, #1 @ r6 += 1;
3fe: b2b6 uxth r6, r6 @ r6 = (uint16_t)r6;
400: 2e10 cmp r6, #16
402: d3f5 bcc.n 0x3f0 @ } while (r6 < 16);
404: 2000 movs r0, #0 @ r0 = 0;
@ do {
406: 0082 lsls r2, r0, #2 @ r2 = r0 << 2;
408: 58b9 ldr r1, [r7, r2] @ r1 = *(uint32_t*)&r7[r2];
40a: 1c40 adds r0, r0, #1 @ r0 += 1;
40c: ba09 rev r1, r1 @ r1 = bswap_32(r1);
40e: b280 uxth r0, r0 @ r0 = (uint16_t)r0;
410: 50b9 str r1, [r7, r2] @ *(uint32_t*)&r7[r2] = r1;
412: 2840 cmp r0, #64 @ 0x40
414: d3f7 bcc.n 0x406 @ } while (r0 < 64);
416: b672 cpsid i @ DisableIRQ();
418: 7a68 ldrb r0, [r5, #9] @ r0 = (uint8_t)packet_ptr[9];// r0=packet_ptr->chunkNumber.hi;
41a: 7a29 ldrb r1, [r5, #8] @ r1 = (uint8_t)packet_ptr[8];// r1=packet_ptr->chunkNumber.lo;
41c: 0200 lsls r0, r0, #8 @ r0 <<= 8;
41e: 4308 orrs r0, r1 @ r0 |= r1; // r0=packet_ptr->chunkNumber;
420: 0201 lsls r1, r0, #8 @ r1 = r0 << 8; // r1=packet_ptr->chunkNumber * 0x100;
422: 2001 movs r0, #1 @ r0 = 1;
424: 0300 lsls r0, r0, #12 @ r0 <<= 12;
426: 1808 adds r0, r1, r0 @ r0 = r1 + r0;
428: 2240 movs r2, #64 @ 0x40 @ r2 = 64;
42a: 490d ldr r1, [pc, #52] @ (0x460) @ r1 = 0x20000F24;
42c: f7ff fede bl 0x1ec @ call_thumb_200001bc(0x1000 + packet_ptr->chunkNumber * 0x100, 0x20000F24, 64); // write flash page???
430: b662 cpsie i @ EnableIRQ();
As you can see, it calls the function at address 0x21c. I name it DecryptBlock128, because it looks like using some hardware cryptographic module to decrypt 128 bits of data:
@ arm-none-eabi-objdump -D -b binary -marm --start-address=0x21c --stop-address=0x254 -Mforce-thumb bootloader.bin
@ DecryptBlock128(r0, r1)
0000021c <.data+0x21c>:
21c: 4a0c ldr r2, [pc, #48] @ (0x250) @ r2 = 0x400BD000;
21e: 6803 ldr r3, [r0, #0]
220: 6093 str r3, [r2, #8] @ *(uint32_t*)&r2[8] = *(uint32_t*)&r0[0]
222: 6843 ldr r3, [r0, #4]
224: 6093 str r3, [r2, #8] @ *(uint32_t*)&r2[8] = *(uint32_t*)&r0[4]
226: 6883 ldr r3, [r0, #8]
228: 6093 str r3, [r2, #8] @ *(uint32_t*)&r2[8] = *(uint32_t*)&r0[8]
22a: 68c0 ldr r0, [r0, #12]
22c: 6090 str r0, [r2, #8] @ *(uint32_t*)&r2[8] = *(uint32_t*)&r0[12]
@ do {
22e: 6850 ldr r0, [r2, #4] @ r0 = *(uint32_t*)&r2[4];
230: 07c0 lsls r0, r0, #31 @ r0 <<= 31;
232: d0fc beq.n 0x22e @ } while (r0==0);
234: 68d0 ldr r0, [r2, #12]
236: 6008 str r0, [r1, #0] @ *(uint32_t*)&r1[0] = *(uint32_t*)&r2[12];
238: 68d0 ldr r0, [r2, #12]
23a: 6048 str r0, [r1, #4] @ *(uint32_t*)&r1[4] = *(uint32_t*)&r2[12];
23c: 68d0 ldr r0, [r2, #12]
23e: 6088 str r0, [r1, #8] @ *(uint32_t*)&r1[8] = *(uint32_t*)&r2[12];
240: 68d0 ldr r0, [r2, #12]
242: 60c8 str r0, [r1, #12] @ *(uint32_t*)&r1[12] = *(uint32_t*)&r2[12];
244: 6810 ldr r0, [r2, #0] @ r0 = *(uint32_t*)&r2[0];
246: 2180 movs r1, #128 @ 0x80
248: 4308 orrs r0, r1
24a: 6010 str r0, [r2, #0] @ *(uint32_t*)&r2[0] = *(uint32_t*)&r2[0] | 0x80;
24c: 4770 bx lr @ return
24e: 0000
250: d000 DCD 0x400BD000
252: 400b
Manually decompiled version:
void DecryptBlock128([r0]uint32_t* src, [r1]uint32_t* dst) {
uint32_t* reg_base = 0x400BD000;
reg_base[2] = src[0];
reg_base[2] = src[1];
reg_base[2] = src[2];
reg_base[2] = src[3];
while ((reg_base[1] & 1)==0) { }
dst[0] = reg_base[3];
dst[1] = reg_base[3];
dst[2] = reg_base[3];
dst[3] = reg_base[3];
reg_base[0] = reg_base[0] | 0x80;
}
As you can see, it sends data to some hardware unit with base address 0x400BD000 then waits for completion and reads data back.
And now it's more clear what meaning the new byte field in the version packet. It selects one of 16 keys stored in the bootloader at address 0x09f0, each key is 32 bytes length (256 bit).
Here is the code of ProcessPacket_057d function which process version packet:
@ arm-none-eabi-objdump -D -b binary -marm --start-address=0x2a8 --stop-address=0x30c -Mforce-thumb bootloader.bin
@ ProcessPacket_057d([r0]uint8_t* packet_ptr)
000002a8 <.data+0x2a8>:
2a8: b510 push {r4, lr}
2aa: 7d01 ldrb r1, [r0, #20] @ r1 = (uint8_t)packet_ptr[20];
2ac: b08c sub sp, #48 @ 0x30 @ sp -= 48; // alloc 48 bytes on stack
2ae: 2910 cmp r1, #16
2b0: d224 bcs.n 0x2fc @ if (r1 >= 16) return;
2b2: 4a13 ldr r2, [pc, #76] @ (0x300) @ r2 = 0x0BF0; // pointer to string "5.00.01"
2b4: 7900 ldrb r0, [r0, #4] @ r0 = (uint8_t)packet_ptr[4]; // first letter of version string
2b6: 7812 ldrb r2, [r2, #0] @ r2 = '5';
2b8: 4290 cmp r0, r2
2ba: d001 beq.n 0x2c0
2bc: 282a cmp r0, #42 @ 0x2a
2be: d11d bne.n 0x2fc @ if (r0!=r2 && r0!='*') return;
2c0: 4a10 ldr r2, [pc, #64] @ (0x304) @ r2 = (uint8_t*)0x20000314; // ram314
2c2: 2001 movs r0, #1 @ r0 = 1;
2c4: 7050 strb r0, [r2, #1] @ *(uint8_t*)&r2[1] = r0; // ram314->IsFlashWriteEnable = 1;
2c6: 4810 ldr r0, [pc, #64] @ (0x308) @ r0 = 0x09f0; // ptr to bootloader area at 0x09f0 offset
2c8: 0149 lsls r1, r1, #5 @ r1 = r1 << 5;
2ca: 1809 adds r1, r1, r0 @ r1 += r0; // r1 = (byte)packet_ptr[20] * 0x20 + 0x09f0;
2cc: 460c mov r4, r1 @ r4 = r1;
2ce: 2210 movs r2, #16 @ r2 = 16;
2d0: a804 add r0, sp, #16 @ r0 = sp+16;
2d2: f7ff ff3f bl 0x154 @ memcpy(r0, r1, r2);
2d6: 4621 mov r1, r4 @ r1 = r4;
2d8: 3110 adds r1, #16 @ r1 += 16;
2da: 2210 movs r2, #16 @ r2 = 16;
2dc: 4668 mov r0, sp @ r0 = sp;
2de: f7ff ff39 bl 0x154 @ memcpy(r0, r1, r2);
2e2: 466a mov r2, sp @ r2 = sp;
2e4: a904 add r1, sp, #16 @ r1 = sp+16;
2e6: 2001 movs r0, #1 @ r0 = 1;
2e8: f000 f902 bl 0x4f0 @ call_4f0(r0, r1, r2);
2ec: a808 add r0, sp, #32 @ r0 = sp+32;
2ee: f7ff ffb1 bl 0x254 @ call_254(r0);
2f2: 466a mov r2, sp @ r2 = sp;
2f4: a908 add r1, sp, #32 @ r1 = sp+32;
2f6: 2002 movs r0, #2 @ r0 = 2;
2f8: f000 f8fa bl 0x4f0 @ call_4f0(r0, r1, r2);
2fc: b00c add sp, #48 @ 0x30 @ sp += 48; // free 48 bytes on stack
2fe: bd10 pop {r4, pc} @ return;
300: 0bf0 DCD 0x00000BF0
302: 0000
304: 0314 DCD 0x20000314
306: 2000
308: 09f0 DCD 0x000009f0
30a: 0000
Manually decompiled version:
void ProcessPacket_057d(uint8_t* packet_ptr) {
uint8_t keyNumber = packet_ptr[20];
if (keyNumber >= 16) {
return;
}
uint8_t version = packet_ptr[4];
if (version != '5' && version != '*') {
return;
}
RAM314_T* ram314 = (RAM314_T*)0x20000314; // Pointer to ram314
ram314->IsFlashWriteEnable = 1; // ram314->u8_1
uint8_t* keyBase = (uint8_t*)0x09f0;
uint8_t* keyPtr = keyBase + (keyNumber << 5);
uint8_t stack[48];
memcpy(stack + 16, keyPtr, 16);
memcpy(stack, keyPtr + 16, 16);
call_4f0(1, stack + 16, stack);
call_254(stack + 32);
call_4f0(2, stack + 32, stack);
}
And the function call_4f0() uses the same base address 0x400BD000 as DecryptBlock128():
@ arm-none-eabi-objdump -D -b binary -marm --start-address=0x4f0 --stop-address=0x534 -Mforce-thumb bootloader.bin > FUN_000004f0.s
@ call_4f0([r0]uint32_t v, [r1]uint32_t* key1, [r2]uint32_t* key2)
000004f0 <.data+0x4f0>:
4f0: b510 push {r4, lr}
4f2: 4b0f ldr r3, [pc, #60] @ (0x530) @ r3 = 0x400BD000;
4f4: 681c ldr r4, [r3, #0] @ r4 = *(uint32_t*)&r3[0];
4f6: 0864 lsrs r4, r4, #1 @ r4 >>= 1;
4f8: 0064 lsls r4, r4, #1 @ r4 <<= 1;
4fa: 601c str r4, [r3, #0] @ *(uint32_t*)&r3[0] = r4;
4fc: 00c0 lsls r0, r0, #3 @ r0 <<= 3;
4fe: 2420 movs r4, #32 @ r4 = 32;
500: 4320 orrs r0, r4 @ r0 |= r4; // r0 = (r0 << 3) | 0x20;
502: 6018 str r0, [r3, #0] @ *(uint32_t*)&r3[0] = r0;
504: 6808 ldr r0, [r1, #0]
506: 61d8 str r0, [r3, #28] @ *(uint32_t*)&r3[28] = *(uint32_t*)&r1[0];
508: 6848 ldr r0, [r1, #4]
50a: 6198 str r0, [r3, #24] @ *(uint32_t*)&r3[24] = *(uint32_t*)&r1[4];
50c: 6888 ldr r0, [r1, #8]
50e: 6158 str r0, [r3, #20] @ *(uint32_t*)&r3[20] = *(uint32_t*)&r1[8];
510: 68c8 ldr r0, [r1, #12]
512: 6118 str r0, [r3, #16] @ *(uint32_t*)&r3[16] = *(uint32_t*)&r1[12];
514: 6810 ldr r0, [r2, #0]
516: 62d8 str r0, [r3, #44] @ 0x2c @ *(uint32_t*)&r3[44] = *(uint32_t*)&r2[0];
518: 6850 ldr r0, [r2, #4]
51a: 6298 str r0, [r3, #40] @ 0x28 @ *(uint32_t*)&r3[40] = *(uint32_t*)&r2[4];
51c: 6890 ldr r0, [r2, #8]
51e: 6258 str r0, [r3, #36] @ 0x24 @ *(uint32_t*)&r3[36] = *(uint32_t*)&r2[8];
520: 68d0 ldr r0, [r2, #12]
522: 6218 str r0, [r3, #32] @ *(uint32_t*)&r3[32] = *(uint32_t*)&r2[12];
524: 6818 ldr r0, [r3, #0]
526: 2101 movs r1, #1
528: 4308 orrs r0, r1
52a: 6018 str r0, [r3, #0] @ *(uint32_t*)&r3[0] = *(uint32_t*)&r3[0] | 1;
52c: bd10 pop {r4, pc}
52e: 0000
530: d000 DCD 0x400BD000
532: 400b
Manually decompiled version:
void call_4f0([r0]uint32_t v, [r1]uint32_t* key1, [r2]uint32_t* key2) {
uint32_t* reg_base = 0x400BD000;
reg_base[0] &= ~1;
reg_base[0] = (v << 3) | 0x20;
reg_base[7] = key1[0];
reg_base[6] = key1[1];
reg_base[5] = key1[2];
reg_base[4] = key1[3];
reg_base[11] = key2[0];
reg_base[10] = key2[1];
reg_base[9] = key2[2];
reg_base[8] = key2[3];
reg_base[0] = reg_base[0] | 1;
}
so it seems like this function setting the decryption key for hardware cryptographic module.
Does anyone know what is the module with base address 0x400BD000? What encryption algorithm it uses? AES 256?