1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17   
 18   
 19   
 20   
 21   
 22  from rekall import obj 
 23  from rekall.plugins.windows import common 
 24  from rekall.plugins.overlays.windows import pe_vtypes 
 25  from rekall_lib import utils 
 26   
 27   
 28  callback_types = { 
 29      '_NOTIFICATION_PACKET' : [0x10, { 
 30          'ListEntry' : [0x0, ['_LIST_ENTRY']], 
 31          'DriverObject' : [0x8, ['pointer', ['_DRIVER_OBJECT']]], 
 32          'NotificationRoutine' : [0xC, ['unsigned int']], 
 33      }], 
 34      '_KBUGCHECK_CALLBACK_RECORD' : [0x20, { 
 35          'Entry' : [0x0, ['_LIST_ENTRY']], 
 36          'CallbackRoutine' : [0x8, ['unsigned int']], 
 37          'Buffer' : [0xC, ['pointer', ['void']]], 
 38          'Length' : [0x10, ['unsigned int']], 
 39          'Component' : [0x14, ['pointer', ['String', dict(length=64)]]], 
 40          'Checksum' : [0x18, ['pointer', ['unsigned int']]], 
 41          'State' : [0x1C, ['unsigned char']], 
 42      }], 
 43      '_KBUGCHECK_REASON_CALLBACK_RECORD' : [0x1C, { 
 44          'Entry' : [0x0, ['_LIST_ENTRY']], 
 45          'CallbackRoutine' : [0x8, ['unsigned int']], 
 46          'Component' : [0xC, ['pointer', ['String', dict(length=8)]]], 
 47          'Checksum' : [0x10, ['pointer', ['unsigned int']]], 
 48          'Reason' : [0x14, ['unsigned int']], 
 49          'State' : [0x18, ['unsigned char']], 
 50      }], 
 51      '_SHUTDOWN_PACKET' : [0xC, { 
 52          'Entry' : [0x0, ['_LIST_ENTRY']], 
 53          'DeviceObject' : [0x8, ['pointer', ['_DEVICE_OBJECT']]], 
 54      }], 
 55      '_EX_CALLBACK_ROUTINE_BLOCK' : [0x8, { 
 56          'RundownProtect' : [0x0, ['unsigned int']], 
 57          'Function' : [0x4, ['unsigned int']], 
 58          'Context' : [0x8, ['unsigned int']], 
 59      }], 
 60      '_GENERIC_CALLBACK' : [0xC, { 
 61          'Callback' : [0x4, ['pointer', ['void']]], 
 62          'Associated' : [0x8, ['pointer', ['void']]], 
 63      }], 
 64      '_REGISTRY_CALLBACK_LEGACY' : [0x38, { 
 65          'CreateTime' : [0x0, ['WinFileTime', {}]], 
 66      }], 
 67      '_REGISTRY_CALLBACK' : [None, { 
 68          'ListEntry' : [0x0, ['_LIST_ENTRY']], 
 69          'Function' : [0x1C, ['pointer', ['void']]], 
 70      }], 
 71      '_DBGPRINT_CALLBACK' : [0x14, { 
 72          'Function' : [0x8, ['pointer', ['void']]], 
 73      }], 
 74      '_NOTIFY_ENTRY_HEADER' : [None, { 
 75          'ListEntry' : [0x0, ['_LIST_ENTRY']], 
 76          'EventCategory' : [0x8, ['Enumeration', dict( 
 77              target='long', choices={ 
 78                  0: 'EventCategoryReserved', 
 79                  1: 'EventCategoryHardwareProfileChange', 
 80                  2: 'EventCategoryDeviceInterfaceChange', 
 81                  3: 'EventCategoryTargetDeviceChange'})]], 
 82          'CallbackRoutine' : [0x14, ['unsigned int']], 
 83          'DriverObject' : [0x1C, ['pointer', ['_DRIVER_OBJECT']]], 
 84      }], 
 85      } 
 86   
 87   
 88  callback_types_x64 = { 
 89      '_GENERIC_CALLBACK' : [ 0x18, { 
 90          'Callback' : [ 0x8, ['pointer', ['void']]], 
 91          'Associated' : [ 0x10, ['pointer', ['void']]], 
 92      } ], 
 93      '_NOTIFICATION_PACKET' : [ 0x30, { 
 94          'ListEntry' : [ 0x0, ['_LIST_ENTRY']], 
 95          'DriverObject' : [ 0x10, ['pointer', ['_DRIVER_OBJECT']]], 
 96          'NotificationRoutine' : [ 0x18, ['address']], 
 97      } ], 
 98      '_SHUTDOWN_PACKET' : [ 0xC, { 
 99          'Entry' : [ 0x0, ['_LIST_ENTRY']], 
100          'DeviceObject' : [ 0x10, ['pointer', ['_DEVICE_OBJECT']]], 
101      } ], 
102      '_DBGPRINT_CALLBACK' : [ 0x14, { 
103          'Function' : [ 0x10, ['pointer', ['void']]], 
104      } ], 
105      '_NOTIFY_ENTRY_HEADER' : [ None, { 
106          'ListEntry' : [ 0x0, ['_LIST_ENTRY']], 
107          'EventCategory' : [ 0x10, ['Enumeration', dict( 
108              target = 'long', choices = { 
109              0: 'EventCategoryReserved', 
110              1: 'EventCategoryHardwareProfileChange', 
111              2: 'EventCategoryDeviceInterfaceChange', 
112              3: 'EventCategoryTargetDeviceChange'})]], 
113          'CallbackRoutine' : [ 0x20, ['address']], 
114          'DriverObject' : [ 0x30, ['pointer', ['_DRIVER_OBJECT']]], 
115      }], 
116      '_REGISTRY_CALLBACK' : [ 0x50, { 
117          'ListEntry' : [ 0x0, ['_LIST_ENTRY']], 
118          'Function' : [ 0x20, ['pointer', ['void']]],  
119      }], 
120   
121       
122      '_KBUGCHECK_CALLBACK_RECORD' : [None, { 
123          'Entry' : [0x0, ['_LIST_ENTRY']], 
124          'CallbackRoutine' : [0x10, ['Pointer']], 
125          'Component' : [0x28, ['Pointer', dict( 
126              target='String', 
127              target_args=dict( 
128                  length=8 
129              ) 
130          )]], 
131      }], 
132   
133       
134      '_KBUGCHECK_REASON_CALLBACK_RECORD' : [None, { 
135          'Entry' : [0x0, ['_LIST_ENTRY']], 
136          'CallbackRoutine' : [0x10, ['Pointer']], 
137          'Component' : [0x18, ['Pointer', dict( 
138              target='String', 
139          )]], 
140      }], 
141  } 
142   
143   
145      """Class for shutdown notification callbacks""" 
146   
148          """ 
149          Perform some checks to see if this object can exist in the provided 
150          address space. 
151          """ 
152   
153          if (not vm.is_valid_address(self.Entry.Flink) or 
154                  not vm.is_valid_address(self.Entry.Blink) or 
155                  not vm.is_valid_address(self.DeviceObject)): 
156              return False 
157   
158           
159          device = self.DeviceObject.dereference(vm=vm) 
160   
161           
162          object_header = self.obj_profile.Object( 
163              "_OBJECT_HEADER", 
164              offset=(device.obj_offset - 
165                      self.obj_profile.get_obj_offset("_OBJECT_HEADER", "Body")), 
166              vm=vm) 
167   
168          return object_header.get_object_type(vm) == "Device" 
  169   
