Please remember to add a category to the bottom of each page that you create.
See categories help for further details, but most will probably be [[Category:HTC ModelName]].

Extract initramfs from zImage

From XDA-Developers
Jump to: navigation, search

initramfs provides the rootfs when booting a Linux kernel on a mobile device.
Below script may be helpful if you are interested in extracting such an image from a zImage kernel file.
Tested on Ubuntu for Samsung Galaxy S firmware. Other zImages may have different archiving options - would require a modification to the script...

   # find start of gziped kernel object in the zImage file:
   pos=`grep -P -a -b -m 1 --only-matching $'\x1F\x8B\x08' $zImage | cut -f 1 -d :`
   echo "-I- Extracting kernel image from $zImage (start = $pos)"
   # the cpio archive might be gzipped too, so two gunzips could be needed:
   dd if=$zImage bs=1 skip=$pos | gunzip > /tmp/kernel.img
   pos=`grep -P -a -b -m 1 --only-matching $'\x1F\x8B\x08' /tmp/kernel.img | cut -f 1 -d :`
   # find start and end of the "cpio" initramfs image inside the kernel object:
   # ASCII cpio header starts with '070701'
   # The end of the cpio archive is marked with an empty file named TRAILER!!!
   if [ ! $pos = "" ]; then
       echo "-I- Extracting compressed cpio image from kernel image (start = $pos)"
       dd if=/tmp/kernel.img bs=1 skip=$pos | gunzip > /tmp/cpio.img
       start=`grep -a -b -m 1 --only-matching '070701' /tmp/cpio.img | head -1 | cut -f 1 -d :`
       end=`grep -a -b -m 1 --only-matching 'TRAILER!!!' /tmp/cpio.img | head -1 | cut -f 1 -d :`
       echo "-I- Already uncompressed cpio.img, not decompressing"
       start=`grep -a -b -m 1 --only-matching '070701' /tmp/kernel.img | head -1 | cut -f 1 -d :`
       end=`grep -a -b -m 1 --only-matching 'TRAILER!!!' /tmp/kernel.img | head -1 | cut -f 1 -d :`
   # 11 bytes = length of TRAILER!!! zero terminated string, fixes premature end of file warning in CPIO
   end=$((end + 11))
   count=$((end - start))
   if (($count < 0)); then
       echo "-E- Couldn't match start/end of the initramfs image."
   echo "-I- Extracting initramfs image from $inputfile (start = $start, end = $end)"
   dd if=$inputfile bs=1 skip=$start count=$count > initramfs.cpio

After extracting the initramfs, it can be unpacked into the local directory with

 cpio -v -i --no-absolute-filenames < <path-to-initramfs.img>

Note that, to the best of my knowledge, it is impossible (or maybe unreasonably difficult) to re-pack a modified initramfs image into existing zImage - the data is linked into the kernel object by the compiler with corresponding pointers to start/end initialized in the code. So the easiest way of modifying initramfs also requires recompiling the whole kernel from source.
Here are instructions on how to do it (assuming the new initramfs size is less or equal to the old one):

for lzma compressed initramfs, this seems to work reasonably well for the second grep:

grep -P -a -b -m 1 --only-matching '\x{5D}\x{00}\x..\x{FF}\x{FF}\x{FF}\x{FF}\x{FF}\x{FF}' /tmp/kernel.img 

The simple magic number alone isn't enough but a couple of numbers after it are variable (describe compression options).. the FF's seem pretty robust, but just from looking at a few examples, not from studying lzma specs, so no promises. Of course you'd need to change the decompression command too inside the conditional.

For initramfs's modified by z4build (z4mod) some initramfs files are brute force stuck in a tar at the end of the zImage and unpacked early in the init script. To unpack the whole thing you need to do something like (basically taken from the z4build init wrapper script itself):

   offset="`dd if=$zImagefile bs=4 skip=11 count=1 2>/dev/null | od -l|tr -s " "|head -n 1|cut -f2 -d " "`"
   offset=`echo $offset | cut -f 2 -d" "`
   dd if=$zImagefile bs=$offset skip=1 | tar xz $zImagefile

If you get problems with the above script also have a look at this improved version: