A tiny image 3 parser

Recently I wanted to fix a longstanding issue with the n1ghtshade jailbreak. Specifically, the fact that image 3 tags are found by what amounts to running memmem over the whole file.

Image 3 tags

The image 3 file format used by apple for most of the 32 bit handhelds they produced was very simple. It starts with a small 0x14 byte header:


struct image3_header {
	uint32_t img_magic; // 'Img3'
	uint32_t img_len; // The length of the image including the header, used in the NOR to allow the offset of the next image to be easily found
	uint32_t buf_len; // The length of the image excluding the header 
	uint32_t signed_len; // The length of the signed portion of the image (that is everything before the SHSH and CERT tags)
	uint32_t img_type; // The type of the image for instance a kernelcache ('krnl') or an iBEC ('ibec')
	uint8_t  img_data[]; // The beginning of the actual image
};

The actual image is made up of image tags:


struct image3_tag {
	uint32_t tag_type; // The type of the tag, most notably 'DATA' which contains the data of the image and 'KBAG' which contains the encryption keys for the data 
	uint32_t next_tag_distance; // The length to the next tag from this one
	uint32_t data_len; // The length of the data of the tag
	uint8_t  tag_data[]; // The data of the tag
};

The whole structure can then be disected by getting the length of the image and going through the image, reading the tag types until you find the one you need.

Parsing Image 3

A very small, ARMv7 assembler tag finder can be written as such

img3_find_tag: @ (void* img3, size_t len, uint32_t tag)
	push	{r4, lr}
	add r0, #0x14 @ Move past the image 3 header to the first tag
	sub r1, #0x14 @ Reduce the image length accordingly
_img3_find_tag_loop:
	ldr r4, [r0] @ Load the tag type
	cmp	r4,	r2 @ Compare it with the target type
	beq _img3_find_ret @ If the found the type return it

_img3_find_tag_loop_continue:
	ldr r4, [r0, #0x4] @ Get the skip distance to the next tag
	add r0, r4 @ Add the skip distance to the image so it sits at the next tag
	sub	r1, r4 @ Reduce the image length accordingly
	cmp r1, #0 @ If the image length == 0
	bgt	_img3_find_tag_loop @ We check the next tag

	mov	r0,	#0	@ not found

_img3_find_ret:
	pop	{r4, pc}

This simple function enables us to parse an image 3 file in order to find the tag we require.