| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Win32::PEFile::PEReader; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 1 |  |  | 1 |  | 3 | use strict; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 28 |  | 
| 4 | 1 |  |  | 1 |  | 3 | use warnings; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 17 |  | 
| 5 | 1 |  |  | 1 |  | 3 | use Encode; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 53 |  | 
| 6 | 1 |  |  | 1 |  | 3 | use Carp; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 36 |  | 
| 7 | 1 |  |  | 1 |  | 3 | use Win32::PEFile::PEBase; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 12 |  | 
| 8 | 1 |  |  | 1 |  | 3 | use Win32::PEFile::PEConstants; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 1096 |  | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | push @Win32::PEFile::PEReader::ISA, 'Win32::PEFile::PEBase'; | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | sub new { | 
| 14 | 3 |  |  | 3 | 0 | 9 | my ($class, %params) = @_; | 
| 15 | 3 |  |  |  |  | 8 | my $self = bless \%params, $class; | 
| 16 |  |  |  |  |  |  |  | 
| 17 | 3 | 50 |  |  |  | 11 | die "Parameter -file is required for $class->new ()\n" | 
| 18 |  |  |  |  |  |  | if !exists $params{'-file'}; | 
| 19 |  |  |  |  |  |  |  | 
| 20 | 3 |  |  |  |  | 6 | $self->{owner}{ok} = eval {$self->_parseFile()}; | 
|  | 3 |  |  |  |  | 8 |  | 
| 21 | 3 |  | 100 |  |  | 14 | $self->{owner}{err} = $@ || ''; | 
| 22 | 3 |  |  |  |  | 14 | return $self; | 
| 23 |  |  |  |  |  |  | } | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | sub getSection { | 
| 27 | 0 |  |  | 0 | 0 | 0 | my ($self, $sectionCode) = @_; | 
| 28 |  |  |  |  |  |  |  | 
| 29 | 0 |  |  |  |  | 0 | return $self->_dispatch(_parseSectionData => $sectionCode); | 
| 30 |  |  |  |  |  |  | } | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | sub getSectionNames { | 
| 34 | 0 |  |  | 0 | 0 | 0 | my ($self) = @_; | 
| 35 | 0 |  |  |  |  | 0 | my @names = keys %{$self->{owner}{DataDir}}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 36 | 0 |  |  |  |  | 0 | my @sections = grep {$self->{owner}{DataDir}{$_}{size}} @names; | 
|  | 0 |  |  |  |  | 0 |  | 
| 37 |  |  |  |  |  |  |  | 
| 38 | 0 |  |  |  |  | 0 | return @sections; | 
| 39 |  |  |  |  |  |  | } | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | sub _parseFile { | 
| 43 | 3 |  |  | 3 |  | 5 | my ($self) = @_; | 
| 44 | 3 |  |  |  |  | 5 | my $buffer = ''; | 
| 45 |  |  |  |  |  |  |  | 
| 46 | 3 |  |  |  |  | 5 | eval { | 
| 47 | 3 | 50 |  |  |  | 121 | open my $peFile, '<', $self->{owner}{'-file'} | 
| 48 |  |  |  |  |  |  | or die "unable to open '$self->{owner}{'-file'}' - $!\n"; | 
| 49 | 3 |  |  |  |  | 8 | binmode $peFile; | 
| 50 |  |  |  |  |  |  |  | 
| 51 | 3 | 50 |  |  |  | 52 | read $peFile, $buffer, 256, 0 or die "file read error: $!\n"; | 
| 52 |  |  |  |  |  |  |  | 
| 53 | 3 | 100 |  |  |  | 30 | die "No MZ header found\n" if $buffer !~ /^MZ/; | 
| 54 |  |  |  |  |  |  |  | 
| 55 | 2 |  |  |  |  | 8 | $self->{peOffset} = substr($buffer, 0x3c, 4); | 
| 56 | 2 |  |  |  |  | 13 | $self->{peOffset} = unpack('V', $self->{peOffset}); | 
| 57 | 2 |  |  |  |  | 7 | seek $peFile, 0x40, 0; | 
| 58 |  |  |  |  |  |  |  | 
| 59 | 2 | 50 |  |  |  | 11 | if ($self->{peOffset} != 0x40) { | 
| 60 | 2 |  |  |  |  | 12 | read $peFile, $buffer, $self->{peOffset} - 0x40, 0; | 
| 61 | 2 |  |  |  |  | 7 | $self->{owner}{MSDOSStub} = $buffer; | 
| 62 |  |  |  |  |  |  | } | 
| 63 |  |  |  |  |  |  |  | 
| 64 | 2 |  |  |  |  | 6 | seek $peFile, $self->{peOffset}, 0; | 
| 65 |  |  |  |  |  |  |  | 
| 66 | 2 | 50 | 33 |  |  | 21 | (read $peFile, $buffer, 4 and $buffer =~ /^PE\0\0/) | 
| 67 |  |  |  |  |  |  | or die "corrupt or not a PE file \n"; | 
| 68 |  |  |  |  |  |  |  | 
| 69 | 2 | 50 |  |  |  | 8 | read $peFile, $buffer, 20, 0 or die "file read error: $!\n"; | 
| 70 | 2 |  |  |  |  | 47 | @{$self->{owner}{COFFHeader}}{@kCOFFKeys} = unpack('vvVVVvv', $buffer); | 
|  | 2 |  |  |  |  | 21 |  | 
| 71 |  |  |  |  |  |  |  | 
| 72 | 2 | 50 |  |  |  | 9 | if ($self->{owner}{COFFHeader}{SizeOfOptionalHeader}) { | 
| 73 | 2 |  |  |  |  | 7 | my $opt = $self->{owner}{OptionalHeader} = {}; | 
| 74 |  |  |  |  |  |  |  | 
| 75 | 2 |  |  |  |  | 7 | read $peFile, $opt->{raw}, | 
| 76 |  |  |  |  |  |  | $self->{owner}{COFFHeader}{SizeOfOptionalHeader}, 0; | 
| 77 | 2 |  |  |  |  | 10 | @{$opt}{@kOptionalHeaderFields} = unpack('vCCVVVVV', $opt->{raw}); | 
|  | 2 |  |  |  |  | 23 |  | 
| 78 |  |  |  |  |  |  |  | 
| 79 | 2 |  |  |  |  | 6 | my $blk = substr $opt->{raw}, 24; | 
| 80 |  |  |  |  |  |  |  | 
| 81 | 2 |  |  |  |  | 7 | $self->{is32Plus} = $opt->{Magic} == 0x20B; | 
| 82 |  |  |  |  |  |  |  | 
| 83 | 2 | 50 |  |  |  | 5 | if ($self->{is32Plus}) { | 
| 84 | 0 |  |  |  |  | 0 | $self->_parsePE32PlusOpt($blk); | 
| 85 |  |  |  |  |  |  | } else { | 
| 86 | 2 |  |  |  |  | 9 | $self->_parsePE32Opt($blk); | 
| 87 |  |  |  |  |  |  | } | 
| 88 |  |  |  |  |  |  |  | 
| 89 | 2 |  |  |  |  | 8 | $self->_parseSectionsTable($peFile); | 
| 90 | 2 |  |  |  |  | 5 | $self->_findDataDirData($peFile); | 
| 91 |  |  |  |  |  |  | } | 
| 92 |  |  |  |  |  |  |  | 
| 93 | 2 |  |  |  |  | 27 | close $peFile; | 
| 94 |  |  |  |  |  |  | }; | 
| 95 |  |  |  |  |  |  |  | 
| 96 | 3 | 100 |  |  |  | 12 | die "Error in PE file $self->{'-file'}: $@\n" if $@; | 
| 97 | 2 |  |  |  |  | 8 | return 1; | 
| 98 |  |  |  |  |  |  | } | 
| 99 |  |  |  |  |  |  |  | 
| 100 |  |  |  |  |  |  |  | 
| 101 |  |  |  |  |  |  | sub _parsePE32Opt { | 
| 102 | 2 |  |  | 2 |  | 2 | my ($self, $blk) = @_; | 
| 103 | 2 |  |  |  |  | 5 | my $len    = length $blk; | 
| 104 | 2 |  |  |  |  | 13 | my @fields = ( | 
| 105 |  |  |  |  |  |  | qw( | 
| 106 |  |  |  |  |  |  | ImageBase SectionAlignment FileAlignment MajorOperatingSystemVersion | 
| 107 |  |  |  |  |  |  | MinorOperatingSystemVersion MajorImageVersion MinorImageVersion | 
| 108 |  |  |  |  |  |  | MajorSubsystemVersion MinorSubsystemVersion Win32VersionValue | 
| 109 |  |  |  |  |  |  | SizeOfImage SizeOfHeaders CheckSum Subsystem DllCharacteristics | 
| 110 |  |  |  |  |  |  | SizeOfStackReserve SizeOfStackCommit SizeOfHeapReserve | 
| 111 |  |  |  |  |  |  | SizeOfHeapCommit LoaderFlags NumberOfRvaAndSizes ) | 
| 112 |  |  |  |  |  |  | ); | 
| 113 |  |  |  |  |  |  |  | 
| 114 | 2 |  |  |  |  | 11 | $self->{owner}{OptionalHeader}{BaseOfData} = | 
| 115 |  |  |  |  |  |  | unpack('V', substr $blk, 0, 4, ''); | 
| 116 | 2 |  |  |  |  | 11 | @{$self->{owner}{OptionalHeader}}{@fields} = | 
|  | 2 |  |  |  |  | 30 |  | 
| 117 |  |  |  |  |  |  | unpack('VVVvvvvvvVVVVvvVVVVVV', $blk); | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | # $blk passed in starts at offset 20 and 4 bytes are removed by substr above | 
| 120 |  |  |  |  |  |  | # so offset to data directory is 96 - (24 + 4) = 68 | 
| 121 | 2 |  |  |  |  | 10 | $self->_parseDataDirectory(substr $blk, 68); | 
| 122 |  |  |  |  |  |  | } | 
| 123 |  |  |  |  |  |  |  | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | sub _parsePE32PlusOpt { | 
| 126 | 0 |  |  | 0 |  | 0 | my ($self, $blk) = @_; | 
| 127 | 0 |  |  |  |  | 0 | my $len    = length $blk; | 
| 128 | 0 |  |  |  |  | 0 | my @fields = ( | 
| 129 |  |  |  |  |  |  | qw( | 
| 130 |  |  |  |  |  |  | ImageBaseL ImageBaseH SectionAlignment FileAlignment | 
| 131 |  |  |  |  |  |  | MajorOperatingSystemVersion MinorOperatingSystemVersion | 
| 132 |  |  |  |  |  |  | MajorImageVersion MinorImageVersion MajorSubsystemVersion | 
| 133 |  |  |  |  |  |  | MinorSubsystemVersion Win32VersionValue SizeOfImage SizeOfHeaders | 
| 134 |  |  |  |  |  |  | CheckSum Subsystem DllCharacteristics SizeOfStackReserveL | 
| 135 |  |  |  |  |  |  | SizeOfStackReserveH SizeOfStackCommitL SizeOfStackCommitH | 
| 136 |  |  |  |  |  |  | SizeOfHeapReserveL SizeOfHeapReserveH SizeOfHeapCommitL | 
| 137 |  |  |  |  |  |  | SizeOfHeapCommitH LoaderFlags NumberOfRvaAndSizes ) | 
| 138 |  |  |  |  |  |  | ); | 
| 139 |  |  |  |  |  |  |  | 
| 140 | 0 |  |  |  |  | 0 | @{$self->{owner}{OptionalHeader}}{@fields} = | 
|  | 0 |  |  |  |  | 0 |  | 
| 141 |  |  |  |  |  |  | unpack('VVVVvvvvvvVVVVvvVVVVVVVVVV', $blk); | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | # $blk passed in starts at offset 20 so offset to data directory is 112 - 24 | 
| 144 | 0 |  |  |  |  | 0 | $self->_parseDataDirectory(substr $blk, 88); | 
| 145 |  |  |  |  |  |  | } | 
| 146 |  |  |  |  |  |  |  | 
| 147 |  |  |  |  |  |  |  | 
| 148 |  |  |  |  |  |  | sub _parseDataDirectory { | 
| 149 | 2 |  |  | 2 |  | 7 | my ($self, $blk) = @_; | 
| 150 | 2 |  |  |  |  | 4 | my $len = length $blk; | 
| 151 | 2 |  |  |  |  | 2 | my @entries; | 
| 152 |  |  |  |  |  |  |  | 
| 153 | 2 |  |  |  |  | 9 | for (1 .. $self->{owner}{OptionalHeader}{NumberOfRvaAndSizes}) { | 
| 154 | 32 |  |  |  |  | 76 | my $addr = unpack('V', substr $blk, 0, 4, ''); | 
| 155 | 32 |  |  |  |  | 32 | my $size = unpack('V', substr $blk, 0, 4, ''); | 
| 156 |  |  |  |  |  |  |  | 
| 157 | 32 |  |  |  |  | 45 | push @entries, {imageRVA => $addr, size => $size}; | 
| 158 | 32 | 100 |  |  |  | 54 | last if !length $blk; | 
| 159 |  |  |  |  |  |  | } | 
| 160 |  |  |  |  |  |  |  | 
| 161 | 2 |  |  |  |  | 5 | @{$self->{owner}{DataDir}}{@kOptHeaderSectionCodes} = @entries; | 
|  | 2 |  |  |  |  | 28 |  | 
| 162 | 2 |  |  |  |  | 8 | return; | 
| 163 |  |  |  |  |  |  | } | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  |  | 
| 166 |  |  |  |  |  |  | sub _parseSectionsTable { | 
| 167 | 2 |  |  | 2 |  | 3 | my ($self, $peFile) = @_; | 
| 168 | 2 |  | 50 |  |  | 13 | my $sections = $self->{owner}{SecData} ||= {}; | 
| 169 |  |  |  |  |  |  |  | 
| 170 | 2 |  |  |  |  | 7 | for (1 .. $self->{owner}{COFFHeader}{NumberOfSections}) { | 
| 171 | 8 |  |  |  |  | 7 | my %section; | 
| 172 |  |  |  |  |  |  | my $raw; | 
| 173 |  |  |  |  |  |  |  | 
| 174 | 8 |  |  |  |  | 12 | read $peFile, $raw, 40, 0; | 
| 175 | 8 |  |  |  |  | 57 | @section{@kSectionHeaderFields} = unpack('a8VVVVVVvvV', $raw); | 
| 176 | 8 |  |  |  |  | 26 | $section{Name} =~ s/\x00+$//; | 
| 177 | 8 |  |  |  |  | 25 | $self->{owner}{SecData}{$section{Name}}{header} = \%section; | 
| 178 |  |  |  |  |  |  | } | 
| 179 |  |  |  |  |  |  | } | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  |  | 
| 182 |  |  |  |  |  |  | sub _findDataDirData { | 
| 183 | 2 |  |  | 2 |  | 3 | my ($self, $peFile) = @_; | 
| 184 |  |  |  |  |  |  |  | 
| 185 | 2 |  |  |  |  | 4 | for my $entry (values %{$self->{owner}{DataDir}}) { | 
|  | 2 |  |  |  |  | 10 |  | 
| 186 | 32 | 100 |  |  |  | 43 | next if !$entry->{size};    # size is zero | 
| 187 |  |  |  |  |  |  |  | 
| 188 | 12 |  |  |  |  | 11 | for my $sectionName (keys %{$self->{owner}{SecData}}) { | 
|  | 12 |  |  |  |  | 20 |  | 
| 189 | 30 |  |  |  |  | 33 | my $header = $self->{owner}{SecData}{$sectionName}{header}; | 
| 190 | 30 |  |  |  |  | 28 | my $start = $header->{VirtualAddress}; | 
| 191 | 30 |  |  |  |  | 24 | my $end = $header->{VirtualAddress} + $header->{VirtualSize}; | 
| 192 |  |  |  |  |  |  |  | 
| 193 | 30 | 100 | 100 |  |  | 72 | next if $start > $entry->{imageRVA} || $end < $entry->{imageRVA}; | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | # Found the section data | 
| 196 | 12 |  |  |  |  | 15 | $entry->{fileBias} = | 
| 197 |  |  |  |  |  |  | $header->{VirtualAddress} - $header->{PointerToRawData}; | 
| 198 | 12 |  |  |  |  | 16 | $entry->{filePos}  = $entry->{imageRVA} - $entry->{fileBias}; | 
| 199 | 12 |  |  |  |  | 12 | $header->{filePos} = $entry->{filePos}; | 
| 200 | 12 |  |  |  |  | 16 | last; | 
| 201 |  |  |  |  |  |  | } | 
| 202 |  |  |  |  |  |  | } | 
| 203 |  |  |  |  |  |  | } | 
| 204 |  |  |  |  |  |  |  | 
| 205 |  |  |  |  |  |  |  | 
| 206 |  |  |  |  |  |  | 1; | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  |  |