WebOS

Unpacking and repacking U-Boot images part 2

2

Scripts for working with uImage files

I’ve created a set of python scripts for working with U-Boot image files when phone-hacking.  The first script is similar to the U-Boot mkimage tool, only it adds the ability to extract data from existing images.

The next two scripts are particularly for WebOS and meta doctor.  These scripts are similar to diff and patch, but they are intended to distribute change instructions for ramdisk image files.  More than just a binary patch, these utilities introduce what I call a copy-patch.  A copy-patch contains instructions for merging one binary file into another without actually containing the contents of either file.  This allows the patch to be distributed without violating the IP rights of the owners of the binary files.

Downloads

ramdiskpatch.py

ramdiskdiff.py

uImage.py

ramdisk.copyPatch

Usage examples

Words often don’t explain as well as examples, so I’ll show an example of updating the TP and A6 firmware in a WebOS 2.1.2 image.  Note that the patch file does not contain the firmware, so I will have to supply the patch tool with the location of the files I intend to graft into the root file system ramdisk image.

 

uImage.py usage and listing image information:

# python uImage.py -h
Usage:
        uImage.py -l image
        uImage.py -c [options] image
        uImage.py -x image
        uImage.py -h

Options:
  -h, --help         show this help message and exit
  -l                 list image contents
  -c                 create new image
  -x                 extract image contents

  Creation Options:
    -c -A arch -O os -T type -C comp -a addr -e ep -n name -d
    data_file[:data_file...] image

    -A ARCHITECTURE  set architecture to 'arch'
    -O OSTYPE        set operating system to 'os'
    -T IMAGETYPE     set image type to 'type'
    -C COMPRESSION   set compression to 'comp'
    -a LOADADDR      iset load address to 'addr'
    -e ENTRYADDR     iset entry point to 'ep'
    -n IMAGENAME     set image name to 'name'
    -d FILESPEC      image data from 'datafile'
#
# python uImage.py -l nova-installer-image-broadway.uImage
Image name:     nova-installer-image-broadway-43
Created:        Wed Dec 31 16:00:00 1969
Image type:     ARM Linux Multi-File (uncompressed)
Data size:      12566392 Bytes
Load Address:   0x000000
Entry Point:    0x000000
Header CRC:     0xcf936b92 ... OK
Data CRC:       0x2defc1cd
Contents:
   Image 0: 2610004 bytes
   Image 1: 9956376 bytes

Multi-File Image 0: Header
--------------------------
Image name:     Linux-2.6.29-palm-shank
Created:        Wed Dec 31 16:00:00 1969
Image type:     ARM Linux Kernel (uncompressed)
Data size:      2609940 Bytes
Load Address:   0x208000
Entry Point:    0x208000
Header CRC:     0xfd901980 ... OK
Data CRC:       0xffbe63e6

Multi-File Image 1: Header
--------------------------
Image name:     ramdisk
Created:        Wed Dec 31 16:00:00 1969
Image type:     ARM Linux RAMDisk (uncompressed)
Data size:      9956312 Bytes
Load Address:   0x000000
Entry Point:    0x000000
Header CRC:     0xccd15a95 ... OK
Data CRC:       0xa6d2f9e9

 

Extracting images:

# python uImage.py -x nova-installer-image-broadway.uImage
Linux-2.6.29-palm-shank.uImage
Linux-2.6.29-palm-shank
ramdisk.uImage
ramdisk

Notice that there were 4 files extracted.  Let me explain:  The main uImage file is a multi-file type that contains two files:  A kernel uImage and a ramdisk uImage.  Each of these two files is also a uImage, so uImage.py extracted their contents as well.  We are left with 4 files:  the ones with .uImage extensions have uImage headers.  The other two are the raw data contents.

Now let’s verify that the ramdisk file is a gzipped EXT2 image and then apply the copy-patch:

# file ramdisk
ramdisk: gzip compressed data, was "nova-installer-image-broadway-4", from Unix, last modified: Fri Apr 15 12:01:11 2011, max compression

