diff options
-rwxr-xr-x | util/extract-version-hash-from-factory.bash | 15 | ||||
-rwxr-xr-x | util/unpack_bootimg.py | 246 |
2 files changed, 258 insertions, 3 deletions
diff --git a/util/extract-version-hash-from-factory.bash b/util/extract-version-hash-from-factory.bash index a9e0778..8e95a5b 100755 --- a/util/extract-version-hash-from-factory.bash +++ b/util/extract-version-hash-from-factory.bash @@ -5,6 +5,7 @@ URL="$1" # Expecting URL like https://dl.google.com/dl/android/aosp/crosshatch-qp1a.191005.007-factory-2989a08d.zip [[ -n $URL ]] || { echo "Usage: $0 URL" >&2; exit 1; } +UTIL="$(dirname "$(readlink -f "$0")")" D="$(mktemp -d)" trap 'rm -rf "$D"' INT TERM EXIT cd "$D" @@ -12,8 +13,16 @@ cd "$D" curl -#o out.zip "$URL" bsdtar --strip-components 1 -xvf out.zip bsdtar -xvf image-*.zip boot.img -abootimg -x boot.img -unlz4 zImage Image -version="$(strings Image | grep '^Linux version [^%]' | head -n 1)" +if [[ $URL =~ bramble || $URL =~ redfin ]]; then + "$UTIL"/unpack_bootimg.py --boot_img boot.img + COMP_IMG=out/kernel + DECOMP_IMG=out/kernel-decomp +else + abootimg -x boot.img + COMP_IMG=zImage + DECOMP_IMG=Image +fi +unlz4 "$COMP_IMG" "$DECOMP_IMG" +version="$(strings "$DECOMP_IMG" | grep '^Linux version [^%]' | head -n 1)" [[ -n $version ]] || { echo "ERROR: no proper version in image" >&2; exit 1; } printf '\n==========================================\n\n%s|%s\n' "$(echo "$version" | sha256sum | cut -d ' ' -f 1)" "$version" diff --git a/util/unpack_bootimg.py b/util/unpack_bootimg.py new file mode 100755 index 0000000..83c2bbe --- /dev/null +++ b/util/unpack_bootimg.py @@ -0,0 +1,246 @@ +#!/usr/bin/env python +# Copyright 2018, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""unpacks the bootimage. + +Extracts the kernel, ramdisk, second bootloader, dtb and recovery dtbo images. +""" + +from __future__ import print_function +from argparse import ArgumentParser, FileType +from struct import unpack +import os + +BOOT_IMAGE_HEADER_V3_PAGESIZE = 4096 +VENDOR_BOOT_IMAGE_HEADER_V3_SIZE = 2112 + +def create_out_dir(dir_path): + """creates a directory 'dir_path' if it does not exist""" + if not os.path.exists(dir_path): + os.makedirs(dir_path) + + +def extract_image(offset, size, bootimage, extracted_image_name): + """extracts an image from the bootimage""" + bootimage.seek(offset) + with open(extracted_image_name, 'wb') as file_out: + file_out.write(bootimage.read(size)) + + +def get_number_of_pages(image_size, page_size): + """calculates the number of pages required for the image""" + return (image_size + page_size - 1) // page_size + + +def cstr(s): + """Remove first NULL character and any character beyond.""" + return s.split('\0', 1)[0] + + +def format_os_version(os_version): + a = os_version >> 14 + b = os_version >> 7 & ((1<<7) - 1) + c = os_version & ((1<<7) - 1) + return '{}.{}.{}'.format(a, b, c) + + +def format_os_patch_level(os_patch_level): + y = os_patch_level >> 4 + y += 2000 + m = os_patch_level & ((1<<4) - 1) + return '{:04d}-{:02d}'.format(y, m) + + +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 unpack_bootimage(args): + """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]) + 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]) + + print('boot image header version: %s' % version) + + 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) + else: + cmdline = cstr(unpack('1536s', args.boot_img.read(1536))[0].decode()) + print('command line args: %s' % cmdline) + + 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] + 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 + + 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) + 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 + + + # 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')] + + num_ramdisk_pages = get_number_of_pages(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')) + + if 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')) + + if recovery_dtbo_size > 0: + image_info_list.append((recovery_dtbo_offset, 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) + dtb_offset = page_size * ( + 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')) + + 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])) + + +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]) + + cmdline = cstr(unpack('2048s', args.boot_img.read(2048))[0].decode()) + print('vendor command line args: %s' % cmdline) + + tags_load_address = unpack('I', args.boot_img.read(1 * 4))[0] + print('kernel tags load address: %#x' % tags_load_address) + + product_name = cstr(unpack('16s', args.boot_img.read(16))[0].decode()) + print('product name: %s' % 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) + + ramdisk_size = kernel_ramdisk_info[4] + page_size = kernel_ramdisk_info[1] + + # 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')] + + dtb_offset = page_size * (num_boot_header_pages + num_boot_ramdisk_pages + ) # header + vendor_ramdisk + image_info_list.append((dtb_offset, 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])) + + +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 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') + return parser.parse_args() + + +def main(): + """parse arguments and unpack boot image""" + args = parse_cmdline() + create_out_dir(args.out) + unpack_image(args) + + +if __name__ == '__main__': + main() |