涉及工具:

mkbootimg、 unpackbootimg、 gzip、 readelf、objdump、simg2img、mount、extract-dtb.py、dt、cpio

概览

1、Android bootimg kernel(boot.img)

1.1、内核boot.img-zImage分析

1.2、设备树

1.3、根目录

2、emmc_appsboot.mbn:

3、splash.img:

4、dtbo.img:

5、mdtp.img:

6、vbmeta.img:

7、Linux rev 1.0 ext4 filesystem data(persist.img)

8、Android sparse image(system.img、userdata.img、vendor.img):

9、Super.img :

详细介绍

上篇日志分析可以看到开机流程中需要使用很多分区,这些分区在eMMC中占用不同的空间,其占用信息存储于分区表(GPT)中。高通智能机分区表详细解析_rockly89的博客-CSDN博客_fsc分区里面是比较全的分区描述,android P以后才有的vbmeta用于验证、dtbo是设备树叠加层。下面是dev下block节点读的信息。

这个博客主要研究刷机到底刷进去的是什么。

MP镜像不管,只分析android的镜像。

查看Android源码编出来的文件。

Android sparse image(稀疏)格式最多,这种格式可以理解文件系统的一种压缩格式,查看需解压。(差分升级如果包括该类镜像,不要用这种格式直接做差分diff)

Linux rev 1.0 ext4 filesystem data,是一种文件系统格式,也是Android sparse image(稀疏)格式的解压,可直接挂载查看。

data,数据类型,该类型实际是未识别的类型,其中splash.img和vbmeta.img未识别,我们后面再分析。(实际电子设备上存储的所有东西都可认为是data)

Android bootimg kernel格式最复杂,主要包含cmdline,设备树,内核,文件系统。由google的andorid源码工具mkbootimg制作,本blog大部分篇幅介绍的是boot.img镜像解析和反汇编。

ELF 32-bit LSB executable,是执行文件类型,其实boot.img镜像中内核可以当作执行文件,为了理解执行文件,将反汇编emmc_appsboot.mbn和boot.img中的内核。

1、Android bootimg kernel(boot.img)

boot.img包含内核,根文件系统,设备树,cmdline。编译使用mkbootimg,解压使用unpackbootimg,GITHUB上不少该工具。

先用 unpackbootimg 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
34
35
36
37
38
39
40
41
42
43
44
45


boot.img:   Android bootimg, kernel, ramdisk, page size: 2048, cmdline (console=ttyMSM0,115200,n8 androidboot.console=ttyMSM0 androidbo)


boot.img-base:       ASCII text


boot.img-board:      very short file (no magic)


boot.img-cmdline:    ASCII text, with very long lines


boot.img-dtb:        very short file (no magic)


boot.img-hash:       ASCII text


boot.img-kerneloff:  ASCII text


boot.img-oslevel:    ASCII text


boot.img-osversion:  ASCII text


boot.img-pagesize:   ASCII text


boot.img-ramdisk.gz: gzip compressed data, from Unix


boot.img-ramdiskoff: ASCII text


boot.img-secondoff:  ASCII text


boot.img-tagsoff:    ASCII text


boot.img-zImage:     gzip compressed data, max compression, from Unix

其中内核文件boot.img-zImage,根目录文件boot.img-ramdisk.gz,设备树在内核文件中,recovery的设备树是boot.img-dtb,cmdline是boot.img-cmdline。

1.1、内核boot.img-zImage分析

重命名

1
2
3


cp boot.img-zImage zImage.gz

解压

1
2
3


gzip -d zImage.gz 

得到的zImage是MS-DOS executable, MZ for MS-DOS格式文件, Beyong Compare对比可以看到zImage就是源码编译出的obj\KERNEL_OBJ\arch\arm64\boot\zImage,它是vmlinux通过编译工具objcopy去掉调试信息后的二进制文件。

源码编译时是使用工具mpgen.py。

可以访问linux kernel编译产生的vmlinux Image zImage之间的关系_潘振杰的博客-CSDN博客_kernel编译完后只有vmlinux查看kernel怎么生成的。(与android的boot.img稍有不同)

这里要对内核反汇编,相关知识比较弱的建议先看下相关blog并动手尝试,下面两个博客有不少分析可执行文件的工具。

  • 逆向与反汇编工具 - mull - 博客园

  • ELF文件-逆向工具_ToTHotSpur的博客-CSDN博客_elf 逆向

描述了ELF文件结构。

执行反汇编命令,解析vmlinux:

1
2
3
4
5
6
7
8
9


readelf -hlSVAI vmlinux(-r/s参数内容多,谨慎使用)





