#!/usr/bin/perl
#
# extract_initramfs.pl: extracts the initramfs out of a packed kernel image
#
# Copyright (C) Martin Schlemmer <azarah@nosferatu.za.org>
#
# Released under the terms of the GNU GPL
#

use strict;
use warnings;

use Cwd;
use Encode;
use IO::File;
use IO::Pipe;
use IO::Uncompress::Gunzip qw (gunzip $GunzipError);

if (! defined ($ARGV[0]) && ! defined ($ARGV[1])) {
	print "usage: $0 VMLINUZ [VMLINUX] [INITRAMFS_IMAGE]\n";
	exit (1);
}

if (! -f $ARGV[0]) {
	print "$ARGV[0] does not exists\n";
	exit (1);
}

my ($input,$vm_output,$rm_output) = @ARGV;

$vm_output = cwd . '/vmlinux',
	if (! defined ($vm_output) || $vm_output eq '');
$rm_output = cwd . '/initramfs.cpio.gz',
	if (! defined ($rm_output) || $rm_output eq '');

eval {
	my $fh = IO::File->new;

	$fh->open ($ARGV[0]) or die ($!);
	$fh->binmode;

	my $data;
	my $count = 0;

	do {
		$data = '';

		$fh->seek ($count++, 0) or die ($!);
		$fh->read ($data, 3) or die ($!);

	} until ($data =~ /^\x{1F}\x{8B}\x{08}$/ || $fh->eof);

	if ($data !~ /^\x{1F}\x{8B}\x{08}$/ || $fh->eof) {
		print STDERR "Could not find GZIP marker!\n";
		exit (1);
	}

	$fh->seek (--$count, 0) or die ($!);

	$data = '';

	while (! $fh->eof) {
		my $buffer;

		$fh->read ($buffer, 2048) or die ($!);
		$data .= $buffer;
	}
	$fh->close;

	my $image;

	gunzip \$data => \$image,
        or die ("Gunzip failed: $GunzipError\n");

	$fh->open ($vm_output, '>') or die ($!);
	$fh->binmode;
	$fh->write ($image) or die ($!);
	$fh->close;

	my $have_ramfs = 0;
	my $pipe = IO::Pipe->new or die ($!);

	$pipe->reader ('objdump', '-h', $vm_output) or die ($!);

	while (my $line = $pipe->getline) {
		if ($line =~ /^\s+\d+\s+(\S+)\s+(\S+)\s+\S+\s+\S+\s+(\S+)\s+/) {
			my ($section,$size,$offset) = ($1,$2,$3);

			next if ($section ne '.init.ramfs');

			$fh->open ($rm_output, '>') or die ($!);
			$fh->write ($image, hex ($size), hex ($offset)) or die ($!);
			$fh->close;

			$have_ramfs = 1;

			last;
		}
	}

	$pipe->close;

	die ('No \'.init.ramfs\' section found!') if (! $have_ramfs);
};
if ($@) {
	printf STDERR "$@\n";
	exit (1);
}

exit (0);
