Image 文件解包及类型转换方法

一、img文件解包

1.1 boot image解包

1.1.1 准备boot解包工具

单编system/core/mkbootimg模块,在out/host/linux-x86/bin/目录会生成mkbootimg和unpack_bootimg可执行文件。
其中mkbootimg可将kernel/dtb/ramdisk打包成boot.img,unpack_bootimg可以将boot.img解包。

1
2
# 单编生成mkbootimg和unpack_bootimg
$ mmm system/core/mkbootimg

也可以直接使用system/core/mkbootimg目录的mkbootimg.py和unpack_bootimg.py脚本来完成打包、解包工作。

1.1.2 使用boot解包工具

1.使用mkbootimg.py脚本创建boot.img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#mkbootimg.py [-h] --kernel KERNEL [--ramdisk RAMDISK] [--second SECOND]
# [--dtb DTB]
# [--recovery_dtbo RECOVERY_DTBO | --recovery_acpio RECOVERY_ACPIO]
# [--cmdline CMDLINE] [--base BASE]
# [--kernel_offset KERNEL_OFFSET]
# [--ramdisk_offset RAMDISK_OFFSET]
# [--second_offset SECOND_OFFSET] [--dtb_offset DTB_OFFSET]
# [--os_version OS_VERSION]
# [--os_patch_level OS_PATCH_LEVEL]
# [--tags_offset TAGS_OFFSET] [--board BOARD]
# [--pagesize {2048,4096,8192,16384}] [--id]
# [--header_version HEADER_VERSION] -o OUTPUT
#-h, --help:显示帮助信息;
#--kernel KERNEL:kernel文件路径;
#--ramdisk RAMDISK:ramdisk文件路径;
#--second SECOND:sbl文件路径;
#--dtb DTB:dtb文件路径;
#--recovery_dtbo RECOVERY_DTBO:recovery dtb文件路径;
#--recovery_acpio RECOVERY_ACPIO:recovery acpio文件路径;
#--cmdline CMDLINE:传入kernel command line的参数;
#--base BASE:基地址;
#--kernel_offset KERNEL_OFFSET:kernel偏移量;
#--ramdisk_offset RAMDISK_OFFSET:ramdisk偏移量;
#--second_offset SECOND_OFFSET:sbl偏移量;
#--dtb_offset DTB_OFFSET:dtb偏移量;
#--os_version OS_VERSION:系统版本;
#--os_patch_level OS_PATCH_LEVEL:系统安全patch版本;
#--tags_offset TAGS_OFFSET:tags偏移量;
#--board BOARD:board名称;
#--pagesize {2048,4096,8192,16384}:页大小;
#--id:标准输出image id;
#--header_version HEADER_VERSION:boot img header版本;
#-o OUTPUT, --output OUTPUT:输出文件路径;

