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
|
|
|
|
|
|
|
|