# Volatility
# Copyright (c) 2008-2013 Volatility Foundation
# Copyright (c) 2008 Brendan Dolan-Gavitt <bdolangavitt@wesleyan.edu>
#
# 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 datetime, struct, calendar
import volatility.plugins.overlays.basic as basic
import volatility.plugins.kpcrscan as kpcr
import volatility.plugins.kdbgscan as kdbg
import volatility.timefmt as timefmt
import volatility.debug as debug
import volatility.obj as obj
import volatility.addrspace as addrspace
import volatility.exceptions as exceptions
import volatility.utils as utils
import volatility.plugins.common as common
import volatility.constants as constants
import volatility.plugins.vadinfo as vadinfo

# Standard vtypes are usually autogenerated by scanning through header
# files, collecting debugging symbol data etc. This file defines
# fixups and improvements to the standard types.
windows_overlay = {
    'VOLATILITY_MAGIC' : [None, {
    # Profile specific values
    'DTBSignature' : [ 0x0, ['VolatilityMagic', dict(value = "Volatility DTBSignature unspecified")]],
    'KUSER_SHARED_DATA' : [ 0x0, ['VolatilityMagic', dict(value = 0xFFDF0000)]],
    'KDBGHeader' : [ 0x0, ['VolatilityMagic', dict(value = 'Volatility KDBGHeader unspecified')]],
    # Configuration options
    'DTB' : [ 0x0, ['VolatilityDTB', dict(configname = "DTB")]],
    'KPCR' : [ 0x0, ['VolatilityMagic', dict(value = 0xffdff000, configname = "KPCR")]],
    'KDBG' : [ 0x0, ['VolatilityKDBG', dict(configname = "KDBG")]],
    'IA32ValidAS': [ 0x0, ['VolatilityIA32ValidAS']],
    'AMD64ValidAS': [ 0x0, ['VolatilityAMD64ValidAS']],
    # Pool allocations are aligned to this many bytes.
    'PoolAlignment': [0x0, ['VolatilityMagic', dict(value = 8)]],
    #hibrfil.sys values
    'HibrProcPage': [0x0, ['VolatilityMagic', dict(value = 0x0)]],
    'HibrEntryCount': [0x0, ['VolatilityMagic', dict(value = 0x0)]],
    'MM_MAX_COMMIT': [ 0x0, ['VolatilityMagic', dict(value = 0x7ffffffffffff)]],
    'PolicyKey': [0x0, ['VolatilityMagic', dict(value = "PolSecretEncryptionKey")]],
    }],

    '_EPROCESS' : [ None, {
    'CreateTime' : [ None, ['WinTimeStamp', dict(is_utc = True)]],
    'ExitTime' : [ None, ['WinTimeStamp', dict(is_utc = True)]],
    'InheritedFromUniqueProcessId' : [ None, ['unsigned int']],
    'ImageFileName' : [ None, ['String', dict(length = 16)]],
    'UniqueProcessId' : [ None, ['unsigned int']],
    }],

    '_ETHREAD' : [ None, {
    'CreateTime' : [ None, ['ThreadCreateTimeStamp', dict(is_utc = True)]],
    'ExitTime' : [ None, ['WinTimeStamp', dict(is_utc = True)]],
    }],

    '_OBJECT_SYMBOLIC_LINK' : [ None, {
    'CreationTime' : [ None, ['WinTimeStamp', dict(is_utc = True)]],
    }],

    '_KUSER_SHARED_DATA' : [ None, {
    'SystemTime' : [ None, ['WinTimeStamp', dict(is_utc = True)]],
    'TimeZoneBias' : [ None, ['WinTimeStamp', {}]],
    }],

    # The DTB is really an array of 2 ULONG_PTR but we only need the first one 
    # which is the value loaded into CR3. The second one, according to procobj.c 
    # of the wrk-v1.2, contains the PTE that maps something called hyper space. 
    '_KPROCESS' : [ None, {
    'DirectoryTableBase' : [ None, ['unsigned long']],
    }],

    '_IMAGE_SECTION_HEADER' : [ None, {
    'Name' : [ 0x0, ['String', dict(length = 8)]],
    }],

    '_IMAGE_FILE_HEADER': [ None, {
    'TimeDateStamp' : [None, ['UnixTimeStamp', dict(is_utc = True)]],
    }],

    '_LDR_DATA_TABLE_ENTRY': [ None, {
    'TimeDateStamp' : [None, ['UnixTimeStamp', dict(is_utc = True)]],
    }],

    '_DBGKD_GET_VERSION64' : [  None, {
    'DebuggerDataList' : [ None, ['pointer', ['unsigned long']]],
    }],

    '_CM_KEY_NODE' : [ None, {
    'Signature' : [ None, ['String', dict(length = 2)]],
    'LastWriteTime' : [ None, ['WinTimeStamp', dict(is_utc = True)]],
    'Name' : [ None, ['String', dict(length = lambda x: x.NameLength)]],
    'Parent': [ None, ['pointer32', ['_CM_KEY_NODE']]],
    }],

    '_CM_NAME_CONTROL_BLOCK' : [ None, {
    'Name' : [ None, ['String', dict(length = lambda x: x.NameLength)]],
    }],

    '_CHILD_LIST' : [ None, {
    'List' : [ None, ['pointer32', ['array', lambda x: x.Count,
                                 ['pointer32', ['_CM_KEY_VALUE']]]]],
    }],

    '_CM_KEY_VALUE' : [ None, {
    'Signature' : [ None, ['String', dict(length = 2)]],
    'Name' : [ None, ['String', dict(length = lambda x: x.NameLength)]],
    }],

    '_CM_KEY_INDEX' : [ None, {
    'Signature' : [ None, ['String', dict(length = 2)]],
    'List' : [ None, ['array', lambda x: x.Count.v() * 2, ['pointer32', ['_CM_KEY_NODE']]]],
    }],

    'PO_MEMORY_IMAGE' : [ None, {
    'Signature':   [ None, ['String', dict(length = 4)]],
    'SystemTime' : [ None, ['WinTimeStamp', dict(is_utc = True)]],
    }],

    '_PHYSICAL_MEMORY_DESCRIPTOR' : [ None, {
    'Run' : [ None, ['array', lambda x: x.NumberOfRuns, ['_PHYSICAL_MEMORY_RUN']]],
    }],

    '_TOKEN' : [ None, {
    'UserAndGroups' : [ None, ['pointer', ['array', lambda x: x.UserAndGroupCount,
                                 ['_SID_AND_ATTRIBUTES']]]],
    }],

    '_SID' : [ None, {
    'SubAuthority' : [ None, ['array', lambda x: x.SubAuthorityCount, ['unsigned long']]],
    }],

    '_CLIENT_ID': [ None, {
    'UniqueProcess' : [ None, ['unsigned int']],
    'UniqueThread' : [ None, ['unsigned int']],
    }],
}

