#!perl -w # (C) 2003-2007 Willem Jan Hengeveld # Web: http://www.xs4all.nl/~itsme/ # http://wiki.xda-developers.com/ # # $Id: $ # # this script analyzes an IBMPC style partition table # you can also specify \\.\PhysicalDrive1 on the commandline use strict; use IO::File; use Getopt::Long; use bigint; use Math::BigInt; my %typelist= ( 0x00=>'Empty', 0x01=>'FAT-12', 0x02=>'XENIX', 0x03=>'XENIX', 0x04=>'FAT-16', 0x05=>'Extended Partition', 0x06=>'DOS >32Meg', 0x07=>'NTFS', 0x0A=>'Boot Manager', 0x0B=>'FAT-32', 0x0e=>'WIN95: DOS 16-bit FAT, LBA', 0x0f=>'WIN95: Extpartition, LBA', 0x11=>'hid FAT-12', 0x14=>'hid FAT-16', 0x17=>'hid OS/2 HPFS', 0x1B=>'hid FAT-32', 0x1c=>'Hid WIN95 OSR2 FAT32, LBA', 0x1e=>'hid WIN95: DOS 16-bit FAT, LBA', 0x1f=>'hid WIN95: Ext partition, LBA', 0x20=>'wince update xip', 0x23=>'wince boot xip', 0x25=>'wince imgfs', 0x41=>'linux/minix', 0x82=>'linux/swap', 0x83=>'linux/native', 0x64=>'Novell', 0x75=>'PCIX', 0xDB=>'CPM/Concurrent', 0xFF=>'BBT', ); sub usage { return <<__EOF__ Usage: dumpptable [-x] [-r] [-o OFFSET] [-b SECSIZE] -r : recurse extended partitions -x : dump raw -v : include hd/cyl/sec info note: imagefile can also be /dev/rdisk1 or \\\\.\\PhysicalDrive1 __EOF__ } my $w=8; my $offset=0; my $sectorsize=0x200; my $recurse; my $dumpraw; my $verbose; GetOptions( "o=s" => sub { $offset=Math::BigInt->new($_[1]); }, # note: there is a problem in perl5.8 which causes eval not to work, while using bigint # workaround: use Math::BigInt->new "b=s" => sub { $sectorsize=Math::BigInt->new($_[1]); }, "r" => sub { $recurse= 1; }, "x" => sub { $dumpraw= 1; }, "v" => sub { $verbose= 1; }, ) or die usage(); if (!@ARGV) { die usage(); } for my $fn (@ARGV) { printf("-----%s\n", $fn); my $fh= IO::File->new($fn, "r") or die "$fn: $!\n"; binmode $fh; dumptable($fh, $offset); $fh->close(); } exit(0); sub dumptable { my ($fh, $offset)= @_; printf("----- %s\n", hexfmt($offset)) if ($recurse); $fh->seek($offset, SEEK_SET) || die "seek $!\n"; my $data; defined $fh->read($data, 512) || die "read $!\n"; if (length($data)<512) { warn sprintf("incomplete partition table sector at %s\n", hexfmt($offset)); return; } if (substr($data, 510, 2) ne "\x55\xaa") { warn sprintf("missing 55aa signature at end of ptable at %s\n", hexfmt($offset)); return; } my $totalsectors= 0; my @entries; for (0..3) { my $pdata= substr($data, 0x1be+0x10*$_, 0x10); next if ($pdata eq "\x00" x 16); dumprawentry($pdata) if ($dumpraw); my $pent= parsepentry($pdata); $totalsectors += $pent->{nrsectors}; push @entries, $pent; } if ($w{startsector} != $sofs) { printf("%s - %s l=%s - gap\n", hexfmt($offset+$sofs*$sectorsize), hexfmt($offset+$pent->{startsector}*$sectorsize), hexfmt(($pent->{startsector}-$sofs)*$sectorsize)); } if (isextended($pent)) { # according to wikipedia: either offs+sofs*ssize or ofs+startsec*ssize dumptable($fh, $offset+$sofs*$sectorsize); if ($pent->{startsector} != $sofs) { dumptable($fh, $offset+$pent->{startsector}*$sectorsize); } } $sofs= $pent->{startsector}+ $pent->{nrsectors}; } } } sub isextended { my $pent= shift; return $pent->{type}==0x05 || $pent->{type}==0x0f || $pent->{type}==0x1f; } sub printpentry { my ($offset, $pent)= @_; printf("%s - %s l=%s: %1s", hexfmt($offset+$pent->{startsector}*$sectorsize), hexfmt($offset+($pent->{startsector}+ $pent->{nrsectors})*$sectorsize), hexfmt($pent->{nrsectors}*$sectorsize), $pent->{bootable}?"B":""); printf("(%s) - (%s) %08lx %08lx", chs_asstring($pent->{startchs}), chs_asstring($pent->{endchs}), $pent->{startsector}, $pent->{nrsectors}) if ($verbose); printf(" %s\n", type_asstring($pent->{type})); } sub hexfmt { my $str= $_[0]->as_hex; $str =~ s/0x//; return '0' x ($w-length($str)) . $str; } sub chs_asstring { my $chs= shift; return sprintf("%03x:%02x:%02x", $chs->{cyl}, $chs->{side}, $chs->{sect}); } sub type_asstring { my $type= shift; if (exists $typelist{$type}) { return sprintf("%02x:%s", $type, $typelist{$type}); } return sprintf("%02x", $type); } sub dumprawentry { printf("%02x %02x:%02x:%02x %02x %02x:%02x:%02x %08x %08x\n",unpack("C8VV", $_[0])); } sub parsepentry { my $pdata= shift; my %pent; my ($startchs, $endchs); ( $pent{bootable}, $startchs, $pent{type}, $endchs, $pent{startsector}, $pent{nrsectors}, ) = unpack("Ca3Ca3VV", $pdata); $pent{startchs}= parsechs($startchs); $pent{endchs}= parsechs($endchs); return \%pent; } sub parsechs { my @chsdata= unpack("C*", shift); return { side=>$chsdata[0], cyl=>(($chsdata[1]&0xc0)<<2) | $chsdata[2], sect=>$chsdata[1]&0x3f, }; }