2.使用unpack_bootimg.py脚本解压boot.img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#unpack_bootimg.py [-h] --boot_img BOOT_IMG [--out OUT]
#-h, --help:显示帮助信息;
#--boot_img BOOT_IMG:boot image文件路径;
#--out OUT:输出文件路径;
$ python system/core/mkbootimg/unpack_bootimg.py --boot_img boot.img --out unpack_boot_image/
boot_magic: ANDROID!
kernel_size: 9056943
kernel load address: 0x40008000
ramdisk size: 6878286
ramdisk load address: 0x51b00000
second bootloader size: 0
second bootloader load address: 0x40f00000
kernel tags load address: 0x47880000
page size: 2048
boot image header version: 2
os version and patch level: 335544634
product name:
command line args: bootopt=64S3,32S1,32S1 buildvariant=userdebug
additional command line args:
recovery dtbo size: 38684
recovery dtbo offset: 0xf33800
boot header size: 1660
dtb size: 88855
dtb address: 0x47880000
$ tree
.
├── dtb
├── kernel
├── ramdisk
└── recovery_dtbo
0 directories, 4 files
1
2
3
4
5
$ file unpack_boot_image/*
dtb: data
kernel: Linux kernel ARM boot executable zImage (little-endian)
ramdisk: gzip compressed data, from Unix
recovery_dtbo: data

可参考dtbo image解包可以将boot.dtb和boot_recovery.dtb解包:

1
2
3
4
5
6
7
8
unpack_boot_image/dtb_unpack$ out/host/linux-x86/bin/mkdtimg dump dtb --out boot_dtb.dump --dtb boot_dtb.dtb
unpack_boot_image/dtb_unpack$ file *
boot_dtb.dtb: Device Tree Blob version 17, size=88791, boot CPU=0, string block size=10439, DT structure block size=78296
boot_dtb.dump: ASCII text
dtb: data
$ out/host/linux-x86/bin/dtc -I dtb -O dts -o boot_dtb.dts boot_dtb.dtb
$ file boot_dtb.dts
boot_dtb.dts: ASCII text, with very long lines
1
2
3
4
5
6
7
8
$ out/host/linux-x86/bin/mkdtimg dump recovery_dtbo --out boot_recovery_dtbo.dump --dtb boot_recovery_dtbo.dtb
$ file *
boot_recovery_dtbo.dtb: Device Tree Blob version 17, size=38620, boot CPU=0, string block size=4392, DT structure block size=34172
boot_recovery_dtbo.dump: ASCII text
recovery_dtbo: data
$ out/host/linux-x86/bin/dtc -I dtb -O dts -o boot_recovery_dtbo.dts boot_recovery_dtbo.dtb
$ file boot_dtb.dts
boot_recovery_dtbo.dts: ASCII text, with very long lines

可参考ramdisk image解包可以将ramdisk解包:

1
2
3
4
5
6
7
8
# 解压gz格式ramdisk
mv ramdisk ramdisk.gz
gunzip ramdisk.gz
# 挂载cpio格式ramdisk
$ file ramdisk
ramdisk: ASCII cpio archive (SVR4 with no CRC)
mv ramdisk ramdisk_cpio/ramdisk.cpio
sudo cpio -idv < ramdisk_cpio/ramdisk.cpio

1.2 dtbo image解包

1.2.1 准备dtbo解包工具

1.编译dtb工具

单编system/libufdt/utils/src木块,在out/host/linux-x86/bin/目录能找到mkdtimg(新系统采用mkdtboimg.py来替代)。
也可以直接使用system/libufdt/utils/src/mkdtboimg.py脚本。

1
2
# 单编生成mkdtimg和mkdtboimg.py
$ mmm system/libufdt/util/src

2.编译dtc工具

可以单编kernel/scripts/dtc目录,会在out/host/linux-x86/bin/目录生成dtc可执行文件。dtc可以将dtb转换为dts文件。

1
2
#单编生成dtc
$ mmm kernel-4.9/scripts/dtc

1.2.2 使用dtbo解包工具

1.使用新的创建dtbo.img脚本

1
2
3
4
5
#mkdtboimg.py [-h] {create,cfg_create,dump,help} ...
#-h, --help:显示帮助信息;
#create:创建dtb或dtbo image文件;扩展指令:[--id ID] [--rev REV] [--flags FLAGS] [--custom0 CUSTOM0] [--custom1 CUSTOM1] [--custom2 CUSTOM2] [dt_file];
#cfg_create:创建一个cfg格式配置文件;扩展指令:[--dtb-dir [DTBDIR]] [conf_file];
#dump:dump dtb信息;加上'--dtb'可以指定dtb文件;加上'--out'可以将dump信息输出到文件中;

2.使用旧的创建dtb文件工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#./out/host/linux-x86/bin/mkdtimg dump <image_file> (<option>...)
#options:
# -o, --output <filename>:输出文件文件名;
# -b, --dtb <filename>:需要dump的dtb文件名;
#./out/host/linux-x86/bin/mkdtimg create <image_file> (<global_option>...) (<dtb_file> (<entry_option>...) ...)
#global_options:
# --dt_type=<type>:设备树的类型(dtb/acpi格式,默认dtb);
# --page_size=<number>:页大小(默认2048);
# --version=<version>:设备叠加树的版本(默认为0);
# --id=<number|path>:dt_table_entry中的id属性(默认为0);
#./out/host/linux-x86/bin/mkdtimg cfg_create <image_file> <config_file> (<option>...)
#options:
# -d, --dtb-dir:加载的dtb文件路径;
# dump dtbo文件,将其由data类型转换为device tree block类型,并将结构信息输出到dump文件中
$ out/host/linux-x86/bin/mkdtimg dump dtbo.img --out unpack_dtbo_image/dtbo.dump --dtb unpack_dtbo_image/dtbo_dump.dtb
$ tree
.
├── dtbo_dump.dtb
└── dtbo.dump
0 directories, 2 files
# 查看dtb文件类型
$ file *
dtbo.dump: ASCII text
dtbo_dump.dtb: Device Tree Blob version 17, size=38620, boot CPU=0, string block size=4392, DT structure block size=34172
# 查看dtb的dump信息
$ cat dtbo.dump
dt_table_header:
magic = d7b7ab1e
total_size = 38684
header_size = 32
dt_entry_size = 32
dt_entry_count = 1
dt_entries_offset = 32
page_size = 2048
version = 0
dt_table_entry[0]:
dt_size = 38620
dt_offset = 64
id = 00000000
rev = 00000000
custom[0] = 00000000
custom[1] = 00000000
custom[2] = 00000000
custom[3] = 00000000
(FDT)size = 38620
(FDT)compatible = (unknown)

3.将dtb转换为dts文件

1
2
3
4
5
6
7
8
9
10
11
#Usage: dtc [options] <input file>
# -I, --in-format <arg>:dtb、dts文件或device-tree目录;
# -o, --out <arg>:dts、dtb或asm格式的目标文件;
# -O, --out-format <arg>:dts、dtb或asm格式的目标文件;
# -s, --sort:按节点属性排序后输出;
# -h, --help:帮助信息。
# 将dtb转换为dts文件
$ out/host/linux-x86/bin/dtc -I dtb -O dts -o dtbo_dump.dts dtbo_dump.dtb
# 查看dts文件格式
$ file dtbo_dump.dts
dtbo_dump.dts: ASCII text, with very long lines

1.3 ramdisk image文件解包

1.3.1 准备ramdisk解包工具
1
2
# 安装gunzip和cpio
sudo apt-get install gzip gunzip cpio
1.3.2 使用ramdisk解包工具

用file指令可以获知ramdisk.img是zip格式的压缩文件。

1
2
$ file ramdisk.img
ramdisk: gzip compressed data, from Unix

将ramdisk.img文件重命名为ramdisk.gz,用gunzip命令解压它。

1
2
cp ramdisk ramdisk.gz
gunzip ramdisk.gz

用file指令可以获知解压后的ramdisk文件是cpio格式的文件,然后将其重命名为ramdisk.cpio文件。
接着使用cpio指令可将其挂载。

1
2
3
4
$ file ramdisk
ramdisk: ASCII cpio archive (SVR4 with no CRC)
cp ramdisk ramdisk_cpio/ramdisk.cpio
sudo cpio -idv < ramdisk_cpio/ramdisk.cpio

1.4 super image文件解包

1.4.1 准备super解包工具

可以单编system/extras/partition_tools模块,在out/host/linux-x86/bin目录生成lpdump、lpmake、lpunpack文件。
其中lpdump可以dump super image信息;lpmake可以打包super image文件;lpunpack可以解包super image文件。

1
mmm system/extras/partition_tools/
1.4.2 使用super解包工具

1.使用lpmake创建super.img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#./out/host/linux-x86/bin/lpmake [options]
#Required options:
# -d,--device-size=SIZE:逻辑分区的块设备的大小;
# -m,--metadata-size=SIZE:分区元数据保留的最大大小;
# -s,--metadata-slots=COUNT:存储元数据副本的slot数;
# -p,--partition=DATA:根据给定的数据信息添加分区;
# -o,--output=FILE:输出的文件;
#Optional:
-b,--block-size=SIZE:物理块大小,默认是4096;
-a,--alignment=N:指定单位字节的分区对齐;
-O,--alignment-offset=N:分区对齐偏移量;
-S,--sparse:输出可供fastboot烧录的sparse格式image文件;
-i,--image=PARTITION=FILE:如果为fastboot编译sparse image,则将给定文件(或sparse文件)作为命名分区的初始数据;
-g,--group=GROUP:SIZE:给定分区组最大大小;
-D,--device=DATA:添加super分区块设备。如果指定,则不得指定-d /-device-size和aligns;
-n,--super-name=NAME:指定将容纳super分区的块设备的名称;
-x,--auto-slot-suffixing:在使用之前,标记需要slot后缀的块设备和分区名称;
#分区数据格式:<name>:<attributes>:<size>[:group],Attrs必须是'none'或'readonly';
#设备数据格式:<partition_name>:<size>[:<alignment>:<alignment_offset>]
# 分区名称是块设备的/dev/block/by-name/路径的名称。大小是设备大小(以字节为单位)。对齐和对齐偏移参数与-a /-alignment和-O /-alignment-offset相同。

2.使用lpdump获取super.img结构信息

1
2
3
4
5
# 查看super.img类型
$ file super.img
super.img: Android sparse image, version: 1.0, Total of 2097152 4096-byte output blocks in 1667 input chunks.
# 将sparse格式转换为ext4格式
$ out/host/linux-x86/bin/simg2img super.img super_ext4.img
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#./out/host/linux-x86/bin/lpdump [-s <SLOT#>|--slot=<SLOT#>] [-j|--json] [FILE|DEVICE]
#Options:
# -s, --slot=N:slot信息;
# -j, --json:以json格式输出dump信息;
# dump ext4格式的super image
$ out/host/linux-x86/bin/lpdump super_ext4.img
Metadata version: 10.0
Metadata size: 720 bytes
Metadata max size: 65536 bytes
Metadata slot count: 3
Partition table:
------------------------
Name: product_a
Group: main_a
Attributes: readonly
Extents:
0 .. 488391 linear super 2048
------------------------
Name: product_b
Group: main_b
Attributes: readonly
Extents:
------------------------
Name: system_a
Group: main_a
Attributes: readonly
Extents:
0 .. 1316607 linear super 491520
------------------------
Name: system_b
Group: main_b
Attributes: readonly
Extents:
------------------------
Name: vendor_a
Group: main_a
Attributes: readonly
Extents:
0 .. 377143 linear super 1808384
------------------------
Name: vendor_b
Group: main_b
Attributes: readonly
Extents:
------------------------
Block device table:
------------------------
Partition name: super
First sector: 2048
Size: 8589934592 bytes
Flags: none
------------------------
Group table:
------------------------
Name: default
Maximum size: 0 bytes
Flags: none
------------------------
Name: main_a
Maximum size: 4110417920 bytes
Flags: none
------------------------
Name: main_b
Maximum size: 4110417920 bytes
Flags: none
------------------------

3.使用lpunpack解包super.img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#./out/host/linux-x86/bin/lpunpack [options...] SUPER_IMAGE [OUTPUT_DIR]
#Options:
# -p, --partition=NAME:需要解包的image分区名称;
# -S, --slot=NUM:解包的image对应的slot信息(默认为0)。
# 解包ext4格式的super image
$ out/host/linux-x86/bin/lpunpack super_ext4.img
$ tree
.
├── product_a.img
├── product_b.img
├── super_ext4.img
├── super.img
├── system_a.img
├── system_b.img
├── vendor_a.img
└── vendor_b.img
0 directories, 8 files
$ file *
product_a.img: Linux rev 1.0 ext2 filesystem data, UUID=453968ff-b43c-54b7-a506-a6a88682c128, volume name "product" (extents) (large files) (huge files)
product_b.img: empty
super_ext4.img: data
super.img: Android sparse image, version: 1.0, Total of 2097152 4096-byte output blocks in 1667 input chunks.
system_a.img: Linux rev 1.0 ext2 filesystem data, UUID=4729639d-b5f2-5cc1-a120-9ac5f788683c (extents) (large files) (huge files)
system_b.img: empty
vendor_a.img: Linux rev 1.0 ext2 filesystem data, UUID=2b96c597-1e2f-5ee1-9851-c4a9fa9de36e, volume name "vendor" (extents) (large files) (huge files)
vendor_b.img: empty
# 从super.img中解包单个logical image
$ out/host/linux-x86/bin/lpunpack --partition=system_a super_ext4.img

二、sparse和ext4格式img文件转换

2.1 用file指令查看img类型

在Android中,为了压缩image文件大小,ext4类型image文件通常会被转为sparse格式存储。可以linux环境下可以用file $image来查看image文件类型,如:

1
2
3
4
5
6
# sparse类型
$ file cache_sparse.img
cache_sparse.img: Android sparse image, version: 1.0, Total of 110592 4096-byte output blocks in 14 input chunks.
# ext4类型
$ file cache_ext4.img
cache_ext4.img: Linux rev 1.0 ext4 filesystem data, UUID=6de9da9d-98ae-4c4d-a307-69f11426b7c8, volume name "cache" (extents) (large files) (huge files)

2.2 准备sparse/ext4格式转换工具

可以单编system/core/libsparse模块,将会在out/host/linux-x86/bin目录生成img2simg和simg2img可执行文件。
其中img2simg ext4.img sparse.img可将ext4格式image转换为sparse格式;simg2img sparse.img ext4.img可将sparse格式image转换为ext4格式。

1
2
3
4
5
6
# 单编libsparse
$ mmm system/core/libsparse/
# sparse -> ext4
$ out/host/linux-x86/bin/simg2img cache_sparse.img cache_ext4.img
# ext4 -> sparse
$ out/host/linux-x86/bin/img2simg cache_ext4.img cache_sparse.img

可以使用system/core/libsparse目录的simg_dump.py脚本dump sparse类型文件的结构信息,也可以单编该模块后用out/host/linux-x86/bin/simg_dump可执行文件。

2.3 使用sparse/ext4格式转换工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#simg_dump.py [-v] [-s] [-c <filename>] sparse_image_file ...
#-v:输出数据块基本信息;
#-s:显示数据块的sha1sum值;
#-c <filename>:以csv格式文件保存数据块信息;
$ python system/core/libsparse/simg_dump.py -sv cache_sparse.img
Total of 110592 4096-byte output blocks in 14 input chunks.
input_bytes output_blocks
chunk offset number offset number
1 40 126976 0 31 Raw data 590d1e3a0f0a82598e06caea4f33fa770c3fa8cb
2 127028 4 31 1727 Fill with 0x00000000 f35b915ea1311e8c1423ed7ea5a5a01520f4b7a2
3 127044 24576 1758 6 Raw data 79888fd640b9265732a9c6fc607f8ca3cbabbf20
4 151632 4 1764 31004 Fill with 0x00000000 1988a9ceacf3216a0b174ac41a8f072a20c548fc
5 151648 8192 32768 2 Raw data e03859b1bcae78b6ff142717d44842b95af44560
6 159852 4 32770 32766 Fill with 0x00000000 51ce802696dc544eab99e2349309ed47c4824c6d
7 159868 4096 65536 1 Raw data 79aca04c21627551edb385f17c9a67312ac740ee
8 163976 4 65537 1729 Fill with 0x00000000 982ec9043e6fd6d2d0a0c7827da821e337b2c16b
9 163992 4096 67266 1 Raw data ea0624d61e7137c60b7c3525e25c59f9cf9f3a94
10 168100 4 67267 31037 Fill with 0x00000000 94bd394a29a98f6cc19030c1652dceae6d0792e5
11 168116 8192 98304 2 Raw data c12e84b15c6f54395762f873b6bb8f84787890f1
12 176320 4 98306 26 Fill with 0x00000000 f447cb15945d8630cc88ed3b7bee049b6f5e4c7d
13 176336 4096 98332 1 Raw data 18eada3b66c94412d5e818031aaff6508050fb46
14 180444 4 98333 12259 Fill with 0x00000000 eeb4e814e300598d4530296fe08003fba4435763
180448 110592 End

可以用十六进制工具(如hexdump)打开sparse格式image文件,你会发现header包含0xed26ff3a的magic和major_version,minor_version等信息。

1
2
3
4
5
$ hexdump cache_sparse.img
0000000 ff3a ed26 0001 0000 001c 000c 1000 0000
0000010 b000 0001 000e 0000 0000 0000 cac1 0000
0000020 001f 0000 f00c 0001 0000 0000 0000 0000
0000030 0000 0000 0000 0000 0000 0000 0000 0000
type length(bytes) example
magic 16 0xed26ff3a
major_version 16 0x0001
minor_version 16 0x0000
file_hdr_sz 16 0x001c
chunk_hdr_sz 16 0x000c
blk_sz 32 0x1000
total_blks 32 0x0001b000
total_chunks 32 0x0000000e
image_checksum 32 0x00000000
chunk_type_raw 16 0xcac1