Firmware validation (WIP)
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
If we search for "Randseq" - as it seems custom - we come across a handy Github repo (https://github.com/BigNerd95/paytonImageEditor). This has a tonne of information and the findings from this can be found in the Appendix. 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 and modules(?)
NOTE: Calculation of size to process (?)
size = firmware_header?->FirmwareHeaderSize +
firmware_header?->NumberOfModules * firmware_header?->ModuleHeaderSize;
This can be seen in the following tidy Ghidra decompilation:
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
TODO
Appendix
Firmware Header
Size (byte) | Type | Name | Description |
---|---|---|---|
16 | Byte array | Magic | Eg: SkOsMo5 fIrMwArE |
32 | Byte array | Signature | Not used |
16 | Byte array | Digest | MD5 of Firmware Header + Modules Headers |
16 | Byte array | Randseq | Not so random |
4 | Unsigned BE Int | Firmware Header Size | Eg: 136 |
4 | Unsigned BE Int | Module Header Size | Eg: 128 |
4 | Unsigned BE Int | Firmware size | Entire firmware image size |
32 | Byte array | Version | Eg: 1.4.1 |
4 | Unsigned BE Int | Modules number | Number of modules present in this firmware image |
Comparing to our firmware header:
We can extract from this the Digest and Randseq
- Digest:
05B910ECA58C1B6275E78185F3FAC6FC
- Randseq:
68974CEF8CE0F0318587EC50083155C0
Alongside:
- Firmware header size:
0x80
- Module (?) header size:
0x40
- Entire firmware size:
0x43162c
(4.19 MB)
- Version:
7.6.2g
- Number of modules:
0x4F