# zcat ramdisk > nova-installer-image-broadway-4

# file nova-installer-image-broadway-4
nova-installer-image-broadway-4: Linux rev 0.0 ext2 filesystem data

# python ramdiskpatch.py nova-installer-image-broadway-4 2.2.4/usr/lib/ipkg/info/a6-firmware.control 2.2.4/usr/lib/ipkg/info/a6-firmware.md5sums 2.2.4/usr/lib/ipkg/info/pma6updater.control 2.2.4/usr/lib/ipkg/info/pma6updater.md5sums 2.2.4/usr/lib/ipkg/info/pmtpupdater.control 2.2.4/usr/lib/ipkg/info/pmtpupdater.md5sums 2.2.4/usr/lib/ipkg/info/pmtpupdater.list 2.2.4/lib/firmware/a6_firmware.txt 2.2.4/lib/firmware/cy8ctma300e.fw 2.2.4/lib/firmware/cy8ctma300e.ver 2.2.4/usr/bin/PmTpUpdater 2.2.4/usr/bin/PmTpWhich 2.2.4/usr/bin/PmA6Updater 2.2.4/usr/bin/catnip < ramdisk.copyPatch
Patching-in "2.2.4/usr/lib/ipkg/info/a6-firmware.control" .Done.
Patching-in "2.2.4/usr/lib/ipkg/info/a6-firmware.md5sums" .Done.
Patching-in "2.2.4/usr/lib/ipkg/info/pma6updater.control" .Done.
Patching-in "2.2.4/usr/lib/ipkg/info/pma6updater.md5sums" .Done.
Patching-in "2.2.4/usr/lib/ipkg/info/pmtpupdater.control" .Done.
Patching-in "2.2.4/usr/lib/ipkg/info/pmtpupdater.md5sums" .Done.
Patching-in "2.2.4/usr/lib/ipkg/info/pmtpupdater.list" .Done.
Patching-in "2.2.4/lib/firmware/a6_firmware.txt" ..............................................Done.
Patching-in "2.2.4/lib/firmware/cy8ctma300e.fw" ....................................................Done.
Patching-in "2.2.4/lib/firmware/cy8ctma300e.ver" .Done.
Patching-in "2.2.4/usr/bin/PmTpUpdater" ......Done.
Patching-in "2.2.4/usr/bin/PmA6Updater" .............Done.
Patching-in "2.2.4/usr/bin/PmTpWhich" .Done.
Patching-in "2.2.4/usr/bin/catnip" .........................................................................................Done.

Performing binary patches to file system structure ..............................................................................................................................................................................................................................................................................................................................................................................................................................................................
Patch successful!  md5: 1cdb4f76aeacc0ba1760cc6496137e61

 

Now we’ll re-compress the ramdisk, and make it into a uImage:

# gzip nova-installer-image-broadway-4

# python uImage.py -c -A arm -T ramdisk -C none -n ramdisk -d nova-installer-image-broadway-4.gz ramdisk.uImage
Image name:     ramdisk
Created:        Sun Jun 24 04:01:21 2012
Image type:     ARM Linux RAMDisk (uncompressed)
Data size:      10051920 Bytes
Load Address:   0x000000
Entry Point:    0x000000
Header CRC:     0x1cf594e7 ... OK
Data CRC:       0xcf80cb73

Now we re-combine the extracted kernel uImage and the ramdisk uImage:

# python uImage.py -c -A arm -T multi -C none -n Linux-2.6.29-palm-shank -d Linux-2.6.29-palm-shank.uImage:ramdisk.uImage my-install-image.uImage
Image name:     Linux-2.6.29-palm-shank
Created:        Sun Jun 24 04:04:59 2012
Image type:     ARM Linux Multi-File (uncompressed)
Data size:      12662000 Bytes
Load Address:   0x000000
Entry Point:    0x000000
Header CRC:     0x71281fb4 ... OK
Data CRC:       0x9f1b4262
Contents:
   Image 0: 2610004 bytes
   Image 1: 10051984 bytes

