#!perl -w # (C) 2003-2007 Willem Jan Hengeveld # Web: http://www.xs4all.nl/~itsme/ # http://wiki.xda-developers.com/ # # $Id$ # use integer; use strict; use warnings; use IO::File; use Getopt::Long; my $writedata; # see http://www.pkware.com/business_and_developers/developer/popups/appnote.txt # new url: http://www.pkware.com/documents/casestudies/APPNOTE.TXT my %pkdecoders= ( 0x02014b50=> { desc=>"direntry", decoder=>\&decode_direntry, dumper=>\&dump_direntry, }, 0x04034b50=> { desc=>"dataentry", decoder=>\&decode_dataentry, dumper=>\&dump_dataentry, }, 0x06054b50=> { desc=>"endofdir", decoder=>\&decode_endofdir, dumper=>\&dump_endofdir, }, 0x08074b50=> { desc=>"datadesc", decoder=>\&decode_datadescriptor, dumper=>\&dump_datadescriptor, } , # 0x08064b50=>\&decode_extrarecord, # 0x03034b50=>\&decode_spannedarchive2, # 0x05054b50=>\&decode_signature, # 0x06064b50=>\&decode_zip64_endofdir, # 0x07064b50=>\&decode_zip64_endofdir_locator, ); GetOptions( "l=s"=>sub { $writedata= eval($_[1]); }); for my $fn (@ARGV) { processfile($fn); } exit(0); sub processfile { my ($fn)= @_; if (@ARGV>1) { printf("------------ %s\n", $fn); } my $fh= IO::File->new($fn, "r") or die "$fn: $!\n"; binmode $fh; my $data; $fh->read($data, -s $fh) or die "reading $fn: $!\n"; $fh->close(); my %pkstats; my %hdrs; my $pos= 0; while (defined $pos && $pos < length($data)) { pos($data)= $pos; if ($data =~ /\G.*?(\x50\x4b[\x00-\x0f][\x00-\x0f])/gs) { $pos= pos($data); my $magic= unpack("V", $1); if (exists $pkdecoders{$magic}) { push @{$hdrs{$magic}}, $pkdecoders{$magic}{decoder}($data, $pos); } $pkstats{$magic}++; } else { last; } } printf("hdrofs -hdrend dathdr: need flgs mthd timestmp crc32 comprssd origsize\n"); for my $magic (sort { $a<=>$b } keys %pkstats) { printf("%08lx : %5d times%s\n", $magic, $pkstats{$magic}, exists $pkdecoders{$magic}?"- $pkdecoders{$magic}{desc}":""); if (exists $pkdecoders{$magic}) { for my $hdr (@{$hdrs{$magic}}) { $pkdecoders{$magic}{dumper}($hdr); } } } printf("%08lx end of file\n", length($data)); if ($#{$hdrs{0x06054b50}} != 0) { printf("WARNING: expected exactly one end of dir entry\n"); } if ($#{$hdrs{0x02014b50}}!=$#{$hdrs{0x04034b50}}) { printf("WARNING: data and dir entry count mismatch\n"); } if ($#{$hdrs{0x02014b50}}!=$#{$hdrs{0x04034b50}}) { printf("WARNING: data and dir entry count mismatch\n"); } my %dirbyname= map { $_->{name}=>$_ } @{$hdrs{0x02014b50}}; my %datbyname= map { $_->{name}=>$_ } @{$hdrs{0x04034b50}}; for my $name (keys %datbyname) { if (exists $dirbyname{$name}) { if (!dir_equals_dat($dirbyname{$name}, $datbyname{$name})) { printf("WARNING: dir/dat don't match for %s\n", $name); } } else { printf("WARNING: no dir for %s\n", $name); } } for my $name (keys %dirbyname) { if (!exists $datbyname{$name}) { printf("WARNING: no dat for %s\n", $name); } } } sub flagsstring { my ($flags, $mth)= @_; my @bits= qw(encrypted b1 b2 hasdd enhdefl patched strongcrypt b7 b8 b9 b10 b11 enhcompr maskedvals b14 b15); my @flags; if ($mth==6) { push @flags, ($flags&2) ? "8kdict" :"4kdict"; push @flags, ($flags&4) ? "3trees" :"2trees"; } elsif ($mth==8 || $mth==9) { my @val= qw(normal max fast super); push @flags, $val[($flags&6)/2]; } elsif ($flags&6) { push @flags, sprintf("undefinedbits21=%d", ($flags&6)/2); } $flags &= ~6; for (0..15) { push @flags, $bits[$_] if ($flags & (1<<$_)); } return join(", ", @flags); } sub dir_equals_dat { my ($dir, $dat)= @_; for (qw(neededversion flags method timestamp crc32 compressedsize originalsize)) { if ($dir->{$_} != $dat->{$_}) { return 0; } } return 1; } sub decode_direntry { my ($data, $ofs)= @_; my %hdr; ( $hdr{createversion}, $hdr{neededversion}, $hdr{flags}, $hdr{method}, $hdr{timestamp}, $hdr{crc32}, $hdr{compressedsize}, $hdr{originalsize}, $hdr{namelength}, $hdr{extralength}, $hdr{commentlength}, $hdr{disknrstart}, $hdr{zipattrs}, $hdr{osattrs}, $hdr{dataofs}, )= unpack("vvvvVVVVvvvvvVV", substr($data, $ofs, 42)); $hdr{name}= substr($data, $ofs+42, $hdr{namelength}) if ($ofs+42+$hdr{namelength}{ofs}, $hdr->{ofs}+46+$hdr->{namelength}+$hdr->{extralength}+$hdr->{commentlength}, $hdr->{createversion}, $hdr->{neededversion}, $hdr->{flags}, $hdr->{method}, $hdr->{timestamp}, $hdr->{crc32}, $hdr->{compressedsize}, $hdr->{originalsize}, $hdr->{disknrstart}, $hdr->{zipattrs}, $hdr->{osattrs}, $hdr->{dataofs}, flagsstring($hdr->{flags}, $hdr->{method})); printf("name %04x:'%s'\n", $hdr->{namelength}, $hdr->{name}) if ($hdr->{namelength}); printf("extra %04x: %s\n", $hdr->{extralength}, unpack("H*", $hdr->{extra})) if ($hdr->{extralength}); printf("comment %04x:'%s'\n", $hdr->{commentlength}, $hdr->{comment}) if ($hdr->{commentlength}); } sub decode_dataentry { my ($data, $ofs)= @_; my %hdr; ( $hdr{neededversion}, $hdr{flags}, $hdr{method}, $hdr{timestamp}, $hdr{crc32}, $hdr{compressedsize}, $hdr{originalsize}, $hdr{namelength}, $hdr{extralength}, )= unpack("vvvVVVVvv", substr($data, $ofs, 26)); $hdr{name}= substr($data, $ofs+26, $hdr{namelength}) if ($ofs+26+$hdr{namelength}{ofs}, $hdr->{ofs}+30+$hdr->{namelength}+$hdr->{extralength}, $hdr->{neededversion}, $hdr->{flags}, $hdr->{method}, $hdr->{timestamp}, $hdr->{crc32}, $hdr->{compressedsize}, $hdr->{originalsize}, flagsstring($hdr->{flags}, $hdr->{method})); printf("name %04x:'%s'\n", $hdr->{namelength}, $hdr->{name}) if ($hdr->{namelength}); printf("extra %04x: %s\n", $hdr->{extralength}, unpack("H*", $hdr->{extra})) if ($hdr->{extralength}); printf("%08lx-%08lx data%s\n", $hdr->{ofs}+30+$hdr->{namelength}+$hdr->{extralength}, $hdr->{ofs}+30+$hdr->{namelength}+$hdr->{extralength}+$hdr->{compressedsize}, $writedata? ' '.unpack("H*", substr($hdr->{data}, 0, $writedata)) : "" ); } sub decode_endofdir { my ($data, $ofs)= @_; my %hdr; ( $hdr{thisdisknr}, $hdr{startdisknr}, $hdr{thisentries}, $hdr{totalentries}, $hdr{dirsize}, $hdr{dirofs}, $hdr{commentlength}, )= unpack("vvvvVVv", substr($data, $ofs ,18)); $hdr{comment}= substr($data, $ofs+18, $hdr{commentlength}) if ($ofs+18+$hdr{commentlength}{ofs}, $hdr->{ofs}+22+$hdr->{commentlength}, $hdr->{thisdisknr}, $hdr->{startdisknr}, $hdr->{thisentries}, $hdr->{totalentries}, $hdr->{dirsize}, $hdr->{dirofs}); printf("comment %04x:'%s'\n", $hdr->{commentlength}, $hdr->{comment}) if ($hdr->{commentlength}); } sub decode_datadescriptor { my ($data, $ofs)= @_; my %hdr; ( $hdr{crc}, $hdr{compsize}, $hdr{uncompsize}, )= unpack("VVV", substr($data, $ofs ,12)); $hdr{ofs}= $ofs-4; return \%hdr; } sub dump_datadescriptor { my ($hdr)= @_; printf("%08lx-%08lx datadesc: %08x %08x %08x\n", $hdr->{ofs}, $hdr->{ofs}+16, $hdr->{crc}, $hdr->{compsize}, $hdr->{uncompsize}); }