#!/usr/bin/env perl # android-split-bootimg -- get kernel and ramdisk from android mtd boot/recovery partition images # Author: Noah Friedman # Created: 2009-07-22 # Public domain # $Id: android-split-bootimg,v 1.1 2009/07/23 02:49:44 friedman Exp $ $^W = 1; # enable warnings use strict; use Symbol; use Fcntl qw(:DEFAULT :seek); # Header information and order taken from android/system/core/mkbootimg/bootimg.h my $bootimg_hdr_template = "A8L10Z16Z512L8"; my @bootimg_hdr_field_order = (qw(magic kernel_size kernel_addr ramdisk_size ramdisk_addr second_size second_addr tags_addr page_size unused0 unused1 name cmdline)); # Ignore these. I don't know what kind of checksum the SHA algorithm in # bionic is writing out but it's only 12 bytes long and doesn't seem to # match the openssl "sha" digest, which is 20 bytes. In any case their # main purpose is to make it possible to differentiate boot images based on # their first page. # id0 id1 id2 id3 id4 id5 id6 id7)); sub xopen { my ($filename, $flags) = @_; my $fh = gensym; unless (sysopen ($fh, $filename, $flags, 0666)) { print STDERR "$0: $filename: $!\n"; exit (1); } return $fh; } # read as much as possible until eof; don't return partial reads sub xread { my ($fh, $size, $offset) = @_[0,2,3]; # buffer $_[1] modified in-place return 0 unless defined $size && $size > 0; $offset = 0 unless defined $offset; my $total = 0; while ($total < $size) { my $rsz = sysread ($fh, $_[1], $size - $total, $offset + $total); return $rsz if $rsz < 0; # error last if $rsz == 0; # eof $total += $rsz; } return $total; } # Compute the offset of the next page after $off, according to page size. sub next_page_offset { use integer; my ($off, $hdr) = @_; my $page_size = $hdr->{page_size}; my $remainder = $off % $page_size; return $off - $remainder + $page_size; } sub xseek_to_next_page { my ($fh, $hdr) = @_; my $cur = sysseek ($fh, 0, SEEK_CUR); sysseek ($fh, next_page_offset ($cur, $hdr), SEEK_SET); } sub xdump { my $filename = $_[0]; my $fh = xopen ($filename, O_WRONLY|O_CREAT|O_EXCL); print $fh $_[1]; close ($fh); } sub header { my @result = unpack ($bootimg_hdr_template, $_[0]); my %hdr; for my $field (@bootimg_hdr_field_order) { $hdr{$field} = shift @result; } return \%hdr; } sub print_header { my ($hdr) = @_; for my $field (@bootimg_hdr_field_order) { my $fmt = "%-12s = %s\n"; if ($field =~ /size|unused/) { $fmt = "%-12s = %10u\n" } elsif ($field =~ /addr$/) { $fmt = "%-12s = 0x%x\n" } printf ($fmt, $field, $hdr->{$field}); } } sub write_image { my ($fh, $hdr, $filename, $size) = @_; local $_; xseek_to_next_page ($fh, $hdr); xread ($fh, $_, $size); xdump ($filename, $_); print "Wrote $filename\n"; } sub main { local $_; my ($filename) = @_; my $fh = xopen ($filename, O_RDONLY); xread ($fh, $_, 1024); my $hdr = header ($_); if ($hdr->{magic} ne "ANDROID!") { print STDERR "$0: $filename does not appear to be a boot image\n"; exit (1); } print_header ($hdr); print "\n"; write_image ($fh, $hdr, "kernel.img", $hdr->{kernel_size}); write_image ($fh, $hdr, "ramdisk.img", $hdr->{ramdisk_size}); write_image ($fh, $hdr, "second.img", $hdr->{second_size}) if $hdr->{second_size}; print "Done!\n"; } main (@ARGV); # eof