This article was originally published on the TextNow Engineering Blog.
To fully grasp the role the Android boot and recovery images play, we first need to understand the general boot-up process of an Android phone. While embedded software is no longer part of my day-to-day work at TextNow, I used to wear the embedded-engineer hat in one of my previous jobs and I dabble with embedded electronics in my spare time, so I am intimately familiar with modern microprocessor boot-up routines. I must caution you that this is a very technical post, but one that is hopefully useful to somebody looking for a reference material on how this all fits together, with links to where to delve more in-depth if required. This type of post would have been very useful when I was learning it :)
The Android Boot-Up Process
When power and reset is applied to the processor,it wakes up and needs to figure out what to do. In modern processors, there isusually a very small program baked-in to the processor whose function is toverify the next component’s integrity. This first component is sometimes referredto as the secure boot ROM, and its job is to ensure the nextcomponent it loads is trusted, verified software. This prevents unwantedtampering of the system software and, as such, increases the security of thedevice. The verification usually involves some sort of cryptographic cipherusing a public/private key scheme, with the private key usually kept a closelyguarded secret by the manufacturer. Note: usually the publickey is programmed in at assembly time by the phone manufacturer and itscontents can never be changed, thereby requiring that processor to always run asigned bootloader, with the private counterpart of the public key burned in atassembly time.
The next component loaded by the secure boot ROM is the what people commonly refer to as the bootloader. The bootloader, as I mentioned, is usually signed with the phone manufacturer’s private key, and it has a few jobs:
1. Load a boot image from non-volatile memory (usually NAND flash nowadays) into volatile memory (RAM) and run it.
2. Optionally, that image needs to becryptographically verified before being loaded (as is the case with more andmore phones these days which don’t even provide a way to unlock the bootloaderto run unsigned images).
3. Provide the ability to flash new boot andother images to the non-volatile memory for future upgrades and recovery fromfailure.
4. Display some sort of information on thescreen, and optionally provide an interface for the user to interact with. Thisis commonly known as fastboot mode on some Motorola, Google,and other manufacturer’s devices.
Android Boot Images
We now come to the final component of the boot-upprocess - loading a boot or recovery image into RAMand running it.
Technically speaking, there are no structuraldifferences between boot and recovery images. The only difference is whichkernel and which initial ramdisk is loaded, but they are packaged the same. Wewill come back to how these are packaged to make a neat and tidy boot image.
There are a few sub-steps involved in running theboot image:
1. Cryptographic verification
2. Loading of the (Linux) kernel at the requiredmemory address
3. Loading of the initial ramdisk at the requiredmemory address
4. Jump to the start address of the kernel
The cryptographic verification is usually manufacturer-specificand it is typically very hard to reverse-engineer this method, though notimpossible - it’s been done before for certain phones (forexample, older Kit-Kat era LG phones).
The kernel is the heart of the operating system,as it ticks its constant heartbeat of interrupts and events which keep a modernoperating system running. This magic has very meager origin as a simple chunkof bytes that was compiled in such a way to wake up and start running from aparticular address of memory. This address varies depending on the particularphone and memory layout. Once execution starts, the kernel will start loadingdrivers and setup more advanced facilities so that it can start running some ofits own programs.
In and of itself, the kernel wouldn’t know whatto do once it’s loaded. It would sit there and do nothing. But what the Linuxkernel expects is an initial ramdisk at a predefined memoryaddress. This ramdisk contains a very rudimentary and usually read-onlyfilesystem which contains the most basic root filesystem possible, and a fileunder /sbin/init which the kernel executes as the last thingit does as part of initialization. This
So now we come back to the difference between aboot and recovery image. While the kernel sometimes is the same as the bootimage, the recovery image ramdisk has a distinct sets of init scriptswhich don’t start the Java VM, but instead start a small C program called (didyou guess it?) recovery. Recovery displays a limited user interfacethat allows the user to start the system, factory reset, clear the cachepartition, etc. Some custom recoveries have their own program that offers evenmore functionality.
Packing an Android Boot Image
The Android boot images have a
The layout of an Android boot image
I should mention something of this second stage.To be completely honest, I do not know its raison d’être, theoriginal intent of it. I have seen it used in more recent times to hold thekernel’s device tree block (DTB, another long and deeply technical topic bestexplained elsewhere) by certain phone manufacturers. The older kernels had theDTB as part of the kernel image, now they’ve split it out into its own separatepiece to make it easier to build a more generic kernel that can be adapted to avariety of hardware configurations within the same architecture.
Moving swiftly along (and back!) to the bootimage header, this primarily tells the bootloader how to load the components ofthe boot image, and it looks something like this:
Android boot image header
As you can see, it has a few “book-keeping”fields about how large the other sub-components of the boot image are, whataddress to load them into, etc.
It also has a field called magic. Themagic field is simply a string of well-known characters to identify this as anAndroid boot image. In this case the string is “ANDROID!”, which fits neatlyinto 8 bytes.
Somebody familiar with the Linux kernel andboot-up process will recognize the kernel tags and command line argument fieldsas ways to pass more custom information into the kernel, such as where theconsole should be, and where to find that initial ramdisk from where to loadits init program.
Working With Android Boot Images
So, since we have custom recovery and boot imagesfor our Android phones (such as TWRP, CWM, etc.), obviously, someone had tocreate their own recovery image or modify an already existing boot image. Mostof the work was done using the already open-sourced Google Android code, butthere only was code there to create a boot image from its components. There wasno tool to unpack a boot image for modification. Now, you can use a tool like
Further Areas to Explore
There are some interesting aspects relating tothe initial ramdisk that apply to both the boot and recovery Android imagesthat this article won’t cover in detail due to its already burgeoning size, butthat are worth mentioning.
● The filesystem that initial ramdisks useis usually a gzipped CPIO archive
● You can pack and unpack the initialramdisks and modify them
● init scripts are written in their owndomain specific language (DSL) and you can read their
● Default SELinux file contexts and policyusually reside in the ramdisk’s root
We can spend many hours talking about and playingwith the above items, but at least for now I will leave them as an exercise tothe keen reader.
Alex Roman is a software engineer at TextNow.
Jump Start Git, 2nd Edition
Visual Studio Code: End-to-End Editing and Debugging Tools for Web Developers
Form Design Patterns