class ExecutiveObjectMixin(object):
    """A mixin for executive objects to allow easy
    derivation of the object's _OBJECT_HEADER struct
    """

    def get_object_header(self):
        return obj.Object("_OBJECT_HEADER", vm = self.obj_vm, 
                        offset = self.obj_offset - 
                        self.obj_vm.profile.get_obj_offset("_OBJECT_HEADER", "Body"), 
                        native_vm = self.obj_native_vm)

class _UNICODE_STRING(obj.CType):
    """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 v(self):
        """
        If the claimed length of the string is acceptable, return a unicode string.
        Otherwise, return a NoneObject.
        """
        data = self.dereference()
        if data:
            return unicode(data)
        return data

    def dereference(self):
        length = self.Length.v()
        if length > 0 and length <= 1024:
            data = self.Buffer.dereference_as('String', encoding = 'utf16', length = length)
            return data
        else:
            return obj.NoneObject("Buffer length {0} for _UNICODE_STRING not within bounds".format(length))

    def proxied(self, _name):
        return str(self)

    def __nonzero__(self):
        ## Unicode strings are valid if they point at a valid memory
        return bool(self.Buffer and self.Length.v() > 0 and self.Length.v() <= 1024)

    def __format__(self, formatspec):
        return format(self.v(), formatspec)

    def __str__(self):
        return str(self.dereference())

    def __unicode__(self):
        return unicode(self.dereference())

    def __len__(self):
        return len(self.dereference())

class _LIST_ENTRY(obj.CType):
    """ Adds iterators for _LIST_ENTRY types """
    def list_of_type(self, type, member, forward = True, head_sentinel = True):
        if not self.is_valid():
            return

        ## Get the first element
        if forward:
            nxt = self.Flink.dereference()
        else:
            nxt = self.Blink.dereference()

        offset = self.obj_vm.profile.get_obj_offset(type, member)

        seen = set()
        if head_sentinel:
            # We're a header element and not to be included in the list
            seen.add(self.obj_offset)

        while nxt.is_valid() and nxt.obj_offset not in seen:
            ## Instantiate the object
            item = obj.Object(type, offset = nxt.obj_offset - offset,
                                    vm = self.obj_vm,
                                    parent = self.obj_parent,
                                    native_vm = self.obj_native_vm,
                                    name = type)

            seen.add(nxt.obj_offset)

            yield item

            if forward:
                nxt = item.m(member).Flink.dereference()
            else:
                nxt = item.m(member).Blink.dereference()

    def __nonzero__(self):
        ## List entries are valid when both Flinks and Blink are valid
        return bool(self.Flink) or bool(self.Blink)

    def __iter__(self):
        return self.list_of_type(self.obj_parent.obj_name, self.obj_name)

class WinTimeStamp(obj.NativeType):
    """Class for handling Windows Time Stamps"""

    def __init__(self, theType, offset, vm, is_utc = False, **kwargs):
        self.is_utc = is_utc
        obj.NativeType.__init__(self, theType, offset, vm, format_string = "q", **kwargs)

    def windows_to_unix_time(self, windows_time):
        """
        Converts Windows 64-bit time to UNIX time

        @type  windows_time:  Integer
        @param windows_time:  Windows time to convert (64-bit number)

        @rtype  Integer
        @return  UNIX time
        """
        if windows_time == None or windows_time == 0:
            unix_time = 0
        else:
            unix_time = windows_time / 10000000
            unix_time = unix_time - 11644473600

        if unix_time < 0:
            unix_time = 0

        return unix_time

    def as_windows_timestamp(self):
        return obj.NativeType.v(self)

    def v(self):
        value = self.as_windows_timestamp()
        return self.windows_to_unix_time(value)

    def __nonzero__(self):
        return self.v() != 0

    def __str__(self):
        return "{0}".format(self)

    def as_datetime(self):
        try:
            dt = datetime.datetime.utcfromtimestamp(self.v())
            if self.is_utc:
                # Only do dt.replace when dealing with UTC
                dt = dt.replace(tzinfo = timefmt.UTC())
        except ValueError, e:
            return obj.NoneObject("Datetime conversion failure: " + str(e))
        return dt

    def __format__(self, formatspec):
        """Formats the datetime according to the timefmt module"""
        dt = self.as_datetime()
        if dt != None:
            return format(timefmt.display_datetime(dt), formatspec)
        return "-"

class DosDate(obj.NativeType):
    def __init__(self, theType, offset, vm, is_utc = False, **kwargs):
        self.is_utc = is_utc
        obj.NativeType.__init__(self, theType, offset, vm, format_string = "<I", **kwargs)

    def as_dos_timestamp(self):
        return obj.NativeType.v(self)

    def v(self):
        value = self.as_dos_timestamp()
        return self.dos_to_unix_time(value)

    def __nonzero__(self):
        return self.v() != 0

    def __str__(self):
        return "{0}".format(self)

    def as_datetime(self):
        try:
            dt = datetime.datetime.utcfromtimestamp(self.v())
            if self.is_utc:
                # Only do dt.replace when dealing with UTC
                dt = dt.replace(tzinfo = timefmt.UTC())
        except ValueError, e:
            return obj.NoneObject("Datetime conversion failure: " + str(e))
        return dt

    def __format__(self, formatspec):
        """Formats the datetime according to the timefmt module"""
        dt = self.as_datetime()
        if dt != None:
            return format(timefmt.display_datetime(dt), formatspec)
        return "-"

    def dos_to_unix_time(self, dosdate):
        """  
        Every previous conversion algorithm takes in two unsigned shorts separately.  
        We're not doing that here, but instead getting those shorts from an unsigned int (dosdate)

        dosdate: 4 bytes little endian converted to:
        date: 2 bytes
        time: 2 bytes

        conversion to datetime taken from: http://code.google.com/p/libforensics/
        dosdate is already in UTC: http://download.polytechnic.edu.na/pub4/download.sourceforge.net/pub/sourceforge/l/project/li/liblnk/Documentation/Windows%20Shell%20Item%20format/Windows%20Shell%20Item%20format.pdf
        """
        date = struct.unpack(">H", ''.join([chr(x) for x in [(dosdate >> 8) & 0xff, (dosdate & 0xff)]]))[0]
        time = struct.unpack(">H", ''.join([chr(x) for x in [(dosdate >> 24) & 0xff, (dosdate >> 16) & 0xff]]))[0]
        seconds = (time & 0x1F) * 2
        minutes = (time & 0x7E0) >> 5
        hours = (time & 0xF800) >> 11
        day = date & 0x1F
        month = (date & 0x1E0) >> 5
        year = ((date & 0xFE00) >> 9) + 1980

        #convert into timestamp and return:
        try:
            return calendar.timegm(datetime.datetime(year, month, day, hours, minutes, seconds).utctimetuple())
        except ValueError:
            return 0
        # if we use the following we need to s/utcfromtimestamp/fromtimestamp/ in as_datetime() function:
        #return time.mktime(datetime.datetime(year, month, day, hours, minutes, seconds).timetuple())


class _EPROCESS(obj.CType, ExecutiveObjectMixin):
    """ An extensive _EPROCESS with bells and whistles """
    @property
    def Peb(self):
        """ Returns a _PEB object which is using the process address space.

        The PEB structure is referencing back into the process address
        space so we need to switch address spaces when we look at
        it. This method ensure this happens automatically.
        """
        process_ad = self.get_process_address_space()
        if process_ad:
            offset = self.m("Peb").v()
            peb = obj.Object("_PEB", offset, vm = process_ad,
                                    name = "Peb", parent = self)

            if peb.is_valid():
                return peb

        return obj.NoneObject("Peb not found")

    def get_process_address_space(self):
        """ Gets a process address space for a task given in _EPROCESS """
        directory_table_base = self.Pcb.DirectoryTableBase.v()

        try:
            process_as = self.obj_vm.__class__(self.obj_vm.base, self.obj_vm.get_config(), dtb = directory_table_base)
        except AssertionError, _e:
            return obj.NoneObject("Unable to get process AS")

        process_as.name = "Process {0}".format(self.UniqueProcessId)

        return process_as

    def _get_modules(self, the_list, the_type):
        """Generator for DLLs in one of the 3 PEB lists"""
        if self.UniqueProcessId and the_list:
            for l in the_list.list_of_type("_LDR_DATA_TABLE_ENTRY", the_type):
                yield l

    def get_init_modules(self):
        return self._get_modules(self.Peb.Ldr.InInitializationOrderModuleList, "InInitializationOrderLinks")

    def get_mem_modules(self):
        return self._get_modules(self.Peb.Ldr.InMemoryOrderModuleList, "InMemoryOrderLinks")

    def get_load_modules(self):
        return self._get_modules(self.Peb.Ldr.InLoadOrderModuleList, "InLoadOrderLinks")

    def get_token(self):
        """Return the process's TOKEN object if its valid"""

        # The dereference checks if the address is valid  
        # and returns obj.NoneObject if it fails 
        token = self.Token.dereference_as("_TOKEN")

        # This check fails if the above dereference failed 
        # or if any of the _TOKEN specific validity tests failed. 
        if token.is_valid():
            return token

        return obj.NoneObject("Cannot get process Token")

    @property
    def IsWow64(self):
        """Returns True if this is a wow64 process"""
        return hasattr(self, 'Wow64Process') and self.Wow64Process.v() != 0

    @property
    def SessionId(self):
        """Returns the Session ID of the process"""

        if self.Session.is_valid():
            process_space = self.get_process_address_space()
            if process_space:
                return obj.Object("_MM_SESSION_SPACE",
                                  offset = self.Session,
                                  vm = process_space).SessionId

        return obj.NoneObject("Cannot find process session")

    def get_vads(self, vad_filter = None, skip_max_commit = False):
        """
        Generator for MMVADs that match specific
        metadata.

        @param vad_filter: a callable that is passed the 
        current MMVAD and applies tests to the MMVAD struct
        members or nested struct members. 

        @param skip_max_commit: boolean, if true then VADs
        for Wow64 processes with the MM_MAX_COMMIT flag set
        will not be yielded. 

        @yields a tuple (mmvad, address_space). Where mmvad is
        the MMVAD object in kernel AS and address_space 
        is the process address space. 
        """

        # We absolutely need a process AS. If this 
        # fails then all else fails
        process_space = self.get_process_address_space()
        if not process_space:
            return

        max_commit = obj.VolMagic(process_space).MM_MAX_COMMIT.v()

        for vad in self.VadRoot.traverse():
            if not vad.is_valid():
                continue

            # Skip Wow64 MM_MAX_COMMIT range
            if skip_max_commit: 
                if self.IsWow64 and vad.CommitCharge == max_commit and vad.End > 0x7fffffff:
                    continue
                elif vad.Length > 0x7f000000000: # see issue #70
                    continue

            # Apply the meta filter if one is supplied
            if vad_filter:
                if not vad_filter(vad):
                    continue
            yield vad, process_space

    def search_process_memory(self, s):
        """
        Search memory for a simple byte string. 
        
        FIXME: as of 2.3 this parameter can also be a list to
        search for mutliple strings concurrently. The 
        single string will be deprecated in 3.0. 

        @param s: the string to search for.

        @returns every occurrance of the string 
        in process memory (as absolute address).
        """

        # Allow for some overlap in case objects are 
        # right on page boundaries 
        overlap = 1024
        
        # Make sure s in a list. This allows you to search for
        # multiple strings at once, without changing the API.
        if type(s) != list:
            debug.warning("Single strings to search_process_memory is deprecated, use a list instead")
            s = [s]

        # All MMVADs that belong to this process.
        for vad, address_space in self.get_vads(skip_max_commit = True):
            offset = vad.Start
            out_of_range = vad.Start + vad.Length
            while offset < out_of_range:
                # Read some data and match it.
                to_read = min(constants.SCAN_BLOCKSIZE + overlap, out_of_range - offset)
                data = address_space.zread(offset, to_read)
                if not data:
                    break
                for x in s:
                    for hit in utils.iterfind(data, x):
                        yield offset + hit
                offset += min(to_read, constants.SCAN_BLOCKSIZE)

    def _injection_filter(self, vad):
        """
        This is a callback that's executed by get_vads()
        when searching for injected code / hidden DLLs. 

        This looks for private allocations that are committed, 
        memory-resident, non-empty (not all zeros) and with an 
        original protection that includes write and execute. 

        It is important to note that protections are applied at 
        the allocation granularity (page level). Thus the original
        protection might not be the current protection, and it
        also might not apply to all pages in the VAD range. 

        @param vad: an MMVAD object.

        @returns: True if the MMVAD looks like it might
        contain injected code. 
        """
        protect = vadinfo.PROTECT_FLAGS.get(vad.VadFlags.Protection.v(), "")
        write_exec = "EXECUTE" in protect and "WRITE" in protect

        # The Write/Execute check applies to everything 
        if not write_exec:
            return False

        # This is a typical VirtualAlloc'd injection 
        if vad.VadFlags.PrivateMemory == 1 and vad.Tag == "VadS":
            return True

        # This is a stuxnet-style injection 
        if (vad.VadFlags.PrivateMemory == 0 and
                protect != "PAGE_EXECUTE_WRITECOPY"):
            return True

        return False

    def _mapped_file_filter(self, vad):
        """
        This is a callback that's executed by get_vads() 
        when searching for memory-mapped files. 

        @param vad: an MMVAD object.

        @returns: True if the MMVAD looks like it might
        contain a mapped file. 
        """

        return vad.VadFlags.PrivateMemory == 0 and vad.ControlArea

    def environment_variables(self):
        """Generator for environment variables. 

        The PEB points to our env block - a series of null-terminated
        unicode strings. Each string cannot be more than 0x7FFF chars. 
        End of the list is a quad-null. 
        """

        # Address of the environment block
        if not self.Peb.ProcessParameters.Environment.is_valid():
            return

        process_space = self.get_process_address_space()
        if not process_space:
            return

        block = self.Peb.ProcessParameters.Environment

        s = obj.Object("String", offset = block, vm = process_space,
            encoding = 'utf16', length = 0x7FFF)

        # The terminator is a quad null 
        while len(s):
            if s.count(u"=") == 1:
                yield s.split(u"=")
            # Scan forward the length of this string plus the null
            next_offset = s.obj_offset + ((len(s) + 1) * 2)
            s = obj.Object("String", offset = next_offset,
                vm = process_space, encoding = 'utf16', length = 0x7FFF)

    def is_valid(self):

        if not obj.CType.is_valid(self):
            return False

        if (self.Pcb.DirectoryTableBase == 0):
            return False

        if (self.Pcb.DirectoryTableBase % 0x20 != 0):
            return False

        list_head = self.ThreadListHead
        kernel = 0x80000000

        if (list_head.Flink < kernel) or (list_head.Blink < kernel):
            return False

        return True


class _TOKEN(obj.CType):
    """A class for Tokens"""

    def is_valid(self):
        """Override BaseObject.is_valid with some additional
        checks specific to _TOKEN objects."""
        return obj.CType.is_valid(self) and self.TokenInUse in (0, 1) and self.SessionId < 10

    def get_sids(self):
        """Generator for process SID strings"""
        if self.UserAndGroupCount < 0xFFFF:
            for sa in self.UserAndGroups.dereference():
                sid = sa.Sid.dereference_as('_SID')
                for i in sid.IdentifierAuthority.Value:
                    id_auth = i
                yield "S-" + "-".join(str(i) for i in (sid.Revision, id_auth) +
                                      tuple(sid.SubAuthority))

    def privileges(self):
        """Generator for privileges.

        @yields a tuple (value, present, enabled, default). 

        We only yield 'present' here for consistency with 
        the Vista+ privileges() generator. In the XP/2003 
        case, values will never be reported unless they're
        present (thus we hard-code it to True) but Vista+
        can be optional due to DKOM.
        """
        # The max size check originates from code seen in the 
        # DisplayPrivileges function of windbg's exts.dll 
        if self.PrivilegeCount < 1024:
            # This is a pointer to an array of _LUID_AND_ATTRIBUTES
            for luid in self.Privileges.dereference():
                # The Attributes member is a flag 
                enabled = luid.Attributes & 2 != 0
                default = luid.Attributes & 1 != 0
                yield luid.Luid.LowPart, True, enabled, default

