- add sources.
[platform/framework/web/crosswalk.git] / src / tools / symsrc / pefile.py
1 # -*- coding: Latin-1 -*-
2 """pefile, Portable Executable reader module
3
4
5 All the PE file basic structures are available with their default names
6 as attributes of the instance returned.
7
8 Processed elements such as the import table are made available with lowercase
9 names, to differentiate them from the upper case basic structure names.
10
11 pefile has been tested against the limits of valid PE headers, that is, malware.
12 Lots of packed malware attempt to abuse the format way beyond its standard use.
13 To the best of my knowledge most of the abuses are handled gracefully.
14
15 Copyright (c) 2005, 2006, 2007, 2008 Ero Carrera <ero@dkbza.org>
16
17 All rights reserved.
18
19 For detailed copyright information see the file COPYING in
20 the root of the distribution archive.
21 """
22
23 __author__ = 'Ero Carrera'
24 __version__ = '1.2.9.1'
25 __contact__ = 'ero@dkbza.org'
26
27
28 import os
29 import struct
30 import time
31 import math
32 import re
33 import exceptions
34 import string
35 import array
36
37 sha1, sha256, sha512, md5 = None, None, None, None
38
39 try:
40     import hashlib
41     sha1 = hashlib.sha1
42     sha256 = hashlib.sha256
43     sha512 = hashlib.sha512
44     md5 = hashlib.md5
45 except ImportError:    
46     try:
47         import sha
48         sha1 = sha.new
49     except ImportError:
50         pass
51     try:
52         import md5
53         md5 = md5.new
54     except ImportError:
55         pass
56
57
58 fast_load = False
59
60 IMAGE_DOS_SIGNATURE             = 0x5A4D
61 IMAGE_OS2_SIGNATURE             = 0x454E
62 IMAGE_OS2_SIGNATURE_LE          = 0x454C
63 IMAGE_VXD_SIGNATURE             = 0x454C
64 IMAGE_NT_SIGNATURE              = 0x00004550
65 IMAGE_NUMBEROF_DIRECTORY_ENTRIES= 16
66 IMAGE_ORDINAL_FLAG              = 0x80000000L
67 IMAGE_ORDINAL_FLAG64            = 0x8000000000000000L
68 OPTIONAL_HEADER_MAGIC_PE        = 0x10b
69 OPTIONAL_HEADER_MAGIC_PE_PLUS   = 0x20b
70
71
72 directory_entry_types = [
73     ('IMAGE_DIRECTORY_ENTRY_EXPORT',        0),
74     ('IMAGE_DIRECTORY_ENTRY_IMPORT',        1),
75     ('IMAGE_DIRECTORY_ENTRY_RESOURCE',      2),
76     ('IMAGE_DIRECTORY_ENTRY_EXCEPTION',     3),
77     ('IMAGE_DIRECTORY_ENTRY_SECURITY',      4),
78     ('IMAGE_DIRECTORY_ENTRY_BASERELOC',     5),
79     ('IMAGE_DIRECTORY_ENTRY_DEBUG',         6),
80     ('IMAGE_DIRECTORY_ENTRY_COPYRIGHT',     7),
81     ('IMAGE_DIRECTORY_ENTRY_GLOBALPTR',     8),
82     ('IMAGE_DIRECTORY_ENTRY_TLS',           9),
83     ('IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG',   10),
84     ('IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT',  11),
85     ('IMAGE_DIRECTORY_ENTRY_IAT',           12),
86     ('IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT',  13),
87     ('IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR',14),
88     ('IMAGE_DIRECTORY_ENTRY_RESERVED',      15) ]
89
90 DIRECTORY_ENTRY = dict([(e[1], e[0]) for e in directory_entry_types]+directory_entry_types)
91  
92
93 image_characteristics = [
94     ('IMAGE_FILE_RELOCS_STRIPPED',          0x0001),
95     ('IMAGE_FILE_EXECUTABLE_IMAGE',         0x0002),
96     ('IMAGE_FILE_LINE_NUMS_STRIPPED',       0x0004),
97     ('IMAGE_FILE_LOCAL_SYMS_STRIPPED',      0x0008),
98     ('IMAGE_FILE_AGGRESIVE_WS_TRIM',        0x0010),
99     ('IMAGE_FILE_LARGE_ADDRESS_AWARE',      0x0020),
100     ('IMAGE_FILE_16BIT_MACHINE',            0x0040),
101     ('IMAGE_FILE_BYTES_REVERSED_LO',        0x0080),
102     ('IMAGE_FILE_32BIT_MACHINE',            0x0100),
103     ('IMAGE_FILE_DEBUG_STRIPPED',           0x0200),
104     ('IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP',  0x0400),
105     ('IMAGE_FILE_NET_RUN_FROM_SWAP',        0x0800),
106     ('IMAGE_FILE_SYSTEM',                   0x1000),
107     ('IMAGE_FILE_DLL',                      0x2000),
108     ('IMAGE_FILE_UP_SYSTEM_ONLY',           0x4000),
109     ('IMAGE_FILE_BYTES_REVERSED_HI',        0x8000) ]
110
111 IMAGE_CHARACTERISTICS = dict([(e[1], e[0]) for e in
112     image_characteristics]+image_characteristics)
113
114     
115 section_characteristics = [
116     ('IMAGE_SCN_CNT_CODE',                  0x00000020),
117     ('IMAGE_SCN_CNT_INITIALIZED_DATA',      0x00000040),
118     ('IMAGE_SCN_CNT_UNINITIALIZED_DATA',    0x00000080),
119     ('IMAGE_SCN_LNK_OTHER',                 0x00000100),
120     ('IMAGE_SCN_LNK_INFO',                  0x00000200),
121     ('IMAGE_SCN_LNK_REMOVE',                0x00000800),
122     ('IMAGE_SCN_LNK_COMDAT',                0x00001000),
123     ('IMAGE_SCN_MEM_FARDATA',               0x00008000),
124     ('IMAGE_SCN_MEM_PURGEABLE',             0x00020000),
125     ('IMAGE_SCN_MEM_16BIT',                 0x00020000),
126     ('IMAGE_SCN_MEM_LOCKED',                0x00040000),
127     ('IMAGE_SCN_MEM_PRELOAD',               0x00080000),
128     ('IMAGE_SCN_ALIGN_1BYTES',              0x00100000),
129     ('IMAGE_SCN_ALIGN_2BYTES',              0x00200000),
130     ('IMAGE_SCN_ALIGN_4BYTES',              0x00300000),
131     ('IMAGE_SCN_ALIGN_8BYTES',              0x00400000),
132     ('IMAGE_SCN_ALIGN_16BYTES',             0x00500000),
133     ('IMAGE_SCN_ALIGN_32BYTES',             0x00600000),
134     ('IMAGE_SCN_ALIGN_64BYTES',             0x00700000),
135     ('IMAGE_SCN_ALIGN_128BYTES',            0x00800000),
136     ('IMAGE_SCN_ALIGN_256BYTES',            0x00900000),
137     ('IMAGE_SCN_ALIGN_512BYTES',            0x00A00000),
138     ('IMAGE_SCN_ALIGN_1024BYTES',           0x00B00000),
139     ('IMAGE_SCN_ALIGN_2048BYTES',           0x00C00000),
140     ('IMAGE_SCN_ALIGN_4096BYTES',           0x00D00000),
141     ('IMAGE_SCN_ALIGN_8192BYTES',           0x00E00000),
142     ('IMAGE_SCN_ALIGN_MASK',                0x00F00000),
143     ('IMAGE_SCN_LNK_NRELOC_OVFL',           0x01000000),
144     ('IMAGE_SCN_MEM_DISCARDABLE',           0x02000000),
145     ('IMAGE_SCN_MEM_NOT_CACHED',            0x04000000),
146     ('IMAGE_SCN_MEM_NOT_PAGED',             0x08000000),
147     ('IMAGE_SCN_MEM_SHARED',                0x10000000),
148     ('IMAGE_SCN_MEM_EXECUTE',               0x20000000),
149     ('IMAGE_SCN_MEM_READ',                  0x40000000),
150     ('IMAGE_SCN_MEM_WRITE',                 0x80000000L) ]
151  
152 SECTION_CHARACTERISTICS = dict([(e[1], e[0]) for e in
153     section_characteristics]+section_characteristics)
154
155
156 debug_types = [
157     ('IMAGE_DEBUG_TYPE_UNKNOWN',        0),
158     ('IMAGE_DEBUG_TYPE_COFF',           1),
159     ('IMAGE_DEBUG_TYPE_CODEVIEW',       2),
160     ('IMAGE_DEBUG_TYPE_FPO',            3),
161     ('IMAGE_DEBUG_TYPE_MISC',           4),
162     ('IMAGE_DEBUG_TYPE_EXCEPTION',      5),
163     ('IMAGE_DEBUG_TYPE_FIXUP',          6),
164     ('IMAGE_DEBUG_TYPE_OMAP_TO_SRC',    7),
165     ('IMAGE_DEBUG_TYPE_OMAP_FROM_SRC',  8),
166     ('IMAGE_DEBUG_TYPE_BORLAND',        9),
167     ('IMAGE_DEBUG_TYPE_RESERVED10',     10) ]
168
169 DEBUG_TYPE = dict([(e[1], e[0]) for e in debug_types]+debug_types)
170
171
172 subsystem_types = [
173     ('IMAGE_SUBSYSTEM_UNKNOWN',     0),
174     ('IMAGE_SUBSYSTEM_NATIVE',      1),
175     ('IMAGE_SUBSYSTEM_WINDOWS_GUI', 2),
176     ('IMAGE_SUBSYSTEM_WINDOWS_CUI', 3),
177     ('IMAGE_SUBSYSTEM_OS2_CUI',     5),
178     ('IMAGE_SUBSYSTEM_POSIX_CUI',   7),
179     ('IMAGE_SUBSYSTEM_WINDOWS_CE_GUI',  9),
180     ('IMAGE_SUBSYSTEM_EFI_APPLICATION', 10),
181     ('IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER', 11),
182     ('IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER',      12),
183     ('IMAGE_SUBSYSTEM_EFI_ROM',     13),
184     ('IMAGE_SUBSYSTEM_XBOX',        14)]
185
186 SUBSYSTEM_TYPE = dict([(e[1], e[0]) for e in subsystem_types]+subsystem_types)
187
188
189 machine_types = [
190     ('IMAGE_FILE_MACHINE_UNKNOWN',  0),
191     ('IMAGE_FILE_MACHINE_AM33',     0x1d3),
192     ('IMAGE_FILE_MACHINE_AMD64',    0x8664),
193     ('IMAGE_FILE_MACHINE_ARM',      0x1c0),
194     ('IMAGE_FILE_MACHINE_EBC',      0xebc),
195     ('IMAGE_FILE_MACHINE_I386',     0x14c),
196     ('IMAGE_FILE_MACHINE_IA64',     0x200),
197     ('IMAGE_FILE_MACHINE_MR32',     0x9041),
198     ('IMAGE_FILE_MACHINE_MIPS16',   0x266),
199     ('IMAGE_FILE_MACHINE_MIPSFPU',  0x366),
200     ('IMAGE_FILE_MACHINE_MIPSFPU16',0x466),
201     ('IMAGE_FILE_MACHINE_POWERPC',  0x1f0),
202     ('IMAGE_FILE_MACHINE_POWERPCFP',0x1f1),
203     ('IMAGE_FILE_MACHINE_R4000',    0x166),
204     ('IMAGE_FILE_MACHINE_SH3',      0x1a2),
205     ('IMAGE_FILE_MACHINE_SH3DSP',   0x1a3),
206     ('IMAGE_FILE_MACHINE_SH4',      0x1a6),
207     ('IMAGE_FILE_MACHINE_SH5',      0x1a8),
208     ('IMAGE_FILE_MACHINE_THUMB',    0x1c2),
209     ('IMAGE_FILE_MACHINE_WCEMIPSV2',0x169),
210  ]
211
212 MACHINE_TYPE = dict([(e[1], e[0]) for e in machine_types]+machine_types)
213
214
215 relocation_types = [
216     ('IMAGE_REL_BASED_ABSOLUTE',        0),
217     ('IMAGE_REL_BASED_HIGH',            1),
218     ('IMAGE_REL_BASED_LOW',             2),
219     ('IMAGE_REL_BASED_HIGHLOW',         3),
220     ('IMAGE_REL_BASED_HIGHADJ',         4),
221     ('IMAGE_REL_BASED_MIPS_JMPADDR',    5),
222     ('IMAGE_REL_BASED_SECTION',         6),
223     ('IMAGE_REL_BASED_REL',             7),
224     ('IMAGE_REL_BASED_MIPS_JMPADDR16',  9),
225     ('IMAGE_REL_BASED_IA64_IMM64',      9),
226     ('IMAGE_REL_BASED_DIR64',           10),
227     ('IMAGE_REL_BASED_HIGH3ADJ',        11) ]
228
229 RELOCATION_TYPE = dict([(e[1], e[0]) for e in relocation_types]+relocation_types)
230
231
232 dll_characteristics = [
233     ('IMAGE_DLL_CHARACTERISTICS_RESERVED_0x0001', 0x0001),
234     ('IMAGE_DLL_CHARACTERISTICS_RESERVED_0x0002', 0x0002),
235     ('IMAGE_DLL_CHARACTERISTICS_RESERVED_0x0004', 0x0004),
236     ('IMAGE_DLL_CHARACTERISTICS_RESERVED_0x0008', 0x0008),
237     ('IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE',      0x0040),
238     ('IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY',   0x0080),
239     ('IMAGE_DLL_CHARACTERISTICS_NX_COMPAT',         0x0100),
240     ('IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION',      0x0200),
241     ('IMAGE_DLL_CHARACTERISTICS_NO_SEH',    0x0400),
242     ('IMAGE_DLL_CHARACTERISTICS_NO_BIND',   0x0800),
243     ('IMAGE_DLL_CHARACTERISTICS_RESERVED_0x1000', 0x1000),
244     ('IMAGE_DLL_CHARACTERISTICS_WDM_DRIVER',    0x2000),
245     ('IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE', 0x8000) ]
246
247 DLL_CHARACTERISTICS = dict([(e[1], e[0]) for e in dll_characteristics]+dll_characteristics)
248
249
250 # Resource types
251 resource_type = [
252     ('RT_CURSOR',          1),
253     ('RT_BITMAP',          2),
254     ('RT_ICON',            3),
255     ('RT_MENU',            4),
256     ('RT_DIALOG',          5),
257     ('RT_STRING',          6),
258     ('RT_FONTDIR',         7),
259     ('RT_FONT',            8),
260     ('RT_ACCELERATOR',     9),
261     ('RT_RCDATA',          10),
262     ('RT_MESSAGETABLE',    11),
263     ('RT_GROUP_CURSOR',    12),
264     ('RT_GROUP_ICON',      14),
265     ('RT_VERSION',         16),
266     ('RT_DLGINCLUDE',      17),
267     ('RT_PLUGPLAY',        19),
268     ('RT_VXD',             20),
269     ('RT_ANICURSOR',       21),
270     ('RT_ANIICON',         22),
271     ('RT_HTML',            23),
272     ('RT_MANIFEST',        24) ]
273
274 RESOURCE_TYPE = dict([(e[1], e[0]) for e in resource_type]+resource_type)
275
276     
277 # Language definitions
278 lang = [
279  ('LANG_NEUTRAL',       0x00),
280  ('LANG_INVARIANT',     0x7f),
281  ('LANG_AFRIKAANS',     0x36),
282  ('LANG_ALBANIAN',      0x1c),
283  ('LANG_ARABIC',        0x01),
284  ('LANG_ARMENIAN',      0x2b),
285  ('LANG_ASSAMESE',      0x4d),
286  ('LANG_AZERI',         0x2c),
287  ('LANG_BASQUE',        0x2d),
288  ('LANG_BELARUSIAN',    0x23),
289  ('LANG_BENGALI',       0x45),
290  ('LANG_BULGARIAN',     0x02),
291  ('LANG_CATALAN',       0x03),
292  ('LANG_CHINESE',       0x04),
293  ('LANG_CROATIAN',      0x1a),
294  ('LANG_CZECH',         0x05),
295  ('LANG_DANISH',        0x06),
296  ('LANG_DIVEHI',        0x65),
297  ('LANG_DUTCH',         0x13),
298  ('LANG_ENGLISH',       0x09),
299  ('LANG_ESTONIAN',      0x25),
300  ('LANG_FAEROESE',      0x38),
301  ('LANG_FARSI',         0x29),
302  ('LANG_FINNISH',       0x0b),
303  ('LANG_FRENCH',        0x0c),
304  ('LANG_GALICIAN',      0x56),
305  ('LANG_GEORGIAN',      0x37),
306  ('LANG_GERMAN',        0x07),
307  ('LANG_GREEK',         0x08),
308  ('LANG_GUJARATI',      0x47),
309  ('LANG_HEBREW',        0x0d),
310  ('LANG_HINDI',         0x39),
311  ('LANG_HUNGARIAN',     0x0e),
312  ('LANG_ICELANDIC',     0x0f),
313  ('LANG_INDONESIAN',    0x21),
314  ('LANG_ITALIAN',       0x10),
315  ('LANG_JAPANESE',      0x11),
316  ('LANG_KANNADA',       0x4b),
317  ('LANG_KASHMIRI',      0x60),
318  ('LANG_KAZAK',         0x3f),
319  ('LANG_KONKANI',       0x57),
320  ('LANG_KOREAN',        0x12),
321  ('LANG_KYRGYZ',        0x40),
322  ('LANG_LATVIAN',       0x26),
323  ('LANG_LITHUANIAN',    0x27),
324  ('LANG_MACEDONIAN',    0x2f),
325  ('LANG_MALAY',         0x3e),
326  ('LANG_MALAYALAM',     0x4c),
327  ('LANG_MANIPURI',      0x58),
328  ('LANG_MARATHI',       0x4e),
329  ('LANG_MONGOLIAN',     0x50),
330  ('LANG_NEPALI',        0x61),
331  ('LANG_NORWEGIAN',     0x14),
332  ('LANG_ORIYA',         0x48),
333  ('LANG_POLISH',        0x15),
334  ('LANG_PORTUGUESE',    0x16),
335  ('LANG_PUNJABI',       0x46),
336  ('LANG_ROMANIAN',      0x18),
337  ('LANG_RUSSIAN',       0x19),
338  ('LANG_SANSKRIT',      0x4f),
339  ('LANG_SERBIAN',       0x1a),
340  ('LANG_SINDHI',        0x59),
341  ('LANG_SLOVAK',        0x1b),
342  ('LANG_SLOVENIAN',     0x24),
343  ('LANG_SPANISH',       0x0a),
344  ('LANG_SWAHILI',       0x41),
345  ('LANG_SWEDISH',       0x1d),
346  ('LANG_SYRIAC',        0x5a),
347  ('LANG_TAMIL',         0x49),
348  ('LANG_TATAR',         0x44),
349  ('LANG_TELUGU',        0x4a),
350  ('LANG_THAI',          0x1e),
351  ('LANG_TURKISH',       0x1f),
352  ('LANG_UKRAINIAN',     0x22),
353  ('LANG_URDU',          0x20),
354  ('LANG_UZBEK',         0x43),
355  ('LANG_VIETNAMESE',    0x2a),
356  ('LANG_GAELIC',        0x3c),
357  ('LANG_MALTESE',       0x3a),
358  ('LANG_MAORI',         0x28),
359  ('LANG_RHAETO_ROMANCE',0x17),
360  ('LANG_SAAMI',         0x3b),
361  ('LANG_SORBIAN',       0x2e),
362  ('LANG_SUTU',          0x30),
363  ('LANG_TSONGA',        0x31),
364  ('LANG_TSWANA',        0x32),
365  ('LANG_VENDA',         0x33),
366  ('LANG_XHOSA',         0x34),
367  ('LANG_ZULU',          0x35),
368  ('LANG_ESPERANTO',     0x8f),
369  ('LANG_WALON',         0x90),
370  ('LANG_CORNISH',       0x91),
371  ('LANG_WELSH',         0x92),
372  ('LANG_BRETON',        0x93) ]
373
374 LANG = dict(lang+[(e[1], e[0]) for e in lang])
375
376
377 # Sublanguage definitions
378 sublang =  [
379  ('SUBLANG_NEUTRAL',                        0x00),
380  ('SUBLANG_DEFAULT',                        0x01),
381  ('SUBLANG_SYS_DEFAULT',                    0x02),
382  ('SUBLANG_ARABIC_SAUDI_ARABIA',            0x01),
383  ('SUBLANG_ARABIC_IRAQ',                    0x02),
384  ('SUBLANG_ARABIC_EGYPT',                   0x03),
385  ('SUBLANG_ARABIC_LIBYA',                   0x04),
386  ('SUBLANG_ARABIC_ALGERIA',                 0x05),
387  ('SUBLANG_ARABIC_MOROCCO',                 0x06),
388  ('SUBLANG_ARABIC_TUNISIA',                 0x07),
389  ('SUBLANG_ARABIC_OMAN',                    0x08),
390  ('SUBLANG_ARABIC_YEMEN',                   0x09),
391  ('SUBLANG_ARABIC_SYRIA',                   0x0a),
392  ('SUBLANG_ARABIC_JORDAN',                  0x0b),
393  ('SUBLANG_ARABIC_LEBANON',                 0x0c),
394  ('SUBLANG_ARABIC_KUWAIT',                  0x0d),
395  ('SUBLANG_ARABIC_UAE',                     0x0e),
396  ('SUBLANG_ARABIC_BAHRAIN',                 0x0f),
397  ('SUBLANG_ARABIC_QATAR',                   0x10),
398  ('SUBLANG_AZERI_LATIN',                    0x01),
399  ('SUBLANG_AZERI_CYRILLIC',                 0x02),
400  ('SUBLANG_CHINESE_TRADITIONAL',            0x01),
401  ('SUBLANG_CHINESE_SIMPLIFIED',             0x02),
402  ('SUBLANG_CHINESE_HONGKONG',               0x03),
403  ('SUBLANG_CHINESE_SINGAPORE',              0x04),
404  ('SUBLANG_CHINESE_MACAU',                  0x05),
405  ('SUBLANG_DUTCH',                          0x01),
406  ('SUBLANG_DUTCH_BELGIAN',                  0x02),
407  ('SUBLANG_ENGLISH_US',                     0x01),
408  ('SUBLANG_ENGLISH_UK',                     0x02),
409  ('SUBLANG_ENGLISH_AUS',                    0x03),
410  ('SUBLANG_ENGLISH_CAN',                    0x04),
411  ('SUBLANG_ENGLISH_NZ',                     0x05),
412  ('SUBLANG_ENGLISH_EIRE',                   0x06),
413  ('SUBLANG_ENGLISH_SOUTH_AFRICA',           0x07),
414  ('SUBLANG_ENGLISH_JAMAICA',                0x08),
415  ('SUBLANG_ENGLISH_CARIBBEAN',              0x09),
416  ('SUBLANG_ENGLISH_BELIZE',                 0x0a),
417  ('SUBLANG_ENGLISH_TRINIDAD',               0x0b),
418  ('SUBLANG_ENGLISH_ZIMBABWE',               0x0c),
419  ('SUBLANG_ENGLISH_PHILIPPINES',            0x0d),
420  ('SUBLANG_FRENCH',                         0x01),
421  ('SUBLANG_FRENCH_BELGIAN',                 0x02),
422  ('SUBLANG_FRENCH_CANADIAN',                0x03),
423  ('SUBLANG_FRENCH_SWISS',                   0x04),
424  ('SUBLANG_FRENCH_LUXEMBOURG',              0x05),
425  ('SUBLANG_FRENCH_MONACO',                  0x06),
426  ('SUBLANG_GERMAN',                         0x01),
427  ('SUBLANG_GERMAN_SWISS',                   0x02),
428  ('SUBLANG_GERMAN_AUSTRIAN',                0x03),
429  ('SUBLANG_GERMAN_LUXEMBOURG',              0x04),
430  ('SUBLANG_GERMAN_LIECHTENSTEIN',           0x05),
431  ('SUBLANG_ITALIAN',                        0x01),
432  ('SUBLANG_ITALIAN_SWISS',                  0x02),
433  ('SUBLANG_KASHMIRI_SASIA',                 0x02),
434  ('SUBLANG_KASHMIRI_INDIA',                 0x02),
435  ('SUBLANG_KOREAN',                         0x01),
436  ('SUBLANG_LITHUANIAN',                     0x01),
437  ('SUBLANG_MALAY_MALAYSIA',                 0x01),
438  ('SUBLANG_MALAY_BRUNEI_DARUSSALAM',        0x02),
439  ('SUBLANG_NEPALI_INDIA',                   0x02),
440  ('SUBLANG_NORWEGIAN_BOKMAL',               0x01),
441  ('SUBLANG_NORWEGIAN_NYNORSK',              0x02),
442  ('SUBLANG_PORTUGUESE',                     0x02),
443  ('SUBLANG_PORTUGUESE_BRAZILIAN',           0x01),
444  ('SUBLANG_SERBIAN_LATIN',                  0x02),
445  ('SUBLANG_SERBIAN_CYRILLIC',               0x03),
446  ('SUBLANG_SPANISH',                        0x01),
447  ('SUBLANG_SPANISH_MEXICAN',                0x02),
448  ('SUBLANG_SPANISH_MODERN',                 0x03),
449  ('SUBLANG_SPANISH_GUATEMALA',              0x04),
450  ('SUBLANG_SPANISH_COSTA_RICA',             0x05),
451  ('SUBLANG_SPANISH_PANAMA',                 0x06),
452  ('SUBLANG_SPANISH_DOMINICAN_REPUBLIC',     0x07),
453  ('SUBLANG_SPANISH_VENEZUELA',              0x08),
454  ('SUBLANG_SPANISH_COLOMBIA',               0x09),
455  ('SUBLANG_SPANISH_PERU',                   0x0a),
456  ('SUBLANG_SPANISH_ARGENTINA',              0x0b),
457  ('SUBLANG_SPANISH_ECUADOR',                0x0c),
458  ('SUBLANG_SPANISH_CHILE',                  0x0d),
459  ('SUBLANG_SPANISH_URUGUAY',                0x0e),
460  ('SUBLANG_SPANISH_PARAGUAY',               0x0f),
461  ('SUBLANG_SPANISH_BOLIVIA',                0x10),
462  ('SUBLANG_SPANISH_EL_SALVADOR',            0x11),
463  ('SUBLANG_SPANISH_HONDURAS',               0x12),
464  ('SUBLANG_SPANISH_NICARAGUA',              0x13),
465  ('SUBLANG_SPANISH_PUERTO_RICO',            0x14),
466  ('SUBLANG_SWEDISH',                        0x01),
467  ('SUBLANG_SWEDISH_FINLAND',                0x02),
468  ('SUBLANG_URDU_PAKISTAN',                  0x01),
469  ('SUBLANG_URDU_INDIA',                     0x02),
470  ('SUBLANG_UZBEK_LATIN',                    0x01),
471  ('SUBLANG_UZBEK_CYRILLIC',                 0x02),
472  ('SUBLANG_DUTCH_SURINAM',                  0x03),
473  ('SUBLANG_ROMANIAN',                       0x01),
474  ('SUBLANG_ROMANIAN_MOLDAVIA',              0x02),
475  ('SUBLANG_RUSSIAN',                        0x01),
476  ('SUBLANG_RUSSIAN_MOLDAVIA',               0x02),
477  ('SUBLANG_CROATIAN',                       0x01),
478  ('SUBLANG_LITHUANIAN_CLASSIC',             0x02),
479  ('SUBLANG_GAELIC',                         0x01),
480  ('SUBLANG_GAELIC_SCOTTISH',                0x02),
481  ('SUBLANG_GAELIC_MANX',                    0x03) ]
482
483 SUBLANG = dict(sublang+[(e[1], e[0]) for e in sublang])
484
485
486 class UnicodeStringWrapperPostProcessor:
487     """This class attemps to help the process of identifying strings
488     that might be plain Unicode or Pascal. A list of strings will be
489     wrapped on it with the hope the overlappings will help make the 
490     decission about their type."""
491     
492     def __init__(self, pe, rva_ptr):
493         self.pe = pe
494         self.rva_ptr = rva_ptr
495         self.string = None
496         
497         
498     def get_rva(self):
499         """Get the RVA of the string."""
500         
501         return self.rva_ptr
502         
503         
504     def __str__(self):
505         """Return the escaped ASCII representation of the string."""
506     
507         def convert_char(char):
508             if char in string.printable:
509                 return char
510             else:
511                 return r'\x%02x' % ord(char)
512                 
513         if self.string:
514             return ''.join([convert_char(c) for c in self.string])
515             
516         return ''
517         
518     
519     def invalidate(self):
520         """Make this instance None, to express it's no known string type."""
521         
522         self = None
523     
524         
525     def render_pascal_16(self):
526     
527         self.string = self.pe.get_string_u_at_rva(
528             self.rva_ptr+2, 
529             max_length=self.__get_pascal_16_length())
530
531
532     def ask_pascal_16(self, next_rva_ptr):
533         """The next RVA is taken to be the one immediately following this one.
534         
535         Such RVA could indicate the natural end of the string and will be checked
536         with the possible length contained in the first word.
537         """
538         
539         length = self.__get_pascal_16_length()
540         
541         if length == (next_rva_ptr - (self.rva_ptr+2)) / 2:
542             self.length = length
543             return True
544             
545         return False
546         
547         
548     def __get_pascal_16_length(self):
549     
550         return self.__get_word_value_at_rva(self.rva_ptr)
551         
552     
553     def __get_word_value_at_rva(self, rva):
554
555         try:
556             data = self.pe.get_data(self.rva_ptr, 2)
557         except PEFormatError, e:
558             return False
559
560         if len(data)<2:
561             return False
562
563         return struct.unpack('<H', data)[0]
564
565     
566     #def render_pascal_8(self):
567     #    """"""
568         
569         
570     def ask_unicode_16(self, next_rva_ptr):
571         """The next RVA is taken to be the one immediately following this one.
572         
573         Such RVA could indicate the natural end of the string and will be checked
574         to see if there's a Unicode NULL character there.
575         """
576         
577         if self.__get_word_value_at_rva(next_rva_ptr-2) == 0:
578             self.length = next_rva_ptr - self.rva_ptr
579             return True
580             
581         return False
582         
583
584     def render_unicode_16(self):
585         """"""
586
587         self.string = self.pe.get_string_u_at_rva(self.rva_ptr)
588             
589
590 class PEFormatError(Exception):
591     """Generic PE format error exception."""
592     
593     def __init__(self, value):
594         self.value = value
595
596     def __str__(self):
597         return repr(self.value)
598
599
600 class Dump:
601     """Convenience class for dumping the PE information."""
602     
603     def __init__(self):
604         self.text = ''
605     
606         
607     def add_lines(self, txt, indent=0):
608         """Adds a list of lines.
609         
610         The list can be indented with the optional argument 'indent'.
611         """
612         for line in txt:
613             self.add_line(line, indent)
614         
615             
616     def add_line(self, txt, indent=0):
617         """Adds a line.
618         
619         The line can be indented with the optional argument 'indent'.
620         """
621         
622         self.add(txt+'\n', indent)
623     
624         
625     def add(self, txt, indent=0):
626         """Adds some text, no newline will be appended.
627         
628         The text can be indented with the optional argument 'indent'.
629         """
630         
631         if isinstance(txt, unicode):
632             s = []
633             for c in txt:
634                 try:
635                     s.append(str(c))
636                 except UnicodeEncodeError, e:
637                     s.append(repr(c))
638                         
639             txt = ''.join(s)
640         
641         self.text += ' '*indent+txt
642     
643         
644     def add_header(self, txt):
645         """Adds a header element."""
646         
647         self.add_line('-'*10+txt+'-'*10+'\n')
648         
649         
650     def add_newline(self):
651         """Adds a newline."""
652         
653         self.text += '\n'
654         
655         
656     def get_text(self):
657         """Get the text in its current state."""
658     
659         return self.text
660
661
662
663 class Structure:
664     """Prepare structure object to extract members from data.
665     
666     Format is a list containing definitions for the elements
667     of the structure.
668     """
669     
670     
671     def __init__(self, format, name=None, file_offset=None):
672         # Format is forced little endian, for big endian non Intel platforms
673         self.__format__ = '<'
674         self.__keys__ = []
675 #        self.values = {}
676         self.__format_length__ = 0
677         self.__set_format__(format[1])
678         self._all_zeroes = False
679         self.__unpacked_data_elms__ = None
680         self.__file_offset__ = file_offset
681         if name:
682             self.name = name
683         else:
684             self.name = format[0]
685                 
686             
687     def __get_format__(self):
688         return self.__format__
689         
690         
691     def get_file_offset(self):
692         return self.__file_offset__
693
694     def set_file_offset(self, offset):
695         self.__file_offset__ = offset
696         
697     def all_zeroes(self):
698         """Returns true is the unpacked data is all zeroes."""
699         
700         return self._all_zeroes
701
702                 
703     def __set_format__(self, format):
704     
705         for elm in format:
706             if ',' in elm:
707                 elm_type, elm_name = elm.split(',', 1)
708                 self.__format__ += elm_type
709                 
710                 elm_names = elm_name.split(',')
711                 names = []
712                 for elm_name in elm_names:
713                     if elm_name in self.__keys__:
714                         search_list = [x[:len(elm_name)] for x in self.__keys__]
715                         occ_count = search_list.count(elm_name)
716                         elm_name = elm_name+'_'+str(occ_count)
717                     names.append(elm_name)
718                 # Some PE header structures have unions on them, so a certain
719                 # value might have different names, so each key has a list of
720                 # all the possible members referring to the data.
721                 self.__keys__.append(names)
722                     
723         self.__format_length__ = struct.calcsize(self.__format__)
724         
725         
726     def sizeof(self):
727         """Return size of the structure."""
728     
729         return self.__format_length__
730         
731         
732     def __unpack__(self, data):
733     
734         if len(data)>self.__format_length__:
735             data = data[:self.__format_length__]
736             
737         # OC Patch:
738         # Some malware have incorrect header lengths.
739         # Fail gracefully if this occurs
740         # Buggy malware: a29b0118af8b7408444df81701ad5a7f
741         #
742         elif len(data)<self.__format_length__:
743             raise PEFormatError('Data length less than expected header length.')
744
745             
746         if data.count(chr(0)) == len(data):
747             self._all_zeroes = True
748             
749         self.__unpacked_data_elms__ = struct.unpack(self.__format__, data)
750         for i in xrange(len(self.__unpacked_data_elms__)):
751             for key in self.__keys__[i]:
752 #                self.values[key] = self.__unpacked_data_elms__[i]
753                 setattr(self, key, self.__unpacked_data_elms__[i])
754
755
756     def __pack__(self):
757     
758         new_values = []
759         
760         for i in xrange(len(self.__unpacked_data_elms__)):
761         
762             for key in self.__keys__[i]:
763                 new_val = getattr(self, key)
764                 old_val = self.__unpacked_data_elms__[i]
765                 
766                 # In the case of Unions, when the first changed value
767                 # is picked the loop is exited
768                 if new_val != old_val:
769                     break
770                 
771             new_values.append(new_val)
772             
773         return struct.pack(self.__format__, *new_values)
774         
775                 
776     def __str__(self):
777         return '\n'.join( self.dump() )
778
779     def __repr__(self):
780         return '<Structure: %s>' % (' '.join( [' '.join(s.split()) for s in self.dump()] ))
781         
782         
783     def dump(self, indentation=0):
784         """Returns a string representation of the structure."""
785     
786         dump = []
787         
788         dump.append('[%s]' % self.name)
789
790         # Refer to the __set_format__ method for an explanation
791         # of the following construct.
792         for keys in self.__keys__:
793             for key in keys:
794
795                 val = getattr(self, key)
796                 if isinstance(val, int) or isinstance(val, long):
797                     val_str = '0x%-8X' % (val)
798                     if key == 'TimeDateStamp' or key == 'dwTimeStamp':
799                         try:
800                             val_str += ' [%s UTC]' % time.asctime(time.gmtime(val))
801                         except exceptions.ValueError, e:
802                             val_str += ' [INVALID TIME]'
803                 else:
804                     val_str = ''.join(filter(lambda c:c != '\0', str(val)))
805
806                 dump.append('%-30s %s' % (key+':', val_str))
807
808         return dump
809
810
811
812 class SectionStructure(Structure):
813     """Convenience section handling class."""
814
815     def get_data(self, start, length=None):
816         """Get data chunk from a section.
817         
818         Allows to query data from the section by passing the
819         addresses where the PE file would be loaded by default.
820         It is then possible to retrieve code and data by its real
821         addresses as it would be if loaded.
822         """
823
824         offset = start - self.VirtualAddress
825
826         if length:
827             end = offset+length
828         else:
829             end = len(self.data)
830             
831         return self.data[offset:end]
832
833
834     def get_rva_from_offset(self, offset):
835         return offset - self.PointerToRawData + self.VirtualAddress
836
837
838     def get_offset_from_rva(self, rva):
839         return (rva - self.VirtualAddress) + self.PointerToRawData
840
841
842     def contains_offset(self, offset):
843         """Check whether the section contains the file offset provided."""
844
845         if not self.PointerToRawData:
846            # bss and other sections containing only uninitialized data must have 0
847            # and do not take space in the file
848            return False
849         return self.PointerToRawData <= offset < self.VirtualAddress + self.SizeOfRawData
850
851
852     def contains_rva(self, rva):
853         """Check whether the section contains the address provided."""
854
855         # PECOFF documentation v8 says:
856         # The total size of the section when loaded into memory.
857         # If this value is greater than SizeOfRawData, the section is zero-padded.
858         # This field is valid only for executable images and should be set to zero
859         # for object files.
860
861         if len(self.data) < self.SizeOfRawData:
862             size = self.Misc_VirtualSize
863         else:
864             size = max(self.SizeOfRawData, self.Misc_VirtualSize)
865             
866         return self.VirtualAddress <= rva < self.VirtualAddress + size
867
868     def contains(self, rva):
869         #print "DEPRECATION WARNING: you should use contains_rva() instead of contains()"
870         return self.contains_rva(rva)
871
872
873     def set_data(self, data):
874         """Set the data belonging to the section."""
875         
876         self.data = data
877         
878         
879     def get_entropy(self):
880         """Calculate and return the entropy for the section."""
881         
882         return self.entropy_H( self.data )
883         
884
885     def get_hash_sha1(self):
886         """Get the SHA-1 hex-digest of the section's data."""
887         
888         if sha1 is not None:
889             return sha1( self.data ).hexdigest()
890     
891
892     def get_hash_sha256(self):
893         """Get the SHA-256 hex-digest of the section's data."""
894         
895         if sha256 is not None:
896             return sha256( self.data ).hexdigest()
897     
898
899     def get_hash_sha512(self):
900         """Get the SHA-512 hex-digest of the section's data."""
901         
902         if sha512 is not None:
903             return sha512( self.data ).hexdigest()
904     
905
906     def get_hash_md5(self):
907         """Get the MD5 hex-digest of the section's data."""
908         
909         if md5 is not None:
910             return md5( self.data ).hexdigest()
911     
912
913     def entropy_H(self, data):
914         """Calculate the entropy of a chunk of data."""
915
916         if len(data) == 0:
917             return 0.0
918     
919         occurences = array.array('L', [0]*256)
920     
921         for x in data:
922             occurences[ord(x)] += 1
923     
924         entropy = 0
925         for x in occurences:
926             if x:
927                 p_x = float(x) / len(data)
928                 entropy -= p_x*math.log(p_x, 2)
929     
930         return entropy
931
932
933
934 class DataContainer:
935     """Generic data container."""
936         
937     def __init__(self, **args):
938         for key, value in args.items():
939             setattr(self, key, value)
940
941
942
943 class ImportDescData(DataContainer):
944     """Holds import descriptor information.
945     
946     dll:        name of the imported DLL
947     imports:    list of imported symbols (ImportData instances)
948     struct:     IMAGE_IMPORT_DESCRIPTOR sctruture
949     """
950
951 class ImportData(DataContainer):
952     """Holds imported symbol's information.
953     
954     ordinal:    Ordinal of the symbol
955     name:       Name of the symbol
956     bound:      If the symbol is bound, this contains
957                 the address.
958     """
959     
960 class ExportDirData(DataContainer):
961     """Holds export directory information.
962                     
963     struct:     IMAGE_EXPORT_DIRECTORY structure
964     symbols:    list of exported symbols (ExportData instances)
965 """
966     
967 class ExportData(DataContainer):
968     """Holds exported symbols' information.
969     
970     ordinal:    ordinal of the symbol
971     address:    address of the symbol
972     name:       name of the symbol (None if the symbol is
973                 exported by ordinal only)
974     forwarder:  if the symbol is forwarded it will
975                 contain the name of the target symbol,
976                 None otherwise.
977     """
978               
979
980 class ResourceDirData(DataContainer):
981     """Holds resource directory information.
982     
983     struct:     IMAGE_RESOURCE_DIRECTORY structure
984     entries:    list of entries (ResourceDirEntryData instances)
985     """
986     
987 class ResourceDirEntryData(DataContainer):
988     """Holds resource directory entry data.
989     
990     struct:     IMAGE_RESOURCE_DIRECTORY_ENTRY structure
991     name:       If the resource is identified by name this
992                 attribute will contain the name string. None
993                 otherwise. If identified by id, the id is
994                 availabe at 'struct.Id'
995     id:         the id, also in struct.Id
996     directory:  If this entry has a lower level directory
997                 this attribute will point to the
998                 ResourceDirData instance representing it.
999     data:       If this entry has no futher lower directories
1000                 and points to the actual resource data, this
1001                 attribute will reference the corresponding
1002                 ResourceDataEntryData instance.
1003     (Either of the 'directory' or 'data' attribute will exist,
1004     but not both.)
1005     """
1006
1007 class ResourceDataEntryData(DataContainer):
1008     """Holds resource data entry information.
1009     
1010     struct:     IMAGE_RESOURCE_DATA_ENTRY structure
1011     lang:       Primary language ID
1012     sublang:    Sublanguage ID
1013     """
1014
1015 class DebugData(DataContainer):
1016     """Holds debug information.
1017     
1018     struct:     IMAGE_DEBUG_DIRECTORY structure
1019     """
1020
1021 class BaseRelocationData(DataContainer):
1022     """Holds base relocation information.
1023     
1024     struct:     IMAGE_BASE_RELOCATION structure
1025     entries:    list of relocation data (RelocationData instances)
1026     """
1027     
1028 class RelocationData(DataContainer):
1029     """Holds relocation information.
1030     
1031     type:       Type of relocation
1032                 The type string is can be obtained by
1033                 RELOCATION_TYPE[type]
1034     rva:        RVA of the relocation
1035     """
1036
1037 class TlsData(DataContainer):
1038     """Holds TLS information.
1039     
1040     struct:     IMAGE_TLS_DIRECTORY structure
1041     """
1042
1043 class BoundImportDescData(DataContainer):
1044     """Holds bound import descriptor data.
1045     
1046     This directory entry will provide with information on the
1047     DLLs this PE files has been bound to (if bound at all).
1048     The structure will contain the name and timestamp of the
1049     DLL at the time of binding so that the loader can know
1050     whether it differs from the one currently present in the
1051     system and must, therefore, re-bind the PE's imports.
1052     
1053     struct:     IMAGE_BOUND_IMPORT_DESCRIPTOR structure
1054     name:       DLL name
1055     entries:    list of entries (BoundImportRefData instances)
1056                 the entries will exist if this DLL has forwarded
1057                 symbols. If so, the destination DLL will have an
1058                 entry in this list.
1059     """
1060
1061 class BoundImportRefData(DataContainer):
1062     """Holds bound import forwader reference data.
1063     
1064     Contains the same information as the bound descriptor but
1065     for forwarded DLLs, if any.
1066     
1067     struct:     IMAGE_BOUND_FORWARDER_REF structure
1068     name:       dll name
1069     """
1070
1071
1072 class PE:
1073     """A Portable Executable representation.
1074     
1075     This class provides access to most of the information in a PE file.
1076     
1077     It expects to be supplied the name of the file to load or PE data
1078     to process and an optional argument 'fast_load' (False by default)
1079     which controls whether to load all the directories information,
1080     which can be quite time consuming.
1081     
1082     pe = pefile.PE('module.dll')
1083     pe = pefile.PE(name='module.dll')
1084     
1085     would load 'module.dll' and process it. If the data would be already
1086     available in a buffer the same could be achieved with:
1087     
1088     pe = pefile.PE(data=module_dll_data)
1089     
1090     The "fast_load" can be set to a default by setting its value in the
1091     module itself by means,for instance, of a "pefile.fast_load = True".
1092     That will make all the subsequent instances not to load the
1093     whole PE structure. The "full_load" method can be used to parse
1094     the missing data at a later stage.
1095     
1096     Basic headers information will be available in the attributes:
1097     
1098     DOS_HEADER
1099     NT_HEADERS
1100     FILE_HEADER
1101     OPTIONAL_HEADER
1102     
1103     All of them will contain among their attrbitues the members of the
1104     corresponding structures as defined in WINNT.H
1105     
1106     The raw data corresponding to the header (from the beginning of the
1107     file up to the start of the first section) will be avaiable in the
1108     instance's attribute 'header' as a string.
1109     
1110     The sections will be available as a list in the 'sections' attribute.
1111     Each entry will contain as attributes all the structure's members.
1112     
1113     Directory entries will be available as attributes (if they exist):
1114     (no other entries are processed at this point)
1115     
1116     DIRECTORY_ENTRY_IMPORT (list of ImportDescData instances)
1117     DIRECTORY_ENTRY_EXPORT (ExportDirData instance)
1118     DIRECTORY_ENTRY_RESOURCE (ResourceDirData instance)
1119     DIRECTORY_ENTRY_DEBUG (list of DebugData instances)
1120     DIRECTORY_ENTRY_BASERELOC (list of BaseRelocationData instances)
1121     DIRECTORY_ENTRY_TLS 
1122     DIRECTORY_ENTRY_BOUND_IMPORT (list of BoundImportData instances)
1123     
1124     The following dictionary attributes provide ways of mapping different
1125     constants. They will accept the numeric value and return the string
1126     representation and the opposite, feed in the string and get the
1127     numeric constant:
1128     
1129     DIRECTORY_ENTRY
1130     IMAGE_CHARACTERISTICS
1131     SECTION_CHARACTERISTICS
1132     DEBUG_TYPE
1133     SUBSYSTEM_TYPE
1134     MACHINE_TYPE
1135     RELOCATION_TYPE
1136     RESOURCE_TYPE
1137     LANG
1138     SUBLANG
1139     """
1140
1141     #
1142     # Format specifications for PE structures.
1143     #
1144     
1145     __IMAGE_DOS_HEADER_format__ = ('IMAGE_DOS_HEADER',
1146         ('H,e_magic', 'H,e_cblp', 'H,e_cp',
1147         'H,e_crlc', 'H,e_cparhdr', 'H,e_minalloc',
1148         'H,e_maxalloc', 'H,e_ss', 'H,e_sp', 'H,e_csum',
1149         'H,e_ip', 'H,e_cs', 'H,e_lfarlc', 'H,e_ovno', '8s,e_res',
1150         'H,e_oemid', 'H,e_oeminfo', '20s,e_res2',
1151         'L,e_lfanew'))
1152         
1153     __IMAGE_FILE_HEADER_format__ = ('IMAGE_FILE_HEADER',
1154         ('H,Machine', 'H,NumberOfSections',
1155         'L,TimeDateStamp', 'L,PointerToSymbolTable',
1156         'L,NumberOfSymbols', 'H,SizeOfOptionalHeader',
1157         'H,Characteristics'))
1158         
1159     __IMAGE_DATA_DIRECTORY_format__ = ('IMAGE_DATA_DIRECTORY',
1160         ('L,VirtualAddress', 'L,Size'))
1161     
1162     
1163     __IMAGE_OPTIONAL_HEADER_format__ = ('IMAGE_OPTIONAL_HEADER',
1164         ('H,Magic', 'B,MajorLinkerVersion',
1165         'B,MinorLinkerVersion', 'L,SizeOfCode',
1166         'L,SizeOfInitializedData', 'L,SizeOfUninitializedData',
1167         'L,AddressOfEntryPoint', 'L,BaseOfCode', 'L,BaseOfData',
1168         'L,ImageBase', 'L,SectionAlignment', 'L,FileAlignment',
1169         'H,MajorOperatingSystemVersion', 'H,MinorOperatingSystemVersion',
1170         'H,MajorImageVersion', 'H,MinorImageVersion',
1171         'H,MajorSubsystemVersion', 'H,MinorSubsystemVersion',
1172         'L,Reserved1', 'L,SizeOfImage', 'L,SizeOfHeaders',
1173         'L,CheckSum', 'H,Subsystem', 'H,DllCharacteristics',
1174         'L,SizeOfStackReserve', 'L,SizeOfStackCommit',
1175         'L,SizeOfHeapReserve', 'L,SizeOfHeapCommit',
1176         'L,LoaderFlags', 'L,NumberOfRvaAndSizes' ))
1177
1178
1179     __IMAGE_OPTIONAL_HEADER64_format__ = ('IMAGE_OPTIONAL_HEADER64',
1180         ('H,Magic', 'B,MajorLinkerVersion',
1181         'B,MinorLinkerVersion', 'L,SizeOfCode',
1182         'L,SizeOfInitializedData', 'L,SizeOfUninitializedData',
1183         'L,AddressOfEntryPoint', 'L,BaseOfCode',
1184         'Q,ImageBase', 'L,SectionAlignment', 'L,FileAlignment',
1185         'H,MajorOperatingSystemVersion', 'H,MinorOperatingSystemVersion',
1186         'H,MajorImageVersion', 'H,MinorImageVersion',
1187         'H,MajorSubsystemVersion', 'H,MinorSubsystemVersion',
1188         'L,Reserved1', 'L,SizeOfImage', 'L,SizeOfHeaders',
1189         'L,CheckSum', 'H,Subsystem', 'H,DllCharacteristics',
1190         'Q,SizeOfStackReserve', 'Q,SizeOfStackCommit',
1191         'Q,SizeOfHeapReserve', 'Q,SizeOfHeapCommit',
1192         'L,LoaderFlags', 'L,NumberOfRvaAndSizes' ))
1193
1194         
1195     __IMAGE_NT_HEADERS_format__ = ('IMAGE_NT_HEADERS', ('L,Signature',))
1196         
1197     __IMAGE_SECTION_HEADER_format__ = ('IMAGE_SECTION_HEADER',
1198         ('8s,Name', 'L,Misc,Misc_PhysicalAddress,Misc_VirtualSize',
1199         'L,VirtualAddress', 'L,SizeOfRawData', 'L,PointerToRawData',
1200         'L,PointerToRelocations', 'L,PointerToLinenumbers',
1201         'H,NumberOfRelocations', 'H,NumberOfLinenumbers',
1202         'L,Characteristics'))
1203
1204     __IMAGE_DELAY_IMPORT_DESCRIPTOR_format__ = ('IMAGE_DELAY_IMPORT_DESCRIPTOR',
1205         ('L,grAttrs', 'L,szName', 'L,phmod', 'L,pIAT', 'L,pINT',
1206         'L,pBoundIAT', 'L,pUnloadIAT', 'L,dwTimeStamp'))
1207
1208     __IMAGE_IMPORT_DESCRIPTOR_format__ =  ('IMAGE_IMPORT_DESCRIPTOR',
1209         ('L,OriginalFirstThunk,Characteristics',
1210         'L,TimeDateStamp', 'L,ForwarderChain', 'L,Name', 'L,FirstThunk'))
1211
1212     __IMAGE_EXPORT_DIRECTORY_format__ =  ('IMAGE_EXPORT_DIRECTORY',
1213         ('L,Characteristics',
1214         'L,TimeDateStamp', 'H,MajorVersion', 'H,MinorVersion', 'L,Name',
1215         'L,Base', 'L,NumberOfFunctions', 'L,NumberOfNames',
1216         'L,AddressOfFunctions', 'L,AddressOfNames', 'L,AddressOfNameOrdinals'))
1217
1218     __IMAGE_RESOURCE_DIRECTORY_format__ = ('IMAGE_RESOURCE_DIRECTORY',
1219         ('L,Characteristics',
1220         'L,TimeDateStamp', 'H,MajorVersion', 'H,MinorVersion',
1221         'H,NumberOfNamedEntries', 'H,NumberOfIdEntries'))
1222
1223     __IMAGE_RESOURCE_DIRECTORY_ENTRY_format__ = ('IMAGE_RESOURCE_DIRECTORY_ENTRY',
1224         ('L,Name',
1225         'L,OffsetToData'))
1226             
1227     __IMAGE_RESOURCE_DATA_ENTRY_format__ = ('IMAGE_RESOURCE_DATA_ENTRY',
1228         ('L,OffsetToData', 'L,Size', 'L,CodePage', 'L,Reserved'))
1229     
1230     __VS_VERSIONINFO_format__ = ( 'VS_VERSIONINFO',
1231         ('H,Length', 'H,ValueLength', 'H,Type' ))
1232     
1233     __VS_FIXEDFILEINFO_format__ = ( 'VS_FIXEDFILEINFO',
1234         ('L,Signature', 'L,StrucVersion', 'L,FileVersionMS', 'L,FileVersionLS',
1235          'L,ProductVersionMS', 'L,ProductVersionLS', 'L,FileFlagsMask', 'L,FileFlags',
1236          'L,FileOS', 'L,FileType', 'L,FileSubtype', 'L,FileDateMS', 'L,FileDateLS'))
1237     
1238     __StringFileInfo_format__ = ( 'StringFileInfo',
1239         ('H,Length', 'H,ValueLength', 'H,Type' ))
1240     
1241     __StringTable_format__ = ( 'StringTable',
1242         ('H,Length', 'H,ValueLength', 'H,Type' ))
1243     
1244     __String_format__ = ( 'String',
1245         ('H,Length', 'H,ValueLength', 'H,Type' ))
1246     
1247     __Var_format__ = ( 'Var', ('H,Length', 'H,ValueLength', 'H,Type' ))
1248
1249     __IMAGE_THUNK_DATA_format__ = ('IMAGE_THUNK_DATA',
1250         ('L,ForwarderString,Function,Ordinal,AddressOfData',))
1251
1252     __IMAGE_THUNK_DATA64_format__ = ('IMAGE_THUNK_DATA',
1253         ('Q,ForwarderString,Function,Ordinal,AddressOfData',))
1254
1255     __IMAGE_DEBUG_DIRECTORY_format__ = ('IMAGE_DEBUG_DIRECTORY',
1256         ('L,Characteristics', 'L,TimeDateStamp', 'H,MajorVersion',
1257         'H,MinorVersion', 'L,Type', 'L,SizeOfData', 'L,AddressOfRawData',
1258         'L,PointerToRawData'))
1259     
1260     __IMAGE_BASE_RELOCATION_format__ = ('IMAGE_BASE_RELOCATION',
1261         ('L,VirtualAddress', 'L,SizeOfBlock') )
1262
1263     __IMAGE_TLS_DIRECTORY_format__ = ('IMAGE_TLS_DIRECTORY',
1264         ('L,StartAddressOfRawData', 'L,EndAddressOfRawData',
1265         'L,AddressOfIndex', 'L,AddressOfCallBacks',
1266         'L,SizeOfZeroFill', 'L,Characteristics' ) )
1267
1268     __IMAGE_TLS_DIRECTORY64_format__ = ('IMAGE_TLS_DIRECTORY',
1269         ('Q,StartAddressOfRawData', 'Q,EndAddressOfRawData',
1270         'Q,AddressOfIndex', 'Q,AddressOfCallBacks',
1271         'L,SizeOfZeroFill', 'L,Characteristics' ) )
1272
1273     __IMAGE_BOUND_IMPORT_DESCRIPTOR_format__ = ('IMAGE_BOUND_IMPORT_DESCRIPTOR',
1274         ('L,TimeDateStamp', 'H,OffsetModuleName', 'H,NumberOfModuleForwarderRefs'))
1275
1276     __IMAGE_BOUND_FORWARDER_REF_format__ = ('IMAGE_BOUND_FORWARDER_REF',
1277         ('L,TimeDateStamp', 'H,OffsetModuleName', 'H,Reserved') )
1278
1279
1280     def __init__(self, name=None, data=None, fast_load=None):
1281     
1282         self.sections = []
1283         
1284         self.__warnings = []
1285         
1286         self.PE_TYPE = None
1287         
1288         if  not name and not data:
1289             return
1290             
1291         # This list will keep track of all the structures created.
1292         # That will allow for an easy iteration through the list
1293         # in order to save the modifications made
1294         self.__structures__ = []
1295
1296         if not fast_load:
1297             fast_load = globals()['fast_load']
1298         self.__parse__(name, data, fast_load)
1299                     
1300         
1301     
1302     def __unpack_data__(self, format, data, file_offset):
1303         """Apply structure format to raw data.
1304         
1305         Returns and unpacked structure object if successful, None otherwise.
1306         """
1307     
1308         structure = Structure(format, file_offset=file_offset)
1309         #if len(data) < structure.sizeof():
1310         #    return None
1311     
1312         try:
1313             structure.__unpack__(data)
1314         except PEFormatError, err:
1315             self.__warnings.append(
1316                 'Corrupt header "%s" at file offset %d. Exception: %s' % (
1317                     format[0], file_offset, str(err))  )
1318             return None
1319
1320         self.__structures__.append(structure)
1321     
1322         return structure
1323         
1324
1325         
1326     def __parse__(self, fname, data, fast_load):
1327         """Parse a Portable Executable file.
1328         
1329         Loads a PE file, parsing all its structures and making them available
1330         through the instance's attributes.
1331         """
1332         
1333         if fname:
1334             fd = file(fname, 'rb')
1335             self.__data__ = fd.read()
1336             fd.close()
1337         elif data:
1338             self.__data__ = data
1339         
1340
1341         self.DOS_HEADER = self.__unpack_data__(
1342             self.__IMAGE_DOS_HEADER_format__,
1343             self.__data__, file_offset=0)
1344             
1345         if not self.DOS_HEADER or self.DOS_HEADER.e_magic != IMAGE_DOS_SIGNATURE:
1346             raise PEFormatError('DOS Header magic not found.')
1347
1348         # OC Patch:
1349         # Check for sane value in e_lfanew
1350         #                
1351         if self.DOS_HEADER.e_lfanew > len(self.__data__):
1352             raise PEFormatError('Invalid e_lfanew value, probably not a PE file')
1353
1354         nt_headers_offset = self.DOS_HEADER.e_lfanew
1355
1356         self.NT_HEADERS = self.__unpack_data__(
1357             self.__IMAGE_NT_HEADERS_format__,
1358             self.__data__[nt_headers_offset:],
1359             file_offset = nt_headers_offset)
1360
1361         # We better check the signature right here, before the file screws
1362         # around with sections:
1363         # OC Patch:
1364         # Some malware will cause the Signature value to not exist at all
1365         if not self.NT_HEADERS or not self.NT_HEADERS.Signature:
1366             raise PEFormatError('NT Headers not found.')
1367
1368         if self.NT_HEADERS.Signature != IMAGE_NT_SIGNATURE:
1369             raise PEFormatError('Invalid NT Headers signature.')
1370                 
1371         self.FILE_HEADER = self.__unpack_data__(
1372             self.__IMAGE_FILE_HEADER_format__,
1373             self.__data__[nt_headers_offset+4:],
1374             file_offset = nt_headers_offset+4)
1375         image_flags = self.retrieve_flags(IMAGE_CHARACTERISTICS, 'IMAGE_FILE_')
1376         
1377         if not self.FILE_HEADER:
1378             raise PEFormatError('File Header missing')
1379
1380         # Set the image's flags according the the Characteristics member
1381         self.set_flags(self.FILE_HEADER, self.FILE_HEADER.Characteristics, image_flags)
1382         
1383         optional_header_offset =    \
1384             nt_headers_offset+4+self.FILE_HEADER.sizeof()
1385
1386         # Note: location of sections can be controlled from PE header:
1387         sections_offset = optional_header_offset + self.FILE_HEADER.SizeOfOptionalHeader
1388
1389         self.OPTIONAL_HEADER = self.__unpack_data__(
1390             self.__IMAGE_OPTIONAL_HEADER_format__,
1391             self.__data__[optional_header_offset:],
1392             file_offset = optional_header_offset)
1393
1394         # According to solardesigner's findings for his
1395         # Tiny PE project, the optional header does not
1396         # need fields beyond "Subsystem" in order to be
1397         # loadable by the Windows loader (given that zeroes
1398         # are acceptable values and the header is loaded
1399         # in a zeroed memory page)
1400         # If trying to parse a full Optional Header fails
1401         # we try to parse it again with some 0 padding
1402         #
1403         MINIMUM_VALID_OPTIONAL_HEADER_RAW_SIZE = 69
1404         
1405         if ( self.OPTIONAL_HEADER is None and 
1406             len(self.__data__[optional_header_offset:])
1407                 >= MINIMUM_VALID_OPTIONAL_HEADER_RAW_SIZE ):
1408         
1409             # Add enough zeroes to make up for the unused fields
1410             #
1411             padding_length = 128
1412             
1413             # Create padding
1414             #
1415             padded_data = self.__data__[optional_header_offset:] + (
1416                 '\0' * padding_length)
1417             
1418             self.OPTIONAL_HEADER = self.__unpack_data__(
1419                 self.__IMAGE_OPTIONAL_HEADER_format__,
1420                 padded_data,
1421                 file_offset = optional_header_offset)
1422          
1423             
1424         # Check the Magic in the OPTIONAL_HEADER and set the PE file
1425         # type accordingly
1426         #
1427         if self.OPTIONAL_HEADER is not None:
1428         
1429             if self.OPTIONAL_HEADER.Magic == OPTIONAL_HEADER_MAGIC_PE:
1430             
1431                 self.PE_TYPE = OPTIONAL_HEADER_MAGIC_PE
1432                 
1433             elif self.OPTIONAL_HEADER.Magic == OPTIONAL_HEADER_MAGIC_PE_PLUS:
1434     
1435                 self.PE_TYPE = OPTIONAL_HEADER_MAGIC_PE_PLUS
1436             
1437                 self.OPTIONAL_HEADER = self.__unpack_data__(
1438                     self.__IMAGE_OPTIONAL_HEADER64_format__,
1439                     self.__data__[optional_header_offset:],
1440                     file_offset = optional_header_offset)
1441
1442                 # Again, as explained above, we try to parse
1443                 # a reduced form of the Optional Header which
1444                 # is still valid despite not including all
1445                 # structure members
1446                 #
1447                 MINIMUM_VALID_OPTIONAL_HEADER_RAW_SIZE = 69+4
1448
1449                 if ( self.OPTIONAL_HEADER is None and 
1450                     len(self.__data__[optional_header_offset:])
1451                         >= MINIMUM_VALID_OPTIONAL_HEADER_RAW_SIZE ):
1452                 
1453                     padding_length = 128
1454                     padded_data = self.__data__[optional_header_offset:] + (
1455                         '\0' * padding_length)
1456                     self.OPTIONAL_HEADER = self.__unpack_data__(
1457                         self.__IMAGE_OPTIONAL_HEADER64_format__,
1458                         padded_data,
1459                         file_offset = optional_header_offset)
1460         
1461         
1462         if not self.FILE_HEADER:
1463             raise PEFormatError('File Header missing')
1464
1465
1466         # OC Patch:
1467         # Die gracefully if there is no OPTIONAL_HEADER field
1468         # 975440f5ad5e2e4a92c4d9a5f22f75c1
1469         if self.PE_TYPE is None or self.OPTIONAL_HEADER is None:
1470             raise PEFormatError("No Optional Header found, invalid PE32 or PE32+ file")
1471             
1472         dll_characteristics_flags = self.retrieve_flags(DLL_CHARACTERISTICS, 'IMAGE_DLL_CHARACTERISTICS_')
1473
1474         # Set the Dll Characteristics flags according the the DllCharacteristics member
1475         self.set_flags(
1476             self.OPTIONAL_HEADER,
1477             self.OPTIONAL_HEADER.DllCharacteristics,
1478             dll_characteristics_flags)
1479
1480
1481         self.OPTIONAL_HEADER.DATA_DIRECTORY = []
1482         #offset = (optional_header_offset + self.FILE_HEADER.SizeOfOptionalHeader)
1483         offset = (optional_header_offset + self.OPTIONAL_HEADER.sizeof())
1484             
1485         
1486         self.NT_HEADERS.FILE_HEADER = self.FILE_HEADER
1487         self.NT_HEADERS.OPTIONAL_HEADER = self.OPTIONAL_HEADER
1488             
1489
1490         # The NumberOfRvaAndSizes is sanitized to stay within 
1491         # reasonable limits so can be casted to an int
1492         #
1493         if self.OPTIONAL_HEADER.NumberOfRvaAndSizes > 0x10:
1494             self.__warnings.append(
1495                 'Suspicious NumberOfRvaAndSizes in the Optional Header. ' +
1496                 'Normal values are never larger than 0x10, the value is: 0x%x' %
1497                 self.OPTIONAL_HEADER.NumberOfRvaAndSizes )
1498                 
1499         for i in xrange(int(0x7fffffffL & self.OPTIONAL_HEADER.NumberOfRvaAndSizes)):
1500
1501             if len(self.__data__[offset:]) == 0:
1502                 break
1503                         
1504             if len(self.__data__[offset:]) < 8:
1505                 data = self.__data__[offset:]+'\0'*8
1506             else:
1507                 data = self.__data__[offset:]
1508
1509             dir_entry = self.__unpack_data__(
1510                 self.__IMAGE_DATA_DIRECTORY_format__,
1511                 data,
1512                 file_offset = offset)
1513                 
1514             if dir_entry is None:
1515                 break
1516
1517             # Would fail if missing an entry
1518             # 1d4937b2fa4d84ad1bce0309857e70ca offending sample
1519             try:
1520                 dir_entry.name = DIRECTORY_ENTRY[i]
1521             except (KeyError, AttributeError):
1522                 break
1523
1524             offset += dir_entry.sizeof()
1525             
1526             self.OPTIONAL_HEADER.DATA_DIRECTORY.append(dir_entry)
1527
1528             # If the offset goes outside the optional header,
1529             # the loop is broken, regardless of how many directories
1530             # NumberOfRvaAndSizes says there are
1531             #
1532             # We assume a normally sized optional header, hence that we do
1533             # a sizeof() instead of reading SizeOfOptionalHeader.
1534             # Then we add a default number of drectories times their size,
1535             # if we go beyond that, we assume the number of directories
1536             # is wrong and stop processing
1537             if offset >= (optional_header_offset + 
1538                 self.OPTIONAL_HEADER.sizeof() + 8*16) :
1539                 
1540                 break
1541                 
1542                         
1543         offset = self.parse_sections(sections_offset)
1544         
1545         # OC Patch:
1546         # There could be a problem if there are no raw data sections
1547         # greater than 0
1548         # fc91013eb72529da005110a3403541b6 example
1549         # Should this throw an exception in the minimum header offset
1550         # can't be found?
1551         #
1552         rawDataPointers = [
1553             s.PointerToRawData for s in self.sections if s.PointerToRawData>0]
1554             
1555         if len(rawDataPointers) > 0:
1556             lowest_section_offset = min(rawDataPointers)
1557         else:
1558             lowest_section_offset = None
1559
1560         if not lowest_section_offset or lowest_section_offset<offset:
1561             self.header = self.__data__[:offset]
1562         else:
1563             self.header = self.__data__[:lowest_section_offset]
1564         
1565
1566         # Check whether the entry point lies within a section
1567         #
1568         if self.get_section_by_rva(self.OPTIONAL_HEADER.AddressOfEntryPoint) is not None:
1569         
1570             # Check whether the entry point lies within the file
1571             #
1572             ep_offset = self.get_offset_from_rva(self.OPTIONAL_HEADER.AddressOfEntryPoint)
1573             if ep_offset > len(self.__data__):
1574             
1575                 self.__warnings.append(
1576                     'Possibly corrupt file. AddressOfEntryPoint lies outside the file. ' +
1577                     'AddressOfEntryPoint: 0x%x' %
1578                     self.OPTIONAL_HEADER.AddressOfEntryPoint )
1579             
1580         else:
1581
1582             self.__warnings.append(
1583                 'AddressOfEntryPoint lies outside the sections\' boundaries. ' +
1584                 'AddressOfEntryPoint: 0x%x' %
1585                 self.OPTIONAL_HEADER.AddressOfEntryPoint )
1586                 
1587         
1588         if not fast_load:
1589             self.parse_data_directories()
1590
1591
1592     def get_warnings(self):
1593         """Return the list of warnings.
1594         
1595         Non-critical problems found when parsing the PE file are
1596         appended to a list of warnings. This method returns the
1597         full list.
1598         """
1599     
1600         return self.__warnings
1601         
1602         
1603     def show_warnings(self):
1604         """Print the list of warnings.
1605         
1606         Non-critical problems found when parsing the PE file are
1607         appended to a list of warnings. This method prints the
1608         full list to standard output.
1609         """
1610     
1611         for warning in self.__warnings:
1612             print '>', warning
1613
1614
1615     def full_load(self):
1616         """Process the data directories.
1617         
1618         This mathod will load the data directories which might not have
1619         been loaded if the "fast_load" option was used.
1620         """
1621         
1622         self.parse_data_directories()
1623         
1624         
1625     def write(self, filename=None):
1626         """Write the PE file.
1627         
1628         This function will process all headers and components
1629         of the PE file and include all changes made (by just
1630         assigning to attributes in the PE objects) and write
1631         the changes back to a file whose name is provided as
1632         an argument. The filename is optional.
1633         The data to be written to the file will be returned
1634         as a 'str' object.
1635         """
1636     
1637         file_data = list(self.__data__)
1638         for struct in self.__structures__:
1639         
1640             struct_data = list(struct.__pack__())
1641             offset = struct.get_file_offset()
1642             
1643             file_data[offset:offset+len(struct_data)] = struct_data
1644             
1645         if hasattr(self, 'VS_VERSIONINFO'):
1646             if hasattr(self, 'FileInfo'):
1647                 for entry in self.FileInfo:
1648                     if hasattr(entry, 'StringTable'):
1649                         for st_entry in entry.StringTable:
1650                             for key, entry in st_entry.entries.items():
1651                             
1652                                 offsets = st_entry.entries_offsets[key]
1653                                 lengths = st_entry.entries_lengths[key]
1654                                 
1655                                 if len( entry ) > lengths[1]:
1656
1657                                     uc = zip(
1658                                             list(entry[:lengths[1]]), ['\0'] * lengths[1] )
1659                                     l = list()
1660                                     map(l.extend, uc)
1661
1662                                     file_data[ 
1663                                         offsets[1] : offsets[1] + lengths[1]*2 ] = l
1664                                         
1665                                 else:
1666                                     
1667                                     uc = zip(
1668                                             list(entry), ['\0'] * len(entry) )
1669                                     l = list()
1670                                     map(l.extend, uc)
1671
1672                                     file_data[ 
1673                                         offsets[1] : offsets[1] + len(entry)*2 ] = l
1674
1675                                     remainder = lengths[1] - len(entry)
1676                                     file_data[ 
1677                                         offsets[1] + len(entry)*2 : 
1678                                         offsets[1] + lengths[1]*2 ] = [
1679                                             u'\0' ] * remainder*2
1680
1681         new_file_data = ''.join( [ chr(ord(c)) for c in file_data ] )
1682
1683         if filename:
1684             f = file(filename, 'wb+')
1685             f.write(new_file_data)
1686             f.close()
1687
1688         return new_file_data
1689         
1690
1691                 
1692     def parse_sections(self, offset):
1693         """Fetch the PE file sections.
1694         
1695         The sections will be readily available in the "sections" attribute.
1696         Its attributes will contain all the section information plus "data"
1697         a buffer containing the section's data.
1698         
1699         The "Characteristics" member will be processed and attributes 
1700         representing the section characteristics (with the 'IMAGE_SCN_'
1701         string trimmed from the constant's names) will be added to the
1702         section instance.
1703         
1704         Refer to the SectionStructure class for additional info.
1705         """
1706         
1707         self.sections = []
1708         
1709         for i in xrange(self.FILE_HEADER.NumberOfSections):
1710             section = SectionStructure(self.__IMAGE_SECTION_HEADER_format__)
1711             if not section:
1712                 break
1713             section_offset = offset + section.sizeof() * i
1714             section.set_file_offset(section_offset)
1715             section.__unpack__(self.__data__[section_offset:])
1716             self.__structures__.append(section)
1717                         
1718             if section.SizeOfRawData > len(self.__data__):
1719                 self.__warnings.append(
1720                     ('Error parsing section %d. ' % i) +
1721                     'SizeOfRawData is larger than file.')
1722
1723             if section.PointerToRawData > len(self.__data__):
1724                 self.__warnings.append(
1725                     ('Error parsing section %d. ' % i) +
1726                     'PointerToRawData points beyond the end of the file.')
1727
1728             if section.Misc_VirtualSize > 0x10000000:
1729                 self.__warnings.append(
1730                     ('Suspicious value found parsing section %d. ' % i) +
1731                     'VirtualSize is extremely large > 256MiB.')
1732
1733             if section.VirtualAddress > 0x10000000:
1734                 self.__warnings.append(
1735                     ('Suspicious value found parsing section %d. ' % i) +
1736                     'VirtualAddress is beyond 0x10000000.')
1737
1738             #
1739             # Some packer used a non-aligned PointerToRawData in the sections,
1740             # which causes several common tools not to load the section data
1741             # properly as they blindly read from the indicated offset.
1742             # It seems that Windows will round the offset down to the largest
1743             # offset multiple of FileAlignment which is smaller than
1744             # PointerToRawData. The following code will do the same.
1745             #
1746             
1747             #alignment = self.OPTIONAL_HEADER.FileAlignment
1748             section_data_start = section.PointerToRawData
1749             
1750             if ( self.OPTIONAL_HEADER.FileAlignment != 0 and 
1751                 (section.PointerToRawData % self.OPTIONAL_HEADER.FileAlignment) != 0):
1752                 self.__warnings.append(
1753                     ('Error parsing section %d. ' % i) +
1754                     'Suspicious value for FileAlignment in the Optional Header. ' +
1755                     'Normally the PointerToRawData entry of the sections\' structures ' +
1756                     'is a multiple of FileAlignment, this might imply the file ' +
1757                     'is trying to confuse tools which parse this incorrectly')
1758             
1759             section_data_end = section_data_start+section.SizeOfRawData
1760             section.set_data(self.__data__[section_data_start:section_data_end])
1761             
1762             section_flags = self.retrieve_flags(SECTION_CHARACTERISTICS, 'IMAGE_SCN_')
1763             
1764             # Set the section's flags according the the Characteristics member
1765             self.set_flags(section, section.Characteristics, section_flags)
1766
1767             if ( section.__dict__.get('IMAGE_SCN_MEM_WRITE', False)  and 
1768                 section.__dict__.get('IMAGE_SCN_MEM_EXECUTE', False) ):
1769                 
1770                 self.__warnings.append(
1771                     ('Suspicious flags set for section %d. ' % i) +
1772                     'Both IMAGE_SCN_MEM_WRITE and IMAGE_SCN_MEM_EXECUTE are set.' +
1773                     'This might indicate a packed executable.')
1774
1775             self.sections.append(section)
1776             
1777         if self.FILE_HEADER.NumberOfSections > 0 and self.sections:
1778             return offset + self.sections[0].sizeof()*self.FILE_HEADER.NumberOfSections
1779         else:
1780             return offset
1781
1782         
1783     def retrieve_flags(self, flag_dict, flag_filter):
1784         """Read the flags from a dictionary and return them in a usable form.
1785         
1786         Will return a list of (flag, value) for all flags in "flag_dict"
1787         matching the filter "flag_filter".
1788         """
1789         
1790         return [(f[0], f[1]) for f in flag_dict.items() if
1791                 isinstance(f[0], str) and f[0].startswith(flag_filter)]
1792
1793                 
1794     def set_flags(self, obj, flag_field, flags):
1795         """Will process the flags and set attributes in the object accordingly.
1796         
1797         The object "obj" will gain attritutes named after the flags provided in
1798         "flags" and valued True/False, matching the results of applyin each
1799         flag value from "flags" to flag_field.
1800         """
1801     
1802         for flag in flags:
1803             if flag[1] & flag_field:
1804                 setattr(obj, flag[0], True)
1805             else:
1806                 setattr(obj, flag[0], False)
1807     
1808     
1809             
1810     def parse_data_directories(self):
1811         """Parse and process the PE file's data directories."""
1812         
1813         directory_parsing = (
1814             ('IMAGE_DIRECTORY_ENTRY_IMPORT', self.parse_import_directory),
1815             ('IMAGE_DIRECTORY_ENTRY_EXPORT', self.parse_export_directory),
1816             ('IMAGE_DIRECTORY_ENTRY_RESOURCE', self.parse_resources_directory),
1817             ('IMAGE_DIRECTORY_ENTRY_DEBUG', self.parse_debug_directory),
1818             ('IMAGE_DIRECTORY_ENTRY_BASERELOC', self.parse_relocations_directory),
1819             ('IMAGE_DIRECTORY_ENTRY_TLS', self.parse_directory_tls),
1820             ('IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT', self.parse_delay_import_directory),
1821             ('IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT', self.parse_directory_bound_imports) )
1822             
1823         for entry in directory_parsing:
1824             # OC Patch:
1825             #
1826             try:
1827                 dir_entry = self.OPTIONAL_HEADER.DATA_DIRECTORY[
1828                     DIRECTORY_ENTRY[entry[0]]]
1829             except IndexError:
1830                 break
1831             if dir_entry.VirtualAddress:
1832                 value = entry[1](dir_entry.VirtualAddress, dir_entry.Size)
1833                 if value:
1834                     setattr(self, entry[0][6:], value)
1835         
1836         
1837     def parse_directory_bound_imports(self, rva, size):
1838         """"""
1839         
1840         bnd_descr = Structure(self.__IMAGE_BOUND_IMPORT_DESCRIPTOR_format__)
1841         bnd_descr_size = bnd_descr.sizeof()
1842         start = rva
1843         
1844         bound_imports = []
1845         while True:
1846
1847             bnd_descr = self.__unpack_data__(
1848                 self.__IMAGE_BOUND_IMPORT_DESCRIPTOR_format__,
1849                    self.__data__[rva:rva+bnd_descr_size],
1850                    file_offset = rva)
1851             if bnd_descr is None:
1852                 # If can't parse directory then silently return.
1853                 # This directory does not necesarily have to be valid to
1854                 # still have a valid PE file
1855
1856                 self.__warnings.append(
1857                     'The Bound Imports directory exists but can\'t be parsed.')
1858
1859                 return
1860                    
1861             if bnd_descr.all_zeroes():
1862                 break
1863                 
1864             rva += bnd_descr.sizeof()
1865             
1866             forwarder_refs = []
1867             for idx in xrange(bnd_descr.NumberOfModuleForwarderRefs):
1868                 # Both structures IMAGE_BOUND_IMPORT_DESCRIPTOR and
1869                 # IMAGE_BOUND_FORWARDER_REF have the same size.
1870                 bnd_frwd_ref = self.__unpack_data__(
1871                     self.__IMAGE_BOUND_FORWARDER_REF_format__,
1872                     self.__data__[rva:rva+bnd_descr_size],
1873                     file_offset = rva)
1874                 # OC Patch:
1875                 if not bnd_frwd_ref:
1876                     raise PEFormatError(
1877                         "IMAGE_BOUND_FORWARDER_REF cannot be read")
1878                 rva += bnd_frwd_ref.sizeof()
1879                 
1880                 name_str =  self.get_string_from_data(
1881                     start+bnd_frwd_ref.OffsetModuleName, self.__data__)
1882                     
1883                 if not name_str:
1884                     break
1885                 forwarder_refs.append(BoundImportRefData(
1886                     struct = bnd_frwd_ref,
1887                     name = name_str))
1888                 
1889             name_str = self.get_string_from_data(
1890                 start+bnd_descr.OffsetModuleName, self.__data__)
1891                 
1892             if not name_str:
1893                 break
1894             bound_imports.append(
1895                 BoundImportDescData(
1896                     struct = bnd_descr,
1897                     name = name_str,
1898                     entries = forwarder_refs))
1899                     
1900         return bound_imports
1901
1902         
1903     def parse_directory_tls(self, rva, size):
1904         """"""
1905             
1906         if self.PE_TYPE == OPTIONAL_HEADER_MAGIC_PE:
1907             format = self.__IMAGE_TLS_DIRECTORY_format__
1908             
1909         elif self.PE_TYPE == OPTIONAL_HEADER_MAGIC_PE_PLUS:
1910             format = self.__IMAGE_TLS_DIRECTORY64_format__
1911
1912         tls_struct = self.__unpack_data__(
1913             format,
1914             self.get_data(rva),
1915             file_offset = self.get_offset_from_rva(rva))
1916             
1917         if not tls_struct:
1918             return None
1919                 
1920         return TlsData( struct = tls_struct )
1921     
1922     
1923     def parse_relocations_directory(self, rva, size):
1924         """"""
1925         
1926         rlc = Structure(self.__IMAGE_BASE_RELOCATION_format__)
1927         rlc_size = rlc.sizeof()
1928         end = rva+size
1929         
1930         relocations = []
1931         while rva<end:
1932             
1933             # OC Patch:
1934             # Malware that has bad rva entries will cause an error.
1935             # Just continue on after an exception
1936             #
1937             try:
1938                 rlc = self.__unpack_data__(
1939                     self.__IMAGE_BASE_RELOCATION_format__,
1940                     self.get_data(rva, rlc_size),
1941                     file_offset = self.get_offset_from_rva(rva) )
1942             except PEFormatError:
1943                 self.__warnings.append(
1944                     'Invalid relocation information. Can\'t read ' +
1945                     'data at RVA: 0x%x' % rva)
1946                 rlc = None
1947             
1948             if not rlc:
1949                 break
1950                 
1951             reloc_entries = self.parse_relocations(
1952                 rva+rlc_size, rlc.VirtualAddress, rlc.SizeOfBlock-rlc_size)
1953                 
1954             relocations.append(
1955                 BaseRelocationData(
1956                     struct = rlc,
1957                     entries = reloc_entries))
1958             
1959             if not rlc.SizeOfBlock:
1960                 break
1961             rva += rlc.SizeOfBlock
1962             
1963         return relocations
1964     
1965         
1966     def parse_relocations(self, data_rva, rva, size):
1967         """"""
1968         
1969         data = self.get_data(data_rva, size)
1970         
1971         entries = []
1972         for idx in xrange(len(data)/2):
1973             word = struct.unpack('<H', data[idx*2:(idx+1)*2])[0]
1974             reloc_type = (word>>12)
1975             reloc_offset = (word&0x0fff)
1976             entries.append(
1977                 RelocationData(
1978                     type = reloc_type,
1979                     rva = reloc_offset+rva))
1980             
1981         return entries
1982
1983         
1984     def parse_debug_directory(self, rva, size):
1985         """"""
1986             
1987         dbg = Structure(self.__IMAGE_DEBUG_DIRECTORY_format__)
1988         dbg_size = dbg.sizeof()
1989         
1990         debug = []
1991         for idx in xrange(size/dbg_size):
1992             try:
1993                 data = self.get_data(rva+dbg_size*idx, dbg_size)
1994             except PEFormatError, e:
1995                 self.__warnings.append(
1996                     'Invalid debug information. Can\'t read ' +
1997                     'data at RVA: 0x%x' % rva)
1998                 return None
1999                 
2000             dbg = self.__unpack_data__(
2001                 self.__IMAGE_DEBUG_DIRECTORY_format__,
2002                 data, file_offset = self.get_offset_from_rva(rva+dbg_size*idx))
2003                 
2004             if not dbg:
2005                 return None
2006                 
2007             debug.append(
2008                 DebugData(
2009                     struct = dbg))
2010             
2011         return debug
2012
2013                         
2014     def parse_resources_directory(self, rva, size=0, base_rva = None, level = 0):
2015         """Parse the resources directory.
2016         
2017         Given the rva of the resources directory, it will process all
2018         its entries.
2019         
2020         The root will have the corresponding member of its structure,
2021         IMAGE_RESOURCE_DIRECTORY plus 'entries', a list of all the
2022         entries in the directory.
2023         
2024         Those entries will have, correspondingly, all the structure's
2025         members (IMAGE_RESOURCE_DIRECTORY_ENTRY) and an additional one,
2026         "directory", pointing to the IMAGE_RESOURCE_DIRECTORY structure
2027         representing upper layers of the tree. This one will also have
2028         an 'entries' attribute, pointing to the 3rd, and last, level.
2029         Another directory with more entries. Those last entries will
2030         have a new atribute (both 'leaf' or 'data_entry' can be used to
2031         access it). This structure finally points to the resource data.
2032         All the members of this structure, IMAGE_RESOURCE_DATA_ENTRY,
2033         are available as its attributes.
2034         """
2035         
2036         # OC Patch:
2037         original_rva = rva
2038         
2039         if base_rva is None:
2040             base_rva = rva
2041         
2042         resources_section = self.get_section_by_rva(rva)
2043         
2044         try:
2045             # If the RVA is invalid all would blow up. Some EXEs seem to be
2046             # specially nasty and have an invalid RVA.
2047             data = self.get_data(rva)
2048         except PEFormatError, e:
2049             self.__warnings.append(
2050                 'Invalid resources directory. Can\'t read ' +
2051                 'directory data at RVA: 0x%x' % rva)
2052             return None
2053             
2054         # Get the resource directory structure, that is, the header
2055         # of the table preceding the actual entries
2056         #
2057         resource_dir = self.__unpack_data__(
2058             self.__IMAGE_RESOURCE_DIRECTORY_format__, data,
2059             file_offset = self.get_offset_from_rva(rva) )
2060         if resource_dir is None:
2061             # If can't parse resources directory then silently return.
2062             # This directory does not necesarily have to be valid to
2063             # still have a valid PE file
2064             self.__warnings.append(
2065                 'Invalid resources directory. Can\'t parse ' +
2066                 'directory data at RVA: 0x%x' % rva)
2067             return None
2068         
2069         dir_entries = []
2070         
2071         # Advance the rva to the positon immediately following the directory
2072         # table header and pointing to the first entry in the table
2073         #
2074         rva += resource_dir.sizeof()
2075         
2076         number_of_entries = (
2077             resource_dir.NumberOfNamedEntries +
2078             resource_dir.NumberOfIdEntries )
2079             
2080         strings_to_postprocess = list()
2081         
2082         for idx in xrange(number_of_entries):
2083             
2084             res = self.parse_resource_entry(rva)
2085             if res is None:
2086                 self.__warnings.append(
2087                     'Error parsing the resources directory, ' +
2088                     'Entry %d is invalid, RVA = 0x%x. ' % 
2089                     (idx, rva) )
2090                 break
2091             
2092
2093             entry_name = None
2094             entry_id = None
2095             
2096             # If all named entries have been processed, only Id ones
2097             # remain
2098             
2099             if idx >= resource_dir.NumberOfNamedEntries:
2100                 entry_id = res.Name
2101             else:
2102                 ustr_offset = base_rva+res.NameOffset
2103                 try:
2104                     #entry_name = self.get_string_u_at_rva(ustr_offset, max_length=16)
2105                     entry_name = UnicodeStringWrapperPostProcessor(self, ustr_offset)
2106                     strings_to_postprocess.append(entry_name)
2107                     
2108                 except PEFormatError, excp:
2109                     self.__warnings.append(
2110                         'Error parsing the resources directory, ' +
2111                         'attempting to read entry name. ' +
2112                         'Can\'t read unicode string at offset 0x%x' % 
2113                         (ustr_offset) )
2114                 
2115                 
2116             if res.DataIsDirectory:
2117                 # OC Patch:
2118                 #
2119                 # One trick malware can do is to recursively reference
2120                 # the next directory. This causes hilarity to ensue when
2121                 # trying to parse everything correctly.
2122                 # If the original RVA given to this function is equal to
2123                 # the next one to parse, we assume that it's a trick.
2124                 # Instead of raising a PEFormatError this would skip some
2125                 # reasonable data so we just break.
2126                 #
2127                 # 9ee4d0a0caf095314fd7041a3e4404dc is the offending sample
2128                 if original_rva == (base_rva + res.OffsetToDirectory):
2129                     
2130                     break
2131                     
2132                 else:
2133                     entry_directory = self.parse_resources_directory(
2134                         base_rva+res.OffsetToDirectory,
2135                         base_rva=base_rva, level = level+1)
2136
2137                 if not entry_directory:
2138                     break
2139                 dir_entries.append(
2140                     ResourceDirEntryData(
2141                         struct = res,
2142                         name = entry_name,
2143                         id = entry_id,
2144                         directory = entry_directory))
2145
2146             else:
2147                 struct = self.parse_resource_data_entry(
2148                     base_rva + res.OffsetToDirectory)
2149
2150                 if struct:
2151                     entry_data = ResourceDataEntryData(
2152                         struct = struct,
2153                         lang = res.Name & 0xff,
2154                         sublang = (res.Name>>8) & 0xff)
2155                         
2156                     dir_entries.append(
2157                         ResourceDirEntryData(
2158                             struct = res,
2159                             name = entry_name,
2160                             id = entry_id,
2161                             data = entry_data))
2162                     
2163                 else:
2164                     break
2165
2166
2167                 
2168             # Check if this entry contains version information
2169             #
2170             if level == 0 and res.Id == RESOURCE_TYPE['RT_VERSION']:
2171                 if len(dir_entries)>0:
2172                     last_entry = dir_entries[-1]
2173                     
2174                 rt_version_struct = None
2175                 try:
2176                     rt_version_struct = last_entry.directory.entries[0].directory.entries[0].data.struct
2177                 except:
2178                     # Maybe a malformed directory structure...?
2179                     # Lets ignore it
2180                     pass
2181                     
2182                 if rt_version_struct is not None:
2183                     self.parse_version_information(rt_version_struct)
2184     
2185             rva += res.sizeof()
2186             
2187                     
2188         string_rvas = [s.get_rva() for s in strings_to_postprocess]
2189         string_rvas.sort()
2190         
2191         for idx, s in enumerate(strings_to_postprocess):
2192             s.render_pascal_16()
2193             
2194             
2195         resource_directory_data = ResourceDirData(
2196             struct = resource_dir,
2197             entries = dir_entries)
2198                         
2199         return resource_directory_data
2200         
2201             
2202     def parse_resource_data_entry(self, rva):
2203         """Parse a data entry from the resources directory."""
2204     
2205         try:
2206             # If the RVA is invalid all would blow up. Some EXEs seem to be
2207             # specially nasty and have an invalid RVA.
2208             data = self.get_data(rva)
2209         except PEFormatError, excp:
2210             self.__warnings.append(
2211                 'Error parsing a resource directory data entry, ' +
2212                 'the RVA is invalid: 0x%x' % ( rva ) )
2213             return None
2214             
2215         data_entry = self.__unpack_data__(
2216             self.__IMAGE_RESOURCE_DATA_ENTRY_format__, data,
2217             file_offset = self.get_offset_from_rva(rva) )
2218             
2219         return data_entry
2220
2221         
2222     def parse_resource_entry(self, rva):
2223         """Parse a directory entry from the resources directory."""
2224
2225         resource = self.__unpack_data__(
2226             self.__IMAGE_RESOURCE_DIRECTORY_ENTRY_format__, self.get_data(rva),
2227             file_offset = self.get_offset_from_rva(rva) )
2228             
2229         if resource is None:
2230             return None
2231             
2232         #resource.NameIsString = (resource.Name & 0x80000000L) >> 31
2233         resource.NameOffset = resource.Name & 0x7FFFFFFFL
2234         
2235         resource.__pad = resource.Name & 0xFFFF0000L
2236         resource.Id = resource.Name & 0x0000FFFFL
2237         
2238         resource.DataIsDirectory = (resource.OffsetToData & 0x80000000L) >> 31
2239         resource.OffsetToDirectory = resource.OffsetToData & 0x7FFFFFFFL
2240         
2241         return resource
2242             
2243             
2244     def parse_version_information(self, version_struct):
2245         """Parse version information structure.
2246         
2247         The date will be made available in three attributes of the PE object.
2248         
2249         VS_VERSIONINFO     will contain the first three fields of the main structure:
2250             'Length', 'ValueLength', and 'Type'
2251             
2252         VS_FIXEDFILEINFO    will hold the rest of the fields, accessible as sub-attributes:
2253             'Signature', 'StrucVersion', 'FileVersionMS', 'FileVersionLS',
2254             'ProductVersionMS', 'ProductVersionLS', 'FileFlagsMask', 'FileFlags',
2255             'FileOS', 'FileType', 'FileSubtype', 'FileDateMS', 'FileDateLS'
2256             
2257         FileInfo    is a list of all StringFileInfo and VarFileInfo structures.
2258         
2259         StringFileInfo structures will have a list as an attribute named 'StringTable'
2260         containing all the StringTable structures. Each of those structures contains a 
2261         dictionary 'entries' with all the key/value version information string pairs.
2262         
2263         VarFileInfo structures will have a list as an attribute named 'Var' containing
2264         all Var structures. Each Var structure will have a dictionary as an attribute
2265         named 'entry' which will contain the name and value of the Var.
2266         """
2267     
2268     
2269         # Retrieve the data for the version info resource
2270         #
2271         start_offset = self.get_offset_from_rva( version_struct.OffsetToData )
2272         raw_data = self.__data__[ start_offset : start_offset+version_struct.Size ]
2273         
2274         
2275         # Map the main structure and the subsequent string
2276         #    
2277         versioninfo_struct = self.__unpack_data__(
2278             self.__VS_VERSIONINFO_format__, raw_data, 
2279             file_offset = start_offset )
2280             
2281         if versioninfo_struct is None:
2282             return 
2283             
2284         ustr_offset = version_struct.OffsetToData + versioninfo_struct.sizeof()
2285         try:
2286             versioninfo_string = self.get_string_u_at_rva( ustr_offset )
2287         except PEFormatError, excp:
2288             self.__warnings.append(
2289                 'Error parsing the version information, ' +
2290                 'attempting to read VS_VERSION_INFO string. Can\'t ' +
2291                 'read unicode string at offset 0x%x' % (
2292                 ustr_offset ) )
2293                 
2294             versioninfo_string = None
2295          
2296         # If the structure does not contain the expected name, it's assumed to be invalid
2297         #            
2298         if versioninfo_string != u'VS_VERSION_INFO':
2299
2300             self.__warnings.append('Invalid VS_VERSION_INFO block')
2301             return
2302
2303
2304         # Set the PE object's VS_VERSIONINFO to this one
2305         #
2306         self.VS_VERSIONINFO = versioninfo_struct
2307
2308         # The the Key attribute to point to the unicode string identifying the structure
2309         #        
2310         self.VS_VERSIONINFO.Key = versioninfo_string
2311
2312
2313         # Process the fixed version information, get the offset and structure
2314         #
2315         fixedfileinfo_offset = self.dword_align(
2316             versioninfo_struct.sizeof() + 2 * (len(versioninfo_string) + 1),
2317             version_struct.OffsetToData)
2318         fixedfileinfo_struct = self.__unpack_data__(
2319             self.__VS_FIXEDFILEINFO_format__,
2320             raw_data[fixedfileinfo_offset:], 
2321             file_offset = start_offset+fixedfileinfo_offset )
2322
2323         if not fixedfileinfo_struct:
2324             return
2325
2326
2327         # Set the PE object's VS_FIXEDFILEINFO to this one
2328         #
2329         self.VS_FIXEDFILEINFO = fixedfileinfo_struct
2330         
2331         
2332         # Start parsing all the StringFileInfo and VarFileInfo structures
2333         #
2334         
2335         # Get the first one
2336         #
2337         stringfileinfo_offset = self.dword_align(
2338             fixedfileinfo_offset + fixedfileinfo_struct.sizeof(),
2339             version_struct.OffsetToData)
2340         original_stringfileinfo_offset = stringfileinfo_offset
2341         
2342         
2343         # Set the PE object's attribute that will contain them all.
2344         #
2345         self.FileInfo = list()
2346
2347
2348         while True:
2349         
2350             # Process the StringFileInfo/VarFileInfo struct
2351             #
2352             stringfileinfo_struct = self.__unpack_data__(
2353                 self.__StringFileInfo_format__, 
2354                 raw_data[stringfileinfo_offset:], 
2355                 file_offset = start_offset+stringfileinfo_offset )
2356                 
2357             if stringfileinfo_struct is None:
2358                 self.__warnings.append(
2359                     'Error parsing StringFileInfo/VarFileInfo struct' )
2360                 return None
2361             
2362             # Get the subsequent string defining the structure.
2363             #
2364             ustr_offset = ( version_struct.OffsetToData + 
2365                 stringfileinfo_offset + versioninfo_struct.sizeof() )
2366             try:
2367                 stringfileinfo_string = self.get_string_u_at_rva( ustr_offset )
2368             except PEFormatError, excp:
2369                 self.__warnings.append(
2370                     'Error parsing the version information, ' +
2371                     'attempting to read StringFileInfo string. Can\'t ' +
2372                     'read unicode string at offset 0x%x' %  ( ustr_offset ) )
2373                 break
2374         
2375             # Set such string as the Key attribute
2376             #
2377             stringfileinfo_struct.Key = stringfileinfo_string
2378             
2379             
2380             # Append the structure to the PE object's list
2381             #
2382             self.FileInfo.append(stringfileinfo_struct)
2383         
2384         
2385             # Parse a StringFileInfo entry
2386             #
2387             if stringfileinfo_string == u'StringFileInfo':
2388                 
2389                 if stringfileinfo_struct.Type == 1 and stringfileinfo_struct.ValueLength == 0:
2390             
2391                     stringtable_offset = self.dword_align(
2392                         stringfileinfo_offset + stringfileinfo_struct.sizeof() + 
2393                             2*(len(stringfileinfo_string)+1),
2394                         version_struct.OffsetToData)
2395                   
2396                     stringfileinfo_struct.StringTable = list()
2397
2398                     # Process the String Table entries
2399                     #
2400                     while True:
2401                         stringtable_struct = self.__unpack_data__(
2402                             self.__StringTable_format__,
2403                             raw_data[stringtable_offset:], 
2404                             file_offset = start_offset+stringtable_offset )
2405                             
2406                         if not stringtable_struct:
2407                             break
2408                             
2409                         ustr_offset = ( version_struct.OffsetToData + stringtable_offset + 
2410                             stringtable_struct.sizeof() )
2411                         try:
2412                             stringtable_string = self.get_string_u_at_rva( ustr_offset )
2413                         except PEFormatError, excp:
2414                             self.__warnings.append(
2415                                 'Error parsing the version information, ' +
2416                                 'attempting to read StringTable string. Can\'t ' +
2417                                 'read unicode string at offset 0x%x' % ( ustr_offset ) )
2418                             break
2419                         
2420                         stringtable_struct.LangID = stringtable_string
2421                         stringtable_struct.entries = dict()
2422                         stringtable_struct.entries_offsets = dict()
2423                         stringtable_struct.entries_lengths = dict()
2424                         stringfileinfo_struct.StringTable.append(stringtable_struct)
2425             
2426                         entry_offset = self.dword_align(
2427                             stringtable_offset + stringtable_struct.sizeof() +
2428                                 2*(len(stringtable_string)+1),
2429                             version_struct.OffsetToData)
2430             
2431                         # Process all entries in the string table
2432                         #
2433             
2434                         while entry_offset < stringtable_offset + stringtable_struct.Length:
2435                     
2436                             string_struct = self.__unpack_data__(
2437                                 self.__String_format__, raw_data[entry_offset:], 
2438                                 file_offset = start_offset+entry_offset )
2439                                 
2440                             if not string_struct:
2441                                 break
2442                                 
2443                             ustr_offset = ( version_struct.OffsetToData + entry_offset +
2444                                 string_struct.sizeof() )
2445                             try:
2446                                 key = self.get_string_u_at_rva( ustr_offset )
2447                                 key_offset = self.get_offset_from_rva( ustr_offset )
2448                             except PEFormatError, excp:
2449                                 self.__warnings.append(
2450                                     'Error parsing the version information, ' +
2451                                     'attempting to read StringTable Key string. Can\'t ' +
2452                                     'read unicode string at offset 0x%x' % ( ustr_offset ) )
2453                                 break
2454                                 
2455                             value_offset = self.dword_align(
2456                                 2*(len(key)+1) + entry_offset + string_struct.sizeof(),
2457                                 version_struct.OffsetToData)
2458             
2459                             ustr_offset = version_struct.OffsetToData + value_offset
2460                             try:
2461                                 value = self.get_string_u_at_rva( ustr_offset,
2462                                     max_length = string_struct.ValueLength )
2463                                 value_offset = self.get_offset_from_rva( ustr_offset )
2464                             except PEFormatError, excp:
2465                                 self.__warnings.append(
2466                                     'Error parsing the version information, ' +
2467                                     'attempting to read StringTable Value string. ' +
2468                                     'Can\'t read unicode string at offset 0x%x' % (
2469                                     ustr_offset ) )
2470                                 break
2471                                 
2472                             if string_struct.Length == 0:
2473                                 entry_offset = stringtable_offset + stringtable_struct.Length
2474                             else:
2475                                 entry_offset = self.dword_align(
2476                                     string_struct.Length+entry_offset, version_struct.OffsetToData)
2477                                 
2478                             key_as_char = []
2479                             for c in key:
2480                                 if ord(c)>128:
2481                                     key_as_char.append('\\x%02x' %ord(c))
2482                                 else:
2483                                     key_as_char.append(c)
2484                             
2485                             key_as_char = ''.join(key_as_char)
2486
2487                             setattr(stringtable_struct, key_as_char, value)
2488                             stringtable_struct.entries[key] = value
2489                             stringtable_struct.entries_offsets[key] = (key_offset, value_offset)
2490                             stringtable_struct.entries_lengths[key] = (len(key), len(value))
2491                             
2492                     
2493                         stringtable_offset = self.dword_align(
2494                             stringtable_struct.Length + stringtable_offset,
2495                             version_struct.OffsetToData)
2496                         if stringtable_offset >= stringfileinfo_struct.Length:
2497                             break
2498                     
2499             # Parse a VarFileInfo entry
2500             #
2501             elif stringfileinfo_string == u'VarFileInfo':
2502             
2503                 varfileinfo_struct = stringfileinfo_struct
2504                 varfileinfo_struct.name = 'VarFileInfo'
2505             
2506                 if varfileinfo_struct.Type == 1 and varfileinfo_struct.ValueLength == 0:
2507               
2508                     var_offset = self.dword_align(
2509                         stringfileinfo_offset + varfileinfo_struct.sizeof() +
2510                             2*(len(stringfileinfo_string)+1),
2511                         version_struct.OffsetToData)
2512                         
2513                     varfileinfo_struct.Var = list()
2514               
2515                     # Process all entries
2516                     #
2517
2518                     while True:
2519                         var_struct = self.__unpack_data__(
2520                             self.__Var_format__,
2521                             raw_data[var_offset:], 
2522                             file_offset = start_offset+var_offset )
2523                             
2524                         if not var_struct:
2525                             break
2526                             
2527                         ustr_offset = ( version_struct.OffsetToData + var_offset + 
2528                             var_struct.sizeof() )
2529                         try:
2530                             var_string = self.get_string_u_at_rva( ustr_offset )
2531                         except PEFormatError, excp:
2532                             self.__warnings.append(
2533                                 'Error parsing the version information, ' +
2534                                 'attempting to read VarFileInfo Var string. ' +
2535                                 'Can\'t read unicode string at offset 0x%x' % (ustr_offset))
2536                             break
2537
2538                         
2539                         varfileinfo_struct.Var.append(var_struct)
2540                 
2541                         varword_offset = self.dword_align(
2542                             2*(len(var_string)+1) + var_offset + var_struct.sizeof(),
2543                             version_struct.OffsetToData)
2544                         orig_varword_offset = varword_offset
2545                             
2546                         while varword_offset < orig_varword_offset + var_struct.ValueLength:
2547                             word1 = self.get_word_from_data(
2548                                 raw_data[varword_offset:varword_offset+2], 0)
2549                             word2 = self.get_word_from_data(
2550                                 raw_data[varword_offset+2:varword_offset+4], 0)
2551                             varword_offset += 4
2552         
2553                             var_struct.entry = {var_string: '0x%04x 0x%04x' % (word1, word2)}
2554
2555                         var_offset = self.dword_align(
2556                             var_offset+var_struct.Length, version_struct.OffsetToData)
2557                             
2558                         if var_offset <= var_offset+var_struct.Length:
2559                             break
2560               
2561               
2562               
2563             # Increment and align the offset
2564             #
2565             stringfileinfo_offset = self.dword_align(
2566                 stringfileinfo_struct.Length+stringfileinfo_offset,
2567                 version_struct.OffsetToData)
2568           
2569             # Check if all the StringFileInfo and VarFileInfo items have been processed
2570             #
2571             if stringfileinfo_struct.Length == 0 or stringfileinfo_offset >= versioninfo_struct.Length:
2572                 break
2573             
2574             
2575                             
2576     def parse_export_directory(self, rva, size):
2577         """Parse the export directory.
2578         
2579         Given the rva of the export directory, it will process all
2580         its entries.
2581         
2582         The exports will be made available through a list "exports"
2583         containing a tuple with the following elements:
2584         
2585             (ordinal, symbol_address, symbol_name)
2586             
2587         And also through a dicionary "exports_by_ordinal" whose keys
2588         will be the ordinals and the values tuples of the from:
2589         
2590             (symbol_address, symbol_name)
2591             
2592         The symbol addresses are relative, not absolute.
2593         """
2594     
2595         try:
2596             export_dir =  self.__unpack_data__(
2597                 self.__IMAGE_EXPORT_DIRECTORY_format__, self.get_data(rva),
2598                 file_offset = self.get_offset_from_rva(rva) )
2599         except PEFormatError:
2600             self.__warnings.append(
2601                 'Error parsing export directory at RVA: 0x%x' % ( rva ) )
2602             return
2603         
2604         if not export_dir:
2605             return
2606         
2607         try:
2608             address_of_names = self.get_data(
2609                 export_dir.AddressOfNames, export_dir.NumberOfNames*4)
2610             address_of_name_ordinals = self.get_data(
2611                 export_dir.AddressOfNameOrdinals, export_dir.NumberOfNames*4)
2612             address_of_functions = self.get_data(
2613                 export_dir.AddressOfFunctions, export_dir.NumberOfFunctions*4)
2614         except PEFormatError:
2615             self.__warnings.append(
2616                 'Error parsing export directory at RVA: 0x%x' % ( rva ) )
2617             return
2618             
2619         exports = []
2620         
2621         for i in xrange(export_dir.NumberOfNames):
2622         
2623                 
2624             symbol_name = self.get_string_at_rva(
2625                 self.get_dword_from_data(address_of_names, i))
2626             
2627             symbol_ordinal = self.get_word_from_data(
2628                 address_of_name_ordinals, i)
2629                 
2630             
2631             if symbol_ordinal*4<len(address_of_functions):
2632                 symbol_address = self.get_dword_from_data(
2633                     address_of_functions, symbol_ordinal)
2634             else:
2635                 # Corrupt? a bad pointer... we assume it's all
2636                 # useless, no exports
2637                 return None
2638             
2639             # If the funcion's rva points within the export directory
2640             # it will point to a string with the forwarded symbol's string
2641             # instead of pointing the the function start address.
2642             
2643             if symbol_address>=rva and symbol_address<rva+size:
2644                 forwarder_str = self.get_string_at_rva(symbol_address)
2645             else:
2646                 forwarder_str = None
2647         
2648             
2649             exports.append(
2650                 ExportData(
2651                     ordinal = export_dir.Base+symbol_ordinal,
2652                     address = symbol_address,
2653                     name = symbol_name,
2654                     forwarder = forwarder_str))
2655                     
2656         ordinals = [exp.ordinal for exp in exports]
2657         
2658         for idx in xrange(export_dir.NumberOfFunctions):
2659
2660             if not idx+export_dir.Base in ordinals:
2661                 symbol_address = self.get_dword_from_data(
2662                     address_of_functions, 
2663                     idx)
2664                 
2665                 #
2666                 # Checking for forwarder again.
2667                 #
2668                 if symbol_address>=rva and symbol_address<rva+size:
2669                     forwarder_str = self.get_string_at_rva(symbol_address)
2670                 else:
2671                     forwarder_str = None
2672                     
2673                 exports.append(
2674                     ExportData(
2675                         ordinal = export_dir.Base+idx,
2676                         address = symbol_address,
2677                         name = None,
2678                         forwarder = forwarder_str))
2679                       
2680         return ExportDirData(
2681                 struct = export_dir,
2682                 symbols = exports)
2683         
2684                     
2685     def dword_align(self, offset, base):
2686         offset += base
2687         return (offset+3) - ((offset+3)%4) - base
2688
2689
2690
2691     def parse_delay_import_directory(self, rva, size):
2692         """Walk and parse the delay import directory."""
2693         
2694         import_descs =  []
2695         while True:
2696             try:
2697                 # If the RVA is invalid all would blow up. Some PEs seem to be
2698                 # specially nasty and have an invalid RVA.
2699                 data = self.get_data(rva)
2700             except PEFormatError, e:
2701                 self.__warnings.append(
2702                     'Error parsing the Delay import directory at RVA: 0x%x' % ( rva ) )
2703                 break
2704                 
2705             import_desc =  self.__unpack_data__(
2706                 self.__IMAGE_DELAY_IMPORT_DESCRIPTOR_format__,
2707                 data, file_offset = self.get_offset_from_rva(rva) )
2708             
2709             
2710             # If the structure is all zeores, we reached the end of the list
2711             if not import_desc or import_desc.all_zeroes():
2712                 break
2713             
2714             
2715             rva += import_desc.sizeof()
2716             
2717             try:
2718                 import_data =  self.parse_imports(
2719                     import_desc.pINT,
2720                     import_desc.pIAT,
2721                     None)
2722             except PEFormatError, e:
2723                 self.__warnings.append(
2724                     'Error parsing the Delay import directory. ' +
2725                     'Invalid import data at RVA: 0x%x' % ( rva ) )
2726                 break
2727             
2728             if not import_data:
2729                 continue
2730             
2731             
2732             dll = self.get_string_at_rva(import_desc.szName)
2733             if dll:
2734                 import_descs.append(
2735                     ImportDescData(
2736                         struct = import_desc,
2737                         imports = import_data,
2738                         dll = dll))
2739         
2740         return import_descs
2741
2742                     
2743
2744     def parse_import_directory(self, rva, size):
2745         """Walk and parse the import directory."""
2746
2747         import_descs =  []
2748         while True:
2749             try:
2750                 # If the RVA is invalid all would blow up. Some EXEs seem to be
2751                 # specially nasty and have an invalid RVA.
2752                 data = self.get_data(rva)
2753             except PEFormatError, e:
2754                 self.__warnings.append(
2755                     'Error parsing the Import directory at RVA: 0x%x' % ( rva ) )
2756                 break
2757                 
2758             import_desc =  self.__unpack_data__(
2759                 self.__IMAGE_IMPORT_DESCRIPTOR_format__,
2760                 data, file_offset = self.get_offset_from_rva(rva) )
2761                 
2762             # If the structure is all zeores, we reached the end of the list
2763             if not import_desc or import_desc.all_zeroes():
2764                 break
2765                 
2766             rva += import_desc.sizeof()
2767                         
2768             try:
2769                 import_data =  self.parse_imports(
2770                     import_desc.OriginalFirstThunk,
2771                     import_desc.FirstThunk,
2772                     import_desc.ForwarderChain)
2773             except PEFormatError, excp:
2774                 self.__warnings.append(
2775                     'Error parsing the Import directory. ' +
2776                     'Invalid Import data at RVA: 0x%x' % ( rva ) )
2777                 break
2778                 #raise excp
2779                 
2780             if not import_data:
2781                 continue
2782                 
2783             dll = self.get_string_at_rva(import_desc.Name)
2784             if dll:
2785                 import_descs.append(
2786                     ImportDescData(
2787                         struct = import_desc,
2788                         imports = import_data,
2789                         dll = dll))
2790
2791         return import_descs
2792             
2793
2794     
2795     def parse_imports(self, original_first_thunk, first_thunk, forwarder_chain):
2796         """Parse the imported symbols.
2797         
2798         It will fill a list, which will be avalable as the dictionary
2799         attribute "imports". Its keys will be the DLL names and the values
2800         all the symbols imported from that object.
2801         """
2802         
2803         imported_symbols = []
2804         imports_section = self.get_section_by_rva(first_thunk)
2805         if not imports_section:
2806             raise PEFormatError, 'Invalid/corrupt imports.'
2807             
2808         
2809         # Import Lookup Table. Contains ordinals or pointers to strings.
2810         ilt = self.get_import_table(original_first_thunk)
2811         # Import Address Table. May have identical content to ILT if
2812         # PE file is not bounded, Will contain the address of the
2813         # imported symbols once the binary is loaded or if it is already
2814         # bound.
2815         iat = self.get_import_table(first_thunk)
2816
2817         # OC Patch:
2818         # Would crash if iat or ilt had None type 
2819         if not iat and not ilt:
2820             raise PEFormatError( 
2821                 'Invalid Import Table information. ' +
2822                 'Both ILT and IAT appear to be broken.')
2823             
2824         if not iat and ilt:
2825             table = ilt
2826         elif iat and not ilt:
2827             table = iat
2828         elif ilt and ((len(ilt) and len(iat)==0) or (len(ilt) == len(iat))):
2829             table = ilt
2830         elif (ilt and len(ilt))==0 and (iat and len(iat)):
2831             table = iat
2832         else:
2833             return None
2834             
2835         for idx in xrange(len(table)):
2836
2837             imp_ord = None
2838             imp_hint = None
2839             imp_name = None
2840             hint_name_table_rva = None
2841                         
2842             if table[idx].AddressOfData:
2843             
2844                 if self.PE_TYPE == OPTIONAL_HEADER_MAGIC_PE:
2845                     ordinal_flag = IMAGE_ORDINAL_FLAG
2846                 elif self.PE_TYPE == OPTIONAL_HEADER_MAGIC_PE_PLUS:
2847                     ordinal_flag = IMAGE_ORDINAL_FLAG64
2848             
2849                 # If imported by ordinal, we will append the ordinal number
2850                 #
2851                 if table[idx].AddressOfData & ordinal_flag:
2852                     import_by_ordinal = True
2853                     imp_ord = table[idx].AddressOfData & 0xffff
2854                     imp_name = None
2855                 else:
2856                     import_by_ordinal = False
2857                     try:
2858                         hint_name_table_rva = table[idx].AddressOfData & 0x7fffffff
2859                         data = self.get_data(hint_name_table_rva, 2)
2860                         # Get the Hint
2861                         imp_hint = self.get_word_from_data(data, 0)
2862                         imp_name = self.get_string_at_rva(table[idx].AddressOfData+2)
2863                     except PEFormatError, e:
2864                         pass
2865
2866             imp_address = first_thunk+self.OPTIONAL_HEADER.ImageBase+idx*4
2867                 
2868             if iat and ilt and ilt[idx].AddressOfData != iat[idx].AddressOfData:
2869                 imp_bound = iat[idx].AddressOfData
2870             else:
2871                 imp_bound = None
2872                 
2873             if imp_name != '' and (imp_ord or imp_name):
2874                 imported_symbols.append(
2875                     ImportData(
2876                         import_by_ordinal = import_by_ordinal,
2877                         ordinal = imp_ord,
2878                         hint = imp_hint,
2879                         name = imp_name,
2880                         bound = imp_bound,
2881                         address = imp_address,
2882                         hint_name_table_rva = hint_name_table_rva))
2883             
2884         return imported_symbols
2885                 
2886
2887
2888     def get_import_table(self, rva):
2889     
2890         table = []
2891         
2892         while True and rva:
2893             try:
2894                 data = self.get_data(rva)
2895             except PEFormatError, e:
2896                 self.__warnings.append(
2897                     'Error parsing the import table. ' +
2898                     'Invalid data at RVA: 0x%x' % ( rva ) )
2899                 return None
2900                 
2901             if self.PE_TYPE == OPTIONAL_HEADER_MAGIC_PE:
2902                 format = self.__IMAGE_THUNK_DATA_format__
2903             elif self.PE_TYPE == OPTIONAL_HEADER_MAGIC_PE_PLUS:
2904                 format = self.__IMAGE_THUNK_DATA64_format__
2905                 
2906             thunk_data = self.__unpack_data__(
2907                 format, data, file_offset=self.get_offset_from_rva(rva) )
2908                     
2909             if not thunk_data or thunk_data.all_zeroes():
2910                 break
2911                 
2912             rva += thunk_data.sizeof()
2913             
2914             table.append(thunk_data)
2915             
2916         return table
2917     
2918     
2919     def get_memory_mapped_image(self, max_virtual_address=0x10000000, ImageBase=None):
2920         """Returns the data corresponding to the memory layout of the PE file.
2921         
2922         The data includes the PE header and the sections loaded at offsets
2923         corresponding to their relative virtual addresses. (the VirtualAddress
2924         section header member).
2925         Any offset in this data corresponds to the absolute memory address
2926         ImageBase+offset.
2927         
2928         The optional argument 'max_virtual_address' provides with means of limiting
2929         which section are processed.
2930         Any section with their VirtualAddress beyond this value will be skipped.
2931         Normally, sections with values beyond this range are just there to confuse
2932         tools. It's a common trick to see in packed executables.
2933         
2934         If the 'ImageBase' optional argument is supplied, the file's relocations
2935         will be applied to the image by calling the 'relocate_image()' method.
2936         """
2937         
2938         # Collect all sections in one code block    
2939         data = self.header
2940         for section in self.sections:
2941
2942             # Miscellanous integrity tests.
2943             # Some packer will set these to bogus values to
2944             # make tools go nuts.
2945             #
2946             if section.Misc_VirtualSize == 0 or section.SizeOfRawData == 0:
2947                 continue
2948             
2949             if section.SizeOfRawData > len(self.__data__):
2950                 continue
2951                 
2952             if section.PointerToRawData > len(self.__data__):
2953                 continue
2954         
2955             if section.VirtualAddress >= max_virtual_address:
2956                 continue
2957                 
2958             padding_length = section.VirtualAddress - len(data)
2959             
2960             if padding_length>0:
2961                 data += '\0'*padding_length
2962             elif padding_length<0:
2963                 data = data[:padding_length]
2964                 
2965             data += section.data
2966             
2967         return data
2968
2969             
2970     def get_data(self, rva, length=None):
2971         """Get data regardless of the section where it lies on.
2972         
2973         Given a rva and the size of the chunk to retrieve, this method
2974         will find the section where the data lies and return the data.
2975         """
2976         
2977         s = self.get_section_by_rva(rva)
2978
2979         if not s:
2980             if rva<len(self.header):
2981                 if length:
2982                     end = rva+length
2983                 else:
2984                     end = None
2985                 return self.header[rva:end]
2986                 
2987             raise PEFormatError, 'data at RVA can\'t be fetched. Corrupt header?'
2988
2989         return s.get_data(rva, length)
2990
2991
2992     def get_rva_from_offset(self, offset):
2993         """Get the rva corresponding to this file offset. """
2994
2995         s = self.get_section_by_offset(offset)
2996         if not s:
2997             raise PEFormatError("specified offset (0x%x) doesn't belong to any section." % offset)
2998         return s.get_rva_from_offset(offset)
2999
3000     def get_offset_from_rva(self, rva):
3001         """Get the file offset corresponding to this rva.
3002         
3003         Given a rva , this method will find the section where the
3004         data lies and return the offset within the file.
3005         """
3006         
3007         s = self.get_section_by_rva(rva)
3008         if not s:
3009                 
3010             raise PEFormatError, 'data at RVA can\'t be fetched. Corrupt header?'
3011             
3012         return s.get_offset_from_rva(rva)
3013         
3014             
3015     def get_string_at_rva(self, rva):
3016         """Get an ASCII string located at the given address."""
3017
3018         s = self.get_section_by_rva(rva)
3019         if not s:
3020             if rva<len(self.header):
3021                 return self.get_string_from_data(rva, self.header)
3022             return None
3023                 
3024         return self.get_string_from_data(rva-s.VirtualAddress, s.data)
3025         
3026         
3027     def get_string_from_data(self, offset, data):
3028         """Get an ASCII string from within the data."""
3029
3030         # OC Patch
3031         b = None
3032         
3033         try:
3034             b = data[offset]
3035         except IndexError:
3036             return ''
3037         
3038         s = ''
3039         while ord(b):
3040             s += b
3041             offset += 1
3042             try:
3043                 b = data[offset]
3044             except IndexError:
3045                 break
3046           
3047         return s
3048
3049                 
3050     def get_string_u_at_rva(self, rva, max_length = 2**16):
3051         """Get an Unicode string located at the given address."""
3052         
3053         try:
3054             # If the RVA is invalid all would blow up. Some EXEs seem to be
3055             # specially nasty and have an invalid RVA.
3056             data = self.get_data(rva, 2)
3057         except PEFormatError, e:
3058             return None
3059
3060         #length = struct.unpack('<H', data)[0]
3061                 
3062         s = u''
3063         for idx in xrange(max_length):
3064             try:
3065                 uchr = struct.unpack('<H', self.get_data(rva+2*idx, 2))[0]
3066             except struct.error:
3067                 break
3068                 
3069             if unichr(uchr) == u'\0':
3070                 break
3071             s += unichr(uchr)
3072             
3073         return s
3074
3075
3076     def get_section_by_offset(self, offset):
3077         """Get the section containing the given file offset."""
3078     
3079         sections = [s for s in self.sections if s.contains_offset(offset)]
3080         
3081         if sections:
3082             return sections[0]
3083                     
3084         return None
3085
3086
3087     def get_section_by_rva(self, rva):
3088         """Get the section containing the given address."""
3089     
3090         sections = [s for s in self.sections if s.contains_rva(rva)]
3091         
3092         if sections:
3093             return sections[0]
3094                     
3095         return None
3096     
3097     def __str__(self):
3098         return self.dump_info()
3099     
3100             
3101     def print_info(self):
3102         """Print all the PE header information in a human readable from."""
3103         print self.dump_info()
3104         
3105         
3106     def dump_info(self, dump=None):
3107         """Dump all the PE header information into human readable string."""
3108         
3109         
3110         if dump is None:
3111             dump = Dump()
3112         
3113         warnings = self.get_warnings()
3114         if warnings:
3115             dump.add_header('Parsing Warnings')
3116             for warning in warnings:
3117                 dump.add_line(warning)
3118                 dump.add_newline()
3119                 
3120         
3121         dump.add_header('DOS_HEADER')
3122         dump.add_lines(self.DOS_HEADER.dump())
3123         dump.add_newline()
3124         
3125         dump.add_header('NT_HEADERS')
3126         dump.add_lines(self.NT_HEADERS.dump())
3127         dump.add_newline()
3128         
3129         dump.add_header('FILE_HEADER')
3130         dump.add_lines(self.FILE_HEADER.dump())
3131         
3132         image_flags = self.retrieve_flags(IMAGE_CHARACTERISTICS, 'IMAGE_FILE_')
3133             
3134         dump.add('Flags: ')
3135         flags = []
3136         for flag in image_flags:
3137             if getattr(self.FILE_HEADER, flag[0]):
3138                 flags.append(flag[0])
3139         dump.add_line(', '.join(flags))
3140         dump.add_newline()
3141         
3142         if hasattr(self, 'OPTIONAL_HEADER') and self.OPTIONAL_HEADER is not None:
3143             dump.add_header('OPTIONAL_HEADER')
3144             dump.add_lines(self.OPTIONAL_HEADER.dump())
3145
3146         dll_characteristics_flags = self.retrieve_flags(DLL_CHARACTERISTICS, 'IMAGE_DLL_CHARACTERISTICS_')
3147             
3148         dump.add('DllCharacteristics: ')
3149         flags = []
3150         for flag in dll_characteristics_flags:
3151             if getattr(self.OPTIONAL_HEADER, flag[0]):
3152                 flags.append(flag[0])
3153         dump.add_line(', '.join(flags))
3154         dump.add_newline()
3155         
3156         
3157         dump.add_header('PE Sections')
3158         
3159         section_flags = self.retrieve_flags(SECTION_CHARACTERISTICS, 'IMAGE_SCN_')
3160         
3161         for section in self.sections:
3162             dump.add_lines(section.dump())
3163             dump.add('Flags: ')
3164             flags = []
3165             for flag in section_flags:
3166                 if getattr(section, flag[0]):
3167                     flags.append(flag[0])
3168             dump.add_line(', '.join(flags))
3169             dump.add_line('Entropy: %f (Min=0.0, Max=8.0)' % section.get_entropy() )
3170             if md5 is not None:
3171                 dump.add_line('MD5     hash: %s' % section.get_hash_md5() )
3172             if sha1 is not None:
3173                 dump.add_line('SHA-1   hash: %s' % section.get_hash_sha1() )
3174             if sha256 is not None:
3175                 dump.add_line('SHA-256 hash: %s' % section.get_hash_sha256() )
3176             if sha512 is not None:
3177                 dump.add_line('SHA-512 hash: %s' % section.get_hash_sha512() )
3178             dump.add_newline()
3179             
3180             
3181         
3182         if (hasattr(self, 'OPTIONAL_HEADER') and 
3183             hasattr(self.OPTIONAL_HEADER, 'DATA_DIRECTORY') ):
3184             
3185             dump.add_header('Directories')
3186             for idx in xrange(len(self.OPTIONAL_HEADER.DATA_DIRECTORY)):
3187                 directory = self.OPTIONAL_HEADER.DATA_DIRECTORY[idx]
3188                 dump.add_lines(directory.dump())
3189             dump.add_newline()
3190
3191
3192         if hasattr(self, 'VS_VERSIONINFO'):
3193             dump.add_header('Version Information')
3194             dump.add_lines(self.VS_VERSIONINFO.dump())
3195             dump.add_newline()
3196
3197             if hasattr(self, 'VS_FIXEDFILEINFO'):
3198                 dump.add_lines(self.VS_FIXEDFILEINFO.dump())
3199                 dump.add_newline()
3200
3201             if hasattr(self, 'FileInfo'):
3202                 for entry in self.FileInfo:
3203                     dump.add_lines(entry.dump())
3204                     dump.add_newline()
3205                     
3206                     if hasattr(entry, 'StringTable'):
3207                         for st_entry in entry.StringTable:
3208                             [dump.add_line('  '+line) for line in st_entry.dump()]
3209                             dump.add_line('  LangID: '+st_entry.LangID)
3210                             dump.add_newline()
3211                             for str_entry in st_entry.entries.items():
3212                                 dump.add_line('    '+str_entry[0]+': '+str_entry[1])
3213                         dump.add_newline()
3214                                 
3215                     elif hasattr(entry, 'Var'):
3216                         for var_entry in entry.Var:
3217                             if hasattr(var_entry, 'entry'):
3218                                 [dump.add_line('  '+line) for line in var_entry.dump()]
3219                                 dump.add_line(
3220                                     '    ' + var_entry.entry.keys()[0] + 
3221                                     ': ' + var_entry.entry.values()[0])
3222                                     
3223                         dump.add_newline()
3224
3225
3226             
3227         if hasattr(self, 'DIRECTORY_ENTRY_EXPORT'):
3228             dump.add_header('Exported symbols')
3229             dump.add_lines(self.DIRECTORY_ENTRY_EXPORT.struct.dump())
3230             dump.add_newline()
3231             dump.add_line('%-10s   %-10s  %s' % ('Ordinal', 'RVA', 'Name'))
3232             for export in self.DIRECTORY_ENTRY_EXPORT.symbols:
3233                 dump.add('%-10d 0x%08Xh    %s' % (
3234                     export.ordinal, export.address, export.name))
3235                 if export.forwarder:
3236                     dump.add_line(' forwarder: %s' % export.forwarder)
3237                 else:
3238                     dump.add_newline()
3239                 
3240             dump.add_newline()
3241         
3242         if hasattr(self, 'DIRECTORY_ENTRY_IMPORT'):
3243             dump.add_header('Imported symbols')
3244             for module in self.DIRECTORY_ENTRY_IMPORT:
3245                 dump.add_lines(module.struct.dump())
3246                 dump.add_newline()
3247                 for symbol in module.imports:
3248                 
3249                     if symbol.import_by_ordinal is True:
3250                         dump.add('%s Ordinal[%s] (Imported by Ordinal)' % (
3251                             module.dll, str(symbol.ordinal)))
3252                     else:
3253                         dump.add('%s.%s Hint[%s]' % (
3254                             module.dll, symbol.name, str(symbol.hint)))
3255
3256                     if symbol.bound:
3257                         dump.add_line(' Bound: 0x%08X' % (symbol.bound))
3258                     else:
3259                         dump.add_newline()
3260                 dump.add_newline()
3261                 
3262         
3263         if hasattr(self, 'DIRECTORY_ENTRY_BOUND_IMPORT'):
3264             dump.add_header('Bound imports')
3265             for bound_imp_desc in self.DIRECTORY_ENTRY_BOUND_IMPORT:
3266             
3267                 dump.add_lines(bound_imp_desc.struct.dump())
3268                 dump.add_line('DLL: %s' % bound_imp_desc.name)
3269                 dump.add_newline()
3270                 
3271                 for bound_imp_ref in bound_imp_desc.entries:
3272                     dump.add_lines(bound_imp_ref.struct.dump(), 4)
3273                     dump.add_line('DLL: %s' % bound_imp_ref.name, 4)
3274                     dump.add_newline()
3275
3276
3277         if hasattr(self, 'DIRECTORY_ENTRY_DELAY_IMPORT'):
3278             dump.add_header('Delay Imported symbols')
3279             for module in self.DIRECTORY_ENTRY_DELAY_IMPORT:
3280             
3281                 dump.add_lines(module.struct.dump())
3282                 dump.add_newline()
3283                 
3284                 for symbol in module.imports:
3285                     if symbol.import_by_ordinal is True:
3286                         dump.add('%s Ordinal[%s] (Imported by Ordinal)' % (
3287                             module.dll, str(symbol.ordinal)))
3288                     else:
3289                         dump.add('%s.%s Hint[%s]' % (
3290                             module.dll, symbol.name, str(symbol.hint)))
3291                     
3292                     if symbol.bound:
3293                         dump.add_line(' Bound: 0x%08X' % (symbol.bound))
3294                     else:
3295                         dump.add_newline()
3296                 dump.add_newline()
3297             
3298         
3299         if hasattr(self, 'DIRECTORY_ENTRY_RESOURCE'):
3300             dump.add_header('Resource directory')
3301             
3302             dump.add_lines(self.DIRECTORY_ENTRY_RESOURCE.struct.dump())
3303             
3304             for resource_type in self.DIRECTORY_ENTRY_RESOURCE.entries:
3305             
3306                 if resource_type.name is not None:
3307                     dump.add_line('Name: [%s]' % resource_type.name, 2)
3308                 else:
3309                     dump.add_line('Id: [0x%X] (%s)' % (
3310                         resource_type.struct.Id, RESOURCE_TYPE.get(
3311                             resource_type.struct.Id, '-')),
3312                         2)
3313                         
3314                 dump.add_lines(resource_type.struct.dump(), 2)
3315
3316                 if hasattr(resource_type, 'directory'):
3317                 
3318                     dump.add_lines(resource_type.directory.struct.dump(), 4)
3319                         
3320                     for resource_id in resource_type.directory.entries:
3321                     
3322                         if resource_id.name is not None:
3323                             dump.add_line('Name: [%s]' % resource_id.name, 6)
3324                         else:
3325                             dump.add_line('Id: [0x%X]' % resource_id.struct.Id, 6)
3326                             
3327                         dump.add_lines(resource_id.struct.dump(), 6)
3328     
3329                         if hasattr(resource_id, 'directory'):
3330                             dump.add_lines(resource_id.directory.struct.dump(), 8)
3331                             
3332                             for resource_lang in resource_id.directory.entries:
3333                             #    dump.add_line('\\--- LANG [%d,%d][%s]' % (
3334                             #        resource_lang.data.lang,
3335                             #        resource_lang.data.sublang,
3336                             #        LANG[resource_lang.data.lang]), 8)
3337                                 dump.add_lines(resource_lang.struct.dump(), 10)
3338                                 dump.add_lines(resource_lang.data.struct.dump(), 12)
3339                 dump.add_newline()
3340     
3341             dump.add_newline()
3342         
3343         
3344         if ( hasattr(self, 'DIRECTORY_ENTRY_TLS') and 
3345              self.DIRECTORY_ENTRY_TLS and 
3346              self.DIRECTORY_ENTRY_TLS.struct ):
3347              
3348             dump.add_header('TLS')
3349             dump.add_lines(self.DIRECTORY_ENTRY_TLS.struct.dump())
3350             dump.add_newline()
3351
3352         
3353         if hasattr(self, 'DIRECTORY_ENTRY_DEBUG'):
3354             dump.add_header('Debug information')
3355             for dbg in self.DIRECTORY_ENTRY_DEBUG:
3356                 dump.add_lines(dbg.struct.dump())
3357                 try:
3358                     dump.add_line('Type: '+DEBUG_TYPE[dbg.struct.Type])
3359                 except KeyError:
3360                     dump.add_line('Type: 0x%x(Unknown)' % dbg.struct.Type)
3361                 dump.add_newline()
3362         
3363         
3364         if hasattr(self, 'DIRECTORY_ENTRY_BASERELOC'):
3365             dump.add_header('Base relocations')
3366             for base_reloc in self.DIRECTORY_ENTRY_BASERELOC:
3367                 dump.add_lines(base_reloc.struct.dump())
3368                 for reloc in base_reloc.entries:
3369                     try:
3370                         dump.add_line('%08Xh %s' % (
3371                             reloc.rva, RELOCATION_TYPE[reloc.type][16:]), 4)
3372                     except KeyError:
3373                         dump.add_line('0x%08X 0x%x(Unknown)' % (
3374                             reloc.rva, reloc.type), 4)
3375                 dump.add_newline()
3376         
3377
3378         return dump.get_text()
3379
3380     # OC Patch
3381     def get_physical_by_rva(self, rva):
3382         """Gets the physical address in the PE file from an RVA value."""
3383         try:
3384             return self.get_offset_from_rva(rva)
3385         except Exception:
3386             return None
3387
3388
3389     ##
3390     # Double-Word get/set
3391     ##
3392
3393     def get_data_from_dword(self, dword):
3394         """Return a four byte string representing the double word value. (little endian)."""
3395         return struct.pack('<L', dword)
3396
3397
3398     def get_dword_from_data(self, data, offset):
3399         """Convert four bytes of data to a double word (little endian)
3400         
3401         'offset' is assumed to index into a dword array. So setting it to
3402         N will return a dword out of the data sarting at offset N*4.
3403         
3404         Returns None if the data can't be turned into a double word.
3405         """
3406         
3407         if (offset+1)*4 > len(data):
3408             return None
3409         
3410         return struct.unpack('<L', data[offset*4:(offset+1)*4])[0]
3411         
3412         
3413     def get_dword_at_rva(self, rva):
3414         """Return the double word value at the given RVA.
3415         
3416         Returns None if the value can't be read, i.e. the RVA can't be mapped
3417         to a file offset.
3418         """
3419         
3420         try:
3421             return self.get_dword_from_data(self.get_data(rva)[:4], 0)
3422         except PEFormatError:
3423             return None
3424
3425
3426     def get_dword_from_offset(self, offset):
3427         """Return the double word value at the given file offset. (little endian)"""
3428         
3429         if offset+4 > len(self.__data__):
3430             return None
3431             
3432         return self.get_dword_from_data(self.__data__[offset:offset+4], 0)
3433
3434
3435     def set_dword_at_rva(self, rva, dword):
3436         """Set the double word value at the file offset corresponding to the given RVA."""
3437         return self.set_bytes_at_rva(rva, self.get_data_from_dword(dword))
3438
3439
3440     def set_dword_at_offset(self, offset, dword):
3441         """Set the double word value at the given file offset."""
3442         return self.set_bytes_at_offset(offset, self.get_data_from_dword(dword))
3443
3444
3445
3446     ##
3447     # Word get/set
3448     ##
3449
3450     def get_data_from_word(self, word):
3451         """Return a two byte string representing the word value. (little endian)."""
3452         return struct.pack('<H', word)
3453
3454
3455     def get_word_from_data(self, data, offset):
3456         """Convert two bytes of data to a word (little endian)
3457         
3458         'offset' is assumed to index into a word array. So setting it to
3459         N will return a dword out of the data sarting at offset N*2.
3460
3461         Returns None if the data can't be turned into a word.
3462         """
3463
3464         if (offset+1)*2 > len(data):
3465             return None
3466
3467         return struct.unpack('<H', data[offset*2:(offset+1)*2])[0]
3468
3469
3470     def get_word_at_rva(self, rva):
3471         """Return the word value at the given RVA.
3472         
3473         Returns None if the value can't be read, i.e. the RVA can't be mapped
3474         to a file offset.
3475         """
3476         
3477         try:
3478             return self.get_word_from_data(self.get_data(rva)[:2], 0)
3479         except PEFormatError:
3480             return None
3481             
3482
3483     def get_word_from_offset(self, offset):
3484         """Return the word value at the given file offset. (little endian)"""
3485         
3486         if offset+2 > len(self.__data__):
3487             return None
3488             
3489         return self.get_word_from_data(self.__data__[offset:offset+2], 0)
3490
3491
3492     def set_word_at_rva(self, rva, word):
3493         """Set the word value at the file offset corresponding to the given RVA."""
3494         return self.set_bytes_at_rva(rva, self.get_data_from_word(word))
3495
3496
3497     def set_word_at_offset(self, offset, word):
3498         """Set the word value at the given file offset."""
3499         return self.set_bytes_at_offset(offset, self.get_data_from_word(word))
3500
3501
3502     ##
3503     # Quad-Word get/set
3504     ##
3505
3506     def get_data_from_qword(self, word):
3507         """Return a eight byte string representing the quad-word value. (little endian)."""
3508         return struct.pack('<Q', word)
3509
3510
3511     def get_qword_from_data(self, data, offset):
3512         """Convert eight bytes of data to a word (little endian)
3513         
3514         'offset' is assumed to index into a word array. So setting it to
3515         N will return a dword out of the data sarting at offset N*8.
3516
3517         Returns None if the data can't be turned into a quad word.
3518         """
3519
3520         if (offset+1)*8 > len(data):
3521             return None
3522
3523         return struct.unpack('<Q', data[offset*8:(offset+1)*8])[0]
3524
3525
3526     def get_qword_at_rva(self, rva):
3527         """Return the quad-word value at the given RVA.
3528         
3529         Returns None if the value can't be read, i.e. the RVA can't be mapped
3530         to a file offset.
3531         """
3532         
3533         try:
3534             return self.get_qword_from_data(self.get_data(rva)[:8], 0)
3535         except PEFormatError:
3536             return None
3537             
3538
3539     def get_qword_from_offset(self, offset):
3540         """Return the quad-word value at the given file offset. (little endian)"""
3541         
3542         if offset+8 > len(self.__data__):
3543             return None
3544             
3545         return self.get_qword_from_data(self.__data__[offset:offset+8], 0)
3546
3547
3548     def set_qword_at_rva(self, rva, qword):
3549         """Set the quad-word value at the file offset corresponding to the given RVA."""
3550         return self.set_bytes_at_rva(rva, self.get_data_from_qword(qword))
3551
3552
3553     def set_qword_at_offset(self, offset, qword):
3554         """Set the quad-word value at the given file offset."""
3555         return self.set_bytes_at_offset(offset, self.get_data_from_qword(qword))
3556
3557
3558
3559     ##
3560     # Set bytes
3561     ##
3562
3563
3564     def set_bytes_at_rva(self, rva, data):
3565         """Overwrite, with the given string, the bytes at the file offset corresponding to the given RVA.
3566         
3567         Return True if successful, False otherwise. It can fail if the
3568         offset is outside the file's boundaries.
3569         """
3570
3571         offset = self.get_physical_by_rva(rva)
3572         if not offset:
3573             raise False
3574             
3575         return self.set_bytes_at_offset(offset, data)
3576
3577         
3578     def set_bytes_at_offset(self, offset, data):
3579         """Overwrite the bytes at the given file offset with the given string.
3580         
3581         Return True if successful, False otherwise. It can fail if the
3582         offset is outside the file's boundaries.
3583         """
3584     
3585         if not isinstance(data, str):
3586             raise TypeError('data should be of type: str')
3587         
3588         if offset >= 0 and offset < len(self.__data__):
3589             self.__data__ = ( self.__data__[:offset] + 
3590                 data +
3591                 self.__data__[offset+len(data):] )
3592         else:
3593             return False
3594             
3595         # Refresh the section's data with the modified information
3596         #
3597         for section in self.sections:
3598             section_data_start = section.PointerToRawData
3599             section_data_end = section_data_start+section.SizeOfRawData
3600             section.data = self.__data__[section_data_start:section_data_end]
3601
3602         return True
3603         
3604
3605
3606     def relocate_image(self, new_ImageBase):
3607         """Apply the relocation information to the image using the provided new image base.
3608         
3609         This method will apply the relocation information to the image. Given the new base,
3610         all the relocations will be processed and both the raw data and the section's data
3611         will be fixed accordingly.
3612         The resulting image can be retrieved as well through the method:
3613         
3614             get_memory_mapped_image()
3615             
3616         In order to get something that would more closely match what could be found in memory
3617         once the Windows loader finished its work.
3618         """
3619         
3620         relocation_difference = new_ImageBase - self.OPTIONAL_HEADER.ImageBase
3621         
3622         
3623         for reloc in self.DIRECTORY_ENTRY_BASERELOC:
3624
3625             virtual_address = reloc.struct.VirtualAddress
3626             size_of_block = reloc.struct.SizeOfBlock
3627             
3628             # We iterate with an index because if the relocation is of type
3629             # IMAGE_REL_BASED_HIGHADJ we need to also process the next entry
3630             # at once and skip it for the next interation
3631             # 
3632             entry_idx = 0
3633             while entry_idx<len(reloc.entries):
3634             
3635                 entry = reloc.entries[entry_idx]
3636                 entry_idx += 1
3637                                 
3638                 if entry.type == RELOCATION_TYPE['IMAGE_REL_BASED_ABSOLUTE']:
3639                     # Nothing to do for this type of relocation
3640                     pass
3641                     
3642                 elif entry.type == RELOCATION_TYPE['IMAGE_REL_BASED_HIGH']:
3643                     # Fix the high 16bits of a relocation
3644                     #
3645                     # Add high 16bits of relocation_difference to the
3646                     # 16bit value at RVA=entry.rva
3647                     
3648                     self.set_word_at_rva(
3649                         entry.rva,
3650                         ( self.get_word_at_rva(entry.rva) + relocation_difference>>16)&0xffff )
3651                   
3652                 elif entry.type == RELOCATION_TYPE['IMAGE_REL_BASED_LOW']:
3653                     # Fix the low 16bits of a relocation
3654                     #
3655                     # Add low 16 bits of relocation_difference to the 16bit value
3656                     # at RVA=entry.rva
3657                     
3658                     self.set_word_at_rva(
3659                         entry.rva,
3660                         ( self.get_word_at_rva(entry.rva) + relocation_difference)&0xffff)
3661                     
3662                 elif entry.type == RELOCATION_TYPE['IMAGE_REL_BASED_HIGHLOW']:
3663                     # Handle all high and low parts of a 32bit relocation
3664                     #
3665                     # Add relocation_difference to the value at RVA=entry.rva
3666                     
3667                     self.set_dword_at_rva(
3668                         entry.rva,
3669                         self.get_dword_at_rva(entry.rva)+relocation_difference)
3670                   
3671                 elif entry.type == RELOCATION_TYPE['IMAGE_REL_BASED_HIGHADJ']:
3672                     # Fix the high 16bits of a relocation and adjust
3673                     #
3674                     # Add high 16bits of relocation_difference to the 32bit value
3675                     # composed from the (16bit value at RVA=entry.rva)<<16 plus 
3676                     # the 16bit value at the next relocation entry.
3677                     #
3678                     
3679                     # If the next entry is beyond the array's limits,
3680                     # abort... the table is corrupt
3681                     #
3682                     if entry_idx == len(reloc.entries):
3683                         break
3684                     
3685                     next_entry = reloc.entries[entry_idx]
3686                     entry_idx += 1
3687                     self.set_word_at_rva( entry.rva, 
3688                         ((self.get_word_at_rva(entry.rva)<<16) + next_entry.rva +
3689                         relocation_difference & 0xffff0000) >> 16 )
3690                 
3691                 elif entry.type == RELOCATION_TYPE['IMAGE_REL_BASED_DIR64']:
3692                     # Apply the difference to the 64bit value at the offset
3693                     # RVA=entry.rva
3694                     
3695                     self.set_qword_at_rva(
3696                         entry.rva,
3697                         self.get_qword_at_rva(entry.rva) + relocation_difference)
3698
3699
3700     def verify_checksum(self):
3701     
3702         return self.OPTIONAL_HEADER.CheckSum == self.generate_checksum()
3703         
3704     
3705     def generate_checksum(self):
3706     
3707         # Get the offset to the CheckSum field in the OptionalHeader
3708         #
3709         checksum_offset = self.OPTIONAL_HEADER.__file_offset__ + 0x40 # 64
3710         
3711         checksum = 0
3712         
3713         for i in range( len(self.__data__) / 4 ):
3714         
3715             # Skip the checksum field
3716             #
3717             if i == checksum_offset / 4:
3718                 continue
3719
3720             dword = struct.unpack('L', self.__data__[ i*4 : i*4+4 ])[0]
3721             checksum = (checksum & 0xffffffff) + dword + (checksum>>32)
3722             if checksum > 2**32:
3723                 checksum = (checksum & 0xffffffff) + (checksum >> 32)
3724         
3725         checksum = (checksum & 0xffff) + (checksum >> 16)
3726         checksum = (checksum) + (checksum >> 16)
3727         checksum = checksum & 0xffff
3728         
3729         return checksum + len(self.__data__)