321 lines
9.9 KiB
Python
321 lines
9.9 KiB
Python
# This file is part of creddump.
|
|
#
|
|
# creddump 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 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# creddump 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 creddump. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
"""
|
|
@author: Brendan Dolan-Gavitt
|
|
@license: GNU General Public License 2.0 or later
|
|
@contact: bdolangavitt@wesleyan.edu
|
|
"""
|
|
|
|
# pylint: disable=missing-docstring,invalid-name,no-else-return,arguments-differ,unused-argument
|
|
|
|
from operator import itemgetter
|
|
from struct import unpack
|
|
|
|
from framework.object import get_obj_offset, builtin_types, read_value, read_unicode_string, read_string, read_obj
|
|
from framework.types import regtypes as types
|
|
|
|
|
|
def get_ptr_type(structure, member):
|
|
"""Return the type a pointer points to.
|
|
|
|
Arguments:
|
|
structure : the name of the structure from vtypes
|
|
member : a list of members
|
|
|
|
Example:
|
|
get_ptr_type('_EPROCESS', ['ActiveProcessLinks', 'Flink']) => ['_LIST_ENTRY']
|
|
"""
|
|
if len(member) > 1:
|
|
_, tp = get_obj_offset(types, [structure, member[0]])
|
|
if tp == 'array':
|
|
return types[structure][1][member[0]][1][2][1]
|
|
else:
|
|
return get_ptr_type(tp, member[1:])
|
|
else:
|
|
return types[structure][1][member[0]][1][1]
|
|
|
|
|
|
class Obj(object):
|
|
"""Base class for all objects.
|
|
|
|
May return a subclass for certain data types to allow
|
|
for special handling.
|
|
"""
|
|
|
|
def __new__(cls, name, address, space):
|
|
if name in globals():
|
|
# This is a bit of "magic"
|
|
# Could be replaced with a dict mapping type names to types
|
|
return globals()[name](name, address, space)
|
|
elif name in builtin_types:
|
|
return Primitive(name, address, space)
|
|
else:
|
|
obj = object.__new__(cls)
|
|
return obj
|
|
|
|
def __init__(self, name, address, space):
|
|
self.name = name
|
|
self.address = address
|
|
self.space = space
|
|
|
|
# Subclasses can add fields to this list if they want them
|
|
# to show up in values() or members(), even if they do not
|
|
# appear in the vtype definition
|
|
self.extra_members = []
|
|
|
|
def __getattribute__(self, attr):
|
|
try:
|
|
return object.__getattribute__(self, attr)
|
|
except AttributeError:
|
|
pass
|
|
|
|
if self.name in builtin_types:
|
|
raise AttributeError("Primitive types have no dynamic attributes")
|
|
|
|
try:
|
|
off, tp = get_obj_offset(types, [self.name, attr])
|
|
except:
|
|
raise AttributeError("'%s' has no attribute '%s'" % (self.name, attr))
|
|
|
|
if tp == 'array':
|
|
a_len = types[self.name][1][attr][1][1]
|
|
l = []
|
|
for i in range(a_len):
|
|
a_off, a_tp = get_obj_offset(types, [self.name, attr, i])
|
|
if a_tp == 'pointer':
|
|
ptp = get_ptr_type(self.name, [attr, i])
|
|
l.append(Pointer(a_tp, self.address + a_off, self.space, ptp))
|
|
else:
|
|
l.append(Obj(a_tp, self.address + a_off, self.space))
|
|
return l
|
|
elif tp == 'pointer':
|
|
# Can't just return a Obj here, since pointers need to also
|
|
# know what type they point to.
|
|
ptp = get_ptr_type(self.name, [attr])
|
|
return Pointer(tp, self.address + off, self.space, ptp)
|
|
else:
|
|
return Obj(tp, self.address + off, self.space)
|
|
|
|
def __truediv__(self, other):
|
|
if isinstance(other, (tuple, list)):
|
|
return Pointer(other[0], self.address, self.space, other[1])
|
|
elif isinstance(other, str):
|
|
return Obj(other, self.address, self.space)
|
|
else:
|
|
raise ValueError("Must provide a type name as string for casting")
|
|
|
|
def members(self):
|
|
"""Return a list of this object's members, sorted by offset."""
|
|
|
|
# Could also just return the list
|
|
membs = [(k, v[0]) for k, v in list(types[self.name][1].items())]
|
|
membs.sort(key=itemgetter(1))
|
|
return list(map(itemgetter(0), membs)) + self.extra_members
|
|
|
|
def values(self):
|
|
"""Return a dictionary of this object's members and their values"""
|
|
|
|
valdict = {}
|
|
for k in self.members():
|
|
valdict[k] = getattr(self, k)
|
|
return valdict
|
|
|
|
def bytes(self, length=-1):
|
|
"""Get bytes starting at the address of this object.
|
|
|
|
Arguments:
|
|
length : the number of bytes to read. Default: size of
|
|
this object.
|
|
"""
|
|
|
|
if length == -1:
|
|
length = self.size()
|
|
return self.space.read(self.address, length)
|
|
|
|
def size(self):
|
|
"""Get the size of this object."""
|
|
|
|
if self.name in builtin_types:
|
|
return builtin_types[self.name][0]
|
|
else:
|
|
return types[self.name][0]
|
|
|
|
def __repr__(self):
|
|
return "<%s @%08x>" % (self.name, self.address)
|
|
|
|
def __eq__(self, other):
|
|
if not isinstance(other, Obj):
|
|
raise TypeError("Types are incomparable")
|
|
return self.address == other.address and self.name == other.name
|
|
|
|
def __ne__(self, other):
|
|
return not self.__eq__(other)
|
|
|
|
def __hash__(self):
|
|
return hash(self.address) ^ hash(self.name)
|
|
|
|
def is_valid(self):
|
|
return self.space.is_valid_address(self.address)
|
|
|
|
def get_offset(self, member):
|
|
return get_obj_offset(types, [self.name] + member)
|
|
|
|
|
|
class Primitive(Obj):
|
|
"""Class to represent a primitive data type.
|
|
|
|
Attributes:
|
|
value : the python primitive value of this type
|
|
"""
|
|
|
|
def __new__(cls, *args, **kwargs):
|
|
obj = object.__new__(cls)
|
|
return obj
|
|
|
|
def __init__(self, name, address, space):
|
|
super(Primitive, self).__init__(name, address, space)
|
|
length, fmt = builtin_types[name]
|
|
data = space.read(address, length)
|
|
if not data:
|
|
self.value = None
|
|
else:
|
|
self.value = unpack(fmt, data)[0]
|
|
|
|
def __repr__(self):
|
|
return repr(self.value)
|
|
|
|
def members(self):
|
|
return []
|
|
|
|
|
|
class Pointer(Obj):
|
|
"""Class to represent pointers.
|
|
|
|
value : the object pointed to
|
|
|
|
If an attribute is not found in this instance,
|
|
the attribute will be looked up in the referenced
|
|
object."""
|
|
|
|
def __new__(cls, *args, **kwargs):
|
|
obj = object.__new__(cls)
|
|
return obj
|
|
|
|
def __init__(self, name, address, space, ptr_type):
|
|
super(Pointer, self).__init__(name, address, space)
|
|
ptr_address = read_value(space, name, address)
|
|
if ptr_type[0] == 'pointer':
|
|
self.value = Pointer(ptr_type[0], ptr_address, self.space, ptr_type[1])
|
|
else:
|
|
self.value = Obj(ptr_type[0], ptr_address, self.space)
|
|
|
|
def __getattribute__(self, attr):
|
|
# It's still nice to be able to access things through pointers
|
|
# without having to explicitly dereference them, so if we don't
|
|
# find an attribute via our superclass, just dereference the pointer
|
|
# and return the attribute in the pointed-to type.
|
|
try:
|
|
return super(Pointer, self).__getattribute__(attr)
|
|
except AttributeError:
|
|
return getattr(self.value, attr)
|
|
|
|
def __repr__(self):
|
|
return "<pointer to [%s @%08x]>" % (self.value.name, self.value.address)
|
|
|
|
def members(self):
|
|
return self.value.members()
|
|
|
|
|
|
class _UNICODE_STRING(Obj):
|
|
"""Class representing a _UNICODE_STRING
|
|
|
|
Adds the following behavior:
|
|
* The Buffer attribute is presented as a Python string rather
|
|
than a pointer to an unsigned short.
|
|
* The __str__ method returns the value of the Buffer.
|
|
"""
|
|
|
|
def __new__(cls, *args, **kwargs):
|
|
obj = object.__new__(cls)
|
|
return obj
|
|
|
|
def __str__(self):
|
|
return self.Buffer
|
|
|
|
# Custom Attributes
|
|
def getBuffer(self):
|
|
return read_unicode_string(self.space, types, [], self.address)
|
|
|
|
Buffer = property(fget=getBuffer)
|
|
|
|
|
|
class _CM_KEY_NODE(Obj):
|
|
def __new__(cls, *args, **kwargs):
|
|
obj = object.__new__(cls)
|
|
return obj
|
|
|
|
def getName(self):
|
|
return read_string(self.space, types, ['_CM_KEY_NODE', 'Name'],
|
|
self.address, self.NameLength.value)
|
|
|
|
Name = property(fget=getName)
|
|
|
|
|
|
class _CM_KEY_VALUE(Obj):
|
|
def __new__(cls, *args, **kwargs):
|
|
obj = object.__new__(cls)
|
|
return obj
|
|
|
|
def getName(self):
|
|
return read_string(self.space, types, ['_CM_KEY_VALUE', 'Name'],
|
|
self.address, self.NameLength.value)
|
|
|
|
Name = property(fget=getName)
|
|
|
|
|
|
class _CHILD_LIST(Obj):
|
|
def __new__(cls, *args, **kwargs):
|
|
obj = object.__new__(cls)
|
|
return obj
|
|
|
|
def getList(self):
|
|
lst = []
|
|
list_address = read_obj(self.space, types,
|
|
['_CHILD_LIST', 'List'], self.address)
|
|
for i in range(self.Count.value):
|
|
lst.append(Pointer("pointer", list_address + (i * 4), self.space,
|
|
["_CM_KEY_VALUE"]))
|
|
return lst
|
|
|
|
List = property(fget=getList)
|
|
|
|
|
|
class _CM_KEY_INDEX(Obj):
|
|
def __new__(cls, *args, **kwargs):
|
|
obj = object.__new__(cls)
|
|
return obj
|
|
|
|
def getList(self):
|
|
lst = []
|
|
for i in range(self.Count.value):
|
|
# we are ignoring the hash value here
|
|
off, __ = get_obj_offset(types, ['_CM_KEY_INDEX', 'List', i * 2])
|
|
lst.append(Pointer("pointer", self.address + off, self.space,
|
|
["_CM_KEY_NODE"]))
|
|
return lst
|
|
|
|
List = property(fget=getList)
|