Firmware validation
The logic to validate the firmware bundle begins at: libupg_validate_firmware_mem
.
This is called from libupg_upgrade_mem
that is subsequently called from the command upgrade
in the psbl
terminal.
This can be seen from the upgrade
command handler:
int upgrade(int argc,char **argv)
{
int iVar1;
undefined4 *param2;
if ((argc == 2 || argc == 4) && ((argc != 4 || (iVar1 = strcmp(argv[1],"-i"), iVar1 == 0)))) {
*argv = "upgrade";
argv[argc] = "/dev/ram";
param2 = (undefined4 *)tftp(argc + 1,argv); // [1]
if (0 < (int)param2) {
iVar1 = libupg_upgrade_mem((astruct *)0xb4500000,param2); // [2]
return iVar1;
}
}
else {
upgrade_usage();
}
return -1;
}
A couple of things to note from this snippet:
- [1] Shows the data is being obtained through
tftp
(wtf) - We can see the address [2]
0xb4500000
being passed intolibupg_upgrade_mem
. This will be the location that the firmware is either downloaded to or where we will begin flashing.
libupg_validate_firmware_mem
The function starts by taking two arguments:
int libupg_validate_firmware_mem(byte *param1,uint param2)
[...]
-
param1
is the pointer into RAM discussed above (0xb4500000
) -
param2
is the result fromtftp
(Looks like it is being used as a data length)
The function begins by validating the firmware's header.
Header validation
The header validation can be found in function validate_firmware_header
.
The function starts by taking a structure (we're going to call it firmware_header_struct
).
The function contains two stages:
- Digest validation
- Randseq validation
Information regarding the "header format" has been split into a separate page. Please see Firmware format for more information. The rest of this section wiill use terminology sourced from it.
Digest validation
To calculate the digest you perform the following:
- Zero out the Signature
- Zero out the Digest
- MD5 hash of the firmware header and module header table.
The size of the headers is calulcated with the formula below:
size = hdr->FirmwareHeaderSize + hdr->NumberOfModules * hdr->ModuleHeaderSize;
The process listed above can be seen in the following tidy Ghidra decompilation (Note: The size
parameter is the one calulcated above)
int gen_fmhdr_digest(void *md5_struct_out, firmware_header_struct *fm_hdr, size_t size) {
byte *pDigest;
byte *pSignature;
astruct_1 MD5Buffer [3];
undefined lSignature [32];
undefined lDigest [16];
// Save signature and digest
pSignature = fm_hdr->Signature;
memcpy(lSignature,pSignature,0x20);
pDigest = fm_hdr->Digest;
memcpy(lDigest,pDigest,0x10);
// Zero signature and digest
memset(pSignature,0,0x20);
memset(pDigest,0,0x10);
// MD5 time
MD5Init(MD5Buffer);
MD5Update(MD5Buffer,fm_hdr,size);
MD5Final(md5_struct_out,MD5Buffer);
// Restore signature and digest
memcpy(pSignature,lSignature,0x20);
memcpy(pDigest,lDigest,0x10);
return 0;
}
Randseq validation
TODOTo calculate the Randseq perform the following:
- Zero out the Signature, Digest and Ranseq elements of the header
- Perform the
nsdigest
on the same data as the MD5 digest above.nsdigest
does the following:- For each 0x20 sized block in the firmware header and module header table:
- For each byte:
- Get the value and add a value that increments by 0x13 each loop.
- Place this result of the addition into a temp buffer array
- Pass the 32 byte arrary into MD5Update
- For each byte:
- Repeat until all bytes have been processed
- For each 0x20 sized block in the firmware header and module header table: