File Coverage

lib/Win32/PEFile/PEReader.pm
Criterion Covered Total %
statement 97 112 86.6
branch 18 26 69.2
condition 7 10 70.0
subroutine 12 15 80.0
pod 0 3 0.0
total 134 166 80.7


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