mirror of
https://github.com/volatilityfoundation/volatility
synced 2026-06-08 18:04:46 +00:00
616 lines
24 KiB
Python
616 lines
24 KiB
Python
# Volatility
|
|
# Copyright (C) 2007-2013 Volatility Foundation
|
|
# Copyright (c) 2010, 2011, 2012 Michael Ligh <michael.ligh@mnin.org>
|
|
#
|
|
# This file is part of Volatility.
|
|
#
|
|
# Volatility is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# Volatility is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with Volatility. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
import volatility.utils as utils
|
|
import volatility.obj as obj
|
|
import volatility.poolscan as poolscan
|
|
import volatility.debug as debug
|
|
import volatility.plugins.common as common
|
|
import volatility.win32.modules as modules
|
|
import volatility.win32.tasks as tasks
|
|
import volatility.plugins.malware.devicetree as devicetree
|
|
from volatility.renderers import TreeGrid
|
|
from volatility.renderers.basic import Address
|
|
|
|
try:
|
|
import distorm3
|
|
has_distorm3 = True
|
|
except ImportError:
|
|
has_distorm3 = False
|
|
|
|
#--------------------------------------------------------------------------------
|
|
# vtypes
|
|
#--------------------------------------------------------------------------------
|
|
|
|
callback_types = {
|
|
'_NOTIFICATION_PACKET' : [ 0x10, {
|
|
'ListEntry' : [ 0x0, ['_LIST_ENTRY']],
|
|
'DriverObject' : [ 0x8, ['pointer', ['_DRIVER_OBJECT']]],
|
|
'NotificationRoutine' : [ 0xC, ['unsigned int']],
|
|
} ],
|
|
'_KBUGCHECK_CALLBACK_RECORD' : [ 0x20, {
|
|
'Entry' : [ 0x0, ['_LIST_ENTRY']],
|
|
'CallbackRoutine' : [ 0x8, ['unsigned int']],
|
|
'Buffer' : [ 0xC, ['pointer', ['void']]],
|
|
'Length' : [ 0x10, ['unsigned int']],
|
|
'Component' : [ 0x14, ['pointer', ['String', dict(length = 64)]]],
|
|
'Checksum' : [ 0x18, ['pointer', ['unsigned int']]],
|
|
'State' : [ 0x1C, ['unsigned char']],
|
|
} ],
|
|
'_KBUGCHECK_REASON_CALLBACK_RECORD' : [ 0x1C, {
|
|
'Entry' : [ 0x0, ['_LIST_ENTRY']],
|
|
'CallbackRoutine' : [ 0x8, ['unsigned int']],
|
|
'Component' : [ 0xC, ['pointer', ['String', dict(length = 8)]]],
|
|
'Checksum' : [ 0x10, ['pointer', ['unsigned int']]],
|
|
'Reason' : [ 0x14, ['unsigned int']],
|
|
'State' : [ 0x18, ['unsigned char']],
|
|
} ],
|
|
'_SHUTDOWN_PACKET' : [ 0xC, {
|
|
'Entry' : [ 0x0, ['_LIST_ENTRY']],
|
|
'DeviceObject' : [ 0x8, ['pointer', ['_DEVICE_OBJECT']]],
|
|
} ],
|
|
'_EX_CALLBACK_ROUTINE_BLOCK' : [ 0x8, {
|
|
'RundownProtect' : [ 0x0, ['unsigned int']],
|
|
'Function' : [ 0x4, ['unsigned int']],
|
|
'Context' : [ 0x8, ['unsigned int']],
|
|
} ],
|
|
'_GENERIC_CALLBACK' : [ 0xC, {
|
|
'Callback' : [ 0x4, ['pointer', ['void']]],
|
|
'Associated' : [ 0x8, ['pointer', ['void']]],
|
|
} ],
|
|
'_REGISTRY_CALLBACK_LEGACY' : [ 0x38, {
|
|
'CreateTime' : [ 0x0, ['WinTimeStamp', dict(is_utc = True)]],
|
|
} ],
|
|
'_REGISTRY_CALLBACK' : [ None, {
|
|
'ListEntry' : [ 0x0, ['_LIST_ENTRY']],
|
|
'Function' : [ 0x1C, ['pointer', ['void']]],
|
|
} ],
|
|
'_DBGPRINT_CALLBACK' : [ 0x14, {
|
|
'Function' : [ 0x8, ['pointer', ['void']]],
|
|
} ],
|
|
'_NOTIFY_ENTRY_HEADER' : [ None, {
|
|
'ListEntry' : [ 0x0, ['_LIST_ENTRY']],
|
|
'EventCategory' : [ 0x8, ['Enumeration', dict(target = 'long', choices = {
|
|
0: 'EventCategoryReserved',
|
|
1: 'EventCategoryHardwareProfileChange',
|
|
2: 'EventCategoryDeviceInterfaceChange',
|
|
3: 'EventCategoryTargetDeviceChange'})]],
|
|
'CallbackRoutine' : [ 0x14, ['unsigned int']],
|
|
'DriverObject' : [ 0x1C, ['pointer', ['_DRIVER_OBJECT']]],
|
|
} ],
|
|
}
|
|
|
|
callback_types_x64 = {
|
|
'_GENERIC_CALLBACK' : [ 0x18, {
|
|
'Callback' : [ 0x8, ['pointer', ['void']]],
|
|
'Associated' : [ 0x10, ['pointer', ['void']]],
|
|
} ],
|
|
'_NOTIFICATION_PACKET' : [ 0x30, {
|
|
'ListEntry' : [ 0x0, ['_LIST_ENTRY']],
|
|
'DriverObject' : [ 0x10, ['pointer', ['_DRIVER_OBJECT']]],
|
|
'NotificationRoutine' : [ 0x18, ['address']],
|
|
} ],
|
|
'_SHUTDOWN_PACKET' : [ 0xC, {
|
|
'Entry' : [ 0x0, ['_LIST_ENTRY']],
|
|
'DeviceObject' : [ 0x10, ['pointer', ['_DEVICE_OBJECT']]],
|
|
} ],
|
|
'_DBGPRINT_CALLBACK' : [ 0x14, {
|
|
'Function' : [ 0x10, ['pointer', ['void']]],
|
|
} ],
|
|
'_NOTIFY_ENTRY_HEADER' : [ None, {
|
|
'ListEntry' : [ 0x0, ['_LIST_ENTRY']],
|
|
'EventCategory' : [ 0x10, ['Enumeration', dict(target = 'long', choices = {
|
|
0: 'EventCategoryReserved',
|
|
1: 'EventCategoryHardwareProfileChange',
|
|
2: 'EventCategoryDeviceInterfaceChange',
|
|
3: 'EventCategoryTargetDeviceChange'})]],
|
|
'CallbackRoutine' : [ 0x20, ['address']],
|
|
'DriverObject' : [ 0x30, ['pointer', ['_DRIVER_OBJECT']]],
|
|
} ],
|
|
'_REGISTRY_CALLBACK' : [ 0x50, {
|
|
'ListEntry' : [ 0x0, ['_LIST_ENTRY']],
|
|
'Function' : [ 0x20, ['pointer', ['void']]], # other could be 28
|
|
} ],
|
|
'_KBUGCHECK_CALLBACK_RECORD' : [ None, {
|
|
'Entry' : [ 0x0, ['_LIST_ENTRY']],
|
|
'CallbackRoutine' : [ 0x10, ['address']],
|
|
'Component' : [ 0x28, ['pointer', ['String', dict(length = 8)]]],
|
|
} ],
|
|
'_KBUGCHECK_REASON_CALLBACK_RECORD' : [ None, {
|
|
'Entry' : [ 0x0, ['_LIST_ENTRY']],
|
|
'CallbackRoutine' : [ 0x10, ['unsigned int']],
|
|
'Component' : [ 0x28, ['pointer', ['String', dict(length = 8)]]],
|
|
} ],
|
|
}
|
|
|
|
#--------------------------------------------------------------------------------
|
|
# object classes
|
|
#--------------------------------------------------------------------------------
|
|
|
|
class _SHUTDOWN_PACKET(obj.CType):
|
|
"""Class for shutdown notification callbacks"""
|
|
|
|
def is_valid(self):
|
|
"""
|
|
Perform some checks.
|
|
Note: obj_native_vm is kernel space.
|
|
"""
|
|
|
|
if not obj.CType.is_valid(self):
|
|
return False
|
|
|
|
if (not self.obj_native_vm.is_valid_address(self.Entry.Flink) or
|
|
not self.obj_native_vm.is_valid_address(self.Entry.Blink) or
|
|
not self.obj_native_vm.is_valid_address(self.DeviceObject)):
|
|
return False
|
|
|
|
# Dereference the device object
|
|
device = self.DeviceObject.dereference()
|
|
|
|
# Carve out the device's object header and check its type
|
|
object_header = obj.Object("_OBJECT_HEADER",
|
|
offset = device.obj_offset -
|
|
self.obj_native_vm.profile.get_obj_offset("_OBJECT_HEADER", "Body"),
|
|
vm = device.obj_vm,
|
|
native_vm = device.obj_native_vm)
|
|
|
|
return object_header.get_object_type() == "Device"
|
|
|
|
#--------------------------------------------------------------------------------
|
|
# profile modifications
|
|
#--------------------------------------------------------------------------------
|
|
|
|
class CallbackMods(obj.ProfileModification):
|
|
before = ['WindowsOverlay']
|
|
conditions = {'os': lambda x: x == 'windows'}
|
|
def modification(self, profile):
|
|
if profile.metadata.get("memory_model", "32bit") == "32bit":
|
|
profile.vtypes.update(callback_types)
|
|
profile.object_classes.update({
|
|
'_SHUTDOWN_PACKET': _SHUTDOWN_PACKET,
|
|
})
|
|
else:
|
|
profile.vtypes.update(callback_types_x64)
|
|
|
|
#--------------------------------------------------------------------------------
|
|
# pool scanners
|
|
#--------------------------------------------------------------------------------
|
|
|
|
class AbstractCallbackScanner(poolscan.PoolScanner):
|
|
"""Return the offset of the callback, no object headers"""
|
|
|
|
class PoolScanFSCallback(AbstractCallbackScanner):
|
|
"""PoolScanner for File System Callbacks"""
|
|
|
|
def __init__(self, address_space):
|
|
AbstractCallbackScanner.__init__(self, address_space)
|
|
|
|
self.pooltag = "IoFs"
|
|
self.struct_name = "_NOTIFICATION_PACKET"
|
|
|
|
if address_space.profile.metadata.get("memory_model", "32bit") == "32bit":
|
|
size = 0x18
|
|
else:
|
|
size = 0x30
|
|
|
|
self.checks = [ ('CheckPoolSize', dict(condition = lambda x: x == size)),
|
|
('CheckPoolType', dict(non_paged = True, paged = True, free = True)),
|
|
#('CheckPoolIndex', dict(value = 4)),
|
|
]
|
|
|
|
class PoolScanShutdownCallback(AbstractCallbackScanner):
|
|
"""PoolScanner for Shutdown Callbacks"""
|
|
|
|
def __init__(self, address_space):
|
|
AbstractCallbackScanner.__init__(self, address_space)
|
|
|
|
self.pooltag = "IoSh"
|
|
self.struct_name = "_SHUTDOWN_PACKET"
|
|
|
|
if address_space.profile.metadata.get("memory_model", "32bit") == "32bit":
|
|
size = 0x18
|
|
else:
|
|
size = 0x30
|
|
|
|
self.checks = [ ('CheckPoolSize', dict(condition = lambda x: x == size)),
|
|
('CheckPoolType', dict(non_paged = True, paged = True, free = True)),
|
|
('CheckPoolIndex', dict(value = 0)),
|
|
]
|
|
|
|
class PoolScanGenericCallback(AbstractCallbackScanner):
|
|
"""PoolScanner for Generic Callbacks"""
|
|
|
|
def __init__(self, address_space):
|
|
AbstractCallbackScanner.__init__(self, address_space)
|
|
|
|
self.pooltag = "Cbrb"
|
|
self.struct_name = "_GENERIC_CALLBACK"
|
|
|
|
if address_space.profile.metadata.get("memory_model", "32bit") == "32bit":
|
|
size = 0x18
|
|
else:
|
|
size = 0x30
|
|
|
|
self.checks = [ ('CheckPoolSize', dict(condition = lambda x: x == size)),
|
|
('CheckPoolType', dict(non_paged = True, paged = True, free = True)),
|
|
# This is a good constraint for all images except Frank's rustock-c.vmem
|
|
#('CheckPoolIndex', dict(value = 1)),
|
|
]
|
|
|
|
class PoolScanDbgPrintCallback(AbstractCallbackScanner):
|
|
"""PoolScanner for DebugPrint Callbacks on Vista and 7"""
|
|
|
|
def __init__(self, address_space):
|
|
AbstractCallbackScanner.__init__(self, address_space)
|
|
|
|
self.pooltag = "DbCb"
|
|
self.struct_name = "_DBGPRINT_CALLBACK"
|
|
|
|
self.checks = [ ('CheckPoolSize', dict(condition = lambda x: x >= 0x20 and x <= 0x40)),
|
|
('CheckPoolType', dict(non_paged = True, paged = True, free = True)),
|
|
#('CheckPoolIndex', dict(value = 0)),
|
|
]
|
|
|
|
class PoolScanRegistryCallback(AbstractCallbackScanner):
|
|
"""PoolScanner for DebugPrint Callbacks on Vista and 7"""
|
|
|
|
def __init__(self, address_space):
|
|
AbstractCallbackScanner.__init__(self, address_space)
|
|
|
|
self.pooltag = "CMcb"
|
|
self.struct_name = "_REGISTRY_CALLBACK"
|
|
|
|
self.checks = [('CheckPoolSize', dict(condition = lambda x: x >= 0x38)),
|
|
('CheckPoolType', dict(non_paged = True, paged = True, free = True)),
|
|
('CheckPoolIndex', dict(value = 4)),
|
|
]
|
|
|
|
class PoolScanPnp9(AbstractCallbackScanner):
|
|
"""PoolScanner for Pnp9 (EventCategoryHardwareProfileChange)"""
|
|
|
|
def __init__(self, address_space):
|
|
AbstractCallbackScanner.__init__(self, address_space)
|
|
|
|
self.pooltag = "Pnp9"
|
|
self.struct_name = "_NOTIFY_ENTRY_HEADER"
|
|
|
|
self.checks = [ # seen as 0x2C on W7, 0x28 on vistasp0 (4 less but needs 8 less)
|
|
('CheckPoolSize', dict(condition = lambda x: x >= 0x30)),
|
|
('CheckPoolType', dict(non_paged = True, paged = True, free = True)),
|
|
('CheckPoolIndex', dict(value = 1)),
|
|
]
|
|
|
|
class PoolScanPnpD(AbstractCallbackScanner):
|
|
"""PoolScanner for PnpD (EventCategoryDeviceInterfaceChange)"""
|
|
|
|
def __init__(self, address_space):
|
|
AbstractCallbackScanner.__init__(self, address_space)
|
|
|
|
self.pooltag = "PnpD"
|
|
self.struct_name = "_NOTIFY_ENTRY_HEADER"
|
|
|
|
self.checks = [('CheckPoolSize', dict(condition = lambda x: x >= 0x40)),
|
|
('CheckPoolType', dict(non_paged = True, paged = True, free = True)),
|
|
('CheckPoolIndex', dict(value = 1)),
|
|
]
|
|
|
|
class PoolScanPnpC(AbstractCallbackScanner):
|
|
"""PoolScanner for PnpC (EventCategoryTargetDeviceChange)"""
|
|
|
|
def __init__(self, address_space):
|
|
AbstractCallbackScanner.__init__(self, address_space)
|
|
|
|
self.pooltag = "PnpC"
|
|
self.struct_name = "_NOTIFY_ENTRY_HEADER"
|
|
|
|
self.checks = [('CheckPoolSize', dict(condition = lambda x: x >= 0x38)),
|
|
('CheckPoolType', dict(non_paged = True, paged = True, free = True)),
|
|
('CheckPoolIndex', dict(value = 1)),
|
|
]
|
|
|
|
#--------------------------------------------------------------------------------
|
|
# callbacks plugin
|
|
#--------------------------------------------------------------------------------
|
|
|
|
class Callbacks(common.AbstractScanCommand):
|
|
"""Print system-wide notification routines"""
|
|
|
|
scanners = [PoolScanFSCallback, PoolScanShutdownCallback, PoolScanGenericCallback]
|
|
|
|
@staticmethod
|
|
def get_kernel_callbacks(nt_mod):
|
|
"""
|
|
Enumerate the Create Process, Create Thread, and Image Load callbacks.
|
|
|
|
On some systems, the byte sequences will be inaccurate or the exported
|
|
function will not be found. In these cases, the PoolScanGenericCallback
|
|
scanner will pick up the pool associated with the callbacks.
|
|
"""
|
|
|
|
bits32 = nt_mod.obj_vm.profile.metadata.get("memory_model", "32bit") == "32bit"
|
|
|
|
if bits32:
|
|
routines = [
|
|
# push esi; mov esi, offset _PspLoadImageNotifyRoutine
|
|
('PsSetLoadImageNotifyRoutine', "\x56\xbe"),
|
|
# push esi; mov esi, offset _PspCreateThreadNotifyRoutine
|
|
('PsSetCreateThreadNotifyRoutine', "\x56\xbe"),
|
|
# mov edi, offset _PspCreateProcessNotifyRoutine
|
|
('PsSetCreateProcessNotifyRoutine', "\xbf"),
|
|
]
|
|
else:
|
|
routines = [
|
|
# lea ecx, offset _PspLoadImageNotifyRoutine
|
|
('PsRemoveLoadImageNotifyRoutine', "\x48\x8d\x0d"),
|
|
# lea rcx, offset _PspCreateThreadNotifyRoutine
|
|
('PsRemoveCreateThreadNotifyRoutine', "\x48\x8d\x0d"),
|
|
# mov edi, offset _PspCreateProcessNotifyRoutine
|
|
#('PsSetCreateProcessNotifyRoutine', "\xbf"),
|
|
]
|
|
|
|
for symbol, hexbytes in routines:
|
|
|
|
# Locate the exported symbol in the NT module
|
|
symbol_rva = nt_mod.getprocaddress(symbol)
|
|
if symbol_rva == None:
|
|
continue
|
|
|
|
symbol_address = symbol_rva + nt_mod.DllBase
|
|
|
|
# Find the global variable referenced by the exported symbol
|
|
data = nt_mod.obj_vm.zread(symbol_address, 100)
|
|
|
|
offset = data.find(hexbytes)
|
|
if offset == -1:
|
|
continue
|
|
|
|
if bits32:
|
|
# Read the pointer to the list
|
|
p = obj.Object('Pointer',
|
|
offset = symbol_address + offset + len(hexbytes),
|
|
vm = nt_mod.obj_vm)
|
|
else:
|
|
# Read the pointer to the list
|
|
v = obj.Object('int',
|
|
offset = symbol_address + offset + len(hexbytes),
|
|
vm = nt_mod.obj_vm)
|
|
p = symbol_address + offset + 7 + v
|
|
|
|
# The list is an array of 8 _EX_FAST_REF objects
|
|
addrs = obj.Object('Array', count = 8, targetType = '_EX_FAST_REF',
|
|
offset = p, vm = nt_mod.obj_vm)
|
|
|
|
for addr in addrs:
|
|
callback = addr.dereference_as("_GENERIC_CALLBACK")
|
|
if callback:
|
|
yield symbol, callback.Callback, None
|
|
|
|
@staticmethod
|
|
def get_bugcheck_callbacks(addr_space):
|
|
"""
|
|
Enumerate generic Bugcheck callbacks.
|
|
|
|
Note: These structures don't exist in tagged pools, but you can find
|
|
them via KDDEBUGGER_DATA64 on all versions of Windows.
|
|
"""
|
|
|
|
kdbg = tasks.get_kdbg(addr_space)
|
|
list_head = kdbg.KeBugCheckCallbackListHead.dereference_as('_KBUGCHECK_CALLBACK_RECORD')
|
|
|
|
for l in list_head.Entry.list_of_type("_KBUGCHECK_CALLBACK_RECORD", "Entry"):
|
|
yield "KeBugCheckCallbackListHead", l.CallbackRoutine, l.Component.dereference()
|
|
|
|
@staticmethod
|
|
def get_registry_callbacks_legacy(nt_mod):
|
|
"""
|
|
Enumerate registry change callbacks.
|
|
|
|
This method of finding a global variable via disassembly of the
|
|
CmRegisterCallback function is only for XP systems. If it fails on
|
|
XP you can still find the callbacks using PoolScanGenericCallback.
|
|
|
|
On Vista and Windows 7, these callbacks are registered using the
|
|
CmRegisterCallbackEx function.
|
|
"""
|
|
|
|
if not has_distorm3:
|
|
return
|
|
|
|
symbol = "CmRegisterCallback"
|
|
|
|
# Get the RVA of the symbol from NT's EAT
|
|
symbol_rva = nt_mod.getprocaddress(symbol)
|
|
if symbol_rva == None:
|
|
return
|
|
|
|
# Absolute VA to the symbol code
|
|
symbol_address = symbol_rva + nt_mod.DllBase
|
|
|
|
# Read the function prologue
|
|
data = nt_mod.obj_vm.zread(symbol_address, 200)
|
|
|
|
c = 0
|
|
vector = None
|
|
|
|
# Looking for MOV EBX, CmpCallBackVector
|
|
# This may be the first or second MOV EBX instruction
|
|
for op in distorm3.Decompose(symbol_address, data, distorm3.Decode32Bits):
|
|
if (op.valid and op.mnemonic == "MOV"
|
|
and len(op.operands) == 2
|
|
and op.operands[0].name == 'EBX'):
|
|
vector = op.operands[1].value
|
|
if c == 1:
|
|
break
|
|
else:
|
|
c += 1
|
|
|
|
# Can't find the global variable
|
|
if vector == None:
|
|
return
|
|
|
|
# The vector is an array of 100 _EX_FAST_REF objects
|
|
addrs = obj.Object("Array", count = 100, offset = vector,
|
|
vm = nt_mod.obj_vm, targetType = "_EX_FAST_REF")
|
|
|
|
for addr in addrs:
|
|
callback = addr.dereference_as("_EX_CALLBACK_ROUTINE_BLOCK")
|
|
if callback:
|
|
yield symbol, callback.Function, None
|
|
|
|
@staticmethod
|
|
def get_bugcheck_reason_callbacks(nt_mod):
|
|
"""
|
|
Enumerate Bugcheck Reason callbacks.
|
|
|
|
Note: These structures don't exist in tagged pools, so we
|
|
find them by locating the list head which is a non-exported
|
|
NT symbol. The method works on all x86 versions of Windows.
|
|
|
|
mov [eax+KBUGCHECK_REASON_CALLBACK_RECORD.Entry.Blink], \
|
|
offset _KeBugCheckReasonCallbackListHead
|
|
"""
|
|
|
|
symbol = "KeRegisterBugCheckReasonCallback"
|
|
|
|
bits32 = nt_mod.obj_vm.profile.metadata.get("memory_model", "32bit") == "32bit"
|
|
|
|
if bits32:
|
|
hexbytes = "\xC7\x40\x04"
|
|
else:
|
|
hexbytes = "\x48\x8d\x0d"
|
|
|
|
# Locate the symbol RVA
|
|
symbol_rva = nt_mod.getprocaddress(symbol)
|
|
if symbol_rva == None:
|
|
return
|
|
|
|
# Compute the absolute virtual address
|
|
symbol_address = symbol_rva + nt_mod.DllBase
|
|
|
|
data = nt_mod.obj_vm.zread(symbol_address, 200)
|
|
|
|
# Search for the pattern
|
|
offset = data.find(hexbytes)
|
|
if offset == -1:
|
|
return
|
|
|
|
if bits32:
|
|
p = obj.Object('Pointer',
|
|
offset = symbol_address + offset + len(hexbytes),
|
|
vm = nt_mod.obj_vm)
|
|
bugs = p.dereference_as('_KBUGCHECK_REASON_CALLBACK_RECORD')
|
|
else:
|
|
v = obj.Object("int", offset = symbol_address + offset + len(hexbytes), vm = nt_mod.obj_vm)
|
|
p = symbol_address + offset + 7 + v
|
|
bugs = obj.Object("_KBUGCHECK_REASON_CALLBACK_RECORD", offset = p, vm = nt_mod.obj_vm)
|
|
|
|
for l in bugs.Entry.list_of_type("_KBUGCHECK_REASON_CALLBACK_RECORD", "Entry"):
|
|
if nt_mod.obj_vm.is_valid_address(l.CallbackRoutine):
|
|
yield symbol, l.CallbackRoutine, l.Component.dereference()
|
|
|
|
def calculate(self):
|
|
addr_space = utils.load_as(self._config)
|
|
|
|
bits32 = addr_space.profile.metadata.get("memory_model", "32bit") == "32bit"
|
|
|
|
# Get the OS version we're analyzing
|
|
version = (addr_space.profile.metadata.get('major', 0),
|
|
addr_space.profile.metadata.get('minor', 0))
|
|
|
|
modlist = list(modules.lsmod(addr_space))
|
|
mods = dict((addr_space.address_mask(mod.DllBase), mod) for mod in modlist)
|
|
mod_addrs = sorted(mods.keys())
|
|
|
|
# Valid for Vista and later
|
|
if version >= (6, 0):
|
|
self.scanners.append(PoolScanDbgPrintCallback)
|
|
self.scanners.append(PoolScanRegistryCallback)
|
|
self.scanners.append(PoolScanPnp9)
|
|
self.scanners.append(PoolScanPnpD)
|
|
self.scanners.append(PoolScanPnpC)
|
|
|
|
for objct in self.scan_results(addr_space):
|
|
name = objct.obj_name
|
|
if name == "_REGISTRY_CALLBACK":
|
|
info = "CmRegisterCallback", objct.Function, None
|
|
yield info, mods, mod_addrs
|
|
elif name == "_DBGPRINT_CALLBACK":
|
|
info = "DbgSetDebugPrintCallback", objct.Function, None
|
|
yield info, mods, mod_addrs
|
|
elif name == "_SHUTDOWN_PACKET":
|
|
driver = objct.DeviceObject.dereference().DriverObject
|
|
index = devicetree.MAJOR_FUNCTIONS.index('IRP_MJ_SHUTDOWN')
|
|
address = driver.MajorFunction[index]
|
|
details = str(driver.DriverName or "-")
|
|
info = "IoRegisterShutdownNotification", address, details
|
|
yield info, mods, mod_addrs
|
|
elif name == "_GENERIC_CALLBACK":
|
|
info = "GenericKernelCallback", objct.Callback, None
|
|
yield info, mods, mod_addrs
|
|
elif name == "_NOTIFY_ENTRY_HEADER":
|
|
# Dereference the driver object pointer
|
|
driver = objct.DriverObject.dereference()
|
|
# Instantiate an object header for the driver name
|
|
header = driver.get_object_header()
|
|
# Grab the object name
|
|
driver_name = header.NameInfo.Name.v()
|
|
info = objct.EventCategory, objct.CallbackRoutine, driver_name
|
|
yield info, mods, mod_addrs
|
|
elif name == "_NOTIFICATION_PACKET":
|
|
info = "IoRegisterFsRegistrationChange", objct.NotificationRoutine, None
|
|
yield info, mods, mod_addrs
|
|
|
|
for info in self.get_kernel_callbacks(modlist[0]):
|
|
yield info, mods, mod_addrs
|
|
|
|
for info in self.get_bugcheck_callbacks(addr_space):
|
|
yield info, mods, mod_addrs
|
|
|
|
for info in self.get_bugcheck_reason_callbacks(modlist[0]):
|
|
yield info, mods, mod_addrs
|
|
|
|
# Valid for XP
|
|
if bits32 and version == (5, 1):
|
|
for info in self.get_registry_callbacks_legacy(modlist[0]):
|
|
yield info, mods, mod_addrs
|
|
|
|
def unified_output(self, data):
|
|
return TreeGrid([("Type", str),
|
|
("Callback", Address),
|
|
("Module", str),
|
|
("Details", str)],
|
|
self.generator(data))
|
|
|
|
def generator(self, data):
|
|
for (sym, cb, detail), mods, mod_addrs in data:
|
|
|
|
module = tasks.find_module(mods, mod_addrs, mods.values()[0].obj_vm.address_mask(cb))
|
|
|
|
## The original callbacks plugin searched driver objects
|
|
## if the owning module isn't found (Rustock.B). We leave that
|
|
## task up to the user this time, and will be incoporating
|
|
## some different module association methods later.
|
|
if module:
|
|
module_name = module.BaseDllName or module.FullDllName
|
|
else:
|
|
module_name = "UNKNOWN"
|
|
|
|
yield (0, [str(sym), Address(cb), str(module_name), str(detail or "-")])
|