1 #!/usr/bin/env python 2 # Copyright 2018, The Android Open Source Project 3 # 4 # Licensed under the Apache License, Version 2.0 (the "License"); 5 # you may not use this file except in compliance with the License. 6 # You may obtain a copy of the License at 7 # 8 # http://www.apache.org/licenses/LICENSE-2.0 9 # 10 # Unless required by applicable law or agreed to in writing, software 11 # distributed under the License is distributed on an "AS IS" BASIS, 12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 # See the License for the specific language governing permissions and 14 # limitations under the License. 15 16 """unpacks the bootimage. 17 18 Extracts the kernel, ramdisk, second bootloader and recovery dtbo images. 19 """ 20 21 from __future__ import print_function 22 from argparse import ArgumentParser, FileType 23 from struct import unpack 24 import os 25 26 27 def create_out_dir(dir_path): 28 """creates a directory 'dir_path' if it does not exist""" 29 if not os.path.exists(dir_path): 30 os.makedirs(dir_path) 31 32 33 def extract_image(offset, size, bootimage, extracted_image_name): 34 """extracts an image from the bootimage""" 35 bootimage.seek(offset) 36 with open(extracted_image_name, 'wb') as file_out: 37 file_out.write(bootimage.read(size)) 38 39 40 def get_number_of_pages(image_size, page_size): 41 """calculates the number of pages required for the image""" 42 return (image_size + page_size - 1) / page_size 43 44 45 def unpack_bootimage(args): 46 """extracts kernel, ramdisk, second bootloader and recovery dtbo""" 47 boot_magic = unpack('8s', args.boot_img.read(8)) 48 print('boot_magic: %s' % boot_magic) 49 kernel_ramdisk_second_info = unpack('10I', args.boot_img.read(10 * 4)) 50 print('kernel_size: %s' % kernel_ramdisk_second_info[0]) 51 print('kernel load address: %s' % kernel_ramdisk_second_info[1]) 52 print('ramdisk size: %s' % kernel_ramdisk_second_info[2]) 53 print('ramdisk load address: %s' % kernel_ramdisk_second_info[3]) 54 print('second bootloader size: %s' % kernel_ramdisk_second_info[4]) 55 print('second bootloader load address: %s' % kernel_ramdisk_second_info[5]) 56 print('kernel tags load address: %s' % kernel_ramdisk_second_info[6]) 57 print('page size: %s' % kernel_ramdisk_second_info[7]) 58 print('boot image header version: %s' % kernel_ramdisk_second_info[8]) 59 print('os version and patch level: %s' % kernel_ramdisk_second_info[9]) 60 61 product_name = unpack('16s', args.boot_img.read(16)) 62 print('product name: %s' % product_name) 63 cmdline = unpack('512s', args.boot_img.read(512)) 64 print('command line args: %s' % cmdline) 65 66 args.boot_img.read(32) # ignore SHA 67 68 extra_cmdline = unpack('1024s', args.boot_img.read(1024)) 69 print('additional command line args: %s' % extra_cmdline) 70 71 kernel_size = kernel_ramdisk_second_info[0] 72 ramdisk_size = kernel_ramdisk_second_info[2] 73 second_size = kernel_ramdisk_second_info[4] 74 page_size = kernel_ramdisk_second_info[7] 75 version = kernel_ramdisk_second_info[8] 76 if version > 0: 77 recovery_dtbo_size = unpack('I', args.boot_img.read(1 * 4))[0] 78 print('recovery dtbo size: %s' % recovery_dtbo_size) 79 recovery_dtbo_offset = unpack('Q', args.boot_img.read(8))[0] 80 print('recovery dtbo offset: %s' % recovery_dtbo_offset) 81 boot_header_size = unpack('I', args.boot_img.read(4))[0] 82 print('boot header size: %s' % boot_header_size) 83 else: 84 recovery_dtbo_size = 0 85 86 # The first page contains the boot header 87 num_header_pages = 1 88 89 num_kernel_pages = get_number_of_pages(kernel_size, page_size) 90 kernel_offset = page_size * num_header_pages # header occupies a page 91 image_info_list = [(kernel_offset, kernel_size, 'kernel')] 92 93 num_ramdisk_pages = get_number_of_pages(ramdisk_size, page_size) 94 ramdisk_offset = page_size * (num_header_pages + num_kernel_pages 95 ) # header + kernel 96 image_info_list.append((ramdisk_offset, ramdisk_size, 'ramdisk')) 97 98 second_offset = page_size * ( 99 num_header_pages + num_kernel_pages + num_ramdisk_pages 100 ) # header + kernel + ramdisk 101 image_info_list.append((second_offset, second_size, 'second')) 102 103 if recovery_dtbo_size > 0: 104 image_info_list.append((recovery_dtbo_offset, recovery_dtbo_size, 105 'recovery_dtbo')) 106 107 for image_info in image_info_list: 108 extract_image(image_info[0], image_info[1], args.boot_img, 109 os.path.join(args.out, image_info[2])) 110 111 112 def parse_cmdline(): 113 """parse command line arguments""" 114 parser = ArgumentParser( 115 description='Unpacks boot.img/recovery.img, extracts the kernel,' 116 'ramdisk, second bootloader and recovery dtbo') 117 parser.add_argument( 118 '--boot_img', 119 help='path to boot image', 120 type=FileType('rb'), 121 required=True) 122 parser.add_argument('--out', help='path to out binaries', default='out') 123 return parser.parse_args() 124 125 126 def main(): 127 """parse arguments and unpack boot image""" 128 args = parse_cmdline() 129 create_out_dir(args.out) 130 unpack_bootimage(args) 131 132 133 if __name__ == '__main__': 134 main() 135