171      """Return the offset of the callback, no object headers""" 
 172   
173   
175      """PoolScanner for File System Callbacks""" 
176      checks = [('PoolTagCheck', dict(tag="IoFs")), 
177                ('CheckPoolSize', dict(condition=lambda x: x == 0x18)), 
178                ('CheckPoolType', dict(non_paged=True, paged=True, 
179                                       free=True)), 
180               ] 
181   
182 -    def scan(self, **kwargs): 
  190   
191   
193      """PoolScanner for Shutdown Callbacks""" 
194      checks = [('PoolTagCheck', dict(tag="IoSh")), 
195                ('CheckPoolSize', dict(condition=lambda x: x == 0x18)), 
196                ('CheckPoolType', dict(non_paged=True, paged=True, 
197                                       free=True)), 
198                ('CheckPoolIndex', dict(value=0)), 
199               ] 
200   
201 -    def __init__(self, kernel_address_space=None, **kwargs): 
 204   
205 -    def scan(self, offset=0, **kwargs): 
 206          for pool_header in super(PoolScanShutdownCallback, self).scan( 
207                  offset=offset, **kwargs): 
208   
209               
210               
211              callback = self.profile._SHUTDOWN_PACKET( 
212                  offset=pool_header.end(), vm=self.address_space) 
213   
214              if not callback.sanity_check(self.kernel_address_space): 
215                  continue 
216   
217               
218               
219              driver_obj = callback.DeviceObject.dereference( 
220                  vm=self.kernel_address_space).DriverObject 
221   
222              function_pointer = driver_obj.MajorFunction['IRP_MJ_SHUTDOWN'] 
223              details = driver_obj.DriverName 
224   
225              yield "IoRegisterShutdownNotification", function_pointer, details 
  226   
