Package rekall :: Package plugins :: Package windows :: Package gui :: Module win32k_core
[frames] | no frames]

Source Code for Module rekall.plugins.windows.gui.win32k_core

  1  # Rekall Memory Forensics 
  2  # Copyright (C) 2007,2008 Volatile Systems 
  3  # Copyright (C) 2010,2011,2012 Michael Hale Ligh <michael.ligh@mnin.org> 
  4  # Copyright 2013 Google Inc. All Rights Reserved. 
  5  # 
  6  # This program is free software; you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation; either version 2 of the License, or (at 
  9  # your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, but 
 12  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 14  # General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program; if not, write to the Free Software 
 18  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 19  # 
 20   
 21  # pylint: disable=protected-access 
 22  from rekall import kb 
 23  from rekall import obj 
 24  from rekall.plugins.overlays.windows import pe_vtypes 
 25  from rekall.plugins.windows.gui import constants 
 26  from rekall.plugins.windows.gui.vtypes import xp 
 27  from rekall_lib import utils 
 28   
 29   
 30  win32k_overlay = { 
 31      '_RTL_ATOM_TABLE_ENTRY': [None, { 
 32          'Name': [None, ['UnicodeString', dict( 
 33              encoding='utf16', 
 34              length=lambda x: x.NameLength * 2)]], 
 35   
 36          'ReferenceCount': lambda x: (x.m("ReferenceCount") or 
 37                                       x.m("Reference.ReferenceCount")), 
 38   
 39          'Pinned': lambda x: x.m("Flags") == 1 or x.m("Reference.Flags") == 1, 
 40          }], 
 41   
 42      'tagWINDOWSTATION': [None, { 
 43          'pGlobalAtomTable': [None, ['Pointer', dict( 
 44              target="_RTL_ATOM_TABLE" 
 45          )]], 
 46   
 47          'pClipBase': [None, ["Pointer", dict( 
 48              target="Array", 
 49              target_args=dict( 
 50                  target='tagCLIP', 
 51                  count=lambda x: x.cNumClipFormats 
 52              ) 
 53          )]], 
 54      }], 
 55   
 56      'tagDESKTOP': [None, { 
 57          'pheapDesktop': [None, ['Pointer', dict( 
 58              target="_HEAP" 
 59          )]], 
 60      }], 
 61   
 62      'tagSHAREDINFO': [None, { 
 63          'aheList': [None, ['Pointer', dict( 
 64              target='Array', 
 65              target_args=dict( 
 66                  target="_HANDLEENTRY", 
 67                  count=lambda x: x.psi.cHandleEntries, 
 68                  ) 
 69              )]], 
 70          }], 
 71   
 72      '_HEAD': [None, { 
 73          'h': [None, ['unsigned int']], 
 74      }], 
 75   
 76      "tagTHREADINFO": [None, { 
 77          "pEThread": [None, ["Pointer", dict( 
 78              target="_ETHREAD")]], 
 79          }], 
 80   
 81      "tagHOOK": [None, { 
 82          "flags": [None, ["Flags", dict( 
 83              bitmap=utils.MaskMapFromDefines( 
 84                  """ 
 85  // 9/18/2011 
 86  // http://forum.sysinternals.com/enumerate-windows-hooks_topic23877.html#122641 
 87  #define HF_GLOBAL   0x0001 
 88  #define HF_ANSI   0x0002 
 89  #define HF_NEEDHC_SKIP   0x0004 
 90  #define HF_HUNG   0x0008 
 91  #define HF_HOOKFAULTED   0x0010 
 92  #define HF_NOPLAYBACKDELAY   0x0020 
 93  #define HF_WX86KNOWINDOWLL   0x0040 
 94  #define HF_DESTROYED   0x0080 
 95  // mask for valid flags 
 96  #define HF_VALID   0x00FF 
 97  """)) 
 98                       ]], 
 99      }], 
100   
101      "_HANDLEENTRY": [None, { 
102          "pOwner": [None, ["Pointer", dict( 
103              target="tagTHREADINFO")]], 
104   
105          "bFlags": [None, ["Flags", dict( 
106              target="byte", 
107              bitmap=utils.MaskMapFromDefines(""" 
108  // 8/17/2011 
109  // http://www.reactos.org/wiki/Techwiki:Win32k/HANDLEENTRY 
110  // HANDLEENTRY.bFlags 
111  #define HANDLEF_DESTROY        0x01 
112  #define HANDLEF_INDESTROY      0x02 
113  #define HANDLEF_INWAITFORDEATH 0x04 
114  #define HANDLEF_FINALDESTROY   0x08 
115  #define HANDLEF_MARKED_OK      0x10 
116  #define HANDLEF_GRANTED        0x20 
117  // mask for valid flags 
118  #define HANDLEF_VALID   0x3F 
119  """), 
120              )]], 
121   
122          'bType': [None, ['Enumeration', dict( 
123              target='unsigned char', 
124              choices=constants.HANDLE_TYPE_ENUM, 
125              )]], 
126          }], 
127   
128      '_THRDESKHEAD': [None, { 
129          "h": [None, ["unsigned int"]], 
130          }], 
131  } 
132   
133  # Reference: 
134  # http://reactos.org/wiki/Techwiki:Win32k/structures 
135   
136  win32k_undocumented_AMD64 = { 
137      # TODO: This is a hack! I do not really understand why this struct is 
138      # sometimes very different. I have an image with the Buckets field at offset 
139      # 0x220. This needs to be implemented using generate_types. 
140   
141      # win32k defines NTOS_MODE_USER which makes this struct different from the 
142      # nt kernel one. 
143      # http://doxygen.reactos.org/d5/df7/ndk_2rtltypes_8h_source.html 
144      '_RTL_ATOM_TABLE': [None, { 
145          # Technically the number of buckets is specified by this field, but this 
146          # field varies a lot between operating systems: 
147   
148          # - Win7: 0x18 
149          # - Win8.1: 0x1c (like the kernel _RTL_ATOM_TABLE) 
150   
151          # It is usually around 25 - if we overestimate we just work through more 
152          # buckets - it does not matter. If we underestimate however, we will 
153          # miss some atoms. We just set it to a large enough value here. 
154   
155          'NumberOfBuckets': lambda x: 0x35,  # Usually this is 0x25. 
156   
157          # 'NumberOfBuckets': [0x18, ["unsigned long", {}]], 
158          'Buckets': [0x20, ['Array', dict( 
159              count=lambda x: x.NumberOfBuckets, 
160              max_count=100, 
161              target="Pointer", 
162              target_args=dict( 
163                  target='_RTL_ATOM_TABLE_ENTRY') 
164              )]], 
165          }], 
166   
167      'tagEVENTHOOK' : [0x60, { 
168          'phkNext' : [0x18, ['Pointer', dict( 
169              target='tagEVENTHOOK' 
170              )]], 
171          'eventMin' : [0x20, ['Enumeration', dict( 
172              target='unsigned long', 
173              choices=constants.EVENT_ID_ENUM)]], 
174   
175          'eventMax' : [0x24, ['Enumeration', dict( 
176              target='unsigned long', 
177              choices=constants.EVENT_ID_ENUM)]], 
178   
179          'dwFlags' : [0x28, ['unsigned long']], 
180          'idProcess' : [0x2C, ['unsigned long']], 
181          'idThread' : [0x30, ['unsigned long']], 
182          'offPfn' : [0x40, ['Pointer', dict(target="Void")]], 
183          'ihmod' : [0x48, ['long']], 
184          }], 
185   
186      'tagHANDLETYPEINFO' : [16, { 
187          'fnDestroy' : [0, ['Pointer', dict( 
188              target="Function" 
189          )]], 
190          'dwAllocTag' : [8, ['String', dict(length=4)]], 
191          'bObjectCreateFlags' : [12, ['Flags', dict( 
192              target='unsigned char', 
193              bitmap={ 
194                  'OCF_THREADOWNED': 0, 
195                  'OCF_PROCESSOWNED': 1, 
196                  'OCF_MARKPROCESS': 2, 
197                  'OCF_USEPOOLQUOTA': 3, 
198                  'OCF_DESKTOPHEAP': 4, 
199                  'OCF_USEPOOLIFNODESKTOP': 5, 
200                  'OCF_SHAREDHEAP': 6, 
201                  'OCF_VARIABLESIZE': 7} 
202              )]], 
203          }], 
204   
205      'tagCLIPDATA' : [None, { 
206          'cbData' : [0x08, ['unsigned int']], 
207          'abData' : [0x0C, ['Array', dict( 
208              count=lambda x: x.cbData, 
209              target='unsigned char')]], 
210      }], 
211  } 
212   
213  win32k_undocumented_I386 = { 
214      '_RTL_ATOM_TABLE': [None, { 
215          'NumberOfBuckets': [0xC, ['unsigned long']], 
216          'Buckets': [0x10, ['Array', dict( 
217              count=lambda x: x.NumberOfBuckets, 
218              max_count=100, 
219              target="Pointer", 
220              target_args=dict( 
221                  target='_RTL_ATOM_TABLE_ENTRY') 
222              )]], 
223          }], 
224   
225      'tagEVENTHOOK' : [0x30, { 
226          'phkNext' : [0xC, ['Pointer', dict( 
227              target='tagEVENTHOOK' 
228              )]], 
229          'eventMin' : [0x10, ['Enumeration', dict( 
230              target='unsigned long', 
231              choices=constants.EVENT_ID_ENUM)]], 
232   
233          'eventMax' : [0x14, ['Enumeration', dict( 
234              target='unsigned long', 
235              choices=constants.EVENT_ID_ENUM)]], 
236   
237          'dwFlags' : [0x18, ['unsigned long']], 
238          'idProcess' : [0x1C, ['unsigned long']], 
239          'idThread' : [0x20, ['unsigned long']], 
240          'offPfn' : [0x24, ['Pointer', dict(target="Void")]], 
241          'ihmod' : [0x28, ['long']], 
242          }], 
243   
244      'tagHANDLETYPEINFO' : [12, { 
245          'fnDestroy' : [0, ['Pointer', dict( 
246              target="Function" 
247          )]], 
248          'dwAllocTag' : [4, ['String', dict(length=4)]], 
249          'bObjectCreateFlags' : [8, ['Flags', dict( 
250              target='unsigned char', 
251              bitmap={ 
252                  'OCF_THREADOWNED': 0, 
253                  'OCF_PROCESSOWNED': 1, 
254                  'OCF_MARKPROCESS': 2, 
255                  'OCF_USEPOOLQUOTA': 3, 
256                  'OCF_DESKTOPHEAP': 4, 
257                  'OCF_USEPOOLIFNODESKTOP': 5, 
258                  'OCF_SHAREDHEAP': 6, 
259                  'OCF_VARIABLESIZE': 7} 
260          )]], 
261      }], 
262      'tagCLIPDATA' : [None, { 
263          'cbData' : [0x08, ['unsigned int']], 
264          'abData' : [0x0C, ['Array', dict( 
265              count=lambda x: x.cbData, 
266              target='unsigned char')]], 
267      }], 
268  } 
269 270 271 -class _MM_SESSION_SPACE(obj.Struct):
272 """A class for session spaces""" 273
274 - def processes(self):
275 """Generator for processes in this session. 276 277 A process is always associated with exactly 278 one session. 279 """ 280 for p in self.ProcessList.list_of_type( 281 "_EPROCESS", "SessionProcessLinks"): 282 yield p
283 284 @utils.safe_property
285 - def Win32KBase(self):
286 """Get the base address of the win32k.sys as mapped 287 into this session's memory. 288 289 Since win32k.sys is always the first image to be 290 mapped, we can just grab the first list entry.""" 291 292 ## An exception may be generated when a process from a terminated 293 ## session still exists in the active process list. 294 try: 295 return list(self.images())[0].Address 296 except IndexError: 297 return obj.NoneObject("No images mapped in this session")
298
299 - def images(self):
300 """Generator for images (modules) loaded into 301 this session's space""" 302 for i in self.ImageList.list_of_type("_IMAGE_ENTRY_IN_SESSION", "Link"): 303 yield i
304
305 - def _section_chunks(self, sec_name):
306 """Get the win32k.sys section as an array of 307 32-bit unsigned longs. 308 309 @param sec_name: name of the PE section in win32k.sys 310 to search for. 311 312 @returns all chunks on a 4-byte boundary. 313 """ 314 ## In the rare case when win32k.sys PE header is paged or corrupted 315 ## thus preventing us from parsing the sections, use the fallback 316 ## mechanism of just reading 5 MB (max size of win32k.sys) from the 317 ## base of the kernel module. 318 section_base = self.Win32KBase 319 section_size = 0x500000 320 321 dos_header = self.obj_profile._IMAGE_DOS_HEADER( 322 offset=self.Win32KBase, vm=self.obj_vm) 323 324 for section in dos_header.NTHeader.Sections: 325 if section.Name == sec_name: 326 section_base = section.VirtualAddress 327 section_size = section.Misc.VirtualSize / 4 328 break 329 330 331 return self.obj_profile.Array(targetType="unsigned long", 332 offset=section_base, 333 count=section_size, vm=self.obj_vm)
334
335 - def find_gahti(self):
336 """Find this session's gahti. 337 338 This can potentially be much faster by searching for 339 '\0' * sizeof(tagHANDLETYPEINFO) instead 340 of moving on a dword aligned boundary through 341 the section. 342 """ 343 344 for chunk in self._section_chunks(".rdata"): 345 if not chunk.is_valid(): 346 continue 347 348 gahti = obj.Object("gahti", offset=chunk.obj_offset, 349 vm=self.obj_vm) 350 351 ## The sanity check here is based on the fact that the first entry 352 ## in the gahti is always for TYPE_FREE. The fnDestroy pointer will 353 ## be NULL, the alloc tag will be an empty string, and the creation 354 ## flags will be zero. We also then check the alloc tag of the first 355 ## USER handle type which should be Uswd (TYPE_WINDOW). 356 if (gahti.types[0].fnDestroy == 0 and 357 str(gahti.types[0].dwAllocTag) == '' and 358 gahti.types[0].bObjectCreateFlags == 0 and 359 str(gahti.types[1].dwAllocTag) == "Uswd"): 360 return gahti 361 362 return obj.NoneObject("Cannot find win32k!_gahti")
363
364 365 -class _HANDLEENTRY(obj.Struct):
366 """A for USER handle entries""" 367
368 - def reference_object(self):
369 """Reference the object this handle represents. 370 371 If the object's type is not in our map, we don't know 372 what type of object to instantiate so its filled with 373 obj.NoneObject() instead. 374 """ 375 376 object_map = dict( 377 TYPE_WINDOW="tagWND", 378 TYPE_HOOK="tagHOOK", 379 TYPE_CLIPDATA="tagCLIPDATA", 380 TYPE_WINEVENTHOOK="tagEVENTHOOK", 381 TYPE_TIMER="tagTIMER", 382 ) 383 384 object_type = object_map.get(str(self.bType)) 385 if object_type is None: 386 return obj.NoneObject("Cannot reference object type") 387 388 return self.obj_profile.Object( 389 object_type, offset=self.phead, vm=self.obj_vm)
390 391 @utils.safe_property
392 - def Free(self):
393 """Check if the handle has been freed""" 394 return str(self.bType) == "TYPE_FREE"
395 396 @utils.safe_property
397 - def ThreadOwned(self):
398 """Handles of these types are always thread owned""" 399 return str(self.bType) in [ 400 'TYPE_WINDOW', 'TYPE_SETWINDOWPOS', 'TYPE_HOOK', 401 'TYPE_DDEACCESS', 'TYPE_DDECONV', 'TYPE_DDEXACT', 402 'TYPE_WINEVENTHOOK', 'TYPE_INPUTCONTEXT', 'TYPE_HIDDATA', 403 'TYPE_TOUCH', 'TYPE_GESTURE']
404 @utils.safe_property
405 - def ProcessOwned(self):
406 """Handles of these types are always process owned""" 407 return str(self.bType) in [ 408 'TYPE_MENU', 'TYPE_CURSOR', 'TYPE_TIMER', 409 'TYPE_CALLPROC', 'TYPE_ACCELTABLE']
410 411 @utils.safe_property
412 - def Thread(self):
413 """Return the ETHREAD if its thread owned""" 414 if self.ThreadOwned: 415 return self.pOwner.\ 416 dereference_as("tagTHREADINFO").\ 417 pEThread.dereference() 418 return obj.NoneObject("Cannot find thread")
419 420 @utils.safe_property
421 - def Process(self):
422 """Return the _EPROCESS if its process or thread owned""" 423 if self.ProcessOwned: 424 return (self.pOwner. 425 dereference_as("tagPROCESSINFO"). 426 Process.dereference()) 427 428 elif self.ThreadOwned: 429 return ( 430 self.m("pOwner.pEThread.ThreadsProcess").deref() or 431 self.pOwner.ppi.Process.dereference() or 432 self.pOwner.pEThread.Tcb.Process.dereference_as( 433 "_EPROCESS")) 434 435 return obj.NoneObject("Cannot find process")
436
437 438 -class tagWINDOWSTATION(obj.Struct):
439 """A class for Windowstation objects""" 440
441 - def is_valid(self):
442 return (super(tagWINDOWSTATION, self).is_valid() and 443 self.dwSessionId < 0xFF)
444 445 @utils.safe_property
446 - def LastRegisteredViewer(self):
447 """The EPROCESS of the last registered clipboard viewer""" 448 return self.m("spwndClipViewer.head.pti.ppi.Process")
449 450 @utils.safe_property
451 - def Interactive(self):
452 """Check if a window station is interactive""" 453 return not self.dwWSF_Flags & 4 # WSF_NOIO
454 455 @utils.safe_property
456 - def Name(self):
457 """Get the window station name. 458 459 Since window stations are securable objects, and are managed by the same 460 object manager as processes, threads, etc, there is an object header 461 which stores the name. 462 """ 463 object_hdr = self.obj_session.profile._OBJECT_HEADER( 464 vm=self.obj_vm, 465 offset=self.obj_offset - self.obj_session.profile.get_obj_offset( 466 '_OBJECT_HEADER', 'Body') 467 ) 468 469 return object_hdr.NameInfo.Name
470
471 - def desktops(self):
472 """A generator that yields the window station's desktops""" 473 return self.rpdeskList.walk_list("rpdeskNext")
474
475 476 -class tagDESKTOP(tagWINDOWSTATION):
477 """A class for Desktop objects""" 478
479 - def is_valid(self):
480 return obj.Struct.is_valid(self) and self.dwSessionId < 0xFF
481 482 @utils.safe_property
483 - def WindowStation(self):
484 """Returns this desktop's parent window station""" 485 return self.rpwinstaParent.dereference()
486 487 @utils.safe_property
488 - def DeskInfo(self):
489 """Returns the desktop info object""" 490 return self.pDeskInfo.dereference()
491
492 - def threads(self):
493 """Generator for _EPROCESS objects attached to this desktop""" 494 for ti in self.PtiList.list_of_type("tagTHREADINFO", "PtiLink"): 495 yield ti
496
497 - def hooks(self):
498 """Generator for tagHOOK info.""" 499 fsHooks = self.DeskInfo.fsHooks 500 501 for pos, (name, value) in enumerate(constants.MESSAGE_TYPES): 502 # Is the bit for this message type WH_* value set ? 503 if fsHooks & (1 << value + 1): 504 for hook in self.DeskInfo.aphkStart[pos].walk_list("phkNext"): 505 yield name, hook
506
507 - def windows(self, win, filter=lambda x: True, level=0):
508 """Traverses windows in their Z order, bottom to top. 509 510 @param win: an HWND to start. Usually this is the desktop 511 window currently in focus. 512 513 @param filter: a callable (usually lambda) to use for filtering 514 the results. See below for examples: 515 516 # only print subclassed windows 517 filter = lambda x : x.lpfnWndProc == x.pcls.lpfnWndProc 518 519 # only print processes named csrss.exe 520 filter = lambda x : str(x.head.pti.ppi.Process.ImageFileName).lower() \ 521 == "csrss.exe" if x.head.pti.ppi else False 522 523 # only print processes by pid 524 filter = lambda x : x.head.pti.pEThread.Cid.UniqueThread == 0x1020 525 526 # only print visible windows 527 filter = lambda x : 'WS_VISIBLE' not in x.get_flags() 528 """ 529 seen = set() 530 wins = [] 531 cur = win 532 while cur.is_valid() and cur.v() != 0: 533 if cur in seen: 534 break 535 seen.add(cur) 536 wins.append(cur) 537 cur = cur.spwndNext.dereference() 538 while wins: 539 cur = wins.pop() 540 if not filter(cur): 541 continue 542 543 yield cur, level 544 545 if cur.spwndChild.is_valid() and cur.spwndChild.v() != 0: 546 for info in self.windows( 547 cur.spwndChild, filter=filter, level=level+1): 548 yield info
549
550 - def heaps(self):
551 """Generator for the desktop heaps""" 552 for segment in self.pheapDesktop.Heap.segments(): 553 for entry in segment.heap_entries(): 554 yield entry
555
556 - def traverse(self, vm=None):
557 """Generator for next desktops in the list""" 558 559 # Include this object in the results 560 yield self 561 # Now walk the singly-linked list 562 nextdesk = self.rpdeskNext.dereference(vm=vm) 563 while nextdesk.is_valid() and nextdesk.v() != 0: 564 yield nextdesk 565 nextdesk = nextdesk.rpdeskNext.dereference(vm=vm)
566
567 -class tagWND(obj.Struct):
568 """A class for window structures""" 569 570 @utils.safe_property
571 - def IsClipListener(self):
572 """Check if this window listens to clipboard changes""" 573 return self.bClipboardListener.v()
574 575 @utils.safe_property
576 - def ClassAtom(self):
577 """The class atom for this window""" 578 return self.pcls.atomClassName
579 580 @utils.safe_property
581 - def SuperClassAtom(self):
582 """The window's super class""" 583 return self.pcls.atomNVClassName
584 585 @utils.safe_property
586 - def Process(self):
587 """The EPROCESS that owns the window""" 588 return self.head.pti.ppi.Process.dereference()
589 590 @utils.safe_property
591 - def Thread(self):
592 """The ETHREAD that owns the window""" 593 return self.head.pti.pEThread.dereference()
594 595 @utils.safe_property
596 - def Visible(self):
597 """Is this window visible on the desktop""" 598 return 'WS_VISIBLE' in self.style
599
600 - def _get_flags(self, member, flags):
601 602 if flags.has_key(member): 603 return flags[member] 604 605 return ','.join([n for (n, v) in flags.items() if member & v == v])
606 607 @utils.safe_property
608 - def style(self):
609 """The basic style flags as a string""" 610 return self._get_flags(self.m('style').v(), constants.WINDOW_STYLES)
611 612 @utils.safe_property
613 - def ExStyle(self):
614 """The extended style flags as a string""" 615 return self._get_flags( 616 self.m('ExStyle').v(), constants.WINDOW_STYLES_EX)
617
618 -class tagRECT(obj.Struct):
619 """A class for window rects""" 620
621 - def get_tup(self):
622 """Return a tuple of the rect's coordinates""" 623 return (self.left, self.top, self.right, self.bottom)
624
625 -class tagCLIPDATA(obj.Struct):
626 """A class for clipboard objects""" 627
628 - def as_string(self, fmt):
629 """Format the clipboard data as a string. 630 631 @param fmt: the clipboard format. 632 633 Note: we cannot simply override __str__ for this 634 purpose, because the clipboard format is not a member 635 of (or in a parent-child relationship with) the 636 tagCLIPDATA structure, so we must pass it in as 637 an argument. 638 """ 639 640 if fmt == "CF_UNICODETEXT": 641 encoding = "utf16" 642 else: 643 encoding = "utf8" 644 645 return obj.String( 646 offset=self.abData.obj_offset, 647 vm=self.obj_vm, encoding=encoding, 648 length=self.cbData)
649
650 - def as_hex(self):
651 """Format the clipboard contents as a hexdump""" 652 data = ''.join([chr(c) for c in self.abData]) 653 return "".join(["{0:#x} {1:<48} {2}\n".format( 654 self.abData.obj_offset + o, h, ''.join(c)) 655 for o, h, c in utils.Hexdump(data)])
656
657 658 -class tagTHREADINFO(obj.Struct):
659 """A class for thread information objects""" 660
661 - def hooks(self):
662 """Generator for tagHOOK info.""" 663 fsHooks = self.fsHooks 664 665 for pos, (name, value) in enumerate(constants.MESSAGE_TYPES): 666 # Is the bit for this message type WH_* value set ? 667 if fsHooks & (1 << value + 1): 668 for hook in self.aphkStart[pos].walk_list("phkNext"): 669 yield name, hook
670
671 672 -class tagEVENTHOOK(obj.Struct):
673 """A class for event hooks""" 674 675 @utils.safe_property
676 - def dwFlags(self):
677 """Event hook flags need special handling so we can't use vtypes""" 678 679 # First we shift the value 680 f = self.m('dwFlags') >> 1 681 682 flags = [ 683 name for (val, name) in constants.EVENT_FLAGS.items() if f & val] 684 685 return '|'.join(flags)
686
687 -class _RTL_ATOM_TABLE(obj.Struct):
688 """A class for atom tables""" 689
690 - def __init__(self, **kwargs):
691 """Give ourselves an atom cache for quick lookups""" 692 self.atom_cache = {} 693 super(_RTL_ATOM_TABLE, self).__init__(**kwargs)
694
695 - def is_valid(self):
696 """Check for validity based on the atom table signature 697 and the maximum allowed number of buckets""" 698 return (super(_RTL_ATOM_TABLE, self).is_valid() and 699 self.Signature == 0x6d6f7441 and 700 self.NumberOfBuckets < 0xFFFF)
701
702 - def atoms(self, vm=None):
703 """Carve all atoms out of this atom table""" 704 for bucket in self.Buckets: 705 for entry in bucket.deref(vm=vm).walk_list("HashLink"): 706 if entry.Atom < 0xf000: 707 yield entry
708
709 - def find_atom(self, atom_to_find, use_cache=False):
710 """Find an atom by its ID. 711 712 @param atom_to_find: the atom ID (ushort) to find 713 714 @returns an _RTL_ATOM_TALE_ENTRY object 715 """ 716 atom_to_find = int(atom_to_find) 717 not_found = obj.NoneObject("Atom not found") 718 719 # Use the cached results if they exist 720 if use_cache: 721 if self.atom_cache: 722 return self.atom_cache.get(atom_to_find, not_found) 723 724 # Build the atom cache 725 self.atom_cache = dict( 726 (int(atom.Atom), atom) for atom in self.atoms()) 727 728 return self.atom_cache.get(atom_to_find, not_found) 729 else: 730 # We often instantiate this atom table once - in this case its not 731 # worth caching it. 732 for atom in self.atoms(): 733 if atom.Atom == atom_to_find: 734 return atom 735 736 return not_found
737
738 739 -class _RTL_ATOM_TABLE_ENTRY(obj.Struct):
740 """A class for atom table entries""" 741
742 - def is_string_atom(self):
743 """Returns True if the atom is a string atom 744 based on its atom ID. 745 746 A string atom has ID 0xC000 - 0xFFFF 747 """ 748 return self.Atom >= 0xC000 and self.Atom <= 0xFFFF
749
750 - def is_valid(self):
751 """Perform some sanity checks on the Atom""" 752 if not super(_RTL_ATOM_TABLE_ENTRY, self).is_valid(): 753 return False 754 755 # There is only one flag (and zero) 756 if self.Flags not in (0, 1): 757 return False 758 # There is a maximum name length enforced 759 return self.NameLength <= 255
760
761 762 -class Win32kPluginMixin(object):
763 """A mixin which loads the relevant win32k profile.""" 764 765 @classmethod
766 - def args(cls, parser):
767 super(Win32kPluginMixin, cls).args(parser) 768 parser.add_argument("--win32k_profile", default=None, 769 help="Force this profile to be used for Win32k.")
770
771 - def _is_valid(self, profile):
772 """Returns true if the profile is a valid win32k one.""" 773 return profile.get_constant("grpWinStaList")
774
775 - def __init__(self, win32k_profile=None, **kwargs):
776 super(Win32kPluginMixin, self).__init__(**kwargs) 777 778 # For the address resolver to load this GUID. 779 if win32k_profile: 780 self.session.SetCache("win32k_profile", win32k_profile) 781 782 # Ask the address resolver to load this profile. 783 module = self.session.address_resolver.GetModuleByName("win32k") 784 self.win32k_profile = module.profile 785 786 # If the resolver loads the dummy profile this is not good enough. 787 if not self._is_valid(self.win32k_profile): 788 raise RuntimeError("Unable to load the profile for Win32k.sys")
789
790 791 -class Win32k(pe_vtypes.BasicPEProfile):
792 """A profile for the Win32 GUI system.""" 793 794 guessed_types = False 795 796 @staticmethod
797 - def _LoadExempler(profile):
798 # Prior to Windows 7, Microsoft did not release symbols for win32k 799 # structs. However, we know that these are basically the same across 800 # different versions. Here we just copy them from the windows 7 801 # profiles. 802 arch = profile.metadata("arch") 803 if arch == "AMD64": 804 exempler = ("win32k/GUID/" 805 "99227A2085CE41969CD5A06F7CC20F522") 806 else: 807 exempler = ("win32k/GUID/" 808 "18EB20F5448A47F5B850023FEE0B24D62") 809 810 result = profile.session.LoadProfile(exempler) 811 if result == None: 812 raise RuntimeError("Unable to load exempler profile %s" % exempler) 813 814 return result
815 816 @classmethod
817 - def Initialize(cls, profile):
818 super(Win32k, cls).Initialize(profile) 819 820 # Merge the kernel's symbols for _RTL_ATOM_TABLE etc. 821 profile.merge(profile.session.profile) 822 823 # Some constants - These will probably change in win8 which does not 824 # allow non ascii tags. 825 profile.add_constants(dict(PoolTag_WindowStation="Win\xe4", 826 PoolTag_Atom="AtmT")) 827 828 profile.add_classes({ 829 'tagWINDOWSTATION': tagWINDOWSTATION, 830 'tagDESKTOP': tagDESKTOP, 831 '_RTL_ATOM_TABLE': _RTL_ATOM_TABLE, 832 '_RTL_ATOM_TABLE_ENTRY': _RTL_ATOM_TABLE_ENTRY, 833 'tagTHREADINFO': tagTHREADINFO, 834 'tagWND': tagWND, 835 '_HANDLEENTRY': _HANDLEENTRY, 836 'tagEVENTHOOK': tagEVENTHOOK, 837 'tagRECT': tagRECT, 838 'tagCLIPDATA': tagCLIPDATA, 839 }) 840 841 profile.add_overlay(win32k_overlay) 842 if profile.metadata("arch") == "AMD64": 843 profile.add_overlay(win32k_undocumented_AMD64) 844 else: 845 profile.add_overlay(win32k_undocumented_I386) 846 847 exempler = None 848 required_types = ["tagWINDOWSTATION", "tagDESKTOP", "tagTHREADINFO", 849 "tagWND", "tagDESKTOPINFO", "tagPROCESSINFO", 850 "tagSHAREDINFO", "_HANDLEENTRY", "_HEAD", "tagHOOK", 851 "_THRDESKHEAD", "_WNDMSG", "tagSERVERINFO"] 852 for item in required_types: 853 if not profile.has_type(item): 854 # Mark the profile as guessed - its not as good as the real 855 # thing, but its a starting point where win32k_autodetect can 856 # start with. 857 profile.guessed_types = True 858 if exempler is None: 859 win7_profile = cls._LoadExempler(profile) 860 861 profile.vtypes[item] = win7_profile.vtypes[item] 862 863 # Specific support for xp types. 864 version = profile.metadata('version') 865 arch = profile.metadata("arch") 866 867 if version < 6.0: 868 if arch == "I386": 869 profile.add_types(xp.vtypes_xp_32) 870 else: 871 profile.add_types(xp.vtypes_xp_64) 872 873 if profile.guessed_types: 874 profile.session.logging.debug( 875 "Win32k profile is incomplete - attempting autodetection.") 876 877 profile.add_overlay( 878 profile.session.plugins.win32k_autodetect().GetWin32kOverlay( 879 profile)) 880 881 # The below code needs refactoring. 882 return profile 883 884 # Add autogenerated vtypes for the different versions. 885 if version.startswith("6.1"): # Windows 7 886 from rekall.plugins.windows.gui.vtypes import win7 887 888 profile = win7.Win32GUIWin7.modify(profile) 889 else: 890 profile = xp.XP2003x86BaseVTypes.modify(profile) 891 892 # The type we want to use is not the same as the one already defined 893 # see http://code.google.com/p/volatility/issues/detail?id=131 894 profile.add_overlay({ 895 'gahti': [None, { 896 'types': [0, ['Array', dict( 897 count=num_handles, 898 target='tagHANDLETYPEINFO')]], 899 }], 900 901 'tagWIN32HEAP': [None, { 902 'Heap': [0, ['_HEAP']], 903 }], 904 'tagCLIPDATA' : [None, { 905 'cbData' : [0x08, ['unsigned int']], 906 'abData' : [0x0C, ['Array', dict( 907 count=lambda x: x.cbData, 908 target='unsigned char')]], 909 }], 910 }) 911 912 # The 64 bit versions of these structs just have their members in 913 # different offsets. 914 if architecture == "AMD64": 915 profile.add_overlay({ 916 '_RTL_ATOM_TABLE': [None, { 917 'NumberOfBuckets': [0x18, ['unsigned long']], 918 'Buckets': [0x20, ['Array', dict( 919 count=lambda x: x.NumBuckets, 920 target="Pointer", 921 target_args=dict( 922 target='_RTL_ATOM_TABLE_ENTRY') 923 )]], 924 }], 925 926 'tagCLIPDATA' : [None, { 927 'cbData' : [0x10, None], 928 'abData' : [0x14, None], 929 }], 930 'tagEVENTHOOK' : [0x60, { 931 'phkNext' : [0x18, None], 932 'eventMin' : [0x20, None], 933 'eventMax' : [0x24, None], 934 'dwFlags' : [0x28, None], 935 'idProcess' : [0x2C, None], 936 'idThread' : [0x30, None], 937 'offPfn' : [0x40, None], 938 'ihmod' : [0x48, None], 939 }], 940 'tagHANDLETYPEINFO' : [16, { 941 'dwAllocTag' : [8, None], 942 'bObjectCreateFlags' : [12, None], 943 }], 944 }) 945 946 # This field appears in the auto-generated vtypes for all OS except XP 947 if architecture == "I386" and version[:2] == (5, 1): 948 profile.add_overlay({ 949 '_MM_SESSION_SPACE': [None, { 950 # nt!MiDereferenceSession 951 'ResidentProcessCount': [0x248, ['long']], 952 }]}) 953 954 return profile
955
956 957 -class Win32kHook(kb.ParameterHook):
958 """Guess the version of win32k.sys from the index. 959 960 NOTE: Win32k needs special attention because it is often not easily 961 detected. 962 """ 963 name = "win32k_profile" 964
965 - def calculate(self):
966 # Require at least 3 comparison points to be matched. 967 for _, guess in self.session.plugins.guess_guid( 968 module="win32k", minimal_match=3).GuessProfiles(): 969 return guess
970