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