%TOC% The sb format is used by most STMP chips to load the firmware. The format is different for different generations of chips. This page describes the sb format used by STMP 36xx and 37xx. It is used for example by Fuze+ (SansaFuzePlus). One can produce such a file using the elftosb2 program which can be found [[http://lyre.svn.sourceforge.net/viewvc/lyre/propendous-imx233_board/firmware/bootstream-1.0/elftosb2/elftosb2 at Lyre project svn]]. The Rockbox svn also contains an open source implementation of elftosb2 (see /utils/sbtools/). The sbtoelf utility which can be found in the Rockbox svn (next to elftosb) is able to decrypt and extract data from a .sb file, and can repack the code in several ELF files. Format of the sb file. Everything is coded as little endian, even strings, except the "STMP" file magic. Update: as of june or july, Freescale put the source code of their elftosb2 tool on their website along with some documentation of the file format. It can be found in the download section of the imx233. ---++ Global file format The .sb file consists of several blocks as show below | *block* | *description* | | file header | basic file header containing version info and sizes of the rest of the file | | chunk headers | this is a list of actual data blocks in the file | | encryption data | not much known about this | | chunk bodies | the actual data, aligned to blocks of 16 bytes | | file signature | encrypted SHA-1 sum of the whole encrypted file, padded with random data | ---+++ File header | *offset* | *length* | *example* | *meaning* | | 0x00 | 0x14 | - | SHA-1 sum of the rest of the file header (from 0x14-0x5F) | | 0x14 | 4 | "STMP" | file magic | | 0x18 | 2 | 01 01 | Major and minor versions of the format | | 0x1A | 2 | 00 01 | a set of flags | | 0x1C | 4 | - | total length of file in 16-byte units | | 0x20 | 4 | 0C 00 00 00 | the elftosb2 program sets this to field (0x2A) + 2 x nr_keys = 6 + nr_chunks + 2 x nr_keys; probably the total size of headers (header+chunks+encryption data). Apparently this is the first boot tag block | | 0x24 | 4 | 00 00 00 00 | First bootable section id (always 0 ?) | | 0x28 | 2 | 01 00 | number of encryption keys | | 0x2A | 2 | 0A 00 | the elftosb2 program sets this to field (0x2C) + field (0x30) x field (0x2E) = 6+nr_chunks all the time; probably the offset to encryption data | | 0x2C | 2 | 06 00 | the elftosb2 program hardwires this field to 6 | | 0x2E | 2 | 04 00 | number of following chunk headers | | 0x30 | 2 | 01 00 | size of a chunk header in units of 16 bytes | | 0x32 | 6 | - | the elftosb2 program puts a random number here, however the fuze+ file has "sgtl" (= short for sigmatel?) at 0x34) | | 0x38 | 8 | - | creation time in microseconds since 2000/1/1 00:00:00 | | 0x40 | 4 | - | product major version | | 0x44 | 4 | - | product minor version | | 0x48 | 4 | - | product sub version | | 0x4C | 4 | - | component major version | | 0x50 | 4 | - | component minor version | | 0x54 | 4 | - | component sub version | | 0x58 | 2 | 00 00 | drive tag | | 0x5A | 6 | - | the elftosb2 program puts a random number here | The set of flags is as follows: bit 0 is display progress, bit 1 is verbose progress. ---+++ Chunk header Chunk headers start at offset 0x60. Each chunk header is 16 bytes long and has the following structure. There are two types of chunks: boot sections and data sections. | *offset* | *length* | *example* | *description* | | 0x0 | 4 | "host" | chunk name | | 0x4 | 4 | - | chunk offset in file in 16-byte units | | 0x8 | 4 | - | chunk size in 16-byte units | | 0xC | 4 | 01 00 00 00 | flags: bit 0 is bootable, bit 1 is cleartext (not encrypted) | See section about chunk data for more information about the content. ---+++ Encryption data The elftosb2 program uses AES-128 in CBC mode. Internally, all important data is encrypted using a random key that will denote PK. This random key PK is written in an encrypted form in the sb file using the keys specified to the elftosb2 program. At this place of the file, the elftosb2 program outputs one 32 bytes value per key: | *offset* | *length* | *example* | *description* | | 0x0 | 16 | - | this is the CBC-MAC of the header plus all the chunk headers (using the ith key if this is the ith key block, and using a 0 as IV) | | 0x10 | 16 | - | this is PK encrypted with the ith key using the first 16-bytes of the sb header as IV (ie part of SHA-1 signature) | Notice that if there are several keys, it is possible to cross-check PK with the several keys. ---+++ Final signature The final signature is a 32-bytes block. When decrypted using PK, it consists of the SHA-1 of the whole file (ie the encrypted file) except the last 32-bytes (of course); since the SHA-1 is 20-bytes long, it is padded with random data. | *offset* | *length* | *example* | *description* | | 0x0 | 20 | - | SHA-1 of the whole file except the last 32 bytes | | 0x14 | 12 | - | random data | ---++ Chunk bodies In the fuze+ firmware file, the following chunks were found: * a chunk consisting of just NULL characters * "host", encrypted * "play", encrypted * "rsrc", plain It is suspected that only boot sections are encrypted. A section is encrypted in AES-128 CBC mode using the PK key (see encryption data section) and the first 16-bytes of the sb header as IV (ie part of SHA-1 signature). The boot sections have a special format (when decrypted) to explain where load the instructions, which part of memory to fill and how to call functions. The format of the data sections has not been investigated. ---++ Boot section format A boot section consist of a list of instruction. Each instruction starts with a 4-byte header with indicates what type of instruction it is. All values are in little-endian. All instruction are of size 16-bytes even, unused bytes are set to zero. | *offset* | *length* | *example* | *description* | | 0x0 | 1 | - | checksum (see explaination below) | | 0x1 | 1 | 0x06 | Opcode | | 0x2 | 2 | - | Always zero except for tag command | The checksum is computed this way. This is 90 plus the sum of all bytes of the instruction except the first one (to avoid circular reference), modulo 2^8=256. Here is an example: | *offset* | *length* | *example* | *description* | | 0x0 | 1 | 0x4F | checksum | | 0x1 | 1 | 0x02 | Opcode (Load) | | 0x2 | 2 | 0x0000 | Always zero | | 0x4 | 4 | 0x08048134 | Loading address | | 0x8 | 4 | 0x00000013 | Length of the data (in bytes) | | 0xC | 4 | 0x099b99e2 | CRC of the data | We check that 0x4F = 90+(0x2+0x00+0x00)+(0x08+0x04+0x81+0x34)+(0x00+0x00+0x00+0x13)+(0x09+0x9b+0x99+0xe2) ---+++ Nop instruction The nop instruction terminates the boot section (it seems), it only consists of the header with opcode 0. | *offset* | *length* | *example* | *description* | | 0x0 | 1 | - | checksum | | 0x1 | 1 | 0x00 | Opcode | | 0x2 | 2 | 0x0000 | Always zero | ---+++ Tag instruction The tag instruction has the opcode 1. It is probably used by the bootloader instead of chunk table. It precedes each section and contains it identifier, length and flags. | *offset* | *length* | *example* | *description* | | 0x0 | 1 | - | checksum | | 0x1 | 1 | 0x01 | Opcode | | 0x2 | 2 | - | Unknown | | 0x4 | 4 | 0 | Section identifier | | 0x8 | 4 | 0 | Section length | | 0xC | 4 | 0 | Section flags | ---+++ Load instruction The load instruction has the opcode 2 and is of variable size. It starts with a 16-byte header followed by some data. The header indicates the size of the data, the address at which to load it and include a CRC checksum. | *offset* | *length* | *example* | *description* | | 0x0 | 1 | - | checksum | | 0x1 | 1 | 0x02 | Opcode | | 0x2 | 2 | 0x0000 | Always zero | | 0x4 | 4 | - | Loading address | | 0x8 | 4 | - | Length of the data (in bytes) | | 0xC | 4 | - | CRC of the data (see explaination below) | The data is padded so next instruction will be aligned on a 16-byte boundary. The padding data is random and IS TAKEN INTO ACCOUNT in the CRC. The CRC is a classical CRC32 algorithm, the tables used can be found in the SVN. ---+++ Fill instruction The fill instruction has the opcode 3 and is of fixed size (16-bytes). The instruction indicates the size of memory range to fill, the starting address and the pattern. | *offset* | *length* | *example* | *description* | | 0x0 | 1 | - | checksum | | 0x1 | 1 | 0x03 | Opcode | | 0x2 | 2 | 0x0000 | Always zero | | 0x4 | 4 | - | Filling address | | 0x8 | 4 | - | Length of the area to fill (in bytes ?) | | 0xC | 4 | - | Pattern | ---+++ Jump instruction The jump instruction is the same as the call instruction (except for the semantics of course) and has opcode 4. ---+++ Call instruction The call instruction has opcode and has a calling address as well as a argument for the function. | *offset* | *length* | *example* | *description* | | 0x0 | 1 | - | checksum | | 0x1 | 1 | 0x05 | Opcode | | 0x2 | 2 | 0x0000 | Always zero | | 0x4 | 4 | - | Calling address | | 0x8 | 4 | 0x0000 | Always zero | | 0xC | 4 | - | Argument | When called by the ROM and *only in the case of a call*, the entry point function takes two argument: the first one (in r0) is the one provided by the command and the second one (in r1) is a pointer to a 32-bit value which will hold the identifier of the current section. The function can return several values: | *value* | *meaning* | | <0 | error | | 0 | success, continue section | | 1 | switch to the section written at [r1] | | 2 | restart bootloader and give [r1] as argument to the driver | | >2 | ignored, same as success | ---+++ Mode instruction The mode instruction has opcode 6 . It is used to change the booting mode. The boot mode value matches the one in the datasheet. That one, the device can start booting from, say, i2c and then the i2c uses a mode command to boot from another device. | *offset* | *length* | *example* | *description* | | 0x0 | 1 | - | checksum | | 0x1 | 1 | 0x06 | Opcode | | 0x2 | 2 | 0x0000 | Always zero | | 0x4 | 4 | 0x0000 | Always zero | | 0x8 | 4 | 0x0000 | Always zero | | 0xC | 4 | 0xa | Boot mode | ---++ Encryption notes See [[http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation][Block ciphers on wikipedia]] for more information on CBC mode. See [[http://en.wikipedia.org/wiki/CBC-MAC][CBC-MAC on wikipedia]] for more information on CBC-MAC. When nothing is said, it means the IV (Initialisation Vector) is taken to be 0. ---++ Canonical sections Firmware produced using the Freescale (or Sigmatel SDK) will usually have a number of canonical sections, it is useful to know there meaning. ---+++ Boot section The boot section comes first. It usually consists in several chunks which can be split in two groups: * initialization chunks: these are the first one and initialize the dram for example * update chunk: the last chunk is usually a full features operating system which handles the updading process ---+++ Play and Host sections The play and host sections usually come after and handle the two most important features of the firmware: * usb mode: this is handled by the host section * media mode: this is handled by the play section The host and play section might or might not contain several chunks. They usually come as one big piece except perhaps for the first chunks which display a logo. This makes it easier to reverse engineer the lcd code since displaying a logo requires minimal code. ---+++ Virtual Memory sections Some firmware use virtual memory sections which comes as data sections in the sb file. They have different possible names but always follow the same scheme: * one "vm2" file ("pvm2" for example) which is believed to always contain some code already present in the corresponding code section (pvm2 <=> play) * one "vmi" file ("pvmi" for example) which contains additional code The virtual memory section might be loaded at any address but address 0x100000 has been reported in at least one firmware. Any section code might have virtual memory sections. For example the host section might come with two additional sections named "hvmi" and "hvm2". -- Main.AmauryPouly - 26 Nov 2010
r18 - 30 Jan 2012 - 14:34:34 -
Copyright © by the contributing authors.