class _OBJECT_TYPE(obj.CType, ExecutiveObjectMixin):
    pass

class _ETHREAD(obj.CType, ExecutiveObjectMixin):
    """ A class for threads """

    def owning_process(self):
        """Return the EPROCESS that owns this thread"""
        return self.ThreadsProcess.dereference()

    def attached_process(self):
        """Return the EPROCESS that this thread is currently
        attached to."""
        return self.Tcb.ApcState.Process.dereference_as("_EPROCESS")

    def is_valid(self):
        if not obj.CType.is_valid(self):
            return False

        ## check the start address
        if self.Cid.UniqueProcess.v() != 0 and self.StartAddress == 0:
            return False

        # win8 _KTHREAD doesn't have this member 
        if (hasattr(self.Tcb, 'SuspendSemaphore') and 
                self.Tcb.SuspendSemaphore.Header.Size != 0x05 and
                self.Tcb.SuspendSemaphore.Header.Type != 0x05):
           return False

        if (self.KeyedWaitSemaphore.Header.Size != 0x05 and
               self.KeyedWaitSemaphore.Header.Type != 0x05):
            return False

        return True

class _HANDLE_TABLE(obj.CType):
    """ A class for _HANDLE_TABLE. 
    
    This used to be a member of _EPROCESS but it was isolated per issue 
    91 so that it could be subclassed and used to service other handle 
    tables, such as the _KDDEBUGGER_DATA64.PspCidTable.
    """

    def get_item(self, entry, handle_value = 0):
        """Returns the OBJECT_HEADER of the associated handle. The parent
        is the _HANDLE_TABLE_ENTRY so that an object can be linked to its 
        GrantedAccess.
        """
        return entry.Object.dereference_as("_OBJECT_HEADER", parent = entry, handle_value = handle_value)

    def _make_handle_array(self, offset, level, depth = 0):
        """ Returns an array of _HANDLE_TABLE_ENTRY rooted at offset,
        and iterates over them.
        """

        # The counts below are calculated by taking the size of a page and dividing 
        # by the size of the data type contained within the page. For more information
        # see http://blogs.technet.com/b/markrussinovich/archive/2009/09/29/3283844.aspx
        if level > 0:
            count = 0x1000 / self.obj_vm.profile.get_obj_size("address")
            targetType = "address"
        else:
            count = 0x1000 / self.obj_vm.profile.get_obj_size("_HANDLE_TABLE_ENTRY")
            targetType = "_HANDLE_TABLE_ENTRY"

        table = obj.Object("Array", offset = offset, vm = self.obj_vm, count = count,
                           targetType = targetType, parent = self, native_vm = self.obj_native_vm)

        if table:
            for entry in table:
                if not entry.is_valid():
                    break

                if level > 0:
                    ## We need to go deeper:
                    for h in self._make_handle_array(entry, level - 1, depth):
                        yield h
                    depth += 1
                else:

                    # All handle values are multiples of four, on both x86 and x64. 
                    handle_multiplier = 4
                    # Calculate the starting handle value for this level. 
                    handle_level_base = depth * count * handle_multiplier
                    # The size of a handle table entry.
                    handle_entry_size = self.obj_vm.profile.get_obj_size("_HANDLE_TABLE_ENTRY")
                    # Finally, compute the handle value for this object. 
                    handle_value = ((entry.obj_offset - offset) /
                                   (handle_entry_size / handle_multiplier)) + handle_level_base

                    ## OK We got to the bottom table, we just resolve
                    ## objects here:
                    item = self.get_item(entry, handle_value)

                    if item == None:
                        continue

                    try:
                        # New object header
                        if item.TypeIndex != 0x0:
                            yield item
                    except AttributeError:
                        if item.Type.Name:
                            yield item

    def handles(self):
        """ A generator which yields this process's handles

        _HANDLE_TABLE tables are multi-level tables at the first level
        they are pointers to second level table, which might be
        pointers to third level tables etc, until the final table
        contains the real _OBJECT_HEADER table.

        This generator iterates over all the handles recursively
        yielding all handles. We take care of recursing into the
        nested tables automatically.
        """
        # This should work equally for 32 and 64 bit systems
        LEVEL_MASK = 7

        TableCode = self.TableCode.v() & ~LEVEL_MASK
        table_levels = self.TableCode.v() & LEVEL_MASK
        offset = TableCode

        for h in self._make_handle_array(offset, table_levels):
            yield h

