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.