File Coverage

blib/lib/Data/ParseBinary/Executable/PE32.pm
Criterion Covered Total %
statement 39 55 70.9
branch 5 12 41.6
condition n/a
subroutine 13 17 76.4
pod 0 3 0.0
total 57 87 65.5


line stmt bran cond sub pod time code
1             package Data::ParseBinary::Executable::PE32;
2 1     1   746 use strict;
  1         2  
  1         29  
3 1     1   4 use warnings;
  1         1  
  1         22  
4 1     1   4 use Data::ParseBinary;
  1         2  
  1         1840  
5            
6             #Portable Executable (PE) 32 bit, little endian
7             #Used on MSWindows systems (including DOS) for EXEs and DLLs
8             #
9             #1999 paper:
10             #http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/pecoff.doc
11             #
12             #2006 with updates relevant for .NET:
13             #http://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/pecoff_v8.doc
14            
15            
16             sub UTCTimeStamp {
17 1     1 0 1 my ($name) = @_;
18 1         9 return Data::ParseBinary::lib::ExecPE32::UTCTimeStampAdapter->create(ULInt32($name));
19             }
20            
21             my $msdos_header = Struct("msdos_header",
22             Magic("MZ"),
23             ULInt16("partPag"),
24             ULInt16("page_count"),
25             ULInt16("relocation_count"),
26             ULInt16("header_size"),
27             ULInt16("minmem"),
28             ULInt16("maxmem"),
29             ULInt16("relocation_stackseg"),
30             ULInt16("exe_stackptr"),
31             ULInt16("checksum"),
32             ULInt16("exe_ip"),
33             ULInt16("relocation_codeseg"),
34             ULInt16("table_offset"),
35             ULInt16("overlay"),
36             Padding(8),
37             ULInt16("oem_id"),
38             ULInt16("oem_info"),
39             Padding(20),
40             ULInt32("coff_header_pointer"),
41             Anchor("_assembly_start"),
42             Field("code", sub { $_->ctx->{coff_header_pointer} - $_->ctx->{_assembly_start} } ),
43             );
44            
45             my $symbol_table = Struct("symbol_table",
46             String("name", 8, padchar => "\x00"),
47             ULInt32("value"),
48             Enum(
49             Data::ParseBinary::lib::ExecPE32::OneDownAdapter->create(SLInt16("section_number")),
50             #ExprAdapter(SLInt16("section_number"),
51             # encoder => sub { $_->obj + 1 },
52             # decoder => sub { $_->obj - 1 },
53             #),
54             UNDEFINED => -1,
55             ABSOLUTE => -2,
56             DEBUG => -3,
57             _default_ => $DefaultPass,
58             ),
59             Enum(ULInt8("complex_type"),
60             NULL => 0,
61             POINTER => 1,
62             FUNCTION => 2,
63             ARRAY => 3,
64             ),
65             Enum(ULInt8("base_type"),
66             NULL => 0,
67             VOID => 1,
68             CHAR => 2,
69             SHORT => 3,
70             INT => 4,
71             LONG => 5,
72             FLOAT => 6,
73             DOUBLE => 7,
74             STRUCT => 8,
75             UNION => 9,
76             ENUM => 10,
77             MOE => 11,
78             BYTE => 12,
79             WORD => 13,
80             UINT => 14,
81             DWORD => 15,
82             ),
83             Enum(ULInt8("storage_class"),
84             END_OF_FUNCTION => 255,
85             NULL => 0,
86             AUTOMATIC => 1,
87             EXTERNAL => 2,
88             STATIC => 3,
89             REGISTER => 4,
90             EXTERNAL_DEF => 5,
91             LABEL => 6,
92             UNDEFINED_LABEL => 7,
93             MEMBER_OF_STRUCT => 8,
94             ARGUMENT => 9,
95             STRUCT_TAG => 10,
96             MEMBER_OF_UNION => 11,
97             UNION_TAG => 12,
98             TYPE_DEFINITION => 13,
99             UNDEFINED_STATIC => 14,
100             ENUM_TAG => 15,
101             MEMBER_OF_ENUM => 16,
102             REGISTER_PARAM => 17,
103             BIT_FIELD => 18,
104             BLOCK => 100,
105             FUNCTION => 101,
106             END_OF_STRUCT => 102,
107             FILE => 103,
108             SECTION => 104,
109             WEAK_EXTERNAL => 105,
110             ),
111             ULInt8("number_of_aux_symbols"),
112             Array(sub { $_->ctx->{number_of_aux_symbols} },
113             Bytes("aux_symbols", 18)
114             )
115             );
116            
117             my $coff_header = Struct("coff_header",
118             Magic("PE\x00\x00"),
119             Enum(ULInt16("machine_type"),
120             UNKNOWN => 0x0,
121             AM33 => 0x1d3,
122             AMD64 => 0x8664,
123             ARM => 0x1c0,
124             EBC => 0xebc,
125             I386 => 0x14c,
126             IA64 => 0x200,
127             M32R => 0x9041,
128             MIPS16 => 0x266,
129             MIPSFPU => 0x366,
130             MIPSFPU16 => 0x466,
131             POWERPC => 0x1f0,
132             POWERPCFP => 0x1f1,
133             R4000 => 0x166,
134             SH3 => 0x1a2,
135             SH3DSP => 0x1a3,
136             SH4 => 0x1a6,
137             SH5=> 0x1a8,
138             THUMB => 0x1c2,
139             WCEMIPSV2 => 0x169,
140             _default_ => $DefaultPass
141             ),
142             ULInt16("number_of_sections"),
143             UTCTimeStamp("time_stamp"),
144             ULInt32("symbol_table_pointer"),
145             ULInt32("number_of_symbols"),
146             ULInt16("optional_header_size"),
147             FlagsEnum(ULInt16("characteristics"),
148             RELOCS_STRIPPED => 0x0001,
149             EXECUTABLE_IMAGE => 0x0002,
150             LINE_NUMS_STRIPPED => 0x0004,
151             LOCAL_SYMS_STRIPPED => 0x0008,
152             AGGRESSIVE_WS_TRIM => 0x0010,
153             LARGE_ADDRESS_AWARE => 0x0020,
154             MACHINE_16BIT => 0x0040,
155             BYTES_REVERSED_LO => 0x0080,
156             MACHINE_32BIT => 0x0100,
157             DEBUG_STRIPPED => 0x0200,
158             REMOVABLE_RUN_FROM_SWAP => 0x0400,
159             SYSTEM => 0x1000,
160             DLL => 0x2000,
161             UNIPROCESSOR_ONLY => 0x4000,
162             BIG_ENDIAN_MACHINE => 0x8000,
163             ),
164            
165             # symbol table
166             Pointer(sub { $_->ctx->{symbol_table_pointer} },
167             Array(sub { $_->ctx->{number_of_symbols} }, $symbol_table)
168             )
169             );
170            
171             sub PEPlusField {
172 5     5 0 16 my ($name) = @_;
173 10     10   28 return IfThenElse($name, sub { $_->ctx->{pe_type} eq "PE32_plus" },
174 5         23 ULInt64(undef),
175             ULInt32(undef),
176             );
177             }
178            
179             my $optional_header = Struct("optional_header",
180             # standard fields
181             Enum(ULInt16("pe_type"),
182             PE32 => 0x10b,
183             PE32_plus => 0x20b,
184             ),
185             ULInt8("major_linker_version"),
186             ULInt8("minor_linker_version"),
187             ULInt32("code_size"),
188             ULInt32("initialized_data_size"),
189             ULInt32("uninitialized_data_size"),
190             ULInt32("entry_point_pointer"),
191             ULInt32("base_of_code"),
192            
193             # only in PE32 files
194             If(sub { $_->ctx->{pe_type} eq "PE32" },
195             ULInt32("base_of_data")
196             ),
197            
198             # WinNT-specific fields
199             PEPlusField("image_base"),
200             ULInt32("section_aligment"),
201             ULInt32("file_alignment"),
202             ULInt16("major_os_version"),
203             ULInt16("minor_os_version"),
204             ULInt16("major_image_version"),
205             ULInt16("minor_image_version"),
206             ULInt16("major_subsystem_version"),
207             ULInt16("minor_subsystem_version"),
208             Padding(4),
209             ULInt32("image_size"),
210             ULInt32("headers_size"),
211             ULInt32("checksum"),
212             Enum(ULInt16("subsystem"),
213             UNKNOWN => 0,
214             NATIVE => 1,
215             WINDOWS_GUI => 2,
216             WINDOWS_CUI => 3,
217             POSIX_CIU => 7,
218             WINDOWS_CE_GUI => 9,
219             EFI_APPLICATION => 10,
220             EFI_BOOT_SERVICE_DRIVER => 11,
221             EFI_RUNTIME_DRIVER => 12,
222             EFI_ROM => 13,
223             XBOX => 14,
224             _defualt_ => $DefaultPass
225             ),
226             FlagsEnum(ULInt16("dll_characteristics"),
227             NO_BIND => 0x0800,
228             WDM_DRIVER => 0x2000,
229             TERMINAL_SERVER_AWARE => 0x8000,
230             ),
231             PEPlusField("reserved_stack_size"),
232             PEPlusField("stack_commit_size"),
233             PEPlusField("reserved_heap_size"),
234             PEPlusField("heap_commit_size"),
235             ULInt32("loader_flags"),
236             ULInt32("number_of_data_directories"),
237            
238             Data::ParseBinary::lib::ExecPE32::NamedSequence->create(
239             Array(sub { $_->ctx->{number_of_data_directories} },
240             Struct("data_directories",
241             ULInt32("address"),
242             ULInt32("size"),
243             )
244             ),
245             mapping => {
246             0 => 'export_table',
247             1 => 'import_table',
248             2 => 'resource_table',
249             3 => 'exception_table',
250             4 => 'certificate_table',
251             5 => 'base_relocation_table',
252             6 => 'debug',
253             7 => 'architecture',
254             8 => 'global_ptr',
255             9 => 'tls_table',
256             10 => 'load_config_table',
257             11 => 'bound_import',
258             12 => 'import_address_table',
259             13 => 'delay_import_descriptor',
260             14 => 'complus_runtime_header',
261             }
262             ),
263             );
264            
265             my $section = Struct("section",
266             String("name", 8, padchar => "\x00"),
267             ULInt32("virtual_size"),
268             ULInt32("virtual_address"),
269             ULInt32("raw_data_size"),
270             ULInt32("raw_data_pointer"),
271             ULInt32("relocations_pointer"),
272             ULInt32("line_numbers_pointer"),
273             ULInt16("number_of_relocations"),
274             ULInt16("number_of_line_numbers"),
275             FlagsEnum(ULInt32("characteristics"),
276             TYPE_REG => 0x00000000,
277             TYPE_DSECT => 0x00000001,
278             TYPE_NOLOAD => 0x00000002,
279             TYPE_GROUP => 0x00000004,
280             TYPE_NO_PAD => 0x00000008,
281             TYPE_COPY => 0x00000010,
282             CNT_CODE => 0x00000020,
283             CNT_INITIALIZED_DATA => 0x00000040,
284             CNT_UNINITIALIZED_DATA => 0x00000080,
285             LNK_OTHER => 0x00000100,
286             LNK_INFO => 0x00000200,
287             TYPE_OVER => 0x00000400,
288             LNK_REMOVE => 0x00000800,
289             LNK_COMDAT => 0x00001000,
290             MEM_FARDATA => 0x00008000,
291             MEM_PURGEABLE => 0x00020000,
292             MEM_16BIT => 0x00020000,
293             MEM_LOCKED => 0x00040000,
294             MEM_PRELOAD => 0x00080000,
295             ALIGN_1BYTES => 0x00100000,
296             ALIGN_2BYTES => 0x00200000,
297             ALIGN_4BYTES => 0x00300000,
298             ALIGN_8BYTES => 0x00400000,
299             ALIGN_16BYTES => 0x00500000,
300             ALIGN_32BYTES => 0x00600000,
301             ALIGN_64BYTES => 0x00700000,
302             ALIGN_128BYTES => 0x00800000,
303             ALIGN_256BYTES => 0x00900000,
304             ALIGN_512BYTES => 0x00A00000,
305             ALIGN_1024BYTES => 0x00B00000,
306             ALIGN_2048BYTES => 0x00C00000,
307             ALIGN_4096BYTES => 0x00D00000,
308             ALIGN_8192BYTES => 0x00E00000,
309             LNK_NRELOC_OVFL => 0x01000000,
310             MEM_DISCARDABLE => 0x02000000,
311             MEM_NOT_CACHED => 0x04000000,
312             MEM_NOT_PAGED => 0x08000000,
313             MEM_SHARED => 0x10000000,
314             MEM_EXECUTE => 0x20000000,
315             MEM_READ => 0x40000000,
316             MEM_WRITE => 0x80000000,
317             ),
318            
319             Pointer(sub { $_->ctx->{raw_data_pointer} },
320             Field("raw_data", sub { $_->ctx->{raw_data_size} })
321             ),
322            
323             Pointer(sub { $_->ctx->{line_numbers_pointer} },
324             Array(sub { $_->ctx->{number_of_line_numbers} },
325             Struct("line_numbers",
326             ULInt32("type"),
327             ULInt16("line_number"),
328             )
329             )
330             ),
331            
332             Pointer(sub { $_->ctx->{relocations_pointer} },
333             Array(sub { $_->ctx->{number_of_relocations} },
334             Struct("relocations",
335             ULInt32("virtual_address"),
336             ULInt32("symbol_table_index"),
337             ULInt16("type"),
338             )
339             )
340             ),
341             );
342            
343             sub min {
344 2     2 0 7 my @values = @_;
345 2 50       9 return undef if @values == 0;
346 2         6 my $ret_val = $values[0];
347 2         6 foreach my $val (@values) {
348 4 50       19 if ($val < $ret_val) {
349 0         0 $ret_val = $val;
350             }
351             }
352             }
353            
354             our $pe32_parser = Struct("pe32_file",
355             # headers
356             $msdos_header,
357             $coff_header,
358             Anchor("_start_of_optional_header"),
359             $optional_header,
360             Anchor("_end_of_optional_header"),
361             Padding(sub { min(0,
362             $_->ctx->{coff_header}->{optional_header_size} -
363             $_->ctx->{_end_of_optional_header} +
364             $_->ctx->{_start_of_optional_header} ) }
365             ),
366            
367             # sections
368             Array(sub { $_->ctx->{coff_header}->{number_of_sections} }, $section),
369             );
370            
371             require Exporter;
372             our @ISA = qw(Exporter);
373             our @EXPORT = qw($pe32_parser);
374            
375             package Data::ParseBinary::lib::ExecPE32::OneDownAdapter;
376             our @ISA;
377 1     1   119 BEGIN { @ISA = qw{Data::ParseBinary::Adapter}; }
378            
379             sub _decode {
380 0     0   0 my ($self, $value) = @_;
381 0         0 return $value - 1;
382             }
383             sub _encode {
384 0     0   0 my ($self, $tvalue) = @_;
385 0         0 return $tvalue + 1;
386             }
387            
388             package Data::ParseBinary::lib::ExecPE32::UTCTimeStampAdapter;
389             our @ISA;
390 1     1   130 BEGIN { @ISA = qw{Data::ParseBinary::Adapter}; }
391            
392             sub _decode {
393 2     2   4 my ($self, $value) = @_;
394 2         5 return $value;
395             #return time.ctime(obj)
396             }
397             sub _encode {
398 0     0   0 my ($self, $tvalue) = @_;
399 0         0 return $tvalue;
400             #return int(time.mktime(time.strptime(obj)))
401             }
402            
403             package Data::ParseBinary::lib::ExecPE32::NamedSequence;
404             our @ISA;
405 1     1   408 BEGIN { @ISA = qw{Data::ParseBinary::Adapter}; }
406            
407             #"""
408             #creates a mapping between the elements of a sequence and their respective
409             #names. this is useful for sequences of a variable length, where each
410             #element in the sequence has a name (as is the case with the data
411             #directories of the PE header)
412             #"""
413            
414             sub _init {
415 1     1   3 my ($self, %params) = @_;
416 1 50       4 die "You need to specify mapping to NamedSequence" unless $params{mapping};
417 1         2 $self->{mapping} = $params{mapping};
418 1         3 my $rev_mapping = {};
419 1         2 while (my ($key, $val) = each %{ $params{mapping} }) {
  16         45  
420 15         31 $rev_mapping->{$val} = $key;
421             }
422 1         4 $self->{rev_mapping} = $rev_mapping;
423             }
424            
425             sub _decode {
426 2     2   4 my ($self, $value) = @_;
427 2         5 my $tvalue = {};
428 2         9 foreach my $ix (0..$#$value) {
429 32         37 my $name = $ix;
430 32 100       91 $name = $self->{mapping}->{$name} if exists $self->{mapping}->{$name};
431 32         83 $tvalue->{$name} = $value->[$ix];
432             }
433 2         9 return $tvalue;
434             }
435            
436             sub _encode {
437 0     0     my ($self, $tvalue) = @_;
438 0           my $value = [];
439 0           while (my ($key, $val) = each %$tvalue) {
440 0           my $index = $key;
441 0 0         if (exists $self->{rev_mapping}->{$index}) {
    0          
442 0           $index = $self->{rev_mapping}->{$index};
443             } elsif ($index !~ /^\d+$/) {
444 0           die "NamedSequence: encoded value should be either a recognized name or a number";
445             }
446 0           $value->[$index] = $val;
447             }
448 0           return $value;
449             }
450            
451             #__slots__ = ["mapping", "rev_mapping"]
452             #prefix = "unnamed_"
453             #def __init__(self, subcon, mapping):
454             # Adapter.__init__(self, subcon)
455             # self.mapping = mapping
456             # self.rev_mapping = dict((v, k) for k, v in mapping.iteritems())
457             #def _encode(self, obj, context):
458             # d = obj.__dict__
459             # obj2 = [None] * len(d)
460             # for name, value in d.iteritems():
461             # if name in self.rev_mapping:
462             # index = self.rev_mapping[name]
463             # elif name.startswith("__"):
464             # obj2.pop(-1)
465             # continue
466             # elif name.startswith(self.prefix):
467             # index = int(name.split(self.prefix)[1])
468             # else:
469             # raise ValueError("no mapping defined for %r" % (name,))
470             # obj2[index] = value
471             # return obj2
472             #def _decode(self, obj, context):
473             # obj2 = Container()
474             # for i, item in enumerate(obj):
475             # if i in self.mapping:
476             # name = self.mapping[i]
477             # else:
478             # name = "%s%d" % (self.prefix, i)
479             # setattr(obj2, name, item)
480             # return obj2
481            
482             1;
483            
484             __END__