class _OBJECT_HEADER(obj.CType):
    """A Volatility object to handle Windows object headers.

    This object applies only to versions below windows 7.
    """

    optional_headers = [('NameInfo', '_OBJECT_HEADER_NAME_INFO'),
                        ('HandleInfo', '_OBJECT_HEADER_HANDLE_INFO'),
                        ('QuotaInfo', '_OBJECT_HEADER_QUOTA_INFO')]

    def __init__(self, *args, **kwargs):
        # Usually we don't add members to objects like this, but its an
        # exception due to lack of better options. See Issue #135. 
        self.HandleValue = kwargs.get("handle_value", 0)
        obj.CType.__init__(self, *args, **kwargs)
        # Create accessors for optional headers
        self.find_optional_headers()

    def find_optional_headers(self):
        """Find this object's optional headers."""
        offset = self.obj_offset

        for name, objtype in self.optional_headers:
            if self.obj_vm.profile.has_type(objtype):
                header_offset = self.m(name + 'Offset').v()
                if header_offset:
                    o = obj.Object(objtype, offset - header_offset, vm = self.obj_vm, native_vm = self.obj_native_vm)
                else:
                    o = obj.NoneObject("Header {0} not set for object at {1:#x}".format(name, offset))

                self.newattr(name, o)

    @property
    def GrantedAccess(self):
        if self.obj_parent:
            return self.obj_parent.GrantedAccess
        return obj.NoneObject("No parent known")

    def dereference_as(self, theType):
        """Instantiate an object from the _OBJECT_HEADER.Body"""
        return obj.Object(theType, offset = self.Body.obj_offset, vm = self.obj_vm,
                         native_vm = self.obj_native_vm, parent = self)

    def get_object_type(self):
        """Return the object's type as a string"""
        type_obj = obj.Object("_OBJECT_TYPE", self.Type, self.obj_native_vm)

        return str(type_obj.Name or '')

    def is_valid(self):
        if not obj.CType.is_valid(self):
            return False

        if self.PointerCount > 0x1000000 or self.PointerCount < 0:
            return False

        return True

