Package rekall :: Package plugins :: Package overlays :: Package windows :: Module pe_vtypes
[frames] | no frames]

Source Code for Module rekall.plugins.overlays.windows.pe_vtypes

   1  # Rekall Memory Forensics 
   2  # 
   3  # Authors: 
   4  # Michael Cohen <scudette@users.sourceforge.net> 
   5  # Copyright (c) 2010, 2011, 2012 Michael Ligh <michael.ligh@mnin.org> 
   6  # Copyright 2013 Google Inc. All Rights Reserved. 
   7  # 
   8  # This program is free software; you can redistribute it and/or modify 
   9  # it under the terms of the GNU General Public License as published by 
  10  # the Free Software Foundation; either version 2 of the License, or (at 
  11  # your option) any later version. 
  12  # 
  13  # This program is distributed in the hope that it will be useful, but 
  14  # WITHOUT ANY WARRANTY; without even the implied warranty of 
  15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
  16  # General Public License for more details. 
  17  # 
  18  # You should have received a copy of the GNU General Public License 
  19  # along with this program; if not, write to the Free Software 
  20  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
  21  # 
  22   
  23  # pylint: disable=protected-access 
  24   
  25  """References: 
  26  http://msdn.microsoft.com/en-us/magazine/ms809762.aspx 
  27  http://msdn.microsoft.com/en-us/magazine/cc301805.aspx 
  28  http://code.google.com/p/corkami/downloads/detail?name=pe-20110117.pdf 
  29  http://code.google.com/p/pefile/ 
  30   
  31  Version information: 
  32  http://msdn.microsoft.com/en-us/library/windows/desktop/ff468916(v=vs.85).aspx 
  33  """ 
  34  import copy 
  35  import re 
  36   
  37  from rekall import addrspace 
  38  from rekall import obj 
  39   
  40  from rekall.plugins.addrspaces import standard 
  41  from rekall.plugins.overlays import basic 
  42  from rekall_lib import utils 
