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 18Extracts the kernel, ramdisk, second bootloader, dtb and recovery dtbo images. 19""" 20 21from __future__ import print_function 22from argparse import ArgumentParser, FileType 23from struct import unpack 24import os 25 26 27def 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 33def 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 40def 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 45def 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: %#x' % kernel_ramdisk_second_info[1]) 52 print('ramdisk size: %s' % kernel_ramdisk_second_info[2]) 53 print('ramdisk load address: %#x' % kernel_ramdisk_second_info[3]) 54 print('second bootloader size: %s' % kernel_ramdisk_second_info[4]) 55 print('second bootloader load address: %#x' % kernel_ramdisk_second_info[5]) 56 print('kernel tags load address: %#x' % 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: %#x' % 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 if version > 1: 86 dtb_size = unpack('I', args.boot_img.read(4))[0] 87 print('dtb size: %s' % dtb_size) 88 dtb_load_address = unpack('Q', args.boot_img.read(8))[0] 89 print('dtb address: %#x' % dtb_load_address) 90 else: 91 dtb_size = 0 92 93 94 # The first page contains the boot header 95 num_header_pages = 1 96 97 num_kernel_pages = get_number_of_pages(kernel_size, page_size) 98 kernel_offset = page_size * num_header_pages # header occupies a page 99 image_info_list = [(kernel_offset, kernel_size, 'kernel')] 100 101 num_ramdisk_pages = get_number_of_pages(ramdisk_size, page_size) 102 ramdisk_offset = page_size * (num_header_pages + num_kernel_pages 103 ) # header + kernel 104 image_info_list.append((ramdisk_offset, ramdisk_size, 'ramdisk')) 105 106 if second_size > 0: 107 second_offset = page_size * ( 108 num_header_pages + num_kernel_pages + num_ramdisk_pages 109 ) # header + kernel + ramdisk 110 image_info_list.append((second_offset, second_size, 'second')) 111 112 if recovery_dtbo_size > 0: 113 image_info_list.append((recovery_dtbo_offset, recovery_dtbo_size, 114 'recovery_dtbo')) 115 if dtb_size > 0: 116 num_second_pages = get_number_of_pages(second_size, page_size) 117 num_recovery_dtbo_pages = get_number_of_pages(recovery_dtbo_size, page_size) 118 dtb_offset = page_size * ( 119 num_header_pages + num_kernel_pages + num_ramdisk_pages + num_second_pages + 120 num_recovery_dtbo_pages 121 ) 122 123 image_info_list.append((dtb_offset, dtb_size, 'dtb')) 124 125 for image_info in image_info_list: 126 extract_image(image_info[0], image_info[1], args.boot_img, 127 os.path.join(args.out, image_info[2])) 128 129 130def parse_cmdline(): 131 """parse command line arguments""" 132 parser = ArgumentParser( 133 description='Unpacks boot.img/recovery.img, extracts the kernel,' 134 'ramdisk, second bootloader, recovery dtbo and dtb') 135 parser.add_argument( 136 '--boot_img', 137 help='path to boot image', 138 type=FileType('rb'), 139 required=True) 140 parser.add_argument('--out', help='path to out binaries', default='out') 141 return parser.parse_args() 142 143 144def main(): 145 """parse arguments and unpack boot image""" 146 args = parse_cmdline() 147 create_out_dir(args.out) 148 unpack_bootimage(args) 149 150 151if __name__ == '__main__': 152 main() 153