From 102a0743326a03cd1a1202ceda21e175b7d3575c Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Tue, 20 Feb 2024 01:20:52 +0000 Subject: [PATCH] add new system file --- kernel/scripts/unpack_bootimg | 577 ++++++++++++++++++++++++++++++++++++++++++++------------- 1 files changed, 447 insertions(+), 130 deletions(-) diff --git a/kernel/scripts/unpack_bootimg b/kernel/scripts/unpack_bootimg index 83c2bbe..a3f1a50 100755 --- a/kernel/scripts/unpack_bootimg +++ b/kernel/scripts/unpack_bootimg @@ -1,4 +1,5 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 +# # Copyright 2018, The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,18 +14,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""unpacks the bootimage. +"""Unpacks the boot image. Extracts the kernel, ramdisk, second bootloader, dtb and recovery dtbo images. """ -from __future__ import print_function -from argparse import ArgumentParser, FileType +from argparse import ArgumentParser, RawDescriptionHelpFormatter from struct import unpack import os +import shlex BOOT_IMAGE_HEADER_V3_PAGESIZE = 4096 -VENDOR_BOOT_IMAGE_HEADER_V3_SIZE = 2112 +VENDOR_RAMDISK_NAME_SIZE = 32 +VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE = 16 + def create_out_dir(dir_path): """creates a directory 'dir_path' if it does not exist""" @@ -50,196 +53,510 @@ def format_os_version(os_version): + if os_version == 0: + return None a = os_version >> 14 b = os_version >> 7 & ((1<<7) - 1) c = os_version & ((1<<7) - 1) - return '{}.{}.{}'.format(a, b, c) + return f'{a}.{b}.{c}' def format_os_patch_level(os_patch_level): + if os_patch_level == 0: + return None y = os_patch_level >> 4 y += 2000 m = os_patch_level & ((1<<4) - 1) - return '{:04d}-{:02d}'.format(y, m) + return f'{y:04d}-{m:02d}' -def print_os_version_patch_level(value): - os_version = value >> 11 - os_patch_level = value & ((1<<11) - 1) - print('os version: %s' % format_os_version(os_version)) - print('os patch level: %s' % format_os_patch_level(os_patch_level)) +def decode_os_version_patch_level(os_version_patch_level): + """Returns a tuple of (os_version, os_patch_level).""" + os_version = os_version_patch_level >> 11 + os_patch_level = os_version_patch_level & ((1<<11) - 1) + return (format_os_version(os_version), + format_os_patch_level(os_patch_level)) -def unpack_bootimage(args): +class BootImageInfoFormatter: + """Formats the boot image info.""" + + def format_pretty_text(self): + lines = [] + lines.append(f'boot magic: {self.boot_magic}') + + if self.header_version < 3: + lines.append(f'kernel_size: {self.kernel_size}') + lines.append( + f'kernel load address: {self.kernel_load_address:#010x}') + lines.append(f'ramdisk size: {self.ramdisk_size}') + lines.append( + f'ramdisk load address: {self.ramdisk_load_address:#010x}') + lines.append(f'second bootloader size: {self.second_size}') + lines.append( + f'second bootloader load address: ' + f'{self.second_load_address:#010x}') + lines.append( + f'kernel tags load address: {self.tags_load_address:#010x}') + lines.append(f'page size: {self.page_size}') + else: + lines.append(f'kernel_size: {self.kernel_size}') + lines.append(f'ramdisk size: {self.ramdisk_size}') + + lines.append(f'os version: {self.os_version}') + lines.append(f'os patch level: {self.os_patch_level}') + lines.append(f'boot image header version: {self.header_version}') + + if self.header_version < 3: + lines.append(f'product name: {self.product_name}') + + lines.append(f'command line args: {self.cmdline}') + + if self.header_version < 3: + lines.append(f'additional command line args: {self.extra_cmdline}') + + if self.header_version in {1, 2}: + lines.append(f'recovery dtbo size: {self.recovery_dtbo_size}') + lines.append( + f'recovery dtbo offset: {self.recovery_dtbo_offset:#018x}') + lines.append(f'boot header size: {self.boot_header_size}') + + if self.header_version == 2: + lines.append(f'dtb size: {self.dtb_size}') + lines.append(f'dtb address: {self.dtb_load_address:#018x}') + + if self.header_version >= 4: + lines.append( + f'boot.img signature size: {self.boot_signature_size}') + + return '\n'.join(lines) + + def format_mkbootimg_argument(self): + args = [] + args.extend(['--header_version', str(self.header_version)]) + if self.os_version: + args.extend(['--os_version', self.os_version]) + if self.os_patch_level: + args.extend(['--os_patch_level', self.os_patch_level]) + + args.extend(['--kernel', os.path.join(self.image_dir, 'kernel')]) + args.extend(['--ramdisk', os.path.join(self.image_dir, 'ramdisk')]) + + if self.header_version <= 2: + if self.second_size > 0: + args.extend(['--second', + os.path.join(self.image_dir, 'second')]) + if self.recovery_dtbo_size > 0: + args.extend(['--recovery_dtbo', + os.path.join(self.image_dir, 'recovery_dtbo')]) + if self.dtb_size > 0: + args.extend(['--dtb', os.path.join(self.image_dir, 'dtb')]) + + args.extend(['--pagesize', f'{self.page_size:#010x}']) + + # Kernel load address is base + kernel_offset in mkbootimg.py. + # However we don't know the value of 'base' when unpacking a boot + # image in this script, so we set 'base' to zero and 'kernel_offset' + # to the kernel load address, 'ramdisk_offset' to the ramdisk load + # address, ... etc. + args.extend(['--base', f'{0:#010x}']) + args.extend(['--kernel_offset', + f'{self.kernel_load_address:#010x}']) + args.extend(['--ramdisk_offset', + f'{self.ramdisk_load_address:#010x}']) + args.extend(['--second_offset', + f'{self.second_load_address:#010x}']) + args.extend(['--tags_offset', f'{self.tags_load_address:#010x}']) + + # dtb is added in boot image v2, and is absent in v1 or v0. + if self.header_version == 2: + # dtb_offset is uint64_t. + args.extend(['--dtb_offset', f'{self.dtb_load_address:#018x}']) + + args.extend(['--board', self.product_name]) + args.extend(['--cmdline', self.cmdline + self.extra_cmdline]) + else: + args.extend(['--cmdline', self.cmdline]) + + return args + + +def unpack_boot_image(boot_img, output_dir): """extracts kernel, ramdisk, second bootloader and recovery dtbo""" - kernel_ramdisk_second_info = unpack('9I', args.boot_img.read(9 * 4)) - version = kernel_ramdisk_second_info[8] - if version < 3: - print('kernel_size: %s' % kernel_ramdisk_second_info[0]) - print('kernel load address: %#x' % kernel_ramdisk_second_info[1]) - print('ramdisk size: %s' % kernel_ramdisk_second_info[2]) - print('ramdisk load address: %#x' % kernel_ramdisk_second_info[3]) - print('second bootloader size: %s' % kernel_ramdisk_second_info[4]) - print('second bootloader load address: %#x' % kernel_ramdisk_second_info[5]) - print('kernel tags load address: %#x' % kernel_ramdisk_second_info[6]) - print('page size: %s' % kernel_ramdisk_second_info[7]) - print_os_version_patch_level(unpack('I', args.boot_img.read(1 * 4))[0]) + info = BootImageInfoFormatter() + info.boot_magic = unpack('8s', boot_img.read(8))[0].decode() + + kernel_ramdisk_second_info = unpack('9I', boot_img.read(9 * 4)) + # header_version is always at [8] regardless of the value of header_version. + info.header_version = kernel_ramdisk_second_info[8] + + if info.header_version < 3: + info.kernel_size = kernel_ramdisk_second_info[0] + info.kernel_load_address = kernel_ramdisk_second_info[1] + info.ramdisk_size = kernel_ramdisk_second_info[2] + info.ramdisk_load_address = kernel_ramdisk_second_info[3] + info.second_size = kernel_ramdisk_second_info[4] + info.second_load_address = kernel_ramdisk_second_info[5] + info.tags_load_address = kernel_ramdisk_second_info[6] + info.page_size = kernel_ramdisk_second_info[7] + os_version_patch_level = unpack('I', boot_img.read(1 * 4))[0] else: - print('kernel_size: %s' % kernel_ramdisk_second_info[0]) - print('ramdisk size: %s' % kernel_ramdisk_second_info[1]) - print_os_version_patch_level(kernel_ramdisk_second_info[2]) + info.kernel_size = kernel_ramdisk_second_info[0] + info.ramdisk_size = kernel_ramdisk_second_info[1] + os_version_patch_level = kernel_ramdisk_second_info[2] + info.second_size = 0 + info.page_size = BOOT_IMAGE_HEADER_V3_PAGESIZE - print('boot image header version: %s' % version) + info.os_version, info.os_patch_level = decode_os_version_patch_level( + os_version_patch_level) - if version < 3: - product_name = cstr(unpack('16s', args.boot_img.read(16))[0].decode()) - print('product name: %s' % product_name) - cmdline = cstr(unpack('512s', args.boot_img.read(512))[0].decode()) - print('command line args: %s' % cmdline) + if info.header_version < 3: + info.product_name = cstr(unpack('16s', + boot_img.read(16))[0].decode()) + info.cmdline = cstr(unpack('512s', boot_img.read(512))[0].decode()) + boot_img.read(32) # ignore SHA + info.extra_cmdline = cstr(unpack('1024s', + boot_img.read(1024))[0].decode()) else: - cmdline = cstr(unpack('1536s', args.boot_img.read(1536))[0].decode()) - print('command line args: %s' % cmdline) + info.cmdline = cstr(unpack('1536s', + boot_img.read(1536))[0].decode()) - if version < 3: - args.boot_img.read(32) # ignore SHA - - if version < 3: - extra_cmdline = cstr(unpack('1024s', - args.boot_img.read(1024))[0].decode()) - print('additional command line args: %s' % extra_cmdline) - - if version < 3: - kernel_size = kernel_ramdisk_second_info[0] - ramdisk_size = kernel_ramdisk_second_info[2] - second_size = kernel_ramdisk_second_info[4] - page_size = kernel_ramdisk_second_info[7] + if info.header_version in {1, 2}: + info.recovery_dtbo_size = unpack('I', boot_img.read(1 * 4))[0] + info.recovery_dtbo_offset = unpack('Q', boot_img.read(8))[0] + info.boot_header_size = unpack('I', boot_img.read(4))[0] else: - kernel_size = kernel_ramdisk_second_info[0] - ramdisk_size = kernel_ramdisk_second_info[1] - second_size = 0 - page_size = BOOT_IMAGE_HEADER_V3_PAGESIZE + info.recovery_dtbo_size = 0 - if 0 < version < 3: - recovery_dtbo_size = unpack('I', args.boot_img.read(1 * 4))[0] - print('recovery dtbo size: %s' % recovery_dtbo_size) - recovery_dtbo_offset = unpack('Q', args.boot_img.read(8))[0] - print('recovery dtbo offset: %#x' % recovery_dtbo_offset) - boot_header_size = unpack('I', args.boot_img.read(4))[0] - print('boot header size: %s' % boot_header_size) + if info.header_version == 2: + info.dtb_size = unpack('I', boot_img.read(4))[0] + info.dtb_load_address = unpack('Q', boot_img.read(8))[0] else: - recovery_dtbo_size = 0 - if 1 < version < 3: - dtb_size = unpack('I', args.boot_img.read(4))[0] - print('dtb size: %s' % dtb_size) - dtb_load_address = unpack('Q', args.boot_img.read(8))[0] - print('dtb address: %#x' % dtb_load_address) - else: - dtb_size = 0 + info.dtb_size = 0 + info.dtb_load_address = 0 + if info.header_version >= 4: + info.boot_signature_size = unpack('I', boot_img.read(4))[0] + else: + info.boot_signature_size = 0 # The first page contains the boot header num_header_pages = 1 - num_kernel_pages = get_number_of_pages(kernel_size, page_size) - kernel_offset = page_size * num_header_pages # header occupies a page - image_info_list = [(kernel_offset, kernel_size, 'kernel')] + # Convenient shorthand. + page_size = info.page_size - num_ramdisk_pages = get_number_of_pages(ramdisk_size, page_size) + num_kernel_pages = get_number_of_pages(info.kernel_size, page_size) + kernel_offset = page_size * num_header_pages # header occupies a page + image_info_list = [(kernel_offset, info.kernel_size, 'kernel')] + + num_ramdisk_pages = get_number_of_pages(info.ramdisk_size, page_size) ramdisk_offset = page_size * (num_header_pages + num_kernel_pages ) # header + kernel - image_info_list.append((ramdisk_offset, ramdisk_size, 'ramdisk')) + image_info_list.append((ramdisk_offset, info.ramdisk_size, 'ramdisk')) - if second_size > 0: + if info.second_size > 0: second_offset = page_size * ( num_header_pages + num_kernel_pages + num_ramdisk_pages ) # header + kernel + ramdisk - image_info_list.append((second_offset, second_size, 'second')) + image_info_list.append((second_offset, info.second_size, 'second')) - if recovery_dtbo_size > 0: - image_info_list.append((recovery_dtbo_offset, recovery_dtbo_size, + if info.recovery_dtbo_size > 0: + image_info_list.append((info.recovery_dtbo_offset, + info.recovery_dtbo_size, 'recovery_dtbo')) - if dtb_size > 0: - num_second_pages = get_number_of_pages(second_size, page_size) - num_recovery_dtbo_pages = get_number_of_pages(recovery_dtbo_size, page_size) + if info.dtb_size > 0: + num_second_pages = get_number_of_pages(info.second_size, page_size) + num_recovery_dtbo_pages = get_number_of_pages( + info.recovery_dtbo_size, page_size) dtb_offset = page_size * ( - num_header_pages + num_kernel_pages + num_ramdisk_pages + num_second_pages + - num_recovery_dtbo_pages - ) + num_header_pages + num_kernel_pages + num_ramdisk_pages + + num_second_pages + num_recovery_dtbo_pages) - image_info_list.append((dtb_offset, dtb_size, 'dtb')) + image_info_list.append((dtb_offset, info.dtb_size, 'dtb')) - for image_info in image_info_list: - extract_image(image_info[0], image_info[1], args.boot_img, - os.path.join(args.out, image_info[2])) + if info.boot_signature_size > 0: + # boot signature only exists in boot.img version >= v4. + # There are only kernel and ramdisk pages before the signature. + boot_signature_offset = page_size * ( + num_header_pages + num_kernel_pages + num_ramdisk_pages) + + image_info_list.append((boot_signature_offset, info.boot_signature_size, + 'boot_signature')) + + create_out_dir(output_dir) + for offset, size, name in image_info_list: + extract_image(offset, size, boot_img, os.path.join(output_dir, name)) + info.image_dir = output_dir + + return info -def unpack_vendor_bootimage(args): - kernel_ramdisk_info = unpack('5I', args.boot_img.read(5 * 4)) - print('vendor boot image header version: %s' % kernel_ramdisk_info[0]) - print('kernel load address: %#x' % kernel_ramdisk_info[2]) - print('ramdisk load address: %#x' % kernel_ramdisk_info[3]) - print('vendor ramdisk size: %s' % kernel_ramdisk_info[4]) +class VendorBootImageInfoFormatter: + """Formats the vendor_boot image info.""" - cmdline = cstr(unpack('2048s', args.boot_img.read(2048))[0].decode()) - print('vendor command line args: %s' % cmdline) + def format_pretty_text(self): + lines = [] + lines.append(f'boot magic: {self.boot_magic}') + lines.append(f'vendor boot image header version: {self.header_version}') + lines.append(f'page size: {self.page_size:#010x}') + lines.append(f'kernel load address: {self.kernel_load_address:#010x}') + lines.append(f'ramdisk load address: {self.ramdisk_load_address:#010x}') + if self.header_version > 3: + lines.append( + f'vendor ramdisk total size: {self.vendor_ramdisk_size}') + else: + lines.append(f'vendor ramdisk size: {self.vendor_ramdisk_size}') + lines.append(f'vendor command line args: {self.cmdline}') + lines.append( + f'kernel tags load address: {self.tags_load_address:#010x}') + lines.append(f'product name: {self.product_name}') + lines.append(f'vendor boot image header size: {self.header_size}') + lines.append(f'dtb size: {self.dtb_size}') + lines.append(f'dtb address: {self.dtb_load_address:#018x}') + if self.header_version > 3: + lines.append( + f'vendor ramdisk table size: {self.vendor_ramdisk_table_size}') + lines.append('vendor ramdisk table: [') + indent = lambda level: ' ' * 4 * level + for entry in self.vendor_ramdisk_table: + (output_ramdisk_name, ramdisk_size, ramdisk_offset, + ramdisk_type, ramdisk_name, board_id) = entry + lines.append(indent(1) + f'{output_ramdisk_name}: ''{') + lines.append(indent(2) + f'size: {ramdisk_size}') + lines.append(indent(2) + f'offset: {ramdisk_offset}') + lines.append(indent(2) + f'type: {ramdisk_type:#x}') + lines.append(indent(2) + f'name: {ramdisk_name}') + lines.append(indent(2) + 'board_id: [') + stride = 4 + for row_idx in range(0, len(board_id), stride): + row = board_id[row_idx:row_idx + stride] + lines.append( + indent(3) + ' '.join(f'{e:#010x},' for e in row)) + lines.append(indent(2) + ']') + lines.append(indent(1) + '}') + lines.append(']') + lines.append( + f'vendor bootconfig size: {self.vendor_bootconfig_size}') - tags_load_address = unpack('I', args.boot_img.read(1 * 4))[0] - print('kernel tags load address: %#x' % tags_load_address) + return '\n'.join(lines) - product_name = cstr(unpack('16s', args.boot_img.read(16))[0].decode()) - print('product name: %s' % product_name) + def format_mkbootimg_argument(self): + args = [] + args.extend(['--header_version', str(self.header_version)]) + args.extend(['--pagesize', f'{self.page_size:#010x}']) + args.extend(['--base', f'{0:#010x}']) + args.extend(['--kernel_offset', f'{self.kernel_load_address:#010x}']) + args.extend(['--ramdisk_offset', f'{self.ramdisk_load_address:#010x}']) + args.extend(['--tags_offset', f'{self.tags_load_address:#010x}']) + args.extend(['--dtb_offset', f'{self.dtb_load_address:#018x}']) + args.extend(['--vendor_cmdline', self.cmdline]) + args.extend(['--board', self.product_name]) - dtb_size = unpack('2I', args.boot_img.read(2 * 4))[1] - print('dtb size: %s' % dtb_size) - dtb_load_address = unpack('Q', args.boot_img.read(8))[0] - print('dtb address: %#x' % dtb_load_address) + if self.dtb_size > 0: + args.extend(['--dtb', os.path.join(self.image_dir, 'dtb')]) - ramdisk_size = kernel_ramdisk_info[4] - page_size = kernel_ramdisk_info[1] + if self.header_version > 3: + args.extend(['--vendor_bootconfig', + os.path.join(self.image_dir, 'bootconfig')]) + for entry in self.vendor_ramdisk_table: + (output_ramdisk_name, _, _, ramdisk_type, + ramdisk_name, board_id) = entry + args.extend(['--ramdisk_type', str(ramdisk_type)]) + args.extend(['--ramdisk_name', ramdisk_name]) + for idx, e in enumerate(board_id): + if e: + args.extend([f'--board_id{idx}', f'{e:#010x}']) + vendor_ramdisk_path = os.path.join( + self.image_dir, output_ramdisk_name) + args.extend(['--vendor_ramdisk_fragment', vendor_ramdisk_path]) + else: + args.extend(['--vendor_ramdisk', + os.path.join(self.image_dir, 'vendor_ramdisk')]) + + return args + + +def unpack_vendor_boot_image(boot_img, output_dir): + info = VendorBootImageInfoFormatter() + info.boot_magic = unpack('8s', boot_img.read(8))[0].decode() + info.header_version = unpack('I', boot_img.read(4))[0] + info.page_size = unpack('I', boot_img.read(4))[0] + info.kernel_load_address = unpack('I', boot_img.read(4))[0] + info.ramdisk_load_address = unpack('I', boot_img.read(4))[0] + info.vendor_ramdisk_size = unpack('I', boot_img.read(4))[0] + info.cmdline = cstr(unpack('2048s', boot_img.read(2048))[0].decode()) + info.tags_load_address = unpack('I', boot_img.read(4))[0] + info.product_name = cstr(unpack('16s', boot_img.read(16))[0].decode()) + info.header_size = unpack('I', boot_img.read(4))[0] + info.dtb_size = unpack('I', boot_img.read(4))[0] + info.dtb_load_address = unpack('Q', boot_img.read(8))[0] + + # Convenient shorthand. + page_size = info.page_size # The first pages contain the boot header - num_boot_header_pages = get_number_of_pages(VENDOR_BOOT_IMAGE_HEADER_V3_SIZE, page_size) - num_boot_ramdisk_pages = get_number_of_pages(ramdisk_size, page_size) - ramdisk_offset = page_size * num_boot_header_pages - image_info_list = [(ramdisk_offset, ramdisk_size, 'vendor_ramdisk')] + num_boot_header_pages = get_number_of_pages(info.header_size, page_size) + num_boot_ramdisk_pages = get_number_of_pages( + info.vendor_ramdisk_size, page_size) + num_boot_dtb_pages = get_number_of_pages(info.dtb_size, page_size) + + ramdisk_offset_base = page_size * num_boot_header_pages + image_info_list = [] + + if info.header_version > 3: + info.vendor_ramdisk_table_size = unpack('I', boot_img.read(4))[0] + vendor_ramdisk_table_entry_num = unpack('I', boot_img.read(4))[0] + vendor_ramdisk_table_entry_size = unpack('I', boot_img.read(4))[0] + info.vendor_bootconfig_size = unpack('I', boot_img.read(4))[0] + num_vendor_ramdisk_table_pages = get_number_of_pages( + info.vendor_ramdisk_table_size, page_size) + vendor_ramdisk_table_offset = page_size * ( + num_boot_header_pages + num_boot_ramdisk_pages + num_boot_dtb_pages) + + vendor_ramdisk_table = [] + vendor_ramdisk_symlinks = [] + for idx in range(vendor_ramdisk_table_entry_num): + entry_offset = vendor_ramdisk_table_offset + ( + vendor_ramdisk_table_entry_size * idx) + boot_img.seek(entry_offset) + ramdisk_size = unpack('I', boot_img.read(4))[0] + ramdisk_offset = unpack('I', boot_img.read(4))[0] + ramdisk_type = unpack('I', boot_img.read(4))[0] + ramdisk_name = cstr(unpack( + f'{VENDOR_RAMDISK_NAME_SIZE}s', + boot_img.read(VENDOR_RAMDISK_NAME_SIZE))[0].decode()) + board_id = unpack( + f'{VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE}I', + boot_img.read( + 4 * VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE)) + output_ramdisk_name = f'vendor_ramdisk{idx:02}' + + image_info_list.append((ramdisk_offset_base + ramdisk_offset, + ramdisk_size, output_ramdisk_name)) + vendor_ramdisk_symlinks.append((output_ramdisk_name, ramdisk_name)) + vendor_ramdisk_table.append( + (output_ramdisk_name, ramdisk_size, ramdisk_offset, + ramdisk_type, ramdisk_name, board_id)) + + info.vendor_ramdisk_table = vendor_ramdisk_table + + bootconfig_offset = page_size * (num_boot_header_pages + + num_boot_ramdisk_pages + num_boot_dtb_pages + + num_vendor_ramdisk_table_pages) + image_info_list.append((bootconfig_offset, info.vendor_bootconfig_size, + 'bootconfig')) + else: + image_info_list.append( + (ramdisk_offset_base, info.vendor_ramdisk_size, 'vendor_ramdisk')) dtb_offset = page_size * (num_boot_header_pages + num_boot_ramdisk_pages ) # header + vendor_ramdisk - image_info_list.append((dtb_offset, dtb_size, 'dtb')) + if info.dtb_size > 0: + image_info_list.append((dtb_offset, info.dtb_size, 'dtb')) - for image_info in image_info_list: - extract_image(image_info[0], image_info[1], args.boot_img, - os.path.join(args.out, image_info[2])) + create_out_dir(output_dir) + for offset, size, name in image_info_list: + extract_image(offset, size, boot_img, os.path.join(output_dir, name)) + info.image_dir = output_dir + + if info.header_version > 3: + vendor_ramdisk_by_name_dir = os.path.join( + output_dir, 'vendor-ramdisk-by-name') + create_out_dir(vendor_ramdisk_by_name_dir) + for src, dst in vendor_ramdisk_symlinks: + src_pathname = os.path.join('..', src) + dst_pathname = os.path.join( + vendor_ramdisk_by_name_dir, f'ramdisk_{dst}') + if os.path.lexists(dst_pathname): + os.remove(dst_pathname) + os.symlink(src_pathname, dst_pathname) + + return info -def unpack_image(args): - boot_magic = unpack('8s', args.boot_img.read(8))[0].decode() - print('boot_magic: %s' % boot_magic) - if boot_magic == "ANDROID!": - unpack_bootimage(args) - elif boot_magic == "VNDRBOOT": - unpack_vendor_bootimage(args) +def unpack_bootimg(boot_img, output_dir): + """Unpacks the |boot_img| to |output_dir|, and returns the 'info' object.""" + with open(boot_img, 'rb') as image_file: + boot_magic = unpack('8s', image_file.read(8))[0].decode() + image_file.seek(0) + if boot_magic == 'ANDROID!': + info = unpack_boot_image(image_file, output_dir) + elif boot_magic == 'VNDRBOOT': + info = unpack_vendor_boot_image(image_file, output_dir) + else: + raise ValueError(f'Not an Android boot image, magic: {boot_magic}') + + return info + + +def print_bootimg_info(info, output_format, null_separator): + """Format and print boot image info.""" + if output_format == 'mkbootimg': + mkbootimg_args = info.format_mkbootimg_argument() + if null_separator: + print('\0'.join(mkbootimg_args) + '\0', end='') + else: + print(shlex.join(mkbootimg_args)) + else: + print(info.format_pretty_text()) + + +def get_unpack_usage(): + return """Output format: + + * info + + Pretty-printed info-rich text format suitable for human inspection. + + * mkbootimg + + Output shell-escaped (quoted) argument strings that can be used to + reconstruct the boot image. For example: + + $ unpack_bootimg --boot_img vendor_boot.img --out out --format=mkbootimg | + tee mkbootimg_args + $ sh -c "mkbootimg $(cat mkbootimg_args) --vendor_boot repacked.img" + + vendor_boot.img and repacked.img would be equivalent. + + If the -0 option is specified, output unescaped null-terminated argument + strings that are suitable to be parsed by a shell script (xargs -0 format): + + $ unpack_bootimg --boot_img vendor_boot.img --out out --format=mkbootimg \\ + -0 | tee mkbootimg_args + $ declare -a MKBOOTIMG_ARGS=() + $ while IFS= read -r -d '' ARG; do + MKBOOTIMG_ARGS+=("${ARG}") + done <mkbootimg_args + $ mkbootimg "${MKBOOTIMG_ARGS[@]}" --vendor_boot repacked.img +""" def parse_cmdline(): """parse command line arguments""" parser = ArgumentParser( - description='Unpacks boot.img/recovery.img, extracts the kernel,' - 'ramdisk, second bootloader, recovery dtbo and dtb') - parser.add_argument( - '--boot_img', - help='path to boot image', - type=FileType('rb'), - required=True) - parser.add_argument('--out', help='path to out binaries', default='out') + formatter_class=RawDescriptionHelpFormatter, + description='Unpacks boot, recovery or vendor_boot image.', + epilog=get_unpack_usage(), + ) + parser.add_argument('--boot_img', required=True, + help='path to the boot, recovery or vendor_boot image') + parser.add_argument('--out', default='out', + help='output directory of the unpacked images') + parser.add_argument('--format', choices=['info', 'mkbootimg'], + default='info', + help='text output format (default: info)') + parser.add_argument('-0', '--null', action='store_true', + help='output null-terminated argument strings') return parser.parse_args() def main(): """parse arguments and unpack boot image""" args = parse_cmdline() - create_out_dir(args.out) - unpack_image(args) + info = unpack_bootimg(args.boot_img, args.out) + print_bootimg_info(info, args.format, args.null) if __name__ == '__main__': -- Gitblit v1.6.2