mirror of
https://github.com/volatilityfoundation/volatility
synced 2026-06-08 18:04:46 +00:00
268 lines
11 KiB
Python
268 lines
11 KiB
Python
# Volatility
|
|
# Copyright (C) 2007-2013 Volatility Foundation
|
|
# Copyright (C) 2010,2011,2012 Michael Hale 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.obj as obj
|
|
import volatility.utils as utils
|
|
import volatility.plugins.gui.atoms as atoms
|
|
import volatility.plugins.gui.constants as consts
|
|
import volatility.plugins.gui.sessions as sessions
|
|
|
|
# Offsets to (_catomSysTableEntries, _aatomSysLoaded) in win32k.sys. We use
|
|
# this for translating the ihmod value into a fully-qualified DLL path name
|
|
# used by messagehooks and eventhooks plugins. If the values for your system
|
|
# aren't in the list, the plugins will still work, but the names of the Hook
|
|
# Module will not be available.
|
|
message_offsets_x86 = [
|
|
(0x001ab0a0, 0x001ab060), # ? (shylock.dmp)
|
|
(0x001aaea0, 0x001aae60), # 5.1.2600.6033 (XP SP3)
|
|
(0x001ac640, 0x001ac600), # 5.1.2600.6149 (XP)
|
|
(0x001a9400, 0x001a93c0), # 5.1.2600.5512 (XP SP3)
|
|
(0x001a9220, 0x001a91e0), # 5.1.2600.3335 (XP SP2)
|
|
(0x001a6f00, 0x001a6ec0), # 5.1.2600.2180 (XP SP2)
|
|
(0x001a0338, 0x001a03c0), # ? (W2K3 SP0)
|
|
(0x001b5600, 0x001b55c0), # 5.2.3790.4980 (W2K3 SP2)
|
|
(0x001b1440, 0x001b1400), # 5.2.3790.1830 (W2K3 SP1)
|
|
(0x001de0e0, 0x001de0a0), # 6.0.6000.16386 (Vista SP0)
|
|
(0x001e01e0, 0x001e01a0), # 6.0.6002.18005 (Vista SP2)
|
|
(0x001df0e0, 0x001df0a0), # 6.0.6001.18000 (W2K8 SP1)
|
|
(0x00219800, 0x002197C0), # 6.1.7600.16385 (Win 7 SP0)
|
|
(0x0021e800, 0x0021e7c0), # 6.1.7600.16988 (Win 7 SP0)
|
|
(0x0021a900, 0x0021a8c0), # 6.1.7601.17514 (Win 7 SP1)
|
|
]
|
|
message_offsets_x64 = [
|
|
(0x003b3880, 0x003b3840), # 5.2.3790.1830 (W2K3 SP1 / XP SP1)
|
|
(0x003b4880, 0x003b4840), # 5.2.3790.3959 (W2K3 SP2 / XP SP2)
|
|
(0x0028ba20, 0x0028b9e0), # 6.0.6000.16386 (Vista SP0)
|
|
(0x00288a20, 0x002889e0), # 6.0.6001.18000 (Vista SP1 / W2K8 SP1)
|
|
(0x00289c20, 0x00289be0), # 6.0.6002.18005 (Vista SP2 / W2K8 SP2)
|
|
(0x002da480, 0x002da440), # 6.1.7600.16385 (Win 7 SP0)
|
|
(0x002db6a0, 0x002db660), # 6.1.7601.17514 (Win 7 SP1)
|
|
(0x002e08a0, 0x002e0860), # 6.1.7601.17842 (W2K8 R2 SP1)
|
|
(0x002e06a0, 0x002e0660), # ?? (W2K8 R2 SP1)
|
|
]
|
|
|
|
class MessageHooks(atoms.Atoms, sessions.SessionsMixin):
|
|
"""List desktop and thread window message hooks"""
|
|
|
|
def calculate(self):
|
|
# Get all the atom tables and window stations
|
|
atom_tables = dict((atom_table, winsta)
|
|
for (atom_table, winsta)
|
|
in atoms.Atoms(self._config).calculate())
|
|
|
|
# Unique window stations
|
|
window_stations = [
|
|
winsta for winsta in atom_tables.values()
|
|
if winsta]
|
|
|
|
for winsta in window_stations:
|
|
yield winsta, atom_tables
|
|
|
|
def translate_atom(self, winsta, atom_tables, atom_id):
|
|
"""
|
|
Translate an atom into an atom name.
|
|
|
|
@param winsta: a tagWINDOWSTATION in the proper
|
|
session space
|
|
|
|
@param atom_tables: a dictionary with _RTL_ATOM_TABLE
|
|
instances as the keys and owning window stations as
|
|
the values.
|
|
|
|
@param index: the index into the atom handle table.
|
|
"""
|
|
|
|
# First check the default atoms
|
|
if consts.DEFAULT_ATOMS.has_key(atom_id):
|
|
return consts.DEFAULT_ATOMS[atom_id].Name
|
|
|
|
# A list of tables to search. The session atom tables
|
|
# have priority and will be searched first.
|
|
table_list = [
|
|
table for (table, window_station)
|
|
in atom_tables.items() if window_station == None
|
|
]
|
|
table_list.append(winsta.AtomTable)
|
|
|
|
## Fixme: the session atom tables are found via physical
|
|
## AS pool tag scanning, and there's no good way (afaik)
|
|
## to associate the table with its session. Thus if more
|
|
## than one session has atoms with the same id but different
|
|
## values, then we could possibly select the wrong one.
|
|
for table in table_list:
|
|
atom = table.find_atom(atom_id)
|
|
if atom:
|
|
return atom.Name
|
|
|
|
return obj.NoneObject("Cannot translate atom {0:#x}".format(atom_id))
|
|
|
|
def translate_hmod(self, winsta, atom_tables, index):
|
|
"""
|
|
Translate an ihmod (index into a handle table) into
|
|
an atom. This requires locating the win32k!_aatomSysLoaded
|
|
symbol. If the symbol cannot be found, we'll just report
|
|
back the ihmod value.
|
|
|
|
@param winsta: a tagWINDOWSTATION in the proper
|
|
session space
|
|
|
|
@param atom_tables: a dictionary with _RTL_ATOM_TABLE
|
|
instances as the keys and owning window stations as
|
|
the values.
|
|
|
|
@param index: the index into the atom handle table.
|
|
"""
|
|
|
|
# No need to translate these
|
|
if index == -1:
|
|
return "(Current Module)"
|
|
|
|
# To get an _MM_SESSION_SPACE we first start with a
|
|
# kernel AS and walk processes.
|
|
kernel_space = utils.load_as(self._config)
|
|
|
|
session = self.find_session_space(
|
|
kernel_space, winsta.dwSessionId)
|
|
|
|
# Report back the ihmod value if we fail
|
|
if not session:
|
|
return hex(index)
|
|
|
|
if winsta.obj_vm.profile.metadata.get('memory_model', '32bit') == '32bit':
|
|
message_offsets = message_offsets_x86
|
|
else:
|
|
message_offsets = message_offsets_x64
|
|
|
|
# Iterate over the possible offsets for win32k globals
|
|
for (count_offset, table_offset) in message_offsets:
|
|
|
|
# This is _catomSysTableEntries
|
|
count = obj.Object("unsigned long",
|
|
offset = session.Win32KBase + count_offset,
|
|
vm = session.obj_vm)
|
|
|
|
# We fail for this offset if the count is unreadable,
|
|
# its greater than 32, or its less than the requested
|
|
# handle table index.
|
|
if (count == None or count == 0 or count > 32 or
|
|
count <= index):
|
|
continue
|
|
|
|
# An array of atom IDs
|
|
atomlist = obj.Object("Array", targetType = "unsigned short",
|
|
offset = session.Win32KBase + table_offset,
|
|
count = count, vm = session.obj_vm)
|
|
|
|
# Our last sanity check is that the number of valid
|
|
# atoms equals the claimed number of atoms. This check
|
|
# is currently commented out because on at least one image
|
|
# (shylock.dmp), the count is 3 but there are only 2 valid
|
|
# atoms, thus we end up skipping it.
|
|
#valid_entries = len([atom for atom in atoms if atom != 0])
|
|
#if count != valid_entries:
|
|
# continue
|
|
|
|
# We can stop after finding a potential atom
|
|
atom_id = atomlist[index]
|
|
|
|
# Attempt to translate the atom into a module name
|
|
module = self.translate_atom(winsta, atom_tables, atom_id)
|
|
if module:
|
|
return module
|
|
|
|
# Report back the ihmod value if we fail
|
|
return hex(index)
|
|
|
|
def render_text(self, outfd, data):
|
|
"""Render output in table form"""
|
|
|
|
self.table_header(outfd,
|
|
[("Offset(V)", "[addrpad]"),
|
|
("Sess", "<6"),
|
|
("Desktop", "20"),
|
|
("Thread", "30"),
|
|
("Filter", "20"),
|
|
("Flags", "20"),
|
|
("Function", "[addrpad]"),
|
|
("Module", ""),
|
|
])
|
|
|
|
for winsta, atom_tables in data:
|
|
for desk in winsta.desktops():
|
|
for name, hook in desk.hooks():
|
|
module = self.translate_hmod(winsta, atom_tables, hook.ihmod)
|
|
self.table_row(outfd,
|
|
hook.obj_offset,
|
|
winsta.dwSessionId,
|
|
"{0}\\{1}".format(winsta.Name, desk.Name),
|
|
"<any>", name,
|
|
str(hook.flags),
|
|
hook.offPfn,
|
|
module,
|
|
)
|
|
|
|
for thrd in desk.threads():
|
|
info = "{0} ({1} {2})".format(
|
|
thrd.pEThread.Cid.UniqueThread,
|
|
thrd.ppi.Process.ImageFileName,
|
|
thrd.ppi.Process.UniqueProcessId
|
|
)
|
|
for name, hook in thrd.hooks():
|
|
module = self.translate_hmod(winsta, atom_tables, hook.ihmod)
|
|
self.table_row(outfd,
|
|
hook.obj_offset,
|
|
winsta.dwSessionId,
|
|
"{0}\\{1}".format(winsta.Name, desk.Name),
|
|
info, name,
|
|
str(hook.flags),
|
|
hook.offPfn,
|
|
module,
|
|
)
|
|
|
|
def render_block(self, outfd, data):
|
|
"""Render output as a block"""
|
|
|
|
def write_block(outfd, winsta, desk, hook, module, thread):
|
|
outfd.write("{0:<10} : {1:#x}\n".format("Offset(V)", hook.obj_offset))
|
|
outfd.write("{0:<10} : {1}\n".format("Session", winsta.dwSessionId))
|
|
outfd.write("{0:<10} : {1}\n".format("Desktop", "{0}\\{1}".format(winsta.Name, desk.Name)))
|
|
outfd.write("{0:<10} : {1}\n".format("Thread", thread))
|
|
outfd.write("{0:<10} : {1}\n".format("Filter", name))
|
|
outfd.write("{0:<10} : {1}\n".format("Flags", str(hook.flags)))
|
|
outfd.write("{0:<10} : {1:#x}\n".format("Procedure", hook.offPfn))
|
|
outfd.write("{0:<10} : {1}\n".format("ihmod", hook.ihmod))
|
|
outfd.write("{0:<10} : {1}\n\n".format("Module", module))
|
|
|
|
for winsta, atom_tables in data:
|
|
for desk in winsta.desktops():
|
|
for name, hook in desk.hooks():
|
|
module = self.translate_hmod(winsta, atom_tables, hook.ihmod)
|
|
write_block(outfd, winsta, desk, hook, module, "<any>")
|
|
|
|
for thrd in desk.threads():
|
|
info = "{0} ({1} {2})".format(
|
|
thrd.pEThread.Cid.UniqueThread,
|
|
thrd.ppi.Process.ImageFileName,
|
|
thrd.ppi.Process.UniqueProcessId
|
|
)
|
|
for name, hook in thrd.hooks():
|
|
module = self.translate_hmod(winsta, atom_tables, hook.ihmod)
|
|
write_block(outfd, winsta, desk, hook, module, info)
|