1# -*- coding: Latin-1 -*- 2"""pefile, Portable Executable reader module 3 4 5All the PE file basic structures are available with their default names 6as attributes of the instance returned. 7 8Processed elements such as the import table are made available with lowercase 9names, to differentiate them from the upper case basic structure names. 10 11pefile has been tested against the limits of valid PE headers, that is, malware. 12Lots of packed malware attempt to abuse the format way beyond its standard use. 13To the best of my knowledge most of the abuses are handled gracefully. 14 15Copyright (c) 2005, 2006, 2007, 2008 Ero Carrera <ero@dkbza.org> 16 17All rights reserved. 18 19For detailed copyright information see the file COPYING in 20the root of the distribution archive. 21""" 22 23__author__ = 'Ero Carrera' 24__version__ = '1.2.9.1' 25__contact__ = 'ero@dkbza.org' 26 27 28import os 29import struct 30import time 31import math 32import re 33import exceptions 34import string 35import array 36 37sha1, sha256, sha512, md5 = None, None, None, None 38 39try: 40 import hashlib 41 sha1 = hashlib.sha1 42 sha256 = hashlib.sha256 43 sha512 = hashlib.sha512 44 md5 = hashlib.md5 45except 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 58fast_load = False 59 60IMAGE_DOS_SIGNATURE = 0x5A4D 61IMAGE_OS2_SIGNATURE = 0x454E 62IMAGE_OS2_SIGNATURE_LE = 0x454C 63IMAGE_VXD_SIGNATURE = 0x454C 64IMAGE_NT_SIGNATURE = 0x00004550 65IMAGE_NUMBEROF_DIRECTORY_ENTRIES= 16 66IMAGE_ORDINAL_FLAG = 0x80000000L 67IMAGE_ORDINAL_FLAG64 = 0x8000000000000000L 68OPTIONAL_HEADER_MAGIC_PE = 0x10b 69OPTIONAL_HEADER_MAGIC_PE_PLUS = 0x20b 70 71 72directory_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 90DIRECTORY_ENTRY = dict([(e[1], e[0]) for e in directory_entry_types]+directory_entry_types) 91 92 93image_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 111IMAGE_CHARACTERISTICS = dict([(e[1], e[0]) for e in 112 image_characteristics]+image_characteristics) 113 114 115section_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 152SECTION_CHARACTERISTICS = dict([(e[1], e[0]) for e in 153 section_characteristics]+section_characteristics) 154 155 156debug_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 169DEBUG_TYPE = dict([(e[1], e[0]) for e in debug_types]+debug_types) 170 171 172subsystem_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 186SUBSYSTEM_TYPE = dict([(e[1], e[0]) for e in subsystem_types]+subsystem_types) 187 188 189machine_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 212MACHINE_TYPE = dict([(e[1], e[0]) for e in machine_types]+machine_types) 213 214 215relocation_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 229RELOCATION_TYPE = dict([(e[1], e[0]) for e in relocation_types]+relocation_types) 230 231 232dll_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 247DLL_CHARACTERISTICS = dict([(e[1], e[0]) for e in dll_characteristics]+dll_characteristics) 248 249 250# Resource types 251resource_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 274RESOURCE_TYPE = dict([(e[1], e[0]) for e in resource_type]+resource_type) 275 276 277# Language definitions 278lang = [ 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 374LANG = dict(lang+[(e[1], e[0]) for e in lang]) 375 376 377# Sublanguage definitions 378sublang = [ 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 483SUBLANG = dict(sublang+[(e[1], e[0]) for e in sublang]) 484 485 486class 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 590class 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 600class 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 663class 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 812class 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 934class 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 943class 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 951class 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 960class ExportDirData(DataContainer): 961 """Holds export directory information. 962 963 struct: IMAGE_EXPORT_DIRECTORY structure 964 symbols: list of exported symbols (ExportData instances) 965""" 966 967class 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 980class ResourceDirData(DataContainer): 981 """Holds resource directory information. 982 983 struct: IMAGE_RESOURCE_DIRECTORY structure 984 entries: list of entries (ResourceDirEntryData instances) 985 """ 986 987class 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 1007class 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 1015class DebugData(DataContainer): 1016 """Holds debug information. 1017 1018 struct: IMAGE_DEBUG_DIRECTORY structure 1019 """ 1020 1021class BaseRelocationData(DataContainer): 1022 """Holds base relocation information. 1023 1024 struct: IMAGE_BASE_RELOCATION structure 1025 entries: list of relocation data (RelocationData instances) 1026 """ 1027 1028class 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 1037class TlsData(DataContainer): 1038 """Holds TLS information. 1039 1040 struct: IMAGE_TLS_DIRECTORY structure 1041 """ 1042 1043class 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 1061class 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 1072class 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__) 3730