Tools selection
The method I present here relies on CyanogenMod's Android source code.
While Google's AOSP only provides 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:
All these tools (and others you may find with any search engine) should work the same way, but some may work better than others 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 of 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:
git clone https://github.com/CyanogenMod/android_system_core.git
cd android_system_core/mkbootimg/
Once in the right directory, compile and install:
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 from 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.img
's 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 usual 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:
adb start-server
Then switch ADB in root mode:
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):
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:
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 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 RAM disk 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 object 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:
mkdir ./ramdisk
cd ./ramdisk/
gzip -dc ../boot.img-ramdisk.gz | cpio -imd
For an LZ4 compressed RAM disk, 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: unnecessarily 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:
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 an LZ4 archive:
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.
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 others expect to receive the content of the file as value. See an example of resulting command below:
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:
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):
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:
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 voluntarily 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).