The imjtool utility is another one of the tools I'm including in my book, this time to accompany the chapter about the Boot process. I deal a lot with the internal format of images there, and realized I needed a quick extractor. This became more important when I started to deal with the L preview, and Google Glass system images I used for research. Over time, as I've encountered more and more proprietary image formats, I've had to extend the tool to support them all. I've also had to change the name (from imgtool to imjtool, as of March 2020) since it turns out the former was taken up by MAME..
Current version: 2.0 official (with -f and range extraction). June 2024
Just unpack the tar. There are macOS (Intel), Linux x86_64 and Android ARM64 images (remember they might need chmod +x to run, and prefix with ./). As usual, you might want to check out the RSS feed, or follow my company's Twitter for more updates.
Darwin users: remember to xattr -d com.apple.quarantine /path/to/imjtool to get past annoying GateKeeper
Why should I care?
Well, most users wouldn't. But if you need a quick tool to unpack Android images, this is useful. Think of it as the inverse of mkbootimg (from the AOSP), coupled with simg2img (the sparse image extractor). Another bonus feature it provides is unpacking the Linux bzimage kernels.
Also, if you're a Darwin user, there's no good LZ4 or XZ built-in to the system.
And if you're ever encountered a broken ZIP file and unzip wasn't too helpful...
Version 2.0 (6/2024) also allows searching inside an image, and range extraction.
What if it doesn't work on _________ (some image or file)?
LET ME KNOW. There are myriad image formats and corner cases which I don't normally run into, but you likely will. If I know about them (preferably through a download link to the image) I can easily fix it.
So how do I use it?
Like So:
morpheus@Zephyr (~)$ imjtool
Usage: imjtool _img_name_ [extract]
Where: _img_name_ is the name of an Android boot or bootloader image (or boot partition)
[extract] is an optional parameter to extract the image components
To obtain an Android system image, you can either get it from a zip (e.g. Google's update, Amazon's update.bin, etc), or by dd'ing for a device, then copying it over. Note that some devices (e.g. the HTC One M8) may need a bit of processing, in this case stripping the 256 header HBoot uses:
morpheus@Zephyr (~/Documents/Android/Book/src/ImgTool) % adb shell
shell@htc_m8wl:/ $ su
root@htc_m8wl:/ # ls -l /dev/block/platform/msm_sdcc.1/by-name/boot
lrwxrwxrwx root root 2014-09-26 04:48 boot -> /dev/block/mmcblk0p43
root@htc_m8wl:/ # dd if=/dev/block/mmcblk0p43 of=/data/local/tmp/boot.img
32768+0 records in
32768+0 records out
16777216 bytes transferred in 0.971 secs (17278286 bytes/sec)
root@htc_m8wl:/ # ^D
shell@htc_m8wl:/ $ ^D
# Back on host
morpheus@Zephyr (~/Documents/Android/Book/src/ImgTool) % adb pull /data/local/tmp/boot.img
5781 KB/s (16777216 bytes in 2.834s)
# Locating the ANDROID! magic
morpheus@Zephyr (~/Documents/Android/Book/src/ImgTool) % od -A d -t c boot.img | grep "A N D"
0000256 A N D R O I D ! Х ** ` \0 \0 200 \0 \0
morpheus@Zephyr (~/Documents/Android/Book/src/ImgTool) % dd if=boot.img of=boot bs=256 skip=1
65535+0 records in
65535+0 records out
16776960 bytes transferred in 0.153107 secs (109576753 bytes/sec)
morpheus@Zephyr (~/Documents/Android/Book/src/ImgTool) % imjtool boot extract
Boot image detected
Part Size Pages Addr
Kernel: 6333904 3093 0x8000
Ramdisk: 834688 408 0x2008000
Secondary: 0 0 0xf00000
Tags: 1e00000
Flash Page Size: 2048 bytes
Name:
CmdLine: console=ttyHSL0,115200,n8 androidboot.hardware=qcom user_debug=31 ehci-hcd.park=3
Extracting contents to directory: extracted
Found GZ Magic at offset 16207
gzip: extracted/kernelimage.gz: decompression OK, trailing garbage ignored
morpheus@Zephyr (~/Documents/Android/Book/src/ImgTool) % ls -l extracted
total 46840
-rw-rw-rw- 1 morpheus staff 6333904 Oct 1 19:28 kernel
-rw------- 1 morpheus staff 16809984 Oct 1 19:28 kernelimage
-rw-rw-rw- 1 morpheus staff 834688 Oct 1 19:28 ramdisk
morpheus@Zephyr (~/Documents/Android/Book/src/ImgTool) % file extracted/*
extracted/ramdisk: gzip compressed data, from Unix
extracted/kernel: Linux kernel ARM boot executable zImage (little-endian)
extracted/kernelimage: data # unrecognized by file(1), but still uncompressed
/**
*
* Not-so-rudimentary-anymore Android image and partition unpacking tool
*
* v0.1 - Support unpacking of BootLoader and Boot images, as well as imgdata
* v0.2 - Supports offset= (for HTC and other boot.imgs where ANDROID! is wrapped)
* Supports cmdline= (to override kernel command line)
*
* v0.3 - Integrated mkbootimg functionality, added dt_size and id
*
* v0.4 - cmdline, addrs..
*
* v0.4.1 - Fix for Edvard Holst on Essential PH1 images.. (who uses Essential nowadays, I wonder?)
*
* v0.5 - Update with LZ4 binaries, fix for extract devicetree even if can't uncompress kernel
*
* v0.6 - Samsung images (with DT > 0) - Device Tree now found whereever it may hide
*
* v0.7 - system.transfer.list support
*
* v0.8 - FBPK (SD845 CrossHatch (Pixel 3 XL) bootloader images) AND Huawei Mate
* Also compiles cleanly
*
* v0.85 - system.transfer.list support for v4 (MTK 64 bit)
*
* v1.0 - EFI. Worthy enough a feature that (after three years) I hit version 1 on it, and - JCOLOR
*
* v1.1 - Samsung baseband images ("TOC") - 03/04/2020
*
* v1.2 - DTBO, super.img (03/20/2020) and integrated Brötli
*
* v1.2.1 - Lots of QCOM EFI GUIDs added. Thanks to anonymous contributor!
*
* v1.3 - Can now also pack super.img and boot.img based on existing!
*
* v1.4 - uBoot uimage format
*
* v1.5 - MTK images!
*
* v1.5.1 - Fixes: QCOM FPBK fixed (accidentally removed during refactoring around 1.3.. oops)
* UEFI now unpacks files by UI, if presenr, rather than GUID
*
* 1.6 - Peek into images to detect payload format, recognize AVB (vbmeta)
*
* 1.7 - boot Img header v2,3 and vendor boot images
*
* 1.8 - CrAu (Chrome Auto-updater - the update.bin of Android and CrumOS OTA images)
*
* 1.9 - LZ4/BZ2 integration, selective extraction (extract ..what.. in commandline for some formats)
*
* 1.9.1 - Motorola bootloader images (SnL, thanks to NL), some bugfixes
*
* 1.10 - FBPK/FBPTv2 (Pixel 6)
*
* 2 - Time for version 2 - ZIP support, including partial/broken zips
*
* 2 - (still 2.0, beta) PBZX, AA/YAA, LZFSE (BVX2) - even in IMG4 containers (for IPSW unpacking)
* AR (.a) and RTKIT FTAB and AAPL symbolcaches
*
* 2 - Finally, 2: Extract by ranges (0x..-.../0x...+....), and find in images (-f)
*
* Acknowledgements/Notes:
* -----------------------
*
* platform-innovation-framework-for-uefi-complete-specifications-v0.90-v0.97 from Intel was invaluable
* for figuring out EFI
*
* Uses Google's Brotli extraction code (there's a limit to how much I'm willing to re-implement!)
* Uses libLZMA and LZ4 (because it's everywhere nowadays)
* Uses lzfse from Apple's GitHub
*
*
*
* This tool is part of the free downloads for the book "Android Internals: A Confectioner's Cookbook"
* But (as of v1.0) is also quite useful or MacOS T2 firmwares and (as of 2.0) for IPSWs.
*
* Author: Jonathan Levin, http://NewAndroidBook.com
*
*
* License: Use freely, BUT give credit where due. This way people learn of the website and books.
*
* If you have suggestions for improvement/new image types/etc - let me know and I'll gladly
* add them in.
*
**/
Version 0.2 Changes
Supports offset= (for HTC and other boot.imgs where ANDROID! is wrapped)
Zephyr:/tmp morpheus$ ~/Documents/Android/src/ImgTool/imjtool
Usage: /Users/morpheus/Documents/Android/src/ImgTool/imjtool _img_name_ [cmdline='args to kernel'] [extract]
Where: _img_name_ is the name of an Android boot or bootloader image (or boot partition)
[extract] is an optional parameter to extract the image components
[offset=...] offset to find ANDROID! magic (e.g. 256 in HTC boot)
[cmdline='args to kernel'] is an optional parameter specifying the kernel command line
[addr=0x.....] is an optional base address to load the kernel into
or: /Users/morpheus/Documents/Android/src/ImgTool/imjtool make _img_name_ _kernel_ _ramdisk_
Make _img_name by combining kernel and ramdisk and creating header
Supports making images (similar functionality to mkbootimg) with the "make" argument
Version 0.5 Changes
Minor updates to support cases where kernel can't be decompressed (notably, Samsung 6Edge - Thanks, Dan!)
Update with LZ4 binaries
Version 0.6 Changes
Samsung images (with DT > 0) - Device Tree now found on next page after ramdisk
I generally recurse when I can detect layering, but - you might need to run this tool more than once since many images have multiple layers of encapsulation:
morpheus@Zephyr (~/Downloads) % imjtool 8WCN25WW/cap/qcfirmware8998v1.0.1075.2507.cap extract
UEFI firmware image detected at offset 0xa15
Size: 2ded80, tag: 4856465f, attr: 3feff, checksum:512d, version: 2, blockSize: 0x40, blockCount:0xb7b6
Next GUID@0xa5d: Lenovo container (2de814 bytes, type Raw, attr 40)
Extracting Lenovo container
Next GUID@0x2df275: C7340E65-0D5D-43D6-ABB7-39751D5EC8E7 (510 bytes, type Raw, attr 40)
Extracting C7340E65-0D5D-43D6-ABB7-39751D5EC8E7
#
# And again:
#
morpheus@Zephyr (~/Downloads) % imjtool extracted/Lenovo\ container
UEFI firmware image detected at offset 0x7eba0
Warning: non zeros in header
Size: 200000, tag: 4856465f, attr: cfeff, checksum:c8a7, version: 2, blockSize: 0x200, blockCount:0x1000
Warning: additional content at offset 0x27eba0
Next GUID@0x7ebe8: FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF (fa0 bytes, type Padding, attr 0)
Next GUID@0x7fb88: QCOM XBL BOOT.XF Terse Executable (26018 bytes, type Security Core, attr 28)
Section @0x7fba0 Type: Raw, Size: 0xf34 (empty)
Section @0x80ad4 Type: Terse Executble, Size: 0x250cc
Next GUID@0xa5ba0: uefiplat.cfg (24ab bytes, type Freeform, attr 0)
Section @0xa5bb8 Type: UI, Size: 0x1e uefiplat.cfg
Section @0xa5bd8 Type: Raw, Size: 0x2473
Next GUID@0xa8050: QCOM package (1adcd0 bytes, type Firmware Volume Image, attr 0)
COMPRESSED - EE4E5898-3914-4259-9D6E-DC7BD79403CF - Magic 0x5d@0xa8080
LZMA! 1adca0 bytes
examining decompressed data (8542152 bytes)
Size: 8257c0, tag: 4856465f, attr: 3feff, checksum:948d, version: 2, blockSize: 0x40, blockCount:0x2095f
Warning: additional content at offset 0x8257c0
Next GUID@0x48: FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF (2c bytes, type Padding, attr 0)
Next GUID@0x78: FC510EE7-FFDC-11D4-BD41-0080C73C8881 (54c bytes, type Freeform, attr 0)
Section @0x90 Type: Raw, Size: 0x534
Next GUID@0x5c8: D6A2CB7F-6A18-4E2F-B43B-9920A733700A (27030 bytes, type Driver eXecution Environment Core, attr 0)
Section @0x5e0 Type: PE32, Size: 0x27004
Section @0x275e4 Type: UI, Size: 0x14 DxeCore
Next GUID@0x275f8: ARM CPU DXE (c03c bytes, type Driver, attr 0)
Section @0x27610 Type: Driver eXecution Env Dependencies, Size: 0x6
Section @0x27618 Type: PE32, Size: 0xc004
Section @0x3361c Type: UI, Size: 0x18 ArmCpuDxe
Next GUID@0x33638: Runtime DXE (803e bytes, type Driver, attr 0)
Section @0x33650 Type: Driver eXecution Env Dependencies, Size: 0x6
Section @0x33658 Type: PE32, Size: 0x8004
Section @0x3b65c Type: UI, Size: 0x1a RuntimeDxe
I don't purport to cover all EFI GUIDs here. Your favorite tool is probably better. I built this for my own use cases (primarily, command line, greppable, scriptable, cross platorm), and I think it's useful enough to provide freely. If your specific image isn't supported, you can always drop me a line. Ranting on twitter denigrating my work and/or me won't help. And btw, you're welcome.
root@Qilin (/NewAndroidBook/ddb/S20Ultra/...)# imjtool super.img extract
Warning: super.img is likely truncated or still compressed
Sparse image v1.0 detected, 2304000 blocks of 4096 bytes
2304000 blocks of 4096 bytes compressed into 56 chunks (20% compressed)
Extracted image is in extracted/image.img
root@Qilin (/NewAndroidBook/ddb/S20Ultra/...)# imjtool extracted/image.img extract
liblp dynamic partition (super.img) - Blocksize 0x1000, 2 slots
LP MD Header @0x3000, version 10.0, with 4 logical partitions on block device at partition super, first sector: 0x800
Partitions @0x3080 in 2 groups:
Group 0: default
Group 1: group_basic
Name: system (read-only, spanning 1 extents and 5597 MB) - extracted
Name: vendor (read-only, spanning 1 extents and 1091 MB) - extracted
Name: product (read-only, spanning 1 extents and 676 MB) - extracted
Name: odm (read-only, spanning 1 extents and 4 MB) - extracted
root@Qilin (/NewAndroidBook/ddb/S20Ultra/...)# file extracted/*
extracted/image.img: data
extracted/odm.img: Linux rev 1.0 ext2 filesystem data, UUID=79c8b8f8-84be-5f6f-b675-dd53f53ebb2c,
volume name "odm" (extents) (large files) (huge files)
extracted/product.img: Linux rev 1.0 ext2 filesystem data, UUID=8bf42db5-b3bc-5890-8b2a-8eeeba73b344,
volume name "product" (extents) (large files) (huge files)
extracted/system.img: Linux rev 1.0 ext2 filesystem data, UUID=2939f12c-6689-53a4-b94a-1c1ce1c83fbd
(extents) (large files) (huge files)
extracted/vendor.img: Linux rev 1.0 ext2 filesystem data, UUID=a8b0696d-9320-55bc-b768-8579563bdfdc,
volume name "vendor" (extents) (large files) (huge files)
Supports DTBO
Supports Samsung TOC
Supports Brotli compressed (....new.br) block based images. For now, work with me and supply the number of blocks by getting them from the ...transfer.list (second line):
root@Qilin (/NewAndroidBook/ddb/RedmiK30) # head -2 vendor.transfer.list
4
345427
root@Qilin (/NewAndroidBook/ddb/RedmiK30) #BLOCKS=345427 imjtool vendor.new.dat.br \
stl=vendor.transfer.list
Attempting Brotli decompression. You might need to supply NUMBLOCKS=(2nd line on the list)
while J works this feature.
Image written to /tmp/extracted.img
root@Qilin (/NewAndroidBook/ddb/RedmiK30) # file /tmp/extracted.img
/tmp/extracted.img: Linux rev 1.0 ext2 filesystem data, UUID=2b96c597-1e2f-5ee1-9851-c4a9fa9de36e,
volume name "vendor" (extents) (large files) (huge files)
v2.0(β) - ZIP/XZ/LZ4/BZ2 support
Including partial zips! For example:
v2.0 - -f and range extraction
Three useful features:
-f:: A simple but useful feature enabling you to search for a string (or non-printable bytes, using '\x' escaping).
extract 0x...-0x....: Will extract a specified range (which is easier for my workflow than messing with dd bsize=... skip=.... count==..
extract 0x...+0x....: Will extract a the +.. count of bytes from the specified start.
Shameless plug for the book, and RFC
Android Vol II is out - and now I'm onto III (security) and IV (hardware/advanced topics). In the interim, I encourage you to try out the tool (as well as the even more powerful Dextra, and the simple but useful bindump) and, of course - JTrace. Shoot me an email (to j@) if you've any questions. Or comments. Or in general. All are welcome.
If you want to learn all about Android Internals, please check out the Android Internals & Reverse Engineering Training course my company, Technologeeks, offers. The next training is likely only a few months away!