Multi-File Image 0: Header
--------------------------
Image name:     Linux-2.6.29-palm-shank
Created:        Wed Dec 31 16:00:00 1969
Image type:     ARM Linux Kernel (uncompressed)
Data size:      2609940 Bytes
Load Address:   0x208000
Entry Point:    0x208000
Header CRC:     0xfd901980 ... OK
Data CRC:       0xffbe63e6

Multi-File Image 1: Header
--------------------------
Image name:     ramdisk
Created:        Sun Jun 24 04:01:21 2012
Image type:     ARM Linux RAMDisk (uncompressed)
Data size:      10051920 Bytes
Load Address:   0x000000
Entry Point:    0x000000
Header CRC:     0x1cf594e7 ... OK
Data CRC:       0xcf80cb73

And that’s it!

Unpacking and repacking U-Boot uImage files

5

Background:

u-boot uImage files have a 64-byte header defined in image.h as follows:

#define IH_MAGIC    0x27051956    /* Image Magic Number     */
#define IH_NMLEN    32            /* Image Name Length      */

typedef struct image_header {
    uint32_t    ih_magic;         /* Image Header Magic Number */
    uint32_t    ih_hcrc;          /* Image Header CRC Checksum */
    uint32_t    ih_time;          /* Image Creation Timestamp  */
    uint32_t    ih_size;          /* Image Data Size           */
    uint32_t    ih_load;          /* Data     Load  Address    */
    uint32_t    ih_ep;            /* Entry Point Address       */
    uint32_t    ih_dcrc;          /* Image Data CRC Checksum   */
    uint8_t     ih_os;            /* Operating System          */
    uint8_t     ih_arch;          /* CPU architecture          */
    uint8_t     ih_type;          /* Image Type                */
    uint8_t     ih_comp;          /* Compression Type          */
    uint8_t     ih_name[IH_NMLEN];    /* Image Name            */
} image_header_t;

According to the u-boot README in the section “More About U-Boot Image Types”:

“Multi-File Images” start with a list of image sizes, each
image size (in bytes) specified by an “uint32_t” in network
byte order. This list is terminated by an “(uint32_t)0”.
Immediately after the terminating 0 follow the images, one by
one, all aligned on “uint32_t” boundaries (size rounded up to
a multiple of 4 bytes).

 

Install uboot-mkimage from ports:

$ cd /usr/ports/devel/u-boot
$ make install clean

List image header:

$ mkimage -l nova-installer-image-broadway.uImage
Image Name:   nova-installer-image-broadway-43
Created:      Wed Dec 31 16:00:00 1969
Image Type:   ARM Linux Multi-File Image (uncompressed)
Data Size:    12566392 Bytes = 12271.87 kB = 11.98 MB
Load Address: 0x00000000
Entry Point:  0x00000000
Contents:
   Image 0:  2610004 Bytes = 2548 kB = 2 MB
   Image 1:  9956376 Bytes = 9723 kB = 9 MB

This image contains two files, so it is a multi-file image.  To extract the two child image files, we must first chop off or skip over the 64-byte header + 4-byte image 0 size + 4 byte image 1 size + 4 byte null terminator = first 76 bytes.  Next we’ll extract Image 0  (2610004 bytes long in this example) and image 1 (9956376 bytes long in this example).  Note that these two files are also uImage files!

Skip first 76 bytes and extract length of first image:

$ dd if=nova-installer-image-broadway.uImage of=kernel.uImage bs=1 skip=76 count=2610004
2610004+0 records in
2610004+0 records out
2610004 bytes transferred in 46.503855 secs (56124 bytes/sec)

Now I’ll skip 76 bytes + the size of the first image to get the second image:

$ dd if=nova-installer-image-broadway.uImage of=ramdisk.uImage bs=1 skip=2610080
9956376+0 records in
9956376+0 records out
9956376 bytes transferred in 168.836628 secs (58970 bytes/sec)

