WebOS
Unpacking and repacking U-Boot images part 2
2Scripts 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
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
5Background:
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: