'Android/tweak' tag logo

How to unpack and edit Android boot img?

Tools selection

The method I present here relies on CyanogenMod’s Android source code.

While Google’s AOSP only provides to the tool to build the boot.img file, CyanogenMod also adds the unpackbootimg tool allowing you to unpack it. This tool does not seem specifically designed for CyanogenMod in any way, so most chances are that it will work for other ROMs as well.

There are however a relatively large number of alternatives to unpack the boot.img file which all work more-or-less the same.

Basically, such unpack tool will extract the content of the boot.img file and display a set of parameters you will have to pass to Google’s mkbootimg tool to build a file whose configuration (mainly kernel parameters and memory addresses) will match the original one.

Here are a few examples, I did not test them personally so cannot recommend any and I present them only for reference purposes:

  • A few are open source or script based:

    • The Android Kitchen was apparently meant to be some kind of Swiss army knife for Android developers. The original author officially stopped maintaining the project, a few other users forked it like javilonas or cmotc.
    • szym targets easiness by adding shell script wrappers to its toolset to make the complete unpacking and repacking process more straightforward.
    • osm0sis proposes a fork of CyanogenMod’s tools “forked and updated” according to the project’s own description. This project seems indeed still maintained which is not very common in this area, however the actual advantage over CyanogenMod’s original tools remains unclear to me.
  • Some are free closed source proprietary binaries:

    • Kuisma’s unmkbootimg appears pretty often in help forums and seem to do a good job in helping to handle odd cases, outputting “human readable” English recommendation when some modification are required in mkbootimg source code and displaying the exact command-line to use to rebuild the image.
    • Xiaolu’s mkbootimg_tools seems also mentioned quite often and allows to unpack and repack boot.img and the device tree file dt.img. Don’t be fooled by the fact it is hosted on GitHub: it is a closed source proprietary binary and only compiled binaries are available on their repository (actually I’m even wondering about the exact interest of using a source-code repository to store binary blobs, but different people, different minds…).
    • CNexus on the XDA forum provides an archive with a selection of tools.
    • This answer in Unix.SE recommends some tools from the now seemingly defunct Android Serial Port project. The file names however lead me to think these should be old prebuilt versions of CyanogenMod’s tools (the project being closed, there is neither documentation nor support).

All these tools (and others you may found with any search engine) should work the same way, but some may work better than other in handling some particular edge-case you may face with your own device. Most of them however, at least in the open source arena, do not seem regularly maintained, so the best bet in my opinion to have working, maintained and documented tools is to go with CyanogenMod’s ones.

Some manufacturers produce ROMs which are more or less far from AOSP standard (unusual addresses, headers, file format, etc.). If the standard procedure below does not work, maybe one these alternative software may do the trick. Otherwise you will have to check for issues specific to your device: some seem to need a specific procedure or even specific tools (confer this question related to MediaTek devices for instance).

Tools installation

Compiling CyanogenMod toolset for boot.img packing and unpacking is quite straightforward.

  • If you already have installed the complete Android source code tree (you can check my other answer to get more information about this), go in system/core/mkbootimg/ directory (as a reminder, Google’s AOSP source code only provide the tool to build the boot.img file, they do not provide any unpacking tool),

  • If you haven’t and don’t need this for any other purpose, an easier and quicker solution is to only clone CyanogenMod’s android_system_core repository:

    1
    2
    git clone https://github.com/CyanogenMod/android_system_core.git
    cd android_system_core/mkbootimg/
    

Once in the right directory, compile and install:

1
2
3
gcc -o ./mkbootimg -I ../include ../libmincrypt/*.c ./mkbootimg.c
gcc -o ./unpackbootimg -I ../include ../libmincrypt/*.c ./unpackbootimg.c
sudo cp ./mkbootimg ./unpackbootimg /usr/bin/

Note that Google is replacing the C mkbootimg with a Python version, so in future versions no compilation may be needed anymore for this command.

You will also need to install Android tools on your computer to let it communicate with your phone. You will need adb (Android Debug Bridge, a shell utility allowing to communicate with Android’s debug subsystem), adbd (the related daemon) and fastboot (a shell utility allowing to communicate with your phone’s bootloader system).

Your favorite Linux distribution may provide them in a single or separate packages, but usually they are always called “android-tools”:

  • Debian / Ubuntu: sudo apt-get install android-tools-{adb,adbd,fastboot}
  • Fedora / CentOS: sudo yum install android-tools
  • OpenSuse: sudo zypper install android-tools

Fetch the boot.img file

Extract the boot.img either for ROM .zip file or directly from the device:

  • From the stock ROM .zip file: some applications like SuperSU may modify the boot.img directly on the device, replacing it with the stock one would break such applications.

  • Directly from the device: some people report read issue leading to corrupted boot.img. IMO these issues are most likely linked to the use of poor USB cables or USB hub and can be simply avoided by using good quality cables connecting directly the phone to the computer. You also need the ability to run ADB in root mode (depending on the ROM used this may be trivial or not).

The first method is very obvious: extract the .zip file with any ZIP software, the boot.img file should be right there at the root of the archive.

For the second method, you will first have to determine the (sadly device-specific) path to the storage device where boot.imgs content can be retrieved.

I know two methods for this:

  • ls /dev/block/platform/*/by-name/ (where * covers yet another device-specific folder name, chances are it is the only directory below platform/), the exact name to search is also platform dependent but makes usually sense (some examples: boot, LNX (acronym for “Linux”)). The files in this directory are actually symbolic links and some people bother to manually go to the target, but I recommend sticking with the higher level name based path which, while longer, remains less error prone. So you will end-up with a path like /dev/block/platform/sdhci-tegra.3/by-name/LNX.

  • On some (older?) devices, the right device could be found by investigating the output of cat /proc/mtd. If you see the device mtd2 associated to the "boot" label, then you will use the path /dev/mtd2.

Now:

  • From the phone’s developer menu:
    • Enable debugging on your phone,
    • Allow root access to ADB (this step applies to phones running CynogenMod, other devices may require some potentially more complex procedure),
  • Connect it to your computer (and from there to the VM guest if you are running Android tools from within a virtual machine).

If this is not already done, I recommend to manually start the ADB server on the computer’s side, this will allow you to directly validate the RSA key on device’s side without affecting the behavior of the following ADB commands:

1
adb start-server

Then switch ADB in root mode:

1
adb root

Finally you should be able to directly extract the boot.img file from the device using such command (the source and destination path and names are given as examples, adapt them to your needs and preferences):

1
adb pull /dev/block/platform/sdhci-tegra.3/by-name/LNX ./boot.img

The command will copy the whole partition, both used and free space, so don’t be surprised that the resulting boot.img file will be larger than the original boot.img file coming with the stock ROM .zip file, the content itself remains similar.

Once the transfer is finished, disconnect the phone and don’t forget to disable both debugging and root access from the developer menu.

Unpack the original boot.img file

Unpack the boot.img file itself using the command compiled previously:

1
unpackbootimg -i ./boot.img

This will output several information essential to let you rebuild a new boot.img with the correct structure with respect to the the stock boot.img. However, don’t rush on your notepad since CyanogenMod’s upackbootimg also saves the very same information in several files we will use later on.

This command generates several files with particular suffixes added to the input file’s name:

  • *-second: This is the second-stage bootloader, optional and rarely used on end-user phones. If this file is empty (the most common case) then the phone’s bootloader will directly call the Linux kernel.
  • *-zImage: This is the Linux kernel.
  • *-ramdisk.gz or *-ramdisk.lz4: the ramdisk used to populate the device’s root directory. The extension differs depending on the compression algorithm used.
  • *-dt: The device tree, populating /dev.
  • The rest are small files each storing one of the values displayed in unpackbootimg output. These values define the command-line parameter to pass to the Linux kernel and the addresses where the bootloader will have to load each objects at boot time.

Most often, one unpacks the boot.img to be able to edit the content of the phone’s root directory. As seen above, this content is stored in the *-ramdisk.gz or *-ramdisk.lz4 file and it can be extracted using the commands below:

1
2
3
mkdir ./ramdisk
cd ./ramdisk/
gzip -dc ../boot.img-ramdisk.gz | cpio -imd

For a LZ4 compressed RAM disks, replace the last step with lz4 -d ../boot.img-ramdisk.lz4 | cpio -imd.

You are now free to do the modification you want before proceeding. However, it could be worth to follow the full unpack - repack - boot procedure once without changing anything to ensure that your tools are working as expected. Otherwise, in case of an issue, you will not be certain if the cause is your modification or some incompatibility (see my remarks at the beginning about some manufacturers requiring non-standard procedures or tools).

Rebuild to get the new new-boot.img file

CyanogenMod ROM building process relies on an internal tool, mkbootfs, to produce the boot.img file (this happens in build/tools/releasetools/common.py). However, the steps to build this tool seems uselessly complex to me, whereas using the system-provided cpio seems to work just as fine. The main difference between the two, as per my understanding after a (very) quick check in mkbootfs souce code, seems to be that the latter applies some sanity measures by not including dotted files and the /root directory in the resulting archive while the cpio-based procedure below will just blindly put the whole selected directory tree in the archive.

Conclusion: unnecessary complex to compile with very few advantages, so let’s stick on system provided tools!

Begin by creating the new RAM disk, from the ramdisk directory created above type:

1
find . ! -name . | LC_ALL=C sort | cpio -o -H newc -R root:root | gzip > ../new-boot.img-ramdisk.gz

Or, if you need to generate a LZ4 archive:

1
find . ! -name . | LC_ALL=C sort | cpio -o -H newc -R root:root | lz4 > ../new-boot.img-ramdisk.lz4

The goal here is to create a new RAM disk file with properties as close as possible to the original one (for instance setting the owner seems often missing in the procedures shared in forums and blogs, however this was required on my device).

Go now in the parent directory to generate the new-boot.img file itself.

1
cd ..

As seen above, CyanogenMod’s unpackbootimg command generates a file matching each parameter expected by mkbootimg. Therefore, all you have to do is issue a mkbootimg -h to get a listing of all parameters, then set each one of them to the appropriate value using the matching file. Note that some parameters expect a file path while other expect to receive the content of the file as value. See an example of resulting command below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
mkbootimg --kernel ./boot.img-zImage \
--ramdisk ./new-boot.img-ramdisk.gz \
--second ./boot.img-second \
--cmdline "$(cat ./boot.img-cmdline)" \
--base "$(cat ./boot.img-base)" \
--pagesize "$(cat ./boot.img-pagesize)" \
--dt ./boot.img-dt \
--ramdisk_offset "$(cat ./boot.img-ramdisk_offset)"
--second_offset "$(cat ./boot.img-second_offset)" \
--tags_offset "$(cat ./boot.img-tags_offset)" \
--output ./new-boot.img

Only two parameters are not set here:

  • --board: As per my understanding this is just an informative field allowing to insert a model name in the resulting image.
  • --id: This one does not expect any value, it just prints out a unique identifier after the image has been built (combining a timestamp and a checksum).

Flash the new-boot.img file onto the device

  • Start the device in fastboot mode (aka bootloader mode, usually by holding power and volume up buttons).

  • Connect the USB cable

  • Check that the device is correctly detected:

    1
    sudo fastboot devices
    
  • Try to boot using the new ROM (without flashing it yet, so in case of issue you just have to restart the phone to get it back on trails, replace the ./new-boot.img file name with your own):

    1
    sudo fastboot boot ./new-boot.img
    
  • If the phone works successfully with the new boot image, then go back in fastboot mode and flash it permanently:

    1
    2
    sudo fastboot flash boot ./new-boot.img
    sudo fastboot reboot
    

Conclusion

This procedure may seem daunting at first, but once you get it you will see it is actually not.

The “daunting” aspect comes from the fact that there is not a single “Android system”: a lot of manufacturers and ROM providers make changes which can range from a subtle path difference to a completely non-standard environment.

What you have to do is determine your particular device’s posture, and then what are the few commands which are appropriate in your case. Once you get them, you can stick to them and even easily script them if you need them often.

I voluntary went into relatively low-level details sometimes because it will make you troubleshooting your issues more easily. Would you use some “easier” opaque utility to build and flash your new boot.img file and see that your device cannot start with it, it would be harder for you to determine which step went wrong. Here, at each step you will be able to compare the data you are manipulating with the data coming from the original boot.img file or the data as seen on the phone, or try for instance to rebuild the boot.img file with either the original or the newly generated RAM disk file to check if that makes any difference (this allows you to pinpoint if the issue comes from the boot.img or the RAM disk file generation procedure).


Article based on a StackExchange answer.


Popular tags see all

Website

Author

Follow