class _OBJECT_SYMBOLIC_LINK(obj.CType, ExecutiveObjectMixin):
    """A symbolic link object"""    

    def is_valid(self):
        return obj.CType.is_valid(self) and self.LinkTarget.v()

class _KMUTANT(obj.CType, ExecutiveObjectMixin):
    """A mutex object"""

class _FILE_OBJECT(obj.CType, ExecutiveObjectMixin):
    """Class for file objects"""

    def file_name_with_device(self):
        """Return the name of the file, prefixed with the name
        of the device object to which the file belongs"""
        name = ""
        if self.DeviceObject:
            object_hdr = obj.Object("_OBJECT_HEADER",
                            self.DeviceObject - self.obj_vm.profile.get_obj_offset("_OBJECT_HEADER", "Body"),
                            self.obj_native_vm)
            if object_hdr:
                name = "\\Device\\{0}".format(str(object_hdr.NameInfo.Name or ''))
        if self.FileName:
            name += str(self.FileName)
        return name

    def access_string(self):
        ## Make a nicely formatted ACL string
        AccessStr = (((self.ReadAccess > 0 and "R") or '-') +
                     ((self.WriteAccess > 0  and "W") or '-') +
                     ((self.DeleteAccess > 0 and "D") or '-') +
                     ((self.SharedRead > 0 and "r") or '-') +
                     ((self.SharedWrite > 0 and "w") or '-') +
                     ((self.SharedDelete > 0 and "d") or '-'))
        return AccessStr

    def is_valid(self):
        return obj.CType.is_valid(self) and self.FileName.v()

class _EX_FAST_REF(obj.CType):

    MAX_FAST_REF = 7

    def dereference_as(self, theType, parent = None, **kwargs):
        """Use the _EX_FAST_REF.Object pointer to resolve an object of the specified type"""
        return obj.Object(theType, self.Object.v() & ~self.MAX_FAST_REF, self.obj_native_vm, parent = parent or self, **kwargs)

class ThreadCreateTimeStamp(WinTimeStamp):
    """Handles ThreadCreateTimeStamps which are bit shifted WinTimeStamps"""
    def __init__(self, *args, **kwargs):
        WinTimeStamp.__init__(self, *args, **kwargs)

    def as_windows_timestamp(self):
        return obj.NativeType.v(self) >> 3

class VolatilityKPCR(obj.VolatilityMagic):
    """A scanner for KPCR data within an address space"""

    def __init__(self, *args, **kwargs):
        # Remove the value kwarg since overlaying one 
        # on the other would give the value precedence
        kwargs.pop('value', None)
        obj.VolatilityMagic.__init__(self, *args, **kwargs)

    def generate_suggestions(self):
        """Returns the results of KCPRScanner for an adderss space"""
        scanner = kpcr.KPCRScanner()
        for val in scanner.scan(self.obj_vm):
            yield val

class VolatilityKDBG(obj.VolatilityMagic):
    """A Scanner for KDBG data within an address space"""

    def v(self):
        if self.value is None:
            return self.get_best_suggestion()
        else:
            return obj.Object("_KDDEBUGGER_DATA64", offset = self.value, vm = self.obj_vm)

    def get_suggestions(self):
        if self.value:
            yield obj.Object("_KDDEBUGGER_DATA64", offset = self.value, vm = self.obj_vm)
        for x in self.generate_suggestions():
            yield x

    def generate_suggestions(self):
        """Generates a list of possible KDBG structure locations"""
        scanner = kdbg.KDBGScanner(needles = [obj.VolMagic(self.obj_vm).KDBGHeader.v()])
        for val in scanner.scan(self.obj_vm):
            val = obj.Object("_KDDEBUGGER_DATA64", offset = val, vm = self.obj_vm)
            yield val

class VolatilityIA32ValidAS(obj.VolatilityMagic):
    """An object to check that an address space is a valid IA32 Paged space"""

    def generate_suggestions(self):
        """Generates a single response of True or False depending on whether the space is a valid Windows AS"""
        # This constraint looks for self referential values within
        # the paging tables
        try:
            if self.obj_vm.pae:
                pde_base = 0xc0600000
                pd = self.obj_vm.get_pdpi(0) & 0xffffffffff000
            else:
                pde_base = 0xc0300000
                pd = self.obj_vm.dtb
            if (self.obj_vm.vtop(pde_base) == pd):
                yield True
                raise StopIteration

        except addrspace.ASAssertionError, _e:
            pass
        debug.debug("Failed to pass the Moyix Valid IA32 AS test", 3)

        # This constraint verifies that _KUSER_ SHARED_DATA is shared
        # between user and kernel address spaces.
        if (self.obj_vm.vtop(0xffdf0000)) == (self.obj_vm.vtop(0x7ffe0000)):
            if self.obj_vm.vtop(0xffdf0000) != None:
                yield True
                raise StopIteration
        debug.debug("Failed to pass the labarum_x Valid IA32 AS test", 3)

        yield False