227   
229      """PoolScanner for Generic Callbacks""" 
230      checks = [('PoolTagCheck', dict(tag="Cbrb")), 
231                ('CheckPoolSize', dict(condition=lambda x: x == 0x18)), 
232                ('CheckPoolType', dict(non_paged=True, paged=True, free=True)), 
233               ] 
234   
235 -    def scan(self, **kwargs): 
 236          """ 
237          Enumerate generic callbacks of the following types: 
238   
239          * PsSetCreateProcessNotifyRoutine 
240          * PsSetThreadCreateNotifyRoutine 
241          * PsSetLoadImageNotifyRoutine 
242          * CmRegisterCallback (on XP only) 
243          * DbgkLkmdRegisterCallback (on Windows 7 only) 
244   
245          The only issue is that you can't distinguish between the types by just 
246          finding the generic callback structure 
247          """ 
248          for pool_header in super(PoolScanGenericCallback, self).scan(**kwargs): 
249              callback = self.profile.Object( 
250                  '_GENERIC_CALLBACK', offset=pool_header.end(), 
251                  vm=self.address_space) 
252   
253              yield "GenericKernelCallback", callback.Callback, None 
  254   
255   
257      """PoolScanner for DebugPrint Callbacks on Vista and 7""" 
258      checks = [('PoolTagCheck', dict(tag="DbCb")), 
259                ('CheckPoolSize', dict(condition=lambda x: x == 0x20)), 
260                ('CheckPoolType', dict(non_paged=True, paged=True, free=True)), 
261               ] 
262   
263 -    def scan(self, offset=0, **kwargs): 
  273   
274   
276      """PoolScanner for DebugPrint Callbacks on Vista and 7""" 
277      checks = [('PoolTagCheck', dict(tag="CMcb")), 
278                 
279                ('CheckPoolSize', dict(condition=lambda x: x >= 0x38)), 
280                ('CheckPoolType', dict(non_paged=True, paged=True, free=True)), 
281                ('CheckPoolIndex', dict(value=4)), 
282               ] 
283   
284 -    def scan(self, offset=0, **kwargs): 
 285          """ 
286          Enumerate registry callbacks on Vista and 7. 
287   
288          These callbacks are installed via CmRegisterCallback 
289          or CmRegisterCallbackEx. 
290          """ 
291          for pool_header in super(PoolScanRegistryCallback, self).scan( 
292                  offset=offset, **kwargs): 
293   
294              callback = self.profile.Object( 
295                  '_REGISTRY_CALLBACK', offset=pool_header.end(), 
296                  vm=self.address_space) 
297   
298              yield "CmRegisterCallback", callback.Function, None 
  299   
300   
302      """PoolScanner for Pnp9 (EventCategoryHardwareProfileChange)""" 
303      checks = [('MultiPoolTagCheck', dict(tags=["Pnp9", "PnpD", "PnpC"])), 
304                 
305                ('CheckPoolSize', dict(condition=lambda x: x >= 0x30)), 
306                ('CheckPoolType', dict(non_paged=True, paged=True, free=True)), 
307                ('CheckPoolIndex', dict(value=1)), 
308               ] 
309   
310 -    def __init__(self, kernel_address_space=None, **kwargs): 
 311          self.kernel_address_space = kernel_address_space 
312          super(PoolScanPnp9, self).__init__(**kwargs) 
 313   
314 -    def scan(self, offset=0, **kwargs): 
 315          """Enumerate IoRegisterPlugPlayNotification""" 