aarch64-linux-androidkernel-objdump -afphG vmlinux (-D/d/S/s/x参数内容太多,谨慎使用)

Linux下利用objdump查看文件空间地址分布

1
2
3


readelf -S vmlinux

真实数据位置偏移根据前面信息计算(NOTE off 0x000000000183cc48 vaddr 0xffffff800993cc48) 0x18408a8=0x99408a8-0x0x8100000

可以使用hexdump以16进制读取可执行文件,对比反汇编的机器码。

1
2
3


hexdump -s 0x18408a8 -n 1024 vmlinux

这里以反汇编方式找到boot.img中内核入口start_kernel代码的反汇编机器码位置,高手可以编辑它,重新打包,关掉相关安全验证(Verify Boot,DM-verity,回滚保护)刷到设备中。

汇编是比较老的语言,但是是离机器最近的语言,玩软件破解和加密应该熟悉。就算不玩破解,知道点反汇编对于代码和程序的理解也会不同。

下面是随便找的一个破解例子

Linux下使用objdump+vim+xxd进行反汇编并修改指令

1.2、设备树

unpackbootimg没看到设备树,对比编译出的Image.gz-dtb与Image.gz可以知道,mkbootimg把Image.gz-dtb(设备树)作为kernel制作boot.img的,设备树还在内核解包的zImage中。要获取设备树需要读取其中信息才可分离出来。GITHUB有从内核中解压设备树工具https://github.com/PabloCastellano/extract-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


python3 extract-dtb.py kernel





Dumped 00_kernel, start=0 end=12116314





Dumped 01_dtbdump_***.dtb, start=12116314 end=12393371