class VolatilityAMD64ValidAS(obj.VolatilityMagic):
    def generate_suggestions(self):
        if self.obj_vm.vtop(0xFFFFF78000000000) != None:
            if (self.obj_vm.vtop(0xFFFFF78000000000)) == (self.obj_vm.vtop(0x7FFE0000)):
                yield True
                raise StopIteration
            if obj.Object("_KUSER_SHARED_DATA", offset = 0xFFFFF78000000000, vm = self.obj_vm).Reserved1 == 0x7FFEFFFF:
                yield True
                raise StopIteration
        yield False

class _CM_KEY_BODY(obj.CType):
    """Registry key"""

    def full_key_name(self):
        output = []
        kcb = self.KeyControlBlock
        while kcb.ParentKcb:
            if kcb.NameBlock.Name == None:
                break
            output.append(str(kcb.NameBlock.Name))
            kcb = kcb.ParentKcb
        return "\\".join(reversed(output))

class _CMHIVE(obj.CType):
    """Registry hive"""

    def get_name(self):
        try:
            name = str(self.FileFullPath or '') or str(self.FileUserName or '') or str(self.HiveRootPath or '') or "[no name]"
        except AttributeError:
            name = "[no name]"

        return name

    def is_valid(self):
        return obj.CType.is_valid(self) and self.Hive.Signature == 0xbee0bee0

class _POOL_HEADER(obj.CType):
    """A class for pool headers"""

    # the maximum size of optional object headers that may
    # exist in an allocation below the pool header but above
    # the actual executive object. 
    MAX_PREAMBLE = 0x60 

    @property
    def FreePool(self):
        return self.PoolType.v() == 0

    @property
    def NonPagedPool(self):
        return self.PoolType.v() % 2 == 1

    @property
    def PagedPool(self):
        return self.PoolType.v() % 2 == 0 and self.PoolType.v() > 0

    def get_object_bottom_up(self, struct_name, object_type, skip_type_check):
        """Get the windows object contained within this pool
        by using the bottom-up approach to finding the object
        """

        if not object_type:
            return obj.Object(struct_name, vm = self.obj_vm, 
                        offset = self.obj_offset +
                        self.obj_vm.profile.get_obj_size("_POOL_HEADER"), 
                        native_vm = self.obj_native_vm)

        pool_alignment = obj.VolMagic(self.obj_vm).PoolAlignment.v()

        the_object = obj.Object(struct_name, vm = self.obj_vm, 
                        offset = (self.obj_offset + self.BlockSize * pool_alignment - 
                        common.pool_align(self.obj_vm, struct_name, pool_alignment)),
                        native_vm = self.obj_native_vm)

        header = the_object.get_object_header()

        if (skip_type_check or 
                    header.get_object_type() == object_type):
            return the_object
        else:
            return obj.NoneObject("Cannot find the object")

    def get_object_top_down(self, object_name, object_type, _skip_type_check):
        """On windows 8, pool allocations are done from preset sizes. This means
        that the allocation is never exactly the same size and we can not use
        the bottom up method like before.

        We therefore, have to build the headers forward by checking the preamble
        size and validity of each object. This is a little slower than with
        earlier versions of windows.
        """

        # we start after the pool header 
        start_offset = self.obj_offset + self.obj_vm.profile.get_obj_size("_POOL_HEADER")

        # allocations containing only one structure 
        if not object_type:
            return obj.Object(object_name, offset = start_offset, 
                              vm = self.obj_vm, 
                              native_vm = self.obj_native_vm)

        # pool aligned boundary 
        pool_alignment = obj.VolMagic(self.obj_vm).PoolAlignment.v()

        # maximum distance to search
        end_offset = start_offset + min(self.MAX_PREAMBLE, self.BlockSize * pool_alignment)

        for addr in range(start_offset, end_offset, pool_alignment):

            header = obj.Object("_OBJECT_HEADER", offset = addr,    
                                vm = self.obj_vm, 
                                native_vm = self.obj_native_vm)

            if (header.is_valid() and 
                        header.get_object_type() == object_type):

                the_object = header.dereference_as(object_name)
                if the_object.is_valid():
                    return the_object

        return obj.NoneObject("Cannot find object")

    def get_object(self, struct_name, object_type = None, use_top_down = False, skip_type_check = False): 
        """Get the windows object contained within this pool
        using whichever method is best for the target OS. 

        @param struct_name: the name of the structure to cast
        such as _EPROCESS.

        @param object_type: the name of the executive object.
        If there is no executive object in the pool allocation,
        then this can be None. 

        @param use_top_down: specify the technique we use to 
        find the object within the pool allocation. 

        @param skip_type_check: specify if we skip unallocated 
        objects or return them. 
        """

        if use_top_down:
            return self.get_object_top_down(struct_name, object_type, skip_type_check)
        else:
            return self.get_object_bottom_up(struct_name, object_type, skip_type_check)

import crash_vtypes
import hibernate_vtypes
import kdbg_vtypes
import tcpip_vtypes
import ssdt_vtypes

class WindowsOverlay(obj.ProfileModification):
    conditions = {'os': lambda x: x == 'windows'}
    before = ['BasicObjectClasses', 'WindowsVTypes']

    def modification(self, profile):
        profile.merge_overlay(windows_overlay)

