File Coverage

lib/Win32/PEFile/PEWriter.pm
Criterion Covered Total %
statement 18 271 6.6
branch 0 60 0.0
condition 0 125 0.0
subroutine 6 24 25.0
pod 0 3 0.0
total 24 483 4.9


line stmt bran cond sub pod time code
1             package Win32::PEFile::PEWriter;
2 1     1   7 use strict;
  1         2  
  1         48  
3 1     1   6 use warnings;
  1         3  
  1         36  
4 1     1   5 use Encode;
  1         3  
  1         92  
5 1     1   6 use Carp;
  1         1  
  1         57  
6 1     1   6 use Win32::PEFile::PEConstants;
  1         3  
  1         249  
7 1     1   5 use Win32::PEFile::SectionHandlers;
  1         2  
  1         10586  
8            
9             push @Win32::PEFile::PEWriter::ISA, 'Win32::PEFile::PEBase';
10            
11            
12             sub new {
13 0     0 0   my ($class, %params) = @_;
14 0           my $self = bless \%params, $class;
15            
16 0 0         die "Parameter -file is required for $class->new ()\n"
17             if !exists $params{'-file'};
18            
19 0           $self->_clearCaches();
20 0   0       $self->{owner}{err} = $@ || '';
21 0           return $self;
22             }
23            
24            
25             sub getSectionNames {
26 0     0 0   my ($self) = @_;
27            
28 0 0         if (! $self->{sectionNames}) {
29 0           @{$self->{sectionNames}} =
  0            
30 0           grep {exists $self->{owner}{SecData}{$_}} @kStdSectionCodes;
31             }
32            
33 0           return @{$self->{sectionNames}};
  0            
34             }
35            
36            
37             sub writeFile {
38 0     0 0   my ($self) = @_;
39 0           my $buffer = '';
40            
41 0           $self->_clearCaches();
42            
43 0           $self->{fileAlignment} = $self->{owner}{OptionalHeader}{FileAlignment};
44 0           $self->{faMasked} = $self->{fileAlignment} - 1;
45 0           $self->{faMask} = ~$self->{faMasked};
46            
47 0           $self->_measureSections();
48 0           $self->{coffHeader}{SizeOfOptionalHeader} = 0;
49 0           $self->{optionalHeaderBin} = undef;
50            
51 0           my $headerSize = length $self->_getOptionalHeaderBin();
52 0           $headerSize += length $self->_getDataDirectoryBin();
53            
54 0 0         open my $peFile, '>', $self->{-file} or die "unable to create file - $!\n";
55 0           binmode $peFile;
56            
57 0           print $peFile 'MZ', ("\0") x 58; # Add sig and pad to PE header index
58 0   0       $self->{peOffset} ||= 0x40; # Default location of the PE header
59            
60 0           my $stubEnd = $self->{peOffset};
61            
62 0 0         if (exists $self->{owner}{MSDOSStub}) {
63             # Adjust PE header location to accomodate MS-DOS stub
64 0           $self->{peOffset} = $stubEnd += length $self->{owner}{MSDOSStub};
65 0           $self->{peOffset} = ($self->{peOffset} + 7) & ~7;
66             }
67            
68             # Write the file address of the PE header start
69 0           print $peFile pack('V', $self->{peOffset});
70            
71 0 0         if ($self->{owner}{MSDOSStub}) {
72             # Write the MS-DOS stub
73 0           print $peFile $self->{owner}{MSDOSStub};
74 0 0         print $peFile "\0" x ($self->{peOffset} - $stubEnd)
75             if $self->{peOffset} != $stubEnd;
76             }
77            
78 0           my $peStart = tell $peFile;
79            
80 0           print $peFile "PE\0\0";
81            
82 0           $self->_resetCoff();
83            
84 0           $self->_calcSectionsData();
85 0           $self->_layoutSections($peStart + $self->_getOptionalHeaderSize());
86            
87             # Update COFF header and write it
88 0   0       $self->{coffHeader}{TimeDateStamp} ||= time;
89 0   0       $self->{coffHeader}{SizeOfOptionalHeader} ||=
90             $self->_getOptionalheaderSize();
91            
92 0           my @coffValues = @{$self->{coffHeader}}{@kCOFFKeys};
  0            
93            
94 0           print $peFile pack('vvVVVvv', @coffValues);
95            
96 0           $self->_writeOptionalHeader($peFile);
97 0           $self->_writeSectionsTable($peFile);
98            
99 0           my $headerEnd = tell $peFile;
100            
101 0           $self->_writeSections($peFile);
102 0           close $peFile;
103 0           return 1;
104             }
105            
106            
107             sub _clearCaches {
108 0     0     my ($self) = @_;
109 0           my $buffer = '';
110            
111 0           $self->{peOffset} = 64;
112 0           $self->{optionalHeaderBin} = undef;
113 0           $self->{sectionNames} = undef;
114 0           return 1;
115             }
116            
117            
118             sub _resetCoff {
119 0     0     my ($self) = @_;
120            
121 0   0       $self->{coffHeader}{Machine} ||= 0x14c; # Intel 386 or later machine
122 0   0       $self->{coffHeader}{NumberOfSections} ||= 0;
123 0   0       $self->{coffHeader}{TimeDateStamp} ||= 0;
124 0   0       $self->{coffHeader}{PointerToSymbolTable} ||= 0;
125 0   0       $self->{coffHeader}{NumberOfSymbols} ||= 0;
126 0   0       $self->{coffHeader}{SizeOfOptionalHeader} ||= 0;
127 0   0       $self->{coffHeader}{Characteristics} ||=
128             $kIMAGE_FILE_EXECUTABLE_IMAGE | $kIMAGE_FILE_32BIT_MACHINE;
129             }
130            
131            
132             sub _getOptionalheaderSize {
133 0     0     my ($self) = @_;
134            
135 0 0         $self->_genOptionalHeader() if !defined $self->{optionalHeader};
136 0           return length $self->{optionalHeader}{end};
137             }
138            
139            
140             sub _measureSections {
141 0     0     my ($self) = @_;
142 0           my $sectionStart = 0;
143            
144 0           for my $secName ($self->getSectionNames()) {
145 0           my $blob;
146 0           my $section = $self->{owner}{SecData}{$secName};
147            
148 0 0         if (exists $kStdSectionCodeLu{$secName}) {
149 0           $self->_dispatch(_assemble => $secName);
150 0 0         $blob = $self->_dispatch(_getSecData => $section->{header}{Name})
151             if $section->{header};
152             } else {
153             # Default handling - just treat the data as a blob and create a data
154             # directory entry in the OptionalHeader table for the section
155 0           die "Need to impliment default section handling in _measureSections\n";
156             }
157            
158 0 0         next if !$section->{header};
159            
160 0 0 0       if (!defined $blob || !length $blob) {
161 0 0         if (exists $section->{rawData}) {
162 0           $blob = $section->{rawData};
163             } else {
164 0           open my $blobIn, '<', $self->{owner}{reader}{-file};
165 0           binmode $blobIn;
166 0           seek $blobIn, $section->{header}{PointerToRawData}, 0;
167 0           read $blobIn, $blob, $section->{header}{SizeOfRawData};
168             }
169             }
170            
171 0 0         $self->{SecData}{$secName} = {} if ! defined $self->{SecData}{$secName};
172 0           my $wSec = $self->{SecData}{$secName};
173            
174 0           @{$wSec->{header}}{@kSectionHeaderFields} =
  0            
175 0           @{$section->{header}}{@kSectionHeaderFields};
176 0           $wSec->{header}{PointerToRawData} = $sectionStart;
177 0           $wSec->{header}{SizeOfRawData} = length $blob;
178 0           $wSec->{blob} = $blob;
179 0           $sectionStart =
180             ($self->{faMasked} + $sectionStart + length $blob) &
181             $self->{faMask};
182             }
183             }
184            
185            
186             sub _layoutSections {
187 0     0     my ($self, $sectionStart) = @_;
188            
189 0           for my $secName ($self->getSectionNames()) {
190 0 0         next if !exists $self->{SecData}{$secName};
191 0           my $wSec = $self->{SecData}{$secName};
192 0   0       my $blobLen = length $wSec->{blob} || next;
193            
194 0           $sectionStart = ($self->{faMasked} + $sectionStart) & $self->{faMask};
195 0           $wSec->{header}{PointerToRawData} = $sectionStart;
196 0           $wSec->{header}{SizeOfRawData} = $blobLen;
197 0           $sectionStart += $blobLen;
198             }
199             }
200            
201            
202             sub _getOptionalHeaderBin {
203 0     0     my ($self) = @_;
204            
205 0 0         return $self->{optionalHeaderBin} if $self->{optionalHeaderBin};
206            
207 0   0       my $opt = $self->{owner}{OptionalHeader} ||= {};
208            
209 0 0         $self->{is32Plus} = $opt->{Magic} == 0x20B if $opt->{Magic};
210 0 0 0       $opt->{Magic} ||= 0x20B if $self->{is32Plus};
211 0   0       $opt->{Magic} ||= 0x10b;
212            
213 0           my $bin = pack('v', $opt->{Magic});
214 0   0       $bin .= pack('C', $opt->{MajorLinkerVersion} ||= 0);
215 0   0       $bin .= pack('C', $opt->{MinorLinkerVersion} ||= 0);
216 0   0       $bin .= pack('V', $opt->{SizeOfCode} ||= 0);
217 0   0       $bin .= pack('V', $opt->{SizeOfInitializedData} ||= 0);
218 0   0       $bin .= pack('V', $opt->{SizeOfUninitializedData} ||= 0);
219 0   0       $bin .= pack('V', $opt->{AddressOfEntryPoint} ||= 0);
220 0   0       $bin .= pack('V', $opt->{BaseOfCode} ||= 0);
221            
222 0 0         if ($self->{is32Plus}) {
223 0   0       $bin .= pack('V', $opt->{ImageBaseL} || 0);
224 0   0       $bin .= pack('V', $opt->{ImageBaseH} || 0);
225             } else {
226 0   0       $bin .= pack('V', $opt->{BaseOfData} ||= 0);
227 0   0       $bin .= pack('V', $opt->{ImageBase} || 0x400000);
228             }
229            
230 0   0       $bin .= pack('V', $opt->{SectionAlignment} ||= 32);
231 0   0       $bin .= pack('V', $opt->{FileAlignment} ||= 32);
232 0   0       $bin .= pack('v', $opt->{MajorOperatingSystemVersion} ||= 4);
233 0   0       $bin .= pack('v', $opt->{MinorOperatingSystemVersion} ||= 0);
234 0   0       $bin .= pack('v', $opt->{MajorImageVersion} ||= 0);
235 0   0       $bin .= pack('v', $opt->{MinorImageVersion} ||= 0);
236 0   0       $bin .= pack('v', $opt->{MajorSubsystemVersion} ||= 0);
237 0   0       $bin .= pack('v', $opt->{MinorSubsystemVersion} ||= 0);
238 0   0       $bin .= pack('V', $opt->{Win32VersionValue} ||= 0);
239 0   0       $bin .= pack('V', $opt->{SizeOfImage} ||= 0);
240 0   0       $bin .= pack('V', $opt->{SizeOfHeaders} ||= 0);
241 0   0       $bin .= pack('V', $opt->{CheckSum} ||= 0);
242 0   0       $bin .= pack('v', $opt->{Subsystem} ||= 0);
243 0   0       $bin .= pack('v', $opt->{DllCharacteristics} ||= 0x400 | 0x800);
244            
245 0 0         if ($self->{is32Plus}) {
246 0   0       $bin .= pack('V', $opt->{SizeOfStackReserveL} ||= 0);
247 0   0       $bin .= pack('V', $opt->{SizeOfStackReserveH} ||= 0);
248 0   0       $bin .= pack('V', $opt->{SizeOfStackCommitL} ||= 0);
249 0   0       $bin .= pack('V', $opt->{SizeOfStackCommitH} ||= 0);
250 0   0       $bin .= pack('V', $opt->{SizeOfHeapReserveL} ||= 0);
251 0   0       $bin .= pack('V', $opt->{SizeOfHeapReserveH} ||= 0);
252 0   0       $bin .= pack('V', $opt->{SizeOfHeapCommitL} ||= 0);
253 0   0       $bin .= pack('V', $opt->{SizeOfHeapCommitH} ||= 0);
254             } else {
255 0   0       $bin .= pack('V', $opt->{SizeOfStackReserve} ||= 0);
256 0   0       $bin .= pack('V', $opt->{SizeOfStackCommit} ||= 0);
257 0   0       $bin .= pack('V', $opt->{SizeOfHeapReserve} ||= 0);
258 0   0       $bin .= pack('V', $opt->{SizeOfHeapCommit} ||= 0);
259             }
260            
261 0   0       $bin .= pack('V', $opt->{LoaderFlags} ||= 0);
262 0   0       $bin .= pack('V', $opt->{NumberOfRvaAndSizes} ||= 0);
263            
264 0           return $self->{optionalHeaderBin} = $bin;
265             }
266            
267            
268             sub _getDataDirectoryBin {
269 0     0     my ($self) = @_;
270            
271 0 0         return $self->{dataDirectoryBin} if $self->{dataDirectoryBin};
272            
273 0           my $opt = $self->{owner}{OptionalHeader};
274 0           my $bin = '';
275 0           my @rFields = qw(imageRVA size);
276            
277 0           for my $field (@kOptHeaderSectionCodes) {
278 0           my $record = $self->{owner}{DataDir}{$field};
279            
280 0   0       $record->{$_} ||= 0 for @rFields;
281 0           $bin .= pack('VV', @{$record}{@rFields});
  0            
282             }
283            
284 0           return $self->{dataDirectoryBin} = $bin;
285             }
286            
287            
288             sub _calcSectionsData {
289 0     0     my ($self) = @_;
290            
291 0           $self->{coffHeader}{NumberOfSections} = @{$self->{sectionNames}};
  0            
292            
293 0           my $alignmentMask = $self->{owner}{OptionalHeader}{FileAlignment} - 1;
294 0           my $headersEnd =
295             $self->{peOffset} +
296             $kCOFFHeaderSize +
297             $self->{coffHeader}{SizeOfOptionalHeader} +
298 0           @{$self->{sectionNames}} * $kSectionHeaderSize;
299 0           my $nextSectionStart = ($headersEnd + $alignmentMask) & ~$alignmentMask;
300            
301 0           $self->{owner}{OptionalHeader}{BaseOfData} = $nextSectionStart;
302            
303 0           for my $sectionName (@{$self->{sectionNames}}) {
  0            
304 0           my $secBin = $self->{secDataBin}{$sectionName};
305 0           my $dirDataEntry = $self->{owner}{DataDir}{$sectionName};
306            
307 0           $dirDataEntry->{size} =
308             $self->_dispatch(_calcSize => $sectionName, $nextSectionStart);
309            
310 0 0         if (!$dirDataEntry->{size}) {
311 0           $dirDataEntry->{size} = 0;
312 0           $dirDataEntry->{imageRVA} = 0;
313 0           next;
314             }
315            
316 0           my $blockSize =
317             ($dirDataEntry->{size} + $alignmentMask) & ~$alignmentMask;
318 0   0       my $header = $self->{owner}{SecData}{$sectionName}{header} ||= {};
319            
320 0           $dirDataEntry->{filePos} = $nextSectionStart;
321 0           $dirDataEntry->{fileBiss} =
322             $dirDataEntry->{imageRVA} - $nextSectionStart;
323 0           $header->{Name} = $sectionName;
324 0           $header->{VirtualSize} = $dirDataEntry->{size};
325 0           $header->{VirtualAddress} = $dirDataEntry->{imageRVA};
326 0           $header->{SizeOfRawData} = $blockSize;
327 0           $header->{PointerToRawData} = $nextSectionStart;
328 0           $header->{PointerToRelocations} = 0;
329 0           $header->{PointerToLinenumbers} = 0;
330 0           $header->{NumberOfRelocations} = 0;
331 0           $header->{NumberOfLinenumbers} = 0;
332 0   0       $header->{Characteristics} ||= 0;
333            
334 0           $nextSectionStart += $blockSize;
335             }
336             }
337            
338            
339             sub _getOptionalHeaderSize {
340 0     0     my ($self) = @_;
341            
342 0 0         $self->{coffHeader}{SizeOfOptionalHeader} = $self->{is32Plus} ? 240 : 224;
343 0           return $self->{coffHeader}{SizeOfOptionalHeader};
344             }
345            
346            
347             sub _writeOptionalHeader {
348 0     0     my ($self, $peFile) = @_;
349 0           my $opt = $self->{owner}{OptionalHeader};
350            
351 0   0       $self->{is32Plus} ||= $opt->{Magic} == 0x20B;
352 0 0         $opt->{Magic} = 0x20B if $self->{is32Plus};
353 0           print $peFile pack('vCCVVVVV', @{$opt}{@kOptionalHeaderFields});
  0            
354            
355 0 0         if ($self->{is32Plus}) {
356 0           $self->_writePE32PlusOpt($peFile);
357             } else {
358 0           $self->_writePE32Opt($peFile);
359             }
360            
361 0           $self->_writeDataDirectory($peFile);
362             }
363            
364            
365             sub _writePE32Opt {
366 0     0     my ($self, $peFile) = @_;
367 0           my $opt = $self->{owner}{OptionalHeader};
368 0           my @fields = (
369             qw(
370             ImageBase SectionAlignment FileAlignment MajorOperatingSystemVersion
371             MinorOperatingSystemVersion MajorImageVersion MinorImageVersion
372             MajorSubsystemVersion MinorSubsystemVersion Win32VersionValue
373             SizeOfImage SizeOfHeaders CheckSum Subsystem DllCharacteristics
374             SizeOfStackReserve SizeOfStackCommit SizeOfHeapReserve
375             SizeOfHeapCommit LoaderFlags NumberOfRvaAndSizes
376             )
377             );
378            
379 0   0       print $peFile pack('V', $opt->{BaseOfData} ||= 0);
380 0           my $start = tell $peFile;
381 0 0         print $peFile pack('VVVvvvvvvVVVVvvVVVVVV', map {$_ || 0} @{$opt}{@fields});
  0            
  0            
382            
383 0           my $pos = tell $peFile;
384 0           return;
385             # $blk passed in starts at offset 20 and 4 bytes are removed by substr above
386             # so offset to data directory is 96 - (24 + 4) = 68
387             }
388            
389            
390             sub _writePE32PlusOpt {
391 0     0     my ($self, $peFile) = @_;
392 0           my $opt = $self->{owner}{OptionalHeader};
393 0           my @fields = (
394             qw(
395             ImageBaseL ImageBaseH SectionAlignment FileAlignment
396             MajorOperatingSystemVersion MinorOperatingSystemVersion
397             MajorImageVersion MinorImageVersion MajorSubsystemVersion
398             MinorSubsystemVersion Win32VersionValue SizeOfImage SizeOfHeaders
399             CheckSum Subsystem DllCharacteristics SizeOfStackReserveL
400             SizeOfStackReserveH SizeOfStackCommitL SizeOfStackCommitH
401             SizeOfHeapReserveL SizeOfHeapReserveH SizeOfHeapCommitL
402             SizeOfHeapCommitH LoaderFlags NumberOfRvaAndSizes
403             )
404             );
405            
406 0           my $start = tell $peFile;
407 0 0         print $peFile
408 0           pack('VVVVvvvvvvVVVVvvVVVVVVVVVV', map {$_ || 0} @{$opt}{@fields});
  0            
409            
410 0           my $pos = tell $peFile;
411 0           return;
412             # $blk passed in starts at offset 20 so offset to data directory is 112 - 24
413             }
414            
415            
416             sub _writeDataDirectory {
417 0     0     my ($self, $peFile) = @_;
418            
419 0           for my $field (@kOptHeaderSectionCodes) {
420 0 0         if (exists $self->{owner}{DataDir}{$field}) {
421 0           my $record = $self->{owner}{DataDir}{$field};
422 0           print $peFile pack('VV', @{$record}{'imageRVA', 'size'});
  0            
423             } else {
424 0           print $peFile pack('VV', 0, 0);
425             }
426             }
427             }
428            
429            
430             sub _writeSectionsTable {
431 0     0     my ($self, $peFile) = @_;
432            
433 0           for my $secName (@{$self->{sectionNames}}) {
  0            
434 0           my $section = $self->{owner}{SecData}{$secName};
435            
436 0 0         next if !exists $section->{header};
437 0           print $peFile
438 0           pack('a8VVVVVVvvV', @{$section->{header}}{@kSectionHeaderFields});
439             }
440             }
441            
442            
443             sub _writeSections {
444 0     0     my ($self, $peFile) = @_;
445            
446 0           for my $secName (@{$self->{sectionNames}}) {
  0            
447 0           my $blob = $self->{SecData}{$secName}{blob};
448 0           my $pos = tell $peFile;
449 0           my $target = ($self->{faMasked} + $pos) & $self->{faMask};
450            
451 0 0         $blob = '' if ! defined $blob;
452            
453 0 0         if ($pos < $target) {
454 0           my $padding = "\0" x ($target - $pos);
455            
456 0           print $peFile $padding;
457             }
458            
459 0           print $peFile $blob;
460             }
461             }
462            
463            
464             1;
465            
466