Dumped 02_dtbdump_xK`)+-;





pw_qK.dtb, start=12393371 end=12393544





Extracted 2 appended dtbs + kernel to dtb


dtc -O dts 01_dtbdump_***.dtb -o test0.dts

可以用dtc来分析源码编译出的设备树obj\KERNEL_OBJ\arch\arm64\boot\dts***.dtb。

1
2
3


dtc -I dtb -O dts obj\KERNEL_OBJ\arch\arm64\boot\dts\***.dtb -o test1.dts

转换成dts格式后,文本编辑器可以直接读。也可比较boot.img解包出的dts、编译的dts两个文件,验证是否一致。

1.3、根目录

解压缩

1
2
3


gzip -d boot.img-ramdisk.gz

解包。

1
2
3


cpio -i -F boot.img-ramdisk

这样就看到根目录。

内核调试可以使用fastboot boot boot.img命令,boot.img不刷入分区,直接载入ram运行。还可以使用下面参数设置,同时注意下设备安全限制(AVB,防回滚)。

 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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81


fastboot -h





-c <cmdline>  Override kernel commandline.





-b, --base <base_addr> Specify a custom kernel base





address (default: 0x10000000).





--kernel-offset        Specify a custom kernel offset.





(default: 0x00008000)





--ramdisk-offset       Specify a custom ramdisk offset.





(default: 0x01000000)





--tags-offset Specify a custom tags offset.





(default: 0x00000100)





-n, --page-size <page size>        Specify the nand page size





(default: 2048).





fastboot boot boot.img





fastboot -c "console=ttyMSM0,115200,n8 androidboot.console=ttyMSM0 androidboot.hardware=qcom msm_rtb.filter=0x237 ehci-hcd.park=3 lpm_levels.sleep_disabled=0 androidboot.bootdevice=7824900.sdhci earlycon=msm_serial_dm,0x78af000 firmware_class.path=/vendor/firmware_mnt/image androidboot.usbconfigfs=true loop.max_part=7 buildvariant=user" -b 0x80000000 boot Z:\boot.img

2、emmc_appsboot.mbn:

bootloader镜像emmc_appsboot.mbn,与zImage一样,可以readelf解析,但不能objdump反汇编。所以我们对编译最后一步sectools.py签名前的文件lk_s.elf进行反汇编。

参照上面vmlinux反编译方式读出关键函数<aboot_init>机器码位置。

  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
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222





aarch64-linux-androidkernel-objdump -d lk_s.elf --start-address=0x8f63f390 --stop-address=0x8f63f8b0





lk_s.elf:     file format elf32-littlearm





Disassembly of section .text:





8f63f390 <aboot_init>:





8f63f390:      e92d4ff0       push   {r4, r5, r6, r7, r8, r9, sl, fp, lr}





8f63f394:      e28db020     add     fp, sp, #32





8f63f398:      e24dd024     sub     sp, sp, #36     ; 0x24





8f63f39c:       e3006980     movw r6, #2432      ; 0x980





8f63f3a0:      e3486f76      movt   r6, #36726    ; 0x8f76





8f63f3a4:      e50be030     str       lr, [fp, #-48]   ; 0xffffffd0





8f63f3a8:      e5963000     ldr       r3, [r6]





8f63f3ac:       e50b3028     str       r3, [fp, #-40]  ; 0xffffffd8





8f63f3b0:      ebffd709       bl        8f634fdc <target_is_emmc_boot>





8f63f3b4:      e3500000     cmp    r0, #0





8f63f3b8:      0a00006f      beq     8f63f57c <aboot_init+0x1ec>





8f63f3bc:      ebff4487       bl        8f6105e0 <mmc_page_size>





8f63f3c0:      e30920d8     movw r2, #37080    ; 0x90d8





8f63f3c4:      e30d33dc     movw r3, #54236    ; 0xd3dc





8f63f3c8:      e3482f75      movt   r2, #36725    ; 0x8f75





8f63f3cc:       e3483f74      movt   r3, #36724    ; 0x8f74





*





*





*





LOAD off    0x00008000 vaddr 0x8f600000





0x47390=0x8f63f390 -0x8f600000+0x00008000





hexdump -s 0x47390 -n 1024 lk_s.elf





0047390 4ff0 e92d b020 e28d d024 e24d 6980 e300





00473a0 6f76 e348 e030 e50b 3000 e596 3028 e50b





00473b0 d709 ebff 0000 e350 006f 0a00 4487 ebff





00473c0 20d8 e309 33dc e30d 2f75 e348 3f74 e348





00473d0 1001 e240 0000 e582 1000 e583 424c ebff





00473e0 20c8 e309 33c8 e30d 2f75 e348 3f74 e348





00473f0 1001 e240 0000 e582 1000 e583 0a64 e300





0047400 0f71 e348 25f6 eb00 a00d e1a0 ff4f ebff





0047410 0a80 e300 0f71 e348 25f1 eb00 0c94 e309





0047420 0f72 e348 f3cb ebff 4239 ebff 3086 e280





0047430 7000 e1a0 3007 e3c3 0c80 e309 d003 e04d

这里机器码与反汇编码对应上了(android有相关签名验证链,一环套一环,无签名与许可不要尝试修改破解)

3、splash.img:

这个是开机画面镜像,生成工具是logo_gen.py,分析可以转多开机界面适配,多dtbo合入。

4、dtbo.img:

该镜像是设备树叠加层,官网有介绍,源码有mkdtimg工具制作和解析,该镜像只包含设备树叠加层,没有设备树,详细可以看看多开机界面适配,多dtbo合入。

5、mdtp.img:

高通源码直接复制而来,无法分析。

6、vbmeta.img:

用于安全验证,bootloader验证vbmeta的签名,再用vbmeta的key以及hash值验证dtbo/boot/system/vendor。

里面格式数据可以访问

https://android.googlesource.com/platform/external/avb/+/master/README.md#The-VBMeta-struct

后面的blog刷机相关的Android的安全中会用avbtool生成该镜像。

7、Linux rev 1.0 ext4 filesystem data(persist.img)

文件系统镜像

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15


mkdir test;





mount persist.img test;





sudo chmod 777 test

8、Android sparse image(system.img、userdata.img、vendor.img):

system.img、userdata.img、vendor.img、persist.img都是sparse压缩文件系统镜像,目的是方便传输/刷机/存储等。想查看内容需先转换为raw格式。

现在system和vendor后有追加哈希树。userdata加密后无法挂载。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21


simg2img system.img system_raw.img;





mkdir test;





mount system_raw.img test;





sudo chmod 777 test

9、Super.img :

super.img是几个只读分区的合并,file读取格式可能为data,android源码有lpunpack工具分解,提示无该命令可以先make lpunpack,

1
2
3


lpunpack super.img

查看服务器编译的system/vendor等sparse格式镜像时,如果挂载失败,可以尝试加只读参数才能来查看:

1
2
3


mount -o ro system_raw.img test;

该系列第二条线就是镜像分析,本博客主要以反汇编方式分析内核镜像。

后面几个blog也会分析其他文件。

android刷机方式将分析nonAB的OTA包中system.dat转换、AB的OTA包中payload.bin的转换。

刷机相关的Android的安全中将分析vbmeta,以及avbtool使用。还有如何查看SELinux规则文件,单编SELinux规则文件。

正确的刷机观中分析gpt文件,NV备份。

多开机界面,多设备树叠加层中将分析splash.img和dtbo.img镜像。