316          for pool_header in super(PoolScanPnp9, self).scan( 
317                  offset=offset, **kwargs): 
318              entry = self.profile.Object( 
319                  "_NOTIFY_ENTRY_HEADER", offset=pool_header.end(), 
320                  vm=self.address_space) 
321   
322               
323              driver = entry.DriverObject.dereference( 
324                  vm=self.kernel_address_space) 
325   
326               
327              header = self.profile.Object( 
328                  "_OBJECT_HEADER", 
329                  offset=(driver.obj_offset - 
330                          driver.obj_profile.get_obj_offset( 
331                              "_OBJECT_HEADER", "Body")), 
332                  vm=driver.obj_vm) 
333   
334               
335              driver_name = header.NameInfo.Name.v() 
336   
337              yield entry.EventCategory, entry.CallbackRoutine, driver_name 
  338   
339   
341      """Print system-wide notification routines by scanning for them. 
342   
343      Note this plugin is quite inefficient - consider using the callbacks plugin 
344      instead. 
345      """ 
346   
347      __name = "callback_scan" 
348   
349 -    def __init__(self, scan_in_kernel_address_space=False, **kwargs): 
 366   
368          """ 
369          Enumerate the Create Process, Create Thread, and Image Load callbacks. 
370   
371          On some systems, the byte sequences will be inaccurate or the exported 
372          function will not be found. In these cases, the PoolScanGenericCallback 
373          scanner will pick up the pool associated with the callbacks. 
374          """ 
375   
376          routines = ["PspLoadImageNotifyRoutine", 
377                      "PspCreateThreadNotifyRoutine", 
378                      "PspCreateProcessNotifyRoutine"] 
379   
380          for symbol in routines: 
381               
382              callbacks = self.profile.get_constant_object( 
383                  symbol, 
384                  target="Array", 
385                  target_args=dict( 
386                      count=8, 
387                      target='_EX_FAST_REF', 
388                      target_args=dict( 
389                          target="_GENERIC_CALLBACK", 
390                      ) 
391                  ) 
392              ) 
393   
394              for callback in callbacks: 
395                  if callback.Callback: 
396                      yield "GenericKernelCallback", callback.Callback, None 
 397   
399          """ 
400          Enumerate generic Bugcheck callbacks. 
401   
402          Note: These structures don't exist in tagged pools, but you can find 
403          them via KDDEBUGGER_DATA64 on all versions of Windows. 
404          """ 
405          KeBugCheckCallbackListHead = self.profile.get_constant_object( 
406              "KeBugCheckCallbackListHead", "Pointer", target_args=dict( 
407                  target='_LIST_ENTRY')) 
408   
409          for l in KeBugCheckCallbackListHead.list_of_type( 
410                  "_KBUGCHECK_CALLBACK_RECORD", "Entry"): 
411              yield ("KeBugCheckCallbackListHead", l.CallbackRoutine, 
412                     l.Component.dereference()) 
 413   
415          """ 
416          Enumerate registry change callbacks. 
417   
418          On XP these are registered using CmRegisterCallback. 
419   
420          On Vista and Windows 7, these callbacks are registered using the 
421          CmRegisterCallbackEx function. 
422          """ 
423           
424          addrs = self.profile.get_constant_object( 
425              "CmpCallBackVector", 
426              target="Array", 
427              target_args=dict( 
428                  count=100, 
429                  target="_EX_FAST_REF") 
430              ) 
431   
432          for addr in addrs: 
433              callback = addr.dereference_as("_EX_CALLBACK_ROUTINE_BLOCK") 
434              if callback: 
435                  yield "Registry", callback.Function, None 
 436   
438          """ 
439          Enumerate Bugcheck Reason callbacks. 
440          """ 
441          bugs = self.profile.get_constant_object( 
442              "KeBugCheckReasonCallbackListHead", 
443              target="_LIST_ENTRY") 
444   
445          for l in bugs.list_of_type( 
446                  "_KBUGCHECK_REASON_CALLBACK_RECORD", "Entry"): 
447              yield ("KeRegisterBugCheckReasonCallback", l.CallbackRoutine, 
448                     l.Component.dereference()) 
 449   
