Skip to content

Instantly share code, notes, and snippets.

@axsddlr
Forked from jberkel/split_bootimg.pl
Last active August 29, 2015 14:07
Show Gist options
  • Select an option

  • Save axsddlr/4803eb1d2060ff956edd to your computer and use it in GitHub Desktop.

Select an option

Save axsddlr/4803eb1d2060ff956edd to your computer and use it in GitHub Desktop.

Revisions

  1. @jberkel jberkel created this gist Jul 17, 2011.
    221 changes: 221 additions & 0 deletions split_bootimg.pl
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,221 @@
    #!/usr/bin/perl
    ######################################################################
    #
    # File : split_bootimg.pl
    # Author(s) : William Enck <enck@cse.psu.edu>
    # Description : Split appart an Android boot image created
    # with mkbootimg. The format can be found in
    # android-src/system/core/mkbootimg/bootimg.h
    #
    # Thanks to alansj on xda-developers.com for
    # identifying the format in bootimg.h and
    # describing initial instructions for splitting
    # the boot.img file.
    #
    # Last Modified : Tue Dec 2 23:36:25 EST 2008
    # By : William Enck <enck@cse.psu.edu>
    #
    # Copyright (c) 2008 William Enck
    #
    ######################################################################

    use strict;
    use warnings;

    # Turn on print flushing
    $|++;

    ######################################################################
    ## Global Variables and Constants

    my $SCRIPT = __FILE__;
    my $IMAGE_FN = undef;

    # Constants (from bootimg.h)
    use constant BOOT_MAGIC => 'ANDROID!';
    use constant BOOT_MAGIC_SIZE => 8;
    use constant BOOT_NAME_SIZE => 16;
    use constant BOOT_ARGS_SIZE => 512;

    # Unsigned integers are 4 bytes
    use constant UNSIGNED_SIZE => 4;

    # Parsed Values
    my $PAGE_SIZE = undef;
    my $KERNEL_SIZE = undef;
    my $RAMDISK_SIZE = undef;
    my $SECOND_SIZE = undef;

    ######################################################################
    ## Main Code

    &parse_cmdline();
    &parse_header($IMAGE_FN);

    =format (from bootimg.h)
    ** +-----------------+
    ** | boot header | 1 page
    ** +-----------------+
    ** | kernel | n pages
    ** +-----------------+
    ** | ramdisk | m pages
    ** +-----------------+
    ** | second stage | o pages
    ** +-----------------+
    **
    ** n = (kernel_size + page_size - 1) / page_size
    ** m = (ramdisk_size + page_size - 1) / page_size
    ** o = (second_size + page_size - 1) / page_size
    =cut

    my $n = int(($KERNEL_SIZE + $PAGE_SIZE - 1) / $PAGE_SIZE);
    my $m = int(($RAMDISK_SIZE + $PAGE_SIZE - 1) / $PAGE_SIZE);
    my $o = int(($SECOND_SIZE + $PAGE_SIZE - 1) / $PAGE_SIZE);

    my $k_offset = $PAGE_SIZE;
    my $r_offset = $k_offset + ($n * $PAGE_SIZE);
    my $s_offset = $r_offset + ($m * $PAGE_SIZE);

    (my $base = $IMAGE_FN) =~ s/.*\/(.*)$/$1/;
    my $k_file = $base . "-kernel";
    my $r_file = $base . "-ramdisk.gz";
    my $s_file = $base . "-second.gz";

    # The kernel is always there
    print "Writing $k_file ...";
    &dump_file($IMAGE_FN, $k_file, $k_offset, $KERNEL_SIZE);
    print " complete.\n";

    # The ramdisk is always there
    print "Writing $r_file ...";
    &dump_file($IMAGE_FN, $r_file, $r_offset, $RAMDISK_SIZE);
    print " complete.\n";

    # The Second stage bootloader is optional
    unless ($SECOND_SIZE == 0) {
    print "Writing $s_file ...";
    &dump_file($IMAGE_FN, $s_file, $s_offset, $SECOND_SIZE);
    print " complete.\n";
    }

    ######################################################################
    ## Supporting Subroutines

    =header_format (from bootimg.h)
    struct boot_img_hdr
    {
    unsigned char magic[BOOT_MAGIC_SIZE];
    unsigned kernel_size; /* size in bytes */
    unsigned kernel_addr; /* physical load addr */
    unsigned ramdisk_size; /* size in bytes */
    unsigned ramdisk_addr; /* physical load addr */
    unsigned second_size; /* size in bytes */
    unsigned second_addr; /* physical load addr */
    unsigned tags_addr; /* physical addr for kernel tags */
    unsigned page_size; /* flash page size we assume */
    unsigned unused[2]; /* future expansion: should be 0 */
    unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */
    unsigned char cmdline[BOOT_ARGS_SIZE];
    unsigned id[8]; /* timestamp / checksum / sha1 / etc */
    };
    =cut
    sub parse_header {
    my ($fn) = @_;
    my $buf = undef;

    open INF, $fn or die "Could not open $fn: $!\n";
    binmode INF;

    # Read the Magic
    read(INF, $buf, BOOT_MAGIC_SIZE);
    unless ($buf eq BOOT_MAGIC) {
    die "Android Magic not found in $fn. Giving up.\n";
    }

    # Read kernel size and address (assume little-endian)
    read(INF, $buf, UNSIGNED_SIZE * 2);
    my ($k_size, $k_addr) = unpack("VV", $buf);

    # Read ramdisk size and address (assume little-endian)
    read(INF, $buf, UNSIGNED_SIZE * 2);
    my ($r_size, $r_addr) = unpack("VV", $buf);

    # Read second size and address (assume little-endian)
    read(INF, $buf, UNSIGNED_SIZE * 2);
    my ($s_size, $s_addr) = unpack("VV", $buf);

    # Ignore tags_addr
    read(INF, $buf, UNSIGNED_SIZE);

    # get the page size (assume little-endian)
    read(INF, $buf, UNSIGNED_SIZE);
    my ($p_size) = unpack("V", $buf);

    # Ignore unused
    read(INF, $buf, UNSIGNED_SIZE * 2);

    # Read the name (board name)
    read(INF, $buf, BOOT_NAME_SIZE);
    my $name = $buf;

    # Read the command line
    read(INF, $buf, BOOT_ARGS_SIZE);
    my $cmdline = $buf;

    # Ignore the id
    read(INF, $buf, UNSIGNED_SIZE * 8);

    # Close the file
    close INF;

    # Print important values
    printf "Page size: %d (0x%08x)\n", $p_size, $p_size;
    printf "Kernel size: %d (0x%08x)\n", $k_size, $k_size;
    printf "Ramdisk size: %d (0x%08x)\n", $r_size, $r_size;
    printf "Second size: %d (0x%08x)\n", $s_size, $s_size;
    printf "Board name: $name\n";
    printf "Command line: $cmdline\n";

    # Save the values
    $PAGE_SIZE = $p_size;
    $KERNEL_SIZE = $k_size;
    $RAMDISK_SIZE = $r_size;
    $SECOND_SIZE = $s_size;
    }

    sub dump_file {
    my ($infn, $outfn, $offset, $size) = @_;
    my $buf = undef;

    open INF, $infn or die "Could not open $infn: $!\n";
    open OUTF, ">$outfn" or die "Could not open $outfn: $!\n";

    binmode INF;
    binmode OUTF;

    seek(INF, $offset, 0) or die "Could not seek in $infn: $!\n";
    read(INF, $buf, $size) or die "Could not read $infn: $!\n";
    print OUTF $buf or die "Could not write $outfn: $!\n";

    close INF;
    close OUTF;
    }

    ######################################################################
    ## Configuration Subroutines

    sub parse_cmdline {
    unless ($#ARGV == 0) {
    die "Usage: $SCRIPT boot.img\n";
    }
    $IMAGE_FN = $ARGV[0];
    }