Let’s look at the kernel image header:

$ mkimage -l kernel.uImage
Image Name:   Linux-2.6.29-palm-shank
Created:      Wed Dec 31 16:00:00 1969
Image Type:   ARM Linux Kernel Image (uncompressed)
Data Size:    2609940 Bytes = 2548.77 kB = 2.49 MB
Load Address: 0x00208000
Entry Point:  0x00208000

And the ramdisk image header:

$ mkimage -l ramdisk.uImage
Image Name:   ramdisk
Created:      Wed Dec 31 16:00:00 1969
Image Type:   ARM Linux RAMDisk Image (uncompressed)
Data Size:    9956312 Bytes = 9722.96 kB = 9.50 MB
Load Address: 0x00000000
Entry Point:  0x00000000

Now let’s pluck off the 64-byte header from the ramdisk:

$ dd if=ramdisk.uImage of=ramdisk bs=64 skip=1
155567+1 records in
155567+1 records out
9956312 bytes transferred in 2.952196 secs (3372511 bytes/sec)

And examine it:

$ file ramdisk
ramdisk: gzip compressed data, was "nova-installer-image-broadway-4", from Unix, last modified: Fri Apr 15 12:01:11 2011, max compression

Now extract it:

 zcat ramdisk > nova-installer-image-broadway-4

And examine it again:

$ file nova-installer-image-broadway-4
nova-installer-image-broadway-4: Linux rev 0.0 ext2 filesystem data

So it’s an ext2 filesystem.  Let’s mount it under FreeBSD:

$ kldload ext2fs
$ mdconfig -a -t vnode -f nova-installer-image-broadway-4
md0
$ mount -t ext2fs /dev/md0 /mnt

Let’s add a file full of random garbage just for fun:

$ dd if=/dev/random of=/mnt/garbage bs=1M count=1
1+0 records in
1+0 records out
1048576 bytes transferred in 0.237938 secs (4406931 bytes/sec)
$ ls /mnt
bin        dev        garbage    lib        media      opt        sbin       tmp        var
boot       etc        home       md5sums.gz mnt        proc       sys        usr

Now it’s time to unmount the file and package everything back together:

$ umount /mnt
$ mdconfig -d -u 0

Don’t forget to gzip the ramdisk image…

$ gzip nova-installer-image-broadway-4

First create the ramdisk uImage:

$ mkimage -A arm -T ramdisk -C none -n ramdisk -d nova-installer-image-broadway-4.gz ramdisk.uImage
Image Name:   ramdisk
Created:      Wed Jun 20 23:34:59 2012
Image Type:   ARM Linux RAMDisk Image (uncompressed)
Data Size:    11036471 Bytes = 10777.80 kB = 10.53 MB
Load Address: 0x00000000
Entry Point:  0x00000000

Now combine the kernel image and ramdisk image into the final one:

$ mkimage -A arm -T multi -C none -n Linux-2.6.29-palm-shank -d kernel.uImage:ramdisk.uImage nova-installer-image-broadway.uImage
Image Name:   Linux-2.6.29-palm-shank
Created:      Wed Jun 20 23:40:05 2012
Image Type:   ARM Linux Multi-File Image (uncompressed)
Data Size:    13646551 Bytes = 13326.71 kB = 13.01 MB
Load Address: 0x00000000
Entry Point:  0x00000000
Contents:
   Image 0:  2610004 Bytes = 2548 kB = 2 MB
   Image 1: 11036535 Bytes = 10777 kB = 10 MB

Note how the overall image size and image 1 size are 1MB larger than the original from the beginning.

 

Now let’s test it with novacom.  Restart the phone and hold the volume-up key to enter bootie the bootloader.  now:

novacom boot mem:// < nova-installer-image-broadway.uImage

 

Wait a few seconds, then launch novaterm and verify that we booted to the new ramdisk with the 1M garbage file:

 

Go to Top