Firmware format
The firmware file is made up of the following components, each located in the firmware file after one another:
- Firmware header
- Module header table
- Module data
This can be extracted using the following Kaitai Struct definition:
Kaitai Struct definition
meta:
id: spa504g
endian: be
license: Butlersaurus
title: SPA504G firmware
bit-endian: be
seq:
- id: header
type: header
- id: modules
type: module
repeat: expr
repeat-expr: header.module_count
types:
header:
seq:
- id: magic
contents: SkOsMo5 fIrMwArE
- id: signature
size: 32
- id: digest
size: 16
- id: random_sequence
size: 16
- id: header_length
type: u4
- id: module_header_length
type: u4
- id: file_length
type: u4
- id: version
type: str
encoding: utf8
size: 32
- id: module_count
type: u4
- id: padding
size: header_length - 128
module:
seq:
- id: module_sequence_number
type: u2
- id: module_compressed_flag
type: u2
- id: module_length
type: u4
- id: module_offset
type: u4
- id: module_digest
size: 16
- id: padding
size: _parent.header.module_header_length - 28
instances:
body:
pos: module_offset
size: module_length
process: zlib
Firmware header
The firmware header is located at the very beginning of the file. Its length is defined by the 32 bit unsigned integer (big endian) located at offset 0x0050
. For example: 0x00000080
(128 bytes).
The format for the firmware header is described below:
Size (byte) | Type | Description | Example |
---|---|---|---|
16 | Byte array | Identifier | SkOsMo5 fIrMwArE |
32 | Byte array | Signature | Not used |
16 | Byte array | MD5 of firmware header + module headers | 90 2D F5 21 17 F8 1F E7 8B 4D 68 27 CC B1 87 A4 |
16 | Byte array | A random sequence | 8C 6C D4 ED 19 B2 74 E9 3E 49 AD EB F6 55 01 A3 |
4 | Unsigned BE Int | Length of the entire firmware header | 0x00000080 (128 bytes) |
4 | Unsigned BE Int | Length of each module header | 0x00000040 (64 bytes) |
4 | Unsigned BE Int | Length of entire file | 0x0043153D (4396349 bytes) |
32 | Byte array | Firmware version number | 7.6.2f |
4 | Unsigned BE Int | Number of modules present in the firmware | 0x0000004F (79 modules) |
Module header table
There are multiple modules in the firmware file. The number of modules is defined at offset 0x007C
in the firmware header. For example: 0x0000004F
(79 modules).
Each module has a module header, located in a module header table. These are of a length defined at offset 0x0054
in the firmware header. For example: 0x00000040
(64 bytes). Each module header is concatenated in order, directly after the firmware header (in this case, from offset 0x0080
).
The format for each module header is described below. The screenshot contains three of the 79 module headers present in this particular firmware (7.6.2f).
Size (byte) | Type | Description | Examples |
---|---|---|---|
2 | Unsigned BE Short | Sector ID | 0x00 (0) 0x01 (1) 0x03 (3) This was observed to increase from 0 to 127, skipping the following values:
|
2 | Unsigned BE Short | Compressed flag? | 0x00 |
4 | Unsigned BE Int | Length of module data | 0x0000778B (30,603 bytes)0x000067F0 (26,608 bytes)0x0000FE8D (65,165 bytes)... |
4 | Unsigned BE Int | Offset to module data from start of file | 0x00001440 (5,184 bytes)0x00008BCB (35,787 bytes)0x0000F3BB (62,395 bytes)... |
16 | Byte array | MD5 digest of the uncompressed module | 16 16 E6 D2 1E A2 87 C2 7A A1 DB 0B 37 8F DD FB 5B 2B 75 F3 8D 75 10 BF 5C 36 C0 84 A4 BC 22 24 87 A9 B1 9C 7D 05 12 01 5C 26 C4 AE 70 C8 1C 85 ... |
4 | Byte array | Unknown | |
32 | Padding | Zero padding |
Module data
The offset and size for each module's data is defined in the corresponding module header in the module header table.
For example, the first module in this particular firmware (7.6.2f) is located at offset 0x00001440
(5,184 bytes) from the beginning of the file, and is 0x0000778B
(30,603 bytes) long.
It looks like all of this data is zlib compressed with a 'windowBits' parameter of 15
, as determined by reversing the 'uncompress' method in the psbl.elf binary.
Using Python, these can be deflated trivially. A PoC deflation script is shown below:
Python deflate script
import zlib
compressed_data = open('spa50x-30x-...-module.bin', 'rb').read()
decompressed_data = zlib.decompress(compressed_data, 15) # windowBits of 15.
with open('spa50x-30x-...-module_deflated.bin', 'wb') as f:
f.write(decompressed_data)
No Comments