451           
452          version = self.profile.metadata('version') 
453   
454           
455          address_space = self.physical_address_space 
456          if self.scan_in_kernel_address_space: 
457              address_space = self.kernel_address_space 
458   
459           
460          scanners = dict( 
461              PoolScanFSCallback=PoolScanFSCallback( 
462                  address_space=address_space, 
463                  profile=self.profile), 
464   
465              PoolScanShutdownCallback=PoolScanShutdownCallback( 
466                  profile=self.profile, 
467                  address_space=address_space, 
468                  kernel_address_space=self.kernel_address_space), 
469   
470              PoolScanGenericCallback=PoolScanGenericCallback( 
471                  address_space=address_space, 
472                  profile=self.profile), 
473              ) 
474   
475           
476          if version >= 6.0: 
477              scanners.update( 
478                  PoolScanDbgPrintCallback=PoolScanDbgPrintCallback( 
479                      address_space=address_space, 
480                      profile=self.profile), 
481   
482                  PoolScanRegistryCallback=PoolScanRegistryCallback( 
483                      address_space=address_space, 
484                      profile=self.profile), 
485   
486                  PoolScanPnp9=PoolScanPnp9( 
487                      profile=self.profile, 
488                      address_space=address_space, 
489                      kernel_address_space=self.kernel_address_space), 
490                  ) 
491   
492          for scanner in scanners.values(): 
493              for info in scanner.scan(): 
494                  yield info 
495   
496           
497          for info in self.get_bugcheck_callbacks(): 
498              yield info 
499   
500          for info in self.get_bugcheck_reason_callbacks(): 
501              yield info 
502   
503          for info in self.get_kernel_callbacks(): 
504              yield info 
505   
506           
507          if version == 5.1: 
508              for info in self.get_registry_callbacks_legacy(): 
509                  yield info 
 510   
512          renderer.table_header([("Type", "type", "36"), 
513                                 ("Callback", "callback", "[addrpad]"), 
514                                 ("Symbol", "symbol", "50"), 
515                                 ("Details", "details", ""), 
516                                ]) 
517   
518          for (sym, cb, detail) in self.generate_hits(): 
519              symbol_name = utils.FormattedAddress( 
520                  self.session.address_resolver, cb) 
521              renderer.table_row(sym, cb, symbol_name, detail) 
  522   
523   
524 -class Callbacks(common.WindowsCommandPlugin): 
 525      """Enumerate callback routines. 
526   
527      This plugin just enumerates installed callback routines from various 
528      sources. It does not scan for them. 
529   
530      This plugin is loosely based on the original Volatility plugin of the same 
531      name but much expanded using new information. 
532   
533      Reference: 
534      <http://www.codemachine.com/notes.html> 
535      """ 
536   
537      name = "callbacks" 
538   
539      table_header = [ 
540          dict(name="type", width=36), 
541          dict(name="offset", style="address"), 
542          dict(name="callback", style="address"), 
543          dict(name="symbol", width=50), 
544          dict(name="details"), 
545      ] 
546   
547   
552   
554          resolver = self.session.address_resolver 
555          for table, table_length in [ 
556                  ("nt!PspLoadImageNotifyRoutine", 
557                   "nt!PspLoadImageNotifyRoutineCount"), 
558                  ("nt!PspCreateThreadNotifyRoutine", 
559                   "nt!PspCreateThreadNotifyRoutineCount"), 
560                  ("nt!PspCreateProcessNotifyRoutine", 
561                   "nt!PspCreateProcessNotifyRoutineCount")]: 
562              array_length = resolver.get_constant_object( 
563                  table_length, "unsigned long long") 
564   
565              array = resolver.get_constant_object( 
566                  table, 
567                  target="Array", 
568                  count=array_length, 
569                  target_args=dict( 
570                      target="_EX_FAST_REF", 
571                      target_args=dict( 
572                          target="_GENERIC_CALLBACK" 
573                      ) 
574                  ) 
575              ) 
576   
577              for callback in array: 
578                  function = callback.Callback 
579                  yield (table, callback, function, 
580                         utils.FormattedAddress(resolver, function)) 
 581   
583          resolver = self.session.address_resolver 
584   
585          for list_head_name, type in [ 
586                  ("nt!KeBugCheckCallbackListHead", "_KBUGCHECK_CALLBACK_RECORD"), 
587                  ("nt!KeBugCheckReasonCallbackListHead", 
588                   "_KBUGCHECK_REASON_CALLBACK_RECORD")]: 
589              list_head = resolver.get_constant_object( 
590                  list_head_name, "_LIST_ENTRY") 
591   
592              for record in list_head.list_of_type(type, "Entry"): 
593                  function = record.CallbackRoutine 
594   
595                  yield (list_head_name, 
596                         record, 
597                         function, 
598                         utils.FormattedAddress(resolver, function), 
599                         record.Component) 
 600   
 607