43 44 45 -class SentinelArray(obj.Array):
46 """A sential terminated array.""" 47
48 - def __init__(self, max_count=100000, **kwargs):
49 # The size of this array is determined by the sentinel. 50 super(SentinelArray, self).__init__( 51 count=max_count, max_count=max_count, **kwargs)
52
53 - def __iter__(self):
54 """Break when the sentinel is reached.""" 55 for member in super(SentinelArray, self).__iter__(): 56 data = member.obj_vm.read(member.obj_offset, member.obj_size) 57 if data == "\x00" * member.obj_size: 58 break 59 60 yield member
61
62 63 -class SentinelListArray(SentinelArray, obj.ListArray):
64 """A variable sized array with a sentinel termination."""
65
66 67 -class RVAPointer(obj.Pointer):
68 """A pointer through a relative virtual address.""" 69 image_base = 0
70 - def __init__(self, image_base=None, **kwargs):
71 super(RVAPointer, self).__init__(**kwargs) 72 self.image_base = image_base or self.obj_context.get("image_base", 0) 73 if callable(self.image_base): 74 self.image_base = self.image_base(self.obj_parent) 75 76 # RVA pointers are always 32 bits - even on 64 bit systems. 77 self._proxy = self.obj_profile.Object( 78 "unsigned int", offset=self.obj_offset, vm=self.obj_vm, 79 context=self.obj_context)
80
81 - def v(self, vm=None):
82 rva_pointer = super(RVAPointer, self).v() 83 if rva_pointer: 84 rva_pointer += self.image_base 85 86 return rva_pointer
87
88 89 -class ResourcePointer(obj.Pointer):
90 """A pointer relative to our resource section.""" 91 resource_base = 0 92
93 - def __init__(self, resource_base=None, **kwargs):
94 super(ResourcePointer, self).__init__(**kwargs) 95 # By default find the resource_base from the context. 96 self.resource_base = (resource_base or 97 self.obj_context.get("resource_base")) 98 99 if self.resource_base is None: 100 for parent in self.parents: 101 if isinstance(parent, _IMAGE_NT_HEADERS): 102 for section in parent.Sections: 103 if section.Name.startswith(".rsrc"): 104 self.resource_base = ( 105 section.VirtualAddress + 106 parent.OptionalHeader.ImageBase) 107 self.obj_context[ 108 'resource_base'] = self.resource_base 109 break
110
111 - def v(self, vm=None):
112 # Only the first 31 bits are meaningful. 113 resource_pointer = int( 114 super(ResourcePointer, self).v()) & ((1 << 31) - 1) 115 if resource_pointer: 116 resource_pointer += self.resource_base 117 118 return resource_pointer
119
120 121 -def RoundUpToWordAlignment(offset):
122 """Round up the next word boundary.""" 123 if offset % 4: 124 offset += 4 - offset % 4 125 126 return offset
127
128 129 -def AlignAfter(name):
130 """Align a Struct's member after another member. 131 132 Produce a callable which returns the next aligned offset after the member of 133 the required name in this struct. This callable is suitable to be specified 134 in the overlay's offset field. 135 """ 136 def get_offset(x): 137 x = getattr(x, name) 138 end_of_object = x.obj_offset + x.obj_size 139 140 return RoundUpToWordAlignment(end_of_object)
141 142 return get_offset 143 144 145 pe_overlays = { 146 "_IMAGE_OPTIONAL_HEADER": [None, { 147 'Subsystem' : [None, ['Enumeration', { 148 'choices': { 149 0: 'IMAGE_SUBSYSTEM_UNKNOWN', 150 1: 'IMAGE_SUBSYSTEM_NATIVE', 151 2: 'IMAGE_SUBSYSTEM_WINDOWS_GUI', 152 3: 'IMAGE_SUBSYSTEM_WINDOWS_CUI', 153 5: 'IMAGE_SUBSYSTEM_OS2_CUI', 154 7: 'IMAGE_SUBSYSTEM_POSIX_CUI', 155 9: 'IMAGE_SUBSYSTEM_WINDOWS_CE_GUI', 156 10:'IMAGE_SUBSYSTEM_EFI_APPLICATION', 157 11:'IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER', 158 12:'IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER', 159 13:'IMAGE_SUBSYSTEM_EFI_ROM', 160 14:'IMAGE_SUBSYSTEM_XBOX'}, 161 'target': 'unsigned short'}]], 162 163 'DataDirectory' : [None, ['IndexedArray', { 164 'count': 16, 165 'index_table': { 166 'IMAGE_DIRECTORY_ENTRY_EXPORT': 0, 167 'IMAGE_DIRECTORY_ENTRY_IMPORT': 1, 168 'IMAGE_DIRECTORY_ENTRY_RESOURCE': 2, 169 'IMAGE_DIRECTORY_ENTRY_EXCEPTION': 3, 170 'IMAGE_DIRECTORY_ENTRY_SECURITY': 4, 171 'IMAGE_DIRECTORY_ENTRY_BASERELOC': 5, 172 'IMAGE_DIRECTORY_ENTRY_DEBUG': 6, 173 'IMAGE_DIRECTORY_ENTRY_COPYRIGHT': 7, 174 'IMAGE_DIRECTORY_ENTRY_GLOBALPTR': 8, 175 'IMAGE_DIRECTORY_ENTRY_TLS': 9, 176 'IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG': 10, 177 'IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT': 11, 178 'IMAGE_DIRECTORY_ENTRY_IAT': 12, 179 'IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT': 13, 180 'IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR':14, 181 'IMAGE_DIRECTORY_ENTRY_RESERVED': 15, 182 }, 183 'target': '_IMAGE_DATA_DIRECTORY'}]], 184 }], 185 "_IMAGE_FILE_HEADER": [None, { 186 'Machine' : [None, ['Enumeration', { 187 'choices': { 188 0x0000: 'IMAGE_FILE_MACHINE_UNKNOWN', 189 0x01d3: 'IMAGE_FILE_MACHINE_AM33', 190 0x8664: 'IMAGE_FILE_MACHINE_AMD64', 191 0x01c0: 'IMAGE_FILE_MACHINE_ARM', 192 0x0ebc: 'IMAGE_FILE_MACHINE_EBC', 193 0x014c: 'IMAGE_FILE_MACHINE_I386', 194 0x0200: 'IMAGE_FILE_MACHINE_IA64', 195 0x9041: 'IMAGE_FILE_MACHINE_MR32', 196 0x0266: 'IMAGE_FILE_MACHINE_MIPS16', 197 0x0366: 'IMAGE_FILE_MACHINE_MIPSFPU', 198 0x0466: 'IMAGE_FILE_MACHINE_MIPSFPU16', 199 0x01f0: 'IMAGE_FILE_MACHINE_POWERPC', 200 0x01f1: 'IMAGE_FILE_MACHINE_POWERPCFP', 201 0x0166: 'IMAGE_FILE_MACHINE_R4000', 202 0x01a2: 'IMAGE_FILE_MACHINE_SH3', 203 0x01a3: 'IMAGE_FILE_MACHINE_SH3DSP', 204 0x01a6: 'IMAGE_FILE_MACHINE_SH4', 205 0x01a8: 'IMAGE_FILE_MACHINE_SH5', 206 0x01c2: 'IMAGE_FILE_MACHINE_THUMB', 207 0x0169: 'IMAGE_FILE_MACHINE_WCEMIPSV2'}, 208 'target': 'unsigned short' 209 }]], 210 211 'Characteristics' : [None, ['Flags', { 212 'maskmap': { 213 'IMAGE_FILE_RELOCS_STRIPPED': 0x0001, 214 'IMAGE_FILE_EXECUTABLE_IMAGE': 0x0002, 215 'IMAGE_FILE_LINE_NUMS_STRIPPED': 0x0004, 216 'IMAGE_FILE_LOCAL_SYMS_STRIPPED': 0x0008, 217 'IMAGE_FILE_AGGRESIVE_WS_TRIM': 0x0010, 218 'IMAGE_FILE_LARGE_ADDRESS_AWARE': 0x0020, 219 'IMAGE_FILE_16BIT_MACHINE': 0x0040, 220 'IMAGE_FILE_BYTES_REVERSED_LO': 0x0080, 221 'IMAGE_FILE_32BIT_MACHINE': 0x0100, 222 'IMAGE_FILE_DEBUG_STRIPPED': 0x0200, 223 'IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP': 0x0400, 224 'IMAGE_FILE_NET_RUN_FROM_SWAP': 0x0800, 225 'IMAGE_FILE_SYSTEM': 0x1000, 226 'IMAGE_FILE_DLL': 0x2000, 227 'IMAGE_FILE_UP_SYSTEM_ONLY': 0x4000, 228 'IMAGE_FILE_BYTES_REVERSED_HI': 0x8000, 229 }, 230 'target': 'unsigned short'}]], 231 'TimeDateStamp' : [None, ['UnixTimeStamp', {}]], 232 }], 233 234 "_IMAGE_SECTION_HEADER": [None, { 235 'Name' : [None, ['String', {'length': 8, 'term': None}]], 236 'Characteristics' : [None, ['Flags', { 237 'maskmap': { 238 'IMAGE_SCN_CNT_CODE': 0x00000020, 239 'IMAGE_SCN_CNT_INITIALIZED_DATA': 0x00000040, 240 'IMAGE_SCN_CNT_UNINITIALIZED_DATA': 0x00000080, 241 'IMAGE_SCN_LNK_OTHER': 0x00000100, 242 'IMAGE_SCN_LNK_INFO': 0x00000200, 243 'IMAGE_SCN_LNK_REMOVE': 0x00000800, 244 'IMAGE_SCN_LNK_COMDAT': 0x00001000, 245 'IMAGE_SCN_MEM_FARDATA': 0x00008000, 246 'IMAGE_SCN_MEM_PURGEABLE': 0x00020000, 247 'IMAGE_SCN_MEM_16BIT': 0x00020000, 248 'IMAGE_SCN_MEM_LOCKED': 0x00040000, 249 'IMAGE_SCN_MEM_PRELOAD': 0x00080000, 250 'IMAGE_SCN_ALIGN_1BYTES': 0x00100000, 251 'IMAGE_SCN_ALIGN_2BYTES': 0x00200000, 252 'IMAGE_SCN_ALIGN_4BYTES': 0x00300000, 253 'IMAGE_SCN_ALIGN_8BYTES': 0x00400000, 254 'IMAGE_SCN_ALIGN_16BYTES': 0x00500000, 255 'IMAGE_SCN_ALIGN_32BYTES': 0x00600000, 256 'IMAGE_SCN_ALIGN_64BYTES': 0x00700000, 257 'IMAGE_SCN_ALIGN_128BYTES': 0x00800000, 258 'IMAGE_SCN_ALIGN_256BYTES': 0x00900000, 259 'IMAGE_SCN_ALIGN_512BYTES': 0x00A00000, 260 'IMAGE_SCN_ALIGN_1024BYTES': 0x00B00000, 261 'IMAGE_SCN_ALIGN_2048BYTES': 0x00C00000, 262 'IMAGE_SCN_ALIGN_4096BYTES': 0x00D00000, 263 'IMAGE_SCN_ALIGN_8192BYTES': 0x00E00000, 264 'IMAGE_SCN_ALIGN_MASK': 0x00F00000, 265 'IMAGE_SCN_LNK_NRELOC_OVFL': 0x01000000, 266 'IMAGE_SCN_MEM_DISCARDABLE': 0x02000000, 267 'IMAGE_SCN_MEM_NOT_CACHED': 0x04000000, 268 'IMAGE_SCN_MEM_NOT_PAGED': 0x08000000, 269 'IMAGE_SCN_MEM_SHARED': 0x10000000, 270 'IMAGE_SCN_MEM_EXECUTE': 0x20000000, 271 'IMAGE_SCN_MEM_READ': 0x40000000, 272 'IMAGE_SCN_MEM_WRITE': 0x80000000L}, 273 'target': 'unsigned long'}]], 274 275 'execution_flags': lambda x: "%s%s%s" % ( 276 "x" if x.Characteristics.IMAGE_SCN_MEM_EXECUTE else "-", 277 "r" if x.Characteristics.IMAGE_SCN_MEM_READ else "-", 278 "w" if x.Characteristics.IMAGE_SCN_MEM_WRITE else "-") 279 }], 280 281 "_IMAGE_IMPORT_DESCRIPTOR": [None, { 282 'Name': [None, ['RVAPointer', dict( 283 target="String", 284 target_args=dict(length=128))]], 285 286 'TimeDateStamp': [None, ['UnixTimeStamp', {}]], 287 288 # This is an RVA pointer to an array of _IMAGE_THUNK_DATA32 structs. 289 'FirstThunk': [None, ['RVAPointer', dict(target="ThunkArray")]], 290 291 # This is a copy of the original IAT in memory. 292 'OriginalFirstThunk': [None, ['RVAPointer', dict( 293 target="ThunkArray" 294 )]], 295 }], 296 297 "_IMAGE_EXPORT_DIRECTORY": [None, { 298 'Name': [None, ['RVAPointer', dict( 299 target="String", 300 target_args=dict(length=128) 301 )]], 302 303 'AddressOfFunctions': [None, ['RVAPointer', dict( 304 target="Array", 305 target_args=dict( 306 target="RVAPointer", 307 target_args=dict(target="Function"), 308 count=lambda x: x.NumberOfFunctions, 309 ) 310 )]], 311 312 'AddressOfNames': [None, ["RVAPointer", dict( 313 target="Array", 314 target_args=dict( 315 target="RVAPointer", 316 target_args=dict(target="String"), 317 count=lambda x: x.NumberOfNames, 318 ) 319 )]], 320 321 'AddressOfNameOrdinals': [None, ['RVAPointer', dict( 322 target="Array", 323 target_args=dict( 324 target="unsigned short int", 325 count=lambda x: x.NumberOfFunctions) 326 )]], 327 }], 328 329 "_IMAGE_THUNK_DATA32": [None, { 330 'AddressOfData' : [0x0, ['RVAPointer', dict( 331 target="_IMAGE_IMPORT_BY_NAME" 332 )]], 333 }], 334 335 "_IMAGE_NT_HEADERS": [None, { 336 # This is a psuedo member to give access to the sections. 337 "Sections": [ 338 # The sections start immediately after the OptionalHeader: 339 lambda x: (x.FileHeader.SizeOfOptionalHeader + 340 x.OptionalHeader.obj_offset), 341 342 # The sections are an array of _IMAGE_SECTION_HEADER structs. 343 # The number of sections is found in the FileHeader 344 ['Array', dict( 345 target="_IMAGE_SECTION_HEADER", 346 count=lambda x: x.FileHeader.NumberOfSections, 347 )]], 348 }], 349 350 "_IMAGE_RESOURCE_DIRECTORY": [None, { 351 "Entries": [0x10, ["Array", dict( 352 target="_IMAGE_RESOURCE_DIRECTORY_ENTRY", 353 count=lambda x: (x.NumberOfIdEntries + 354 x.NumberOfNamedEntries), 355 )]], 356 }], 357 358 "_IMAGE_RESOURCE_DIRECTORY_ENTRY": [None, { 359 "Name": [None, ['ResourcePointer', dict(target="PrefixedString")]], 360 "Type": [0, ["Enumeration", dict(choices={ 361 1: 'RT_CURSOR', 362 2: 'RT_BITMAP', 363 3: 'RT_ICON', 364 4: 'RT_MENU', 365 5: 'RT_DIALOG', 366 6: 'RT_STRING', 367 7: 'RT_FONTDIR', 368 8: 'RT_FONT', 369 9: 'RT_ACCELERATOR', 370 10: 'RT_RCDATA', 371 11: 'RT_MESSAGETABLE', 372 12: 'RT_GROUP_CURSOR', 373 14: 'RT_GROUP_ICON', 374 16: 'RT_VERSION', 375 17: 'RT_DLGINCLUDE', 376 19: 'RT_PLUGPLAY', 377 20: 'RT_VXD', 378 21: 'RT_ANICURSOR', 379 22: 'RT_ANIICON', 380 23: 'RT_HTML', 381 24: 'RT_MANIFEST'})]], 382 383 # This is true when we need to use the Name field. 384 "OffsetToDataInt": [0x04, ['unsigned int']], 385 "OffsetToData": [0x04, ['ResourcePointer', dict( 386 target="_IMAGE_RESOURCE_DATA_ENTRY", 387 )]], 388 "Entry": [0x04, ['ResourcePointer', dict( 389 target="_IMAGE_RESOURCE_DIRECTORY")]], 390 391 # If this is set the child is another 392 # _IMAGE_RESOURCE_DIRECTORY_ENTRY 393 "ChildIsEntry": [0x04, ['BitField', dict( 394 start_bit=31, 395 end_bit=32, 396 )]], 397 }], 398 399 'PrefixedString' : [0x02, { 400 'Length' : [0x0, ['unsigned short']], 401 'Buffer' : [0x2, ['UnicodeString', dict( 402 length=lambda x: x.Length * 2 + 1, 403 )]], 404 }], 405 406 '_IMAGE_RESOURCE_DATA_ENTRY': [0x10, { 407 'OffsetToData': [0x00, ['RVAPointer', dict( 408 target="String", 409 target_args=dict(length=lambda x: x.Size))]], 410 'Size': [0x04, ['unsigned int']], 411 'CodePage': [0x08, ['unsigned int']], 412 }], 413 414 '_IMAGE_IMPORT_BY_NAME' : [None, { 415 'Name' : [None, ['String', dict(length=128)]], 416 }], 417 418 '_IMAGE_DATA_DIRECTORY' : [None, { 419 'VirtualAddress' : [None, ['RVAPointer', dict( 420 target='unsigned int' 421 )]], 422 }], 423 424 '_IMAGE_DEBUG_DIRECTORY': [None, { 425 "AddressOfRawData": [20, ["RVAPointer", dict( 426 # We only support CV_RSDS_HEADER for XP+ 427 target="CV_RSDS_HEADER", 428 )]], 429 "TimeDateStamp": [0x4, ["UnixTimeStamp"]], 430 "Type": [12, ["Enumeration", dict( 431 choices={ 432 0: "IMAGE_DEBUG_TYPE_UNKNOWN", 433 1: "IMAGE_DEBUG_TYPE_COFF", 434 2: "IMAGE_DEBUG_TYPE_CODEVIEW", 435 3: "IMAGE_DEBUG_TYPE_FPO", 436 4: "IMAGE_DEBUG_TYPE_MISC", 437 5: "IMAGE_DEBUG_TYPE_EXCEPTION", 438 6: "IMAGE_DEBUG_TYPE_FIXUP", 439 7: "IMAGE_DEBUG_TYPE_OMAP_TO_SRC", 440 8: "IMAGE_DEBUG_TYPE_OMAP_FROM_SRC", 441 9: "IMAGE_DEBUG_TYPE_BORLAND", 442 10: "IMAGE_DEBUG_TYPE_RESERVED", 443 }, 444 target="unsigned int", 445 )]], 446 }], 447 448 "_GUID": [16, { 449 "Data4": [8, ["String", dict(length=8, term=None)]], 450 "AsString": lambda x: ("%08x%04x%04x%s" % ( 451 x.Data1, x.Data2, x.Data3, x.Data4.v().encode('hex'))).upper(), 452 }], 453 454 # This struct is reversed. 455 'CV_RSDS_HEADER': [None, { 456 "Signature": [0, ["String", dict(length=4)]], 457 "GUID": [4, ["_GUID"]], 458 "GUID_AGE": lambda x: "%s%X" % (x.GUID.AsString, x.Age), 459 "Age": [20, ["unsigned int"]], 460 "Filename": [24, ["String"]], 461 }], 462 463 '_IMAGE_THUNK_DATA64' : [None, { 464 'AddressOfData' : [0, ['RVAPointer', dict( 465 target="_IMAGE_IMPORT_BY_NAME" 466 )]], 467 468 # Fake member for testing if the highest bit is set 469 'OrdinalBit' : [0, ['BitField', dict( 470 start_bit=63, 471 end_bit=64 472 )]], 473 474 }], 475 476 'tagVS_FIXEDFILEINFO': [None, { 477 "dwFileOS": [None, ["Flags", dict( 478 maskmap={ 479 "VOS_DOS": 0x00010000, 480 "VOS_NT": 0x00040000, 481 "VOS__WINDOWS16": 0x00000001, 482 "VOS__WINDOWS32": 0x00000004, 483 }, 484 target='unsigned int')]], 485 "dwFileType": [None, ['Enumeration', dict( 486 choices={ 487 1: "VFT_APP (Application)", 488 2: "VFT_DLL (DLL)", 489 3: "VFT_DRV (Driver)", 490 4: "VFT_FORT (Font)", 491 5: "VFT_VXD", 492 7: "VFT_STATIC_LIB", 493 }, 494 target='unsigned int')]], 495 "dwFileDate": [lambda x: x.m("dwFileDateLS").obj_offset, 496 ['WinFileTime', {}]], 497 }], 498 499 # The size of this is given by the Length member. 500 "StringFileInfo": [lambda x: RoundUpToWordAlignment(x.Length), { 501 "Length": [0x00, ['unsigned short int']], 502 "ValueLength": [0x02, ['unsigned short int']], 503 "Type": [0x04, ['unsigned short int']], 504 505 # Must be "StringFileInfo" 506 "Key": [0x06, ['UnicodeString', dict(length=28)]], 507 508 "Children": [AlignAfter("Key"), ['ListArray', dict( 509 target='StringTable', 510 maximum_offset=lambda x: x.Length + x.obj_offset)]], 511 }], 512 513 # The size of this is given by the Length member. 514 "VarFileInfo": [lambda x: RoundUpToWordAlignment(x.Length), { 515 "Length": [0x00, ['unsigned short int']], 516 "ValueLength": [0x02, ['unsigned short int']], 517 "Type": [0x04, ['unsigned short int']], 518 519 # Must be "VarFileInfo" 520 "Key": [0x06, ['UnicodeString', dict(length=24)]], 521 522 "Children": [AlignAfter("Key"), ['ListArray', dict( 523 target='Var', 524 maximum_offset=lambda x: x.Length + x.obj_offset)]], 525 }], 526 527 # Round up the size of the struct to word alignment. 528 "Var": [lambda x: RoundUpToWordAlignment(x.Length), { 529 "Length": [0x00, ['unsigned short int']], 530 "ValueLength": [0x02, ['unsigned short int']], 531 "Type": [0x04, ['unsigned short int']], 532 533 # This is exactly Translation 534 "Key": [0x06, ['UnicodeString', dict(length=24)]], 535 536 "Value": [AlignAfter("Key"), ['String', dict( 537 length=lambda x: x.ValueLength, term=None)]], 538 }], 539 540 "StringTable": [lambda x: RoundUpToWordAlignment(x.Length), { 541 "Length": [0x00, ['unsigned short int']], 542 "ValueLength": [0x02, ['unsigned short int']], 543 "Type": [0x04, ['unsigned short int']], 544 545 # In MSDN this is called szKey. 546 "LangID": [0x06, ['UnicodeString', dict(length=16, term=None)]], 547 548 "Children": [AlignAfter("LangID"), ['ListArray', dict( 549 target='ResourceString', 550 maximum_offset=lambda x: x.Length + x.obj_offset)]], 551 }], 552 553 # Round up the size of the struct to word alignment. 554 "ResourceString": [lambda x: RoundUpToWordAlignment(x.Length), { 555 "Length": [0x00, ['unsigned short int']], 556 "ValueLength": [0x02, ['unsigned short int']], 557 "Type": [0x04, ['unsigned short int']], 558 559 # This is a null terminated unicode string representing the key. 560 "Key": [0x06, ['UnicodeString', dict(length=1024)]], 561 562 "Value": [AlignAfter("Key"), ['UnicodeString', dict( 563 length=lambda x: x.ValueLength * 2)]], 564 }], 565 566 # Note this is a problematic structure due to the alignment 567 # requirements. Its not too much of a problem for the Rekall Memory 568 # Forensics object system though :-) 569 570 # http://msdn.microsoft.com/en-us/library/windows/desktop/ms647001(v=vs.85).aspx 571 'VS_VERSIONINFO': [0x06, { 572 "Length": [0x00, ['unsigned short int']], 573 "ValueLength": [0x02, ['unsigned short int']], 574 "Type": [0x04, ["Enumeration", dict( 575 choices={ 576 0: "Binary", 577 1: "Text", 578 }, 579 target='unsigned short int')]], 580 581 # Must be VS_VERSION_INFO\x00 in utf16 582 "szKey": [0x06, ["UnicodeString", dict(length=32)]], 583 584 # The member is 32bit aligned after the szKey member. 585 "Value": [AlignAfter("szKey"), ["tagVS_FIXEDFILEINFO"]], 586 587 # This member is also aligned after the Value member. 588 "Children": [AlignAfter("Value"), ['ListArray', dict( 589 target="StringFileInfo", 590 maximum_offset=lambda x: x.Length + x.obj_offset)]], 591 }], 592 } 593 594 # _IMAGE_OPTIONAL_HEADER64 is the same as _IMAGE_OPTIONAL_HEADER but offsets are 595 # different 596 pe_overlays["_IMAGE_OPTIONAL_HEADER64"] = copy.deepcopy( 597 pe_overlays["_IMAGE_OPTIONAL_HEADER"])
598 599 -class _LDR_DATA_TABLE_ENTRY(obj.Struct):
600 """ 601 Class for PE file / modules 602 603 If these classes are instantiated by _EPROCESS.list_*_modules() 604 then its guaranteed to be in the process address space. 605 """ 606 _pe = None 607 608 @utils.safe_property
609 - def PE(self):
610 if self._pe is None: 611 self._pe = PE(address_space=self.obj_vm, image_base=self.DllBase, 612 session=self.obj_session) 613 614 return self._pe
615 616 @utils.safe_property
617 - def NTHeader(self):
618 """Return the _IMAGE_NT_HEADERS object""" 619 620 dos_header = self.obj_profile._IMAGE_DOS_HEADER( 621 self.DllBase.v(), vm=self.obj_vm) 622 623 return dos_header.NTHeader
624
625 626 -class _IMAGE_DOS_HEADER(obj.Struct):
627 """DOS header""" 628 629 #Put checks in constructor. 630 631 @utils.safe_property
632 - def NTHeader(self):
633 """Get the NT header""" 634 if self.e_magic != 0x5a4d: 635 return obj.NoneObject( 636 'e_magic {0:04X} is not a valid DOS signature.'.format( 637 self.e_magic or 0)) 638 639 nt_header = self.obj_profile._IMAGE_NT_HEADERS( 640 offset=self.e_lfanew + self.obj_offset, 641 vm=self.obj_vm, context=self.obj_context) 642 643 if nt_header.Signature != 0x4550: 644 return obj.NoneObject( 645 'NT header signature {0:04X} is not a valid'.format( 646 nt_header.Signature or 0)) 647 648 return nt_header
649
650 651 -class _IMAGE_NT_HEADERS(obj.Struct):
652 """PE header""" 653 654 @utils.safe_property
655 - def OptionalHeader(self):
656 optional_header = self.m("OptionalHeader") 657 if optional_header.Magic == 0x20b: 658 self.obj_context["mode"] = "AMD64" 659 return optional_header.cast("_IMAGE_OPTIONAL_HEADER64") 660 661 self.obj_context["mode"] = "I386" 662 return optional_header
663
664 665 -class _IMAGE_SECTION_HEADER(obj.Struct):
666 """PE section""" 667
668 - def sanity_check_section(self):
669 """Sanity checks address boundaries""" 670 # Note: all addresses here are RVAs 671 image_size = self.obj_parent.OptionalHeader.SizeOfImage 672 if self.VirtualAddress > image_size: 673 raise ValueError('VirtualAddress {0:08x} is past the end of ' 674 'image.'.format(self.VirtualAddress)) 675 676 if self.Misc.VirtualSize > image_size: 677 raise ValueError('VirtualSize {0:08x} is larger than image ' 678 'size.'.format(self.Misc.VirtualSize)) 679 680 if self.SizeOfRawData > image_size: 681 raise ValueError('SizeOfRawData {0:08x} is larger than image ' 682 'size.'.format(self.SizeOfRawData))
683
684 685 -class _IMAGE_DATA_DIRECTORY(obj.Struct):
686 """A data directory.""" 687
688 - def dereference(self, vm=None):
689 """Automatically resolve the data directory according to our name.""" 690 result = self.m("VirtualAddress") 691 692 if self.obj_name == "IMAGE_DIRECTORY_ENTRY_IMPORT": 693 return result.dereference_as( 694 target="SentinelArray", target_args=dict( 695 target="_IMAGE_IMPORT_DESCRIPTOR" 696 ), 697 vm=vm, 698 ) 699 700 elif self.obj_name == "IMAGE_DIRECTORY_ENTRY_EXPORT": 701 return result.dereference_as("_IMAGE_EXPORT_DIRECTORY", vm=vm) 702 703 elif self.obj_name == "IMAGE_DIRECTORY_ENTRY_RESOURCE": 704 return result.dereference_as("_IMAGE_RESOURCE_DIRECTORY", vm=vm) 705 706 return result.dereference(vm=vm)
707
708 709 -class _IMAGE_RESOURCE_DIRECTORY(obj.Struct):
710 """Represents a node in the resource tree.""" 711
712 - def __iter__(self):
713 for entry in self.Entries: 714 yield entry
715
716 - def Open(self, node_name):
717 """Opens a specific node child.""" 718 for entry in self.Entries: 719 if entry.Name == node_name or entry.Type == node_name: 720 return entry.Entry 721 722 if entry.Type == 0: 723 break 724 725 return obj.NoneObject("node %s not found" % node_name)
726
727 - def Traverse(self):
728 """A generator for _IMAGE_RESOURCE_DATA_ENTRY under this node.""" 729 for entry in self: 730 if entry.ChildIsEntry: 731 for subentry in entry.Entry.Traverse(): 732 yield subentry 733 else: 734 yield entry.OffsetToData.dereference()
735
736 737 -class _IMAGE_RESOURCE_DIRECTORY_ENTRY(obj.Struct):
738 739 @utils.safe_property
740 - def Name(self):
741 if self.NameIsString: 742 return utils.SmartUnicode(self.m("Name").Buffer) 743 else: 744 return utils.SmartUnicode(self.Type)
745 746 @utils.safe_property
747 - def Entry(self):
748 if self.ChildIsEntry: 749 return self.m("Entry").dereference() 750 else: 751 return self.m("OffsetToData")
752
753 754 -class ThunkArray(SentinelArray):
755 """A sential terminated array of thunks.""" 756
757 - def __init__(self, parent=None, context=None, **kwargs):
758 # Are we in a 64 bit file? 759 if context.get("mode") == "AMD64": 760 target = "_IMAGE_THUNK_DATA64" 761 else: 762 target = "_IMAGE_THUNK_DATA32" 763 764 super(ThunkArray, self).__init__( 765 target=target, parent=parent, context=context, **kwargs)
766
767 -class VS_VERSIONINFO(obj.Struct):
768 769 @utils.safe_property
770 - def Children(self):
771 """Get all the children of this node. 772 773 The child is either a StringFileInfo or VarFileInfo depending on the 774 key.""" 775 for child in self.m("Children"): 776 if child.Key.startswith("VarFileInfo"): 777 yield child.cast("VarFileInfo") 778 elif child.Key.startswith("StringFileInfo"): 779 yield child 780 else: 781 break
782
783 - def Strings(self, item=None):
784 """Generates all the ResourceString structs by recursively traversing 785 the Children tree. 786 """ 787 if item is None: 788 item = self 789 790 for child in item.Children: 791 try: 792 for subchild in self.Strings(child): 793 yield subchild 794 except AttributeError: 795 yield child
796
797 798 799 -class PE(object):
800 """A convenience object to access PE file information.""" 801
802 - def __init__(self, address_space=None, image_base=0, filename=None, 803 session=None):
804 """Constructor. 805 806 Args: 807 address_space: An address space to examine. 808 809 image_base: The address of the dos header in the virtual address 810 space. 811 812 filename: If a filename is provided we open the file as a PE File. In 813 this case, image_base and address_space are ignored. 814 """ 815 self.session = session 816 if session is None: 817 raise RuntimeError("Session must be provided.") 818 819 # Use the session to load the pe profile. 820 self.profile = self.session.LoadProfile("pe") 821 822 # If neither filename or address_space were provided we just get the 823 # session default. 824 if filename is None and address_space is None: 825 address_space = self.session.GetParameter("default_address_space") 826 if address_space == None: 827 raise IOError("Filename or address_space not specified.") 828 829 self.vm = address_space 830 self.image_base = image_base 831 832 elif address_space: 833 # Resolve the correct address space. This allows the address space 834 # to be specified from the command line (e.g. "P") 835 load_as = self.session.plugins.load_as(session=self.session) 836 address_space = load_as.ResolveAddressSpace(address_space) 837 838 self.vm = address_space 839 self.image_base = image_base 840 if self.image_base == None: 841 raise RuntimeError("Image base is invalid.") 842 843 else: 844 file_address_space = standard.FileAddressSpace( 845 filename=filename, session=self.session) 846 847 self.vm = PEFileAddressSpace( 848 base=file_address_space, session=self.session) 849 850 self.image_base = self.vm.image_base 851 852 self.dos_header = self.profile.Object( 853 "_IMAGE_DOS_HEADER", vm=self.vm, offset=self.image_base, 854 context=dict(image_base=self.image_base)) 855 856 self.nt_header = self.dos_header.NTHeader
857 858 @utils.safe_property
859 - def RSDS(self):
860 return self.nt_header.OptionalHeader.DataDirectory[ 861 "IMAGE_DIRECTORY_ENTRY_DEBUG"].VirtualAddress.dereference_as( 862 "_IMAGE_DEBUG_DIRECTORY").AddressOfRawData
863
864 - def ImportDirectory(self):
865 """A generator over the import directory. 866 867 Note that this iterates over the OriginalFirstThunk which still remains 868 from the on-disk executable. The IAT is constructed by the linker at 869 load time, and is stored in FirstThunk in memory. Hence the IAT() method 870 is going to return code objects while this method simply returns names. 871 872 Yields: 873 a tuple of (dll, function_name) 874 """ 875 import_directory = self.nt_header.OptionalHeader.DataDirectory[ 876 'IMAGE_DIRECTORY_ENTRY_IMPORT'].dereference() 877 878 for directory in import_directory: 879 dll = directory.Name.dereference() 880 for thunk in directory.OriginalFirstThunk.dereference(): 881 function_name = thunk.AddressOfData.Name 882 883 yield dll, function_name, thunk.AddressOfData.Hint
884
885 - def IAT(self):
886 """A generator over the IAT. 887 888 Note that this iterates over the FirstThunk imports. In memory, these 889 contain the IAT which has been resolved by the loader. 890 891 Yields: 892 a tuple of (dll, function_name) 893 """ 894 import_directory = self.nt_header.OptionalHeader.DataDirectory[ 895 'IMAGE_DIRECTORY_ENTRY_IMPORT'].dereference() 896 897 for directory in import_directory: 898 dll = directory.Name.dereference() 899 for thunk in directory.FirstThunk.dereference(): 900 function = thunk.u1.Function 901 902 yield dll, function, thunk.u1.Ordinal
903
904 - def ExportDirectory(self):
905 """A generator over the export directory.""" 906 export_directory = self.nt_header.OptionalHeader.DataDirectory[ 907 'IMAGE_DIRECTORY_ENTRY_EXPORT'].dereference() 908 909 dll = export_directory.Name.dereference() 910 function_table = export_directory.AddressOfFunctions.dereference() 911 name_table = export_directory.AddressOfNames.dereference() 912 ordinal_table = export_directory.AddressOfNameOrdinals.dereference() 913 914 seen_ordinals = set() 915 916 # First do the names. 917 for i, name in enumerate(name_table): 918 ordinal = int(ordinal_table[i]) 919 seen_ordinals.add(ordinal) 920 func = function_table[ordinal] 921 func.obj_name = "%s:%s" % (dll, name.dereference()) 922 923 yield (dll, func, name.dereference(), ordinal) 924 925 # Now the functions without names 926 for i, func in enumerate(function_table): 927 ordinal = export_directory.Base + i 928 if ordinal in seen_ordinals: 929 continue 930 931 yield (dll, function_table[ordinal], 932 obj.NoneObject("Name not accessible"), ordinal)
933
934 - def GetProcAddress(self, name):
935 """Scan the export table for a function of the given name. 936 937 Similar to the GetProcAddress function. 938 """ 939 for _, function_pointer, func_name, _ in self.ExportDirectory(): 940 if func_name == name: 941 return function_pointer.dereference()
942
943 - def VersionInformation(self):
944 """A generator of key, value pairs.""" 945 resource_directory = self.nt_header.OptionalHeader.DataDirectory[ 946 'IMAGE_DIRECTORY_ENTRY_RESOURCE'].dereference() 947 948 # Find all the versions and their strings 949 for data in resource_directory.Open("RT_VERSION").Traverse(): 950 version_info = data.OffsetToData.dereference_as( 951 "VS_VERSIONINFO") 952 953 for string in version_info.Strings(): 954 yield unicode(string.Key), unicode(string.Value)
955
956 - def VersionInformationDict(self):
957 return dict(self.VersionInformation())
958
959 - def Sections(self):
960 for section in self.nt_header.Sections: 961 yield (section.execution_flags, section.Name, section.VirtualAddress, 962 section.SizeOfRawData)
963
964 965 -class PEProfile(basic.BasicClasses):
966 """A profile for PE files. 967 968 This profile is available from the repository under the name "pe". 969 """ 970 971 @classmethod
972 - def Initialize(cls, profile):
973 super(PEProfile, cls).Initialize(profile) 974 if not profile.has_class("unsigned int"): 975 basic.ProfileLLP64.Initialize(profile) 976 977 profile.add_classes({ 978 '_IMAGE_DOS_HEADER': _IMAGE_DOS_HEADER, 979 '_IMAGE_NT_HEADERS': _IMAGE_NT_HEADERS, 980 '_IMAGE_SECTION_HEADER': _IMAGE_SECTION_HEADER, 981 '_LDR_DATA_TABLE_ENTRY': _LDR_DATA_TABLE_ENTRY, 982 '_IMAGE_DATA_DIRECTORY': _IMAGE_DATA_DIRECTORY, 983 "SentinelArray": SentinelArray, 984 "ThunkArray": ThunkArray, 985 "RVAPointer": RVAPointer, 986 "ResourcePointer": ResourcePointer, 987 "_IMAGE_RESOURCE_DIRECTORY": _IMAGE_RESOURCE_DIRECTORY, 988 "_IMAGE_RESOURCE_DIRECTORY_ENTRY": _IMAGE_RESOURCE_DIRECTORY_ENTRY, 989 "VS_VERSIONINFO": VS_VERSIONINFO, 990 }) 991 profile.add_overlay(pe_overlays)
992
993 994 -class PEFileAddressSpace(addrspace.RunBasedAddressSpace):
995 """An address space which applies to PE files. 996 997 This basically remaps sections in the PE file to the virtual address space. 998 See http://code.google.com/p/corkami/downloads/detail?name=pe-20110117.pdf 999 1000 The PE file is divided into sections, each section is mapped into memory at 1001 a different place: 1002 1003 File on Disk Memory Image 1004 0-> ------------ image base-> ------------ 1005 Header Header 1006 ------------ ------------ 1007 Section 1 1008 ------------ ------------ 1009 Section 2 Section 1 1010 ------------ ------------ 1011 1012 ------------ 1013 Section 2 1014 ------------ 1015 1016 This address space expands the file from disk into the memory image view as 1017 shown. Since all internal pe RVA references are within the virtual space, 1018 this helps resolution. 1019 """
1020 - def __init__(self, **kwargs):
1021 """We layer on top of the file address space.""" 1022 super(PEFileAddressSpace, self).__init__(**kwargs) 1023 1024 self.as_assert(self.base is not None, "Must layer on another AS.") 1025 self.as_assert(self.base.read(0, 2) == "MZ", 1026 "File does not have a valid signature for a PE file.") 1027 1028 self.profile = self.session.LoadProfile("pe") 1029 1030 nt_header = self.profile._IMAGE_DOS_HEADER(vm=self.base).NTHeader 1031 self.image_base = obj.Pointer.integer_to_address( 1032 nt_header.OptionalHeader.ImageBase) 1033 1034 # The first run maps the file header over the base address 1035 self.add_run(self.image_base, 0, nt_header.OptionalHeader.SizeOfHeaders) 1036 1037 for section in nt_header.Sections: 1038 length = section.SizeOfRawData.v() 1039 if length > 0: 1040 virtual_address = section.VirtualAddress.v() + self.image_base 1041 file_offset = section.PointerToRawData.v() 1042 self.add_run(virtual_address, file_offset, length) 1043 1044 # The real nt header is based at the virtual address of the image. 1045 self.nt_header = self.profile._IMAGE_DOS_HEADER( 1046 vm=self, offset=self.image_base, 1047 context=dict(image_base=self.image_base)).NTHeader
1048
1049 - def __str__(self):
1050 return "<PEFileAddressSpace @ %#x >" % self.image_base
1051
1052 1053 -class Demangler(object):
1054 """A utility class to demangle VC++ names. 1055 1056 This is not a complete or accurate demangler, it simply extract the name and 1057 strips out args etc. 1058 1059 Ref: 1060 http://www.kegel.com/mangle.html 1061 """ 1062 STRING_MANGLE_MAP = { 1063 "^0": ",", 1064 "^2": r"\\", 1065 "^4": ".", 1066 "^3": ":", 1067 "^5": "_", # Really space. 1068 "^6": ".", # Really \n. 1069 r"\$AA": "", 1070 r"\$AN": "", # Really \r. 1071 r"\$CF": "%", 1072 r"\$EA": "@", 1073 r"\$CD": "#", 1074 r"\$CG": "&", 1075 r"\$HO": "~", 1076 r"\$CI": "(", 1077 r"\$CJ": ")", 1078 r"\$DM1": "</", 1079 r"\$DMO": ">", 1080 r"\$DN": "=", 1081 r"\$CK": "*", 1082 r"\$CB": "!", 1083 1084 } 1085
1086 - def __init__(self, metadata):
1087 self._metadata = metadata
1088
1089 - def _UnpackMangledString(self, string):
1090 string = string.split("@")[3] 1091 1092 result = [] 1093 for cap in string.split("?"): 1094 for k, v in self.STRING_MANGLE_MAP.items(): 1095 cap = re.sub(k, v, cap) 1096 1097 result.append(cap) 1098 1099 return "str:" + "".join(result).strip()
1100 1101 SIMPLE_X86_CALL = re.compile(r"[_@]([A-Za-z0-9_]+)@(\d{1,3})$") 1102 FUNCTION_NAME_RE = re.compile(r"\?([A-Za-z0-9_]+)@")
1103 - def DemangleName(self, mangled_name):
1104 """Returns the de-mangled name. 1105 1106 At this stage we don't really do proper demangling since we usually dont 1107 care about the prototype, nor c++ exports. In the future we should 1108 though. 1109 """ 1110 m = self.SIMPLE_X86_CALL.match(mangled_name) 1111 if m: 1112 # If we see x86 name mangling (_cdecl, __stdcall) with stack sizes 1113 # of 4 bytes, this is definitely a 32 bit pdb. Sometimes we dont 1114 # know the architecture of the pdb file for example if we do not 1115 # have the original binary, but only the GUID as extracted by 1116 # version_scan. 1117 if m.group(2) in ["4", "12"]: 1118 self._metadata.setdefault("arch", "I386") 1119 1120 return m.group(1) 1121 1122 m = self.FUNCTION_NAME_RE.match(mangled_name) 1123 if m: 1124 return m.group(1) 1125 1126 # Strip the first _ from the name. I386 mangled constants have a 1127 # leading _ but their AMD64 counterparts do not. 1128 if mangled_name and mangled_name[0] in "_.": 1129 mangled_name = mangled_name[1:] 1130 1131 elif mangled_name.startswith("??_C@"): 1132 return self._UnpackMangledString(mangled_name) 1133 1134 return mangled_name
1135
1136 1137 -class BasicPEProfile(basic.RelativeOffsetMixin, basic.BasicClasses):
1138 """A basic profile for a pe image. 1139 1140 This profile deals with Microsoft Oddities like name mangling, and 1141 correcting global offsets to the base image address. 1142 """ 1143 1144 image_base = 0 1145 1146 METADATA = dict(os="windows") 1147
1148 - def GetImageBase(self):
1149 return self.image_base
1150
1151 - def add_constants(self, constants=None, **opts):
1152 """Add the demangled constants. 1153 1154 This allows us to handle 32 bit vs 64 bit constant names easily since 1155 the mangling rules are different. 1156 """ 1157 demangler = Demangler(self._metadata) 1158 result = {} 1159 1160 for k, v in constants.iteritems(): 1161 result[demangler.DemangleName(k)] = v 1162 1163 super(BasicPEProfile, self).add_constants( 1164 constants=result, **opts)
1165
1166 - def copy(self):
1167 result = super(BasicPEProfile, self).copy() 1168 result.image_base = self.image_base 1169 return result
1170 1171 @classmethod
1172 - def Initialize(cls, profile):
1173 super(BasicPEProfile, cls).Initialize(profile) 1174 1175 # If the architecture is not added yet default to 64 bit. NOTE that with 1176 # PE Profiles we normally guess the architecture based on the name 1177 # mangling conventions. 1178 if profile.metadata("arch") is None: 1179 profile.set_metadata("arch", "AMD64") 1180 1181 # Add the basic compiler model for windows. 1182 if profile.metadata("arch") == "AMD64": 1183 basic.ProfileLLP64.Initialize(profile) 1184 1185 elif profile.metadata("arch") == "I386": 1186 basic.Profile32Bits.Initialize(profile)
1187