Difference between revisions of "IMG1"

From freemyipod.org
Jump to: navigation, search
(Created page with "== Introduction == IMG1 is a pseudonym for the image format used by early iOS devices and all S5L-based iPods. It is sometimes called the '8900' image, which is how it was c...")
 
(IMG1 v2.0)
Line 12: Line 12:
  
 
   struct IMG1_20 {
 
   struct IMG1_20 {
     char magic[4];         // SoC digits, eg. `8720`.
+
     char magic[4];         // SoC digits, eg. `8720`.
 
     char version[3];        // `2.0`.
 
     char version[3];        // `2.0`.
 
     byte format;            // Encryption/signature format: 3 for encrypted/signed. Same as IMG1 1.0.
 
     byte format;            // Encryption/signature format: 3 for encrypted/signed. Same as IMG1 1.0.

Revision as of 00:23, 10 December 2021

Introduction

IMG1 is a pseudonym for the image format used by early iOS devices and all S5L-based iPods.

It is sometimes called the '8900' image, which is how it was called on the original iPhone / S5L8900. It is also sometimes called the 'DFU image' format (because it's used in DFU mode to load WTF).

The iPhone Wiki has some basic information about the format. However, that only describes the '1.0' version of IMG1. The lineage of IMG1 has continued in iPods long after iOS-based devices stopped its use, with IMG2/IMG3 never making it to the newer non-Touch iPods.

IMG1 v2.0

This is what we call the newer iteration of S5L images, as used in the Nano4G and up (and maybe Nano3G? to check). Here is the structure definition:

 struct IMG1_20 {
   char magic[4];          // SoC digits, eg. `8720`.
   char version[3];        // `2.0`.
   byte format;            // Encryption/signature format: 3 for encrypted/signed. Same as IMG1 1.0.
   uint entrypoint;        // Offset to jump to within body (after header).
   uint bodyLen;           // Size of the image body, ie. the data loaded into memory, before the
                           // signature/certificates start, after the header.
   uint dataLen;           // Size of everything that's not the header (body + signature + certificates).
   uint footerCertOffset;  // Offset of certificate start (after header).
   uint footerCertLen;     // Size of certificate bundle.
   byte salt[32];          // Random data.
   ushort unk1;
   ushort unk2;            // Security epoch?
   byte headerSign[16];    // AES-encrypted SHA1 signature of everything up to headerSign.
   byte headerLeftover[4]; // Last four bytes of unencrypted SHA1, usually leftover in images, but not
                           // checked by firmware. Curiosity.
 }

The header is padded to either 0x600 (S5L8720) or 0x400 (S5L8740) bytes. The different sections are a bit tricky to reason about, there's an attempted overview:

 0:     Header (0x40 + 0x14 bytes, first 0x40 signed into last 0x14 bytes)
 0x54:  Padding until $header_size (magic dependent, 0x600 in this example) 
 0x600: Body, bodyLen bytes.
 ...
 0x600 + bodyLen: body signature
 0x680 + bodyLen (also 0x600+footerCertLen): certificate bundle
 0x680 + bodyLen + footerCertLen: end of file.

The body signature is always 0x80 bytes long, and its length is not counted into bodyLen or footerCertLen.

A few assertions should hold:

  1. File size == $header_size + bodyLen + footerCertLen + 0x80
  2. dataLen = bodyLen + 0x80 + footerCertLen

Differences with v1.0

There don't seem to be any practical differences, other than the different header padding sizes per device. Our field names are different from The iPhone Wiki, but the meaning seems of the fields seems to be the same?

Leftover SHA

It seems like whatever generates IMG1 images does so in the following pseudocode:

 sha1(src=data, srcLen=0x40, dst=data+0x40)
 aes(src=data+0x40, size=0x10)
 // data is ready, ship it!

Because after the 0x10 bytes of the AES-encrypted SHA1 signature, there are 4 bytes of unencrypted SHA1 (because a SHA1 digest is 0x14 bytes, while an AES128 block is 0x10 bytes). This means that you can check the header signature yourself (or, well, 32 bits of the signature) by performing the following:

 sha1s(data[0:0x40]).digest()[-4:] == data[0x50:0x54]

This has likely zero security implications, but is nonetheless a fascinating curiosity.