class WindowsVTypes(obj.ProfileModification):
    conditions = {'os': lambda x: x == 'windows'}
    before = ['BasicObjectClasses']

    def modification(self, profile):
        profile.vtypes.update(crash_vtypes.crash_vtypes)
        profile.vtypes.update(hibernate_vtypes.hibernate_vtypes)
        profile.vtypes.update(kdbg_vtypes.kdbg_vtypes)
        profile.vtypes.update(tcpip_vtypes.tcpip_vtypes)
        profile.vtypes.update(ssdt_vtypes.ssdt_vtypes)

class WindowsObjectClasses(obj.ProfileModification):
    conditions = {'os': lambda x: x == 'windows'}
    before = ['BasicObjectClasses', 'WindowsVTypes', 'WindowsOverlay']

    def modification(self, profile):
        profile.object_classes.update({
            '_UNICODE_STRING': _UNICODE_STRING,
            '_LIST_ENTRY': _LIST_ENTRY,
            'WinTimeStamp': WinTimeStamp,
            'DosDate':DosDate,
            '_EPROCESS': _EPROCESS,
            '_ETHREAD': _ETHREAD,
            '_HANDLE_TABLE': _HANDLE_TABLE,
            '_OBJECT_HEADER': _OBJECT_HEADER,
            '_FILE_OBJECT': _FILE_OBJECT,
            '_EX_FAST_REF': _EX_FAST_REF,
            'ThreadCreateTimeStamp': ThreadCreateTimeStamp,
            'IpAddress': basic.IpAddress,
            'Ipv6Address': basic.Ipv6Address,
            'VolatilityKPCR': VolatilityKPCR,
            'VolatilityKDBG': VolatilityKDBG,
            'VolatilityIA32ValidAS': VolatilityIA32ValidAS,
            'VolatilityAMD64ValidAS': VolatilityAMD64ValidAS,
            '_CM_KEY_BODY': _CM_KEY_BODY,
            '_TOKEN': _TOKEN,
            '_POOL_HEADER': _POOL_HEADER,
            '_OBJECT_SYMBOLIC_LINK': _OBJECT_SYMBOLIC_LINK,
            '_KMUTANT': _KMUTANT,
            '_CMHIVE': _CMHIVE,
            '_OBJECT_TYPE': _OBJECT_TYPE,
            })

class VolMagicPoolTag(obj.VolatilityMagic):
    """The pool tag for a specific data structure on a given OS"""

    def __init__(self, *args, **kwargs):
        kwargs.pop('value', None)
        self.protected = kwargs.get("protected", False)
        self.tag = kwargs.get("tag", None)
        obj.VolatilityMagic.__init__(self, *args, **kwargs)

    def generate_suggestions(self):
        """Return the tag value, setting the protected bit if necessary"""

        tag = struct.unpack("I", self.tag)[0]
        if self.protected:
            tag |= 0x80000000
        yield struct.pack("I", tag)

class HandleTableEntryPreWin8(obj.ProfileModification):
    """A modification for handle table entries before Windows 8"""

    conditions = {"os": lambda x: x == "windows"}

    def modification(self, profile):

        version = (profile.metadata.get('major', 0), 
                   profile.metadata.get('minor', 0))

        if version <= (6, 1):
            profile.merge_overlay({
                '_HANDLE_TABLE_ENTRY' : [ None, {
                'Object' : [ None, ['_EX_FAST_REF']],
                }]})

class PoolTagModification(obj.ProfileModification):
    """A modification for variable pool tags across Windows versions"""

    conditions = {'os': lambda x: x == 'windows'}

    def modification(self, profile):
        profile.object_classes.update({'VolMagicPoolTag': VolMagicPoolTag})

        # win8 / 2012 pool tags are not protected 
        if (profile.metadata.get('major', 0) == 6 and 
                    profile.metadata.get('minor', 0) >= 2):
            protected = False
        else:
            protected = True

        profile.merge_overlay({
            'VOLATILITY_MAGIC': [ None, { 
            'ProcessPoolTag': [ 0x0, ['VolMagicPoolTag', dict(tag = "Proc", protected = protected)]],
            'MutexPoolTag': [ 0x0, ['VolMagicPoolTag', dict(tag = "Muta", protected = protected)]],
            'SymlinkPoolTag': [ 0x0, ['VolMagicPoolTag', dict(tag = "Symb", protected = protected)]],
            'DriverPoolTag': [ 0x0, ['VolMagicPoolTag', dict(tag = "Driv", protected = protected)]],
            'FilePoolTag': [ 0x0, ['VolMagicPoolTag', dict(tag = "File", protected = protected)]],
            'WindPoolTag': [ 0x0, ['VolMagicPoolTag', dict(tag = "Wind", protected = protected)]],
            'ThreadPoolTag': [ 0x0, ['VolMagicPoolTag', dict(tag = "Thre", protected = protected)]],
            'ObjectTypePoolTag': [ 0x0, ['VolMagicPoolTag', dict(tag = "ObjT", protected = protected)]],
            }]})

class AbstractKDBGMod(obj.ProfileModification):
    kdbgsize = 0x290

    def modification(self, profile):
        signature = '\x00\x00\x00\x00\x00\x00\x00\x00' if profile.metadata.get('memory_model', '32bit') == '32bit' else '\x00\xf8\xff\xff'
        signature += 'KDBG' + struct.pack('<H', self.kdbgsize)
        profile.merge_overlay({'VOLATILITY_MAGIC': [ None, {
                                'KDBGHeader': [ None, ['VolatilityMagic', dict(value = signature)]]
                                                            }
                                                    ]})
