#!/usr/bin/env perl #This is a utility to scrape out the EFI boot image from an el torito enabled disk use strict; use Fcntl qw/SEEK_SET/; my $iso; my $outfile; GetOptions("out=s" => \$outfile, "iso=s" => \$iso); my $emul; my $blksize = 2048; my $istream; use Getopt::Long; sub grabimage { my $bootcat = shift; my $image; my @recdata = unpack("C2vC2vV", $bootcat); unless ($recdata[0] == 0x88) { printf "Error: EFI torito record not bootable\n"; exit; } $emul = $recdata[1]; #for now, ignore load segment, system type seek($istream, $recdata[6] * $blksize, SEEK_SET); my $size = $recdata[5]; if ($size == 0) { $size = 1; } #if 0/1, it's auto-detect time my $readbytes = read($istream, $image, $size * 512); if ($size == 1) { #we have to interpret the FAT header to get the real size seek($istream, $recdata[6] * $blksize, SEEK_SET); # go back to beginning my @fatheader = unpack("C11vCvC3vC11V", $image); my $secsize = $fatheader[11]; if ($fatheader[17]) { $size = $secsize * ($fatheader[17]); } else { die "Unsupported FAT header, requires test of commented code segment"; } # } elsif ($fatheader[-1]) { # $size=$secsize*$fatheader[-1]; # } $readbytes = read($istream, $image, $size); } my $outh; open($outh, ">", $outfile); print $outh $image; close($outh); } if (!-r $iso) { printf "Error, $iso does not seem to exist or is not readable"; exit 1; } open($istream, "<", $iso) || die "Error opening $iso"; my $bootsect; seek($istream, 17 * $blksize, SEEK_SET); my $readbytes = read($istream, $bootsect, 0x4b); unless ($readbytes == 0x4b) { printf "Error reading boot record volume from $iso\n"; exit 1; } my @recdata; @recdata = unpack("CA5CA32C32V", $bootsect); #it would have been nice if the el torito actually said little endian on it.. unless ($recdata[0] == 0 and $recdata[1] eq 'CD001' and $recdata[2] == 1 and $recdata[3] eq "EL TORITO SPECIFICATION") { printf "Error: Boot record volume format invalid\n"; exit 1; } my $bootcatidx = $recdata[-1]; my $bootcat; seek($istream, $bootcatidx * $blksize, SEEK_SET); $readbytes = read($istream, $bootcat, 0x20); unless ($readbytes == 0x20) { printf "Error reading boot catalog at $bootcatidx\n"; exit 1; } @recdata = unpack("C*", $bootcat); unless ($recdata[0] == 1 and $recdata[0x1e] == 0x55 and $recdata[0x1f] == 0xaa) { printf "Boot catalog has invalid header\n"; exit 1; } $readbytes = read($istream, $bootcat, 0x20); my $image; unless ($readbytes == 0x20) { printf "Error reading default El torito record\n"; exit; } if ($recdata[1] == 0xef) { #wow, the efi record came first, actually interpret that first record grabimage($bootcat); } else { # keep looking for an *EFI* record #read in first header read($istream, $bootcat, 0x20); @recdata = unpack("CCv", $bootcat); while ($recdata[0] == 0x90 and $recdata[1] != 0xef) { my $additionalrecords = $recdata[2]; while ($additionalrecords) { read($istream, $bootcat, 0x20); #throw away irrelevant sectors $additionalrecords -= 1; } read($istream, $bootcat, 0x20); #throw away irrelevant sectors @recdata = unpack("CCv", $bootcat); } if ($recdata[1] != 0xef) { printf "No EFI boot image found\n"; exit 1; } $readbytes = read($istream, $bootcat, 0x20); grabimage($bootcat); }