def is_valid(self) -> bool:
        """Determine if the object is valid."""

        # if self.InfoMask > 0x48:
        #    return False

        try:
            if self.PointerCount > 0x1000000 or self.PointerCount < 0:
                return False
        except exceptions.InvalidAddressException:
            return False

        return True

def
is_valid
(
self
)
->
bool
:
# if self.InfoMask > 0x48:
#    return False
try
:
if
self
.
PointerCount
>
0x1000000
or
self
.
PointerCount
<
0
:
return
False
except
exceptions
.
InvalidAddressException
:
return
False
return
True


Across all Windows versions the _OBJECT_HEADER embeds details on the type of object ( i . e . process file ) but the way its embedded differs between versions .

def get_object_type(self, type_map: Dict[int, str], cookie: int = None) -> Optional[str]:
        """Across all Windows versions, the _OBJECT_HEADER embeds details on
        the type of object (i.e. process, file) but the way its embedded
        differs between versions.

        This API abstracts away those details.
        """

        if self.vol.get('object_header_object_type', None) is not None:
            return self.vol.object_header_object_type

        try:
            # vista and earlier have a Type member
            self._vol['object_header_object_type'] = self.Type.Name.String
        except AttributeError:
            # windows 7 and later have a TypeIndex, but windows 10
            # further encodes the index value with nt1!ObHeaderCookie
            try:
                type_index = ((self.vol.offset >> 8) ^ cookie ^ self.TypeIndex) & 0xFF
            except (AttributeError, TypeError):
                type_index = self.TypeIndex

            self._vol['object_header_object_type'] = type_map.get(type_index)
        return self.vol.object_header_object_type


def is_valid(self) -> bool:
        """Determine if the structure is valid."""
        if self.Order < 0 or self.Order > 0xFFFF:
            return False

        try:
            _ = self.State.description
            _ = self.Start.description
        except ValueError:
            return False

        return True

def is_valid ( self ) -> bool : if self . Order < 0 or self . Order > 0xFFFF : return False try : _ = self . State . description _ = self . Start . description except ValueError : return False return True


def get_pid(self) -> Union[int, interfaces.renderers.BaseAbsentValue]:
        """Return the pid of the process, if any."""
        if self.State.description != "SERVICE_RUNNING" or "PROCESS" not in self.get_type():
            return renderers.NotApplicableValue()

        try:
            return self.ServiceProcess.ProcessId
        except exceptions.InvalidAddressException:
            return renderers.UnreadableValue()

def get_pid ( self ) -> Union [ int , interfaces . renderers . BaseAbsentValue ] : if self . State . description != "SERVICE_RUNNING" or "PROCESS" not in self . get_type ( ) : return renderers . NotApplicableValue ( ) try : return self . ServiceProcess . ProcessId except exceptions . InvalidAddressException : return renderers . UnreadableValue ( )


Returns the binary associated with the service .

def get_binary(self) -> Union[str, interfaces.renderers.BaseAbsentValue]:
        """Returns the binary associated with the service."""
        if self.State.description != "SERVICE_RUNNING":
            return renderers.NotApplicableValue()

        # depending on whether the service is for a process
        # or kernel driver, the binary path is stored differently
        try:
            if "PROCESS" in self.get_type():
                return self.ServiceProcess.BinaryPath.dereference().cast("string",
                                                                         encoding = "utf-16",
                                                                         errors = "replace",
                                                                         max_length = 512)
            else:
                return self.DriverName.dereference().cast("string",
                                                          encoding = "utf-16",
                                                          errors = "replace",
                                                          max_length = 512)
        except exceptions.InvalidAddressException:
            return renderers.UnreadableValue()


def get_name(self) -> Union[str, interfaces.renderers.BaseAbsentValue]:
        """Returns the service name."""
        try:
            return self.ServiceName.dereference().cast("string",
                                                       encoding = "utf-16",
                                                       errors = "replace",
                                                       max_length = 512)
        except exceptions.InvalidAddressException:
            return renderers.UnreadableValue()

def get_name ( self ) -> Union [ str , interfaces . renderers . BaseAbsentValue ] : try : return self . ServiceName . dereference ( ) . cast ( "string" , encoding = "utf-16" , errors = "replace" , max_length = 512 ) except exceptions . InvalidAddressException : return renderers . UnreadableValue ( )


def get_display(self) -> Union[str, interfaces.renderers.BaseAbsentValue]:
        """Returns the service display."""
        try:
            return self.DisplayName.dereference().cast("string",
                                                       encoding = "utf-16",
                                                       errors = "replace",
                                                       max_length = 512)
        except exceptions.InvalidAddressException:
            return renderers.UnreadableValue()

def get_display ( self ) -> Union [ str , interfaces . renderers . BaseAbsentValue ] : try : return self . DisplayName . dereference ( ) . cast ( "string" , encoding = "utf-16" , errors = "replace" , max_length = 512 ) except exceptions . InvalidAddressException : return renderers . UnreadableValue ( )


def get_type(self) -> str:
        """Returns the binary types."""

        SERVICE_TYPE_FLAGS = {
            'SERVICE_KERNEL_DRIVER': 1,
            'SERVICE_FILE_SYSTEM_DRIVER': 2,
            'SERVICE_ADAPTOR': 4,
            'SERVICE_RECOGNIZER_DRIVER': 8,
            'SERVICE_WIN32_OWN_PROCESS': 16,
            'SERVICE_WIN32_SHARE_PROCESS': 32,
            'SERVICE_INTERACTIVE_PROCESS': 256
        }

        type_flags = Flags(choices = SERVICE_TYPE_FLAGS)
        return "|".join(type_flags(self.Type))

def get_type ( self ) -> str : SERVICE_TYPE_FLAGS = { 'SERVICE_KERNEL_DRIVER' : 1 , 'SERVICE_FILE_SYSTEM_DRIVER' : 2 , 'SERVICE_ADAPTOR' : 4 , 'SERVICE_RECOGNIZER_DRIVER' : 8 , 'SERVICE_WIN32_OWN_PROCESS' : 16 , 'SERVICE_WIN32_SHARE_PROCESS' : 32 , 'SERVICE_INTERACTIVE_PROCESS' : 256 } type_flags = Flags ( choices = SERVICE_TYPE_FLAGS ) return "|" . join ( type_flags ( self . Type ) )


Generator that enumerates other services .

def traverse(self):
        """Generator that enumerates other services."""

        try:
            if hasattr(self, "PrevEntry"):
                yield self
                # make sure we dereference these pointers, or the
                # is_valid() checks will apply to the pointer and
                # not the _SERVICE_RECORD object as intended.
                rec = self.PrevEntry
                while rec and rec.is_valid():
                    yield rec
                    rec = rec.PrevEntry
            else:
                rec = self
                while rec and rec.is_valid():
                    yield rec
                    rec = rec.ServiceList.Blink.dereference()
        except exceptions.InvalidAddressException:
            return


def is_valid(self) -> bool:
        """Determine if the structure is valid."""
        try:
            return self.ServiceRecord.is_valid()
        except exceptions.InvalidAddressException:
            return False

def is_valid ( self ) -> bool : try : return self . ServiceRecord . is_valid ( ) except exceptions . InvalidAddressException : return False


An alternative method for using this enum when the value may be unknown .

def get(cls, value):
        """An alternative method for using this enum when the value may be
        unknown.

        This is used to support unknown value requests in Python <3.6.
        """
        try:
            return cls(value)
        except ValueError:
            return cls(RegValueTypes.REG_UNKNOWN)

def get ( cls , value ) : try : return cls ( value ) except ValueError : return cls ( RegValueTypes . REG_UNKNOWN )


def is_valid(self) -> bool:
        """Determine if the object is valid."""
        try:
            return self.Hive.Signature == 0xbee0bee0
        except exceptions.InvalidAddressException:
            return False

def is_valid ( self ) -> bool : try : return self . Hive . Signature == 0xbee0bee0 except exceptions . InvalidAddressException : return False


def get_name(self) -> Optional[interfaces.objects.ObjectInterface]:
        """Determine a name for the hive.

        Note that some attributes are unpredictably blank across
        different OS versions while others are populated, so we check
        all possibilities and take the first one that's not empty
        """

        for attr in ["FileFullPath", "FileUserName", "HiveRootPath"]:
            try:
                return getattr(self, attr).get_string()
            except (AttributeError, exceptions.InvalidAddressException):
                pass

        return None

def get_name ( self ) -> Optional [ interfaces . objects . ObjectInterface ] : for attr in [ "FileFullPath" , "FileUserName" , "HiveRootPath" ] : try : return getattr ( self , attr ) . get_string ( ) except ( AttributeError , exceptions . InvalidAddressException ) : pass return None


Win10 14393 introduced an extra path element that it skips over by checking for Flags that contain KEY_HIVE_ENTRY .

def _skip_key_hive_entry_path(self, kcb_flags):
        """Win10 14393 introduced an extra path element that it skips over by
        checking for Flags that contain KEY_HIVE_ENTRY."""

        # _CM_KEY_BODY.Trans introduced in Win10 14393
        if hasattr(self, "Trans") and RegKeyFlags.KEY_HIVE_ENTRY & kcb_flags == RegKeyFlags.KEY_HIVE_ENTRY:
            return True

        return False


def get_subkeys(self) -> Iterable[interfaces.objects.ObjectInterface]:
        """Returns a list of the key nodes."""
        hive = self._context.layers[self.vol.layer_name]
        if not isinstance(hive, RegistryHive):
            raise TypeError("CM_KEY_NODE was not instantiated on a RegistryHive layer")
        for index in range(2):
            # Use get_cell because it should *always* be a KeyIndex
            subkey_node = hive.get_cell(self.SubKeyLists[index]).u.KeyIndex
            yield from self._get_subkeys_recursive(hive, subkey_node)


Recursively descend a node returning subkeys .

def _get_subkeys_recursive(
            self, hive: RegistryHive,
            node: interfaces.objects.ObjectInterface) -> Iterable[interfaces.objects.ObjectInterface]:
        """Recursively descend a node returning subkeys."""
        # The keylist appears to include 4 bytes of key name after each value
        # We can either double the list and only use the even items, or
        # We could change the array type to a struct with both parts
        try:
            signature = node.cast('string', max_length = 2, encoding = 'latin-1')
        except (exceptions.InvalidAddressException, RegistryFormatException):
            return

        listjump = None
        if signature == 'ri':
            listjump = 1
        elif signature == 'lh' or signature == 'lf':
            listjump = 2
        elif node.vol.type_name.endswith(constants.BANG + "_CM_KEY_NODE"):
            yield node
        else:
            vollog.debug("Unexpected node type encountered when traversing subkeys: {}, signature: {}".format(
                node.vol.type_name, signature))

        if listjump:
            node.List.count = node.Count * listjump
            for subnode_offset in node.List[::listjump]:
                if (subnode_offset & 0x7fffffff) > hive.maximum_address:
                    vollog.log(constants.LOGLEVEL_VVV,
                               "Node found with address outside the valid Hive size: {}".format(hex(subnode_offset)))
                else:
                    try:
                        subnode = hive.get_node(subnode_offset)
                    except (exceptions.InvalidAddressException, RegistryFormatException):
                        vollog.log(constants.LOGLEVEL_VVV,
                                   "Failed to get node at {}, skipping".format(hex(subnode_offset)))
                        continue
                    yield from self._get_subkeys_recursive(hive, subnode)


Returns a list of the Value nodes for a key .

def get_values(self) -> Iterable[interfaces.objects.ObjectInterface]:
        """Returns a list of the Value nodes for a key."""
        hive = self._context.layers[self.vol.layer_name]
        if not isinstance(hive, RegistryHive):
            raise TypeError("CM_KEY_NODE was not instantiated on a RegistryHive layer")
        child_list = hive.get_cell(self.ValueList.List).u.KeyList
        child_list.count = self.ValueList.Count

        try:
            for v in child_list:
                if v != 0:
                    try:
                        node = hive.get_node(v)
                    except (RegistryInvalidIndex, RegistryFormatException) as excp:
                        vollog.debug("Invalid address {}".format(excp))
                        continue
                    if node.vol.type_name.endswith(constants.BANG + '_CM_KEY_VALUE'):
                        yield node
        except (exceptions.InvalidAddressException, RegistryFormatException) as excp:
            vollog.debug("Invalid address in get_values iteration: {}".format(excp))
            return


def get_name(self) -> interfaces.objects.ObjectInterface:
        """Gets the name for the current key node"""
        namelength = self.NameLength
        self.Name.count = namelength
        return self.Name.cast("string", max_length = namelength, encoding = "latin-1")

def get_name ( self ) -> interfaces . objects . ObjectInterface : namelength = self . NameLength self . Name . count = namelength return self . Name . cast ( "string" , max_length = namelength , encoding = "latin-1" )


def get_name(self) -> interfaces.objects.ObjectInterface:
        """Gets the name for the current key value"""
        namelength = self.NameLength
        self.Name.count = namelength
        return self.Name.cast("string", max_length = namelength, encoding = "latin-1")


def decode_data(self) -> Union[int, bytes]:
        """Properly decodes the data associated with the value node"""
        # Determine if the data is stored inline
        datalen = self.DataLength
        data = b""
        # Check if the data is stored inline
        layer = self._context.layers[self.vol.layer_name]
        if not isinstance(layer, RegistryHive):
            raise TypeError("Key value was not instantiated on a RegistryHive layer")

        # If the high-bit is set
        if datalen & 0x80000000:
            # Remove the high bit
            datalen = datalen & 0x7fffffff
            if (0 > datalen or datalen > 4):
                raise ValueError("Unable to read inline registry value with excessive length: {}".format(datalen))
            else:
                data = layer.read(self.Data.vol.offset, datalen)
        elif layer.hive.Version == 5 and datalen > 0x4000:
            # We're bigdata
            big_data = layer.get_node(self.Data)
            # Oddly, we get a list of addresses, at which are addresses, which then point to data blocks
            for i in range(big_data.Count):
                # The value 4 should actually be unsigned-int.size, but since it's a file format that shouldn't change
                # the direct value 4 can be used instead
                block_offset = layer.get_cell(big_data.List + (i * 4)).cast("unsigned int")
                if isinstance(block_offset, int) and block_offset < layer.maximum_address:
                    amount = min(BIG_DATA_MAXLEN, datalen)
                    data += layer.read(offset = layer.get_cell(block_offset).vol.offset, length = amount)
                    datalen -= amount
        else:
            # Suspect Data actually points to a Cell,
            # but the length at the start could be negative so just adding 4 to jump past it
            data = layer.read(self.Data + 4, datalen)

        self_type = RegValueTypes.get(self.Type)
        if self_type == RegValueTypes.REG_DWORD:
            if len(data) != struct.calcsize("<L"):
                raise ValueError("Size of data does not match the type of registry value {}".format(self.get_name()))
            return struct.unpack("<L", data)[0]
        if self_type == RegValueTypes.REG_DWORD_BIG_ENDIAN:
            if len(data) != struct.calcsize(">L"):
                raise ValueError("Size of data does not match the type of registry value {}".format(self.get_name()))
            return struct.unpack(">L", data)[0]
        if self_type == RegValueTypes.REG_QWORD:
            if len(data) != struct.calcsize("<Q"):
                raise ValueError("Size of data does not match the type of registry value {}".format(self.get_name()))
            return struct.unpack("<Q", data)[0]
        if self_type in [
                RegValueTypes.REG_SZ, RegValueTypes.REG_EXPAND_SZ, RegValueTypes.REG_LINK, RegValueTypes.REG_MULTI_SZ,
                RegValueTypes.REG_BINARY, RegValueTypes.REG_FULL_RESOURCE_DESCRIPTOR, RegValueTypes.REG_RESOURCE_LIST,
                RegValueTypes.REG_RESOURCE_REQUIREMENTS_LIST
        ]:
            return data
        if self_type == RegValueTypes.REG_NONE:
            return b''

        # Fall back if it's something weird
        vollog.debug("Unknown registry value type encountered: {}".format(self.Type))
        return data


Returns the NT build lab string from the KDBG .

def get_build_lab(self):
        """Returns the NT build lab string from the KDBG."""

        layer_name = self.vol.layer_name
        symbol_table_name = self.get_symbol_table_name()

        return self._context.object(symbol_table_name + constants.BANG + "string",
                                    layer_name = layer_name,
                                    offset = self.NtBuildLab,
                                    max_length = 32,
                                    errors = "replace")

def get_build_lab ( self ) : layer_name = self . vol . layer_name symbol_table_name = self . get_symbol_table_name ( ) return self . _context . object ( symbol_table_name + constants . BANG + "string" , layer_name = layer_name , offset = self . NtBuildLab , max_length = 32 , errors = "replace" )


Returns the CSDVersion as an integer ( i . e . Service Pack number )

def get_csdversion(self):
        """Returns the CSDVersion as an integer (i.e. Service Pack number)"""

        layer_name = self.vol.layer_name
        symbol_table_name = self.get_symbol_table_name()

        csdresult = self._context.object(symbol_table_name + constants.BANG + "unsigned long",
                                         layer_name = layer_name,
                                         offset = self.CmNtCSDVersion)

        return (csdresult >> 8) & 0xffffffff

def
get_csdversion
(
self
)
:
layer_name
=
self
.
vol
.
layer_name
symbol_table_name
=
self
.
get_symbol_table_name
(
)
csdresult
=
self
.
_context
.
object
(
symbol_table_name
+
constants
.
BANG
+
"unsigned long"
,
layer_name
=
layer_name
,
offset
=
self
.
CmNtCSDVersion
)
return
(
csdresult
>>
8
)
&
0xffffffff


def mask_mods_list(cls, context: interfaces.context.ContextInterface, layer_name: str,
                       mods: Iterator[Any]) -> List[Tuple[interfaces.objects.ObjectInterface, Any, Any]]:
        """
        A helper function to mask the starting and end address of kernel modules
        """
        mask = context.layers[layer_name].address_mask

        return [(objects.utility.array_to_string(mod.name), mod.address & mask, (mod.address & mask) + mod.size)
                for mod in mods]

def mask_mods_list ( cls , context : interfaces . context . ContextInterface , layer_name : str , mods : Iterator [ Any ] ) -> List [ Tuple [ interfaces . objects . ObjectInterface , Any , Any ] ] : mask = context . layers [ layer_name ] . address_mask return [ ( objects . utility . array_to_string ( mod . name ) , mod . address & mask , ( mod . address & mask ) + mod . size ) for mod in mods ]


def files_descriptors_for_process(cls, context: interfaces.context.ContextInterface, symbol_table_name: str,
                                      task: interfaces.objects.ObjectInterface):
        """Creates a generator for the file descriptors of a process

        Args:
            symbol_table_name: The name of the symbol table associated with the process
            context:
            task: The process structure to enumerate file descriptors from

        Return:
            A 3 element tuple is yielded for each file descriptor:
            1) The file's object
            2) The path referenced by the descriptor.
                The path is either empty, the full path of the file in the file system, or the formatted name for sockets, pipes, etc.
            3) The file descriptor number
        """

        try:
            num_fds = task.p_fd.fd_lastfile
        except exceptions.InvalidAddressException:
            num_fds = 1024

        try:
            nfiles = task.p_fd.fd_nfiles
        except exceptions.InvalidAddressException:
            nfiles = 1024

        if nfiles > num_fds:
            num_fds = nfiles

        if num_fds > 4096:
            num_fds = 1024

        file_type = symbol_table_name + constants.BANG + 'fileproc'

        try:
            table_addr = task.p_fd.fd_ofiles.dereference()
        except exceptions.InvalidAddressException:
            return

        fds = objects.utility.array_of_pointers(table_addr, count = num_fds, subtype = file_type, context = context)

        for fd_num, f in enumerate(fds):
            if f != 0:
                try:
                    ftype = f.f_fglob.get_fg_type()
                except exceptions.InvalidAddressException:
                    continue

                if ftype == 'VNODE':
                    vnode = f.f_fglob.fg_data.dereference().cast("vnode")
                    path = vnode.full_path()
                elif ftype:
                    path = "<{}>".format(ftype.lower())

                yield f, path, fd_num

def files_descriptors_for_process ( cls , context : interfaces . context . ContextInterface , symbol_table_name : str , task : interfaces . objects . ObjectInterface ) : try : num_fds = task . p_fd . fd_lastfile except exceptions . InvalidAddressException : num_fds = 1024 try : nfiles = task . p_fd . fd_nfiles except exceptions . InvalidAddressException : nfiles = 1024 if nfiles > num_fds : num_fds = nfiles if num_fds > 4096 : num_fds = 1024 file_type = symbol_table_name + constants . BANG + 'fileproc' try : table_addr = task . p_fd . fd_ofiles . dereference ( ) except exceptions . InvalidAddressException : return fds = objects . utility . array_of_pointers ( table_addr , count = num_fds , subtype = file_type , context = context ) for fd_num , f in enumerate ( fds ) : if f != 0 : try : ftype = f . f_fglob . get_fg_type ( ) except exceptions . InvalidAddressException : continue if ftype == 'VNODE' : vnode = f . f_fglob . fg_data . dereference ( ) . cast ( "vnode" ) path = vnode . full_path ( ) elif ftype : path = "<{}>" . format ( ftype . lower ( ) ) yield f , path , fd_num


Constructs a new layer based on the process s DTB .

def add_process_layer(self, config_prefix: str = None, preferred_name: str = None) -> Optional[str]:
        """Constructs a new layer based on the process's DTB.

        Returns the name of the Layer or None.
        """
        parent_layer = self._context.layers[self.vol.layer_name]

        if not isinstance(parent_layer, interfaces.layers.TranslationLayerInterface):
            raise TypeError("Parent layer is not a translation layer, unable to construct process layer")

        try:
            dtb = self.get_task().map.pmap.pm_cr3
        except exceptions.InvalidAddressException:
            return None

        if preferred_name is None:
            preferred_name = self.vol.layer_name + "_Process{}".format(self.p_pid)

        # Add the constructed layer and return the name
        return self._add_process_layer(self._context, dtb, config_prefix, preferred_name)


Returns a list of sections based on the memory manager s view of this task s virtual memory .

def get_process_memory_sections(self,
                                    context: interfaces.context.ContextInterface,
                                    config_prefix: str,
                                    rw_no_file: bool = False) -> \
            Generator[Tuple[int, int], None, None]:
        """Returns a list of sections based on the memory manager's view of
        this task's virtual memory."""
        for vma in self.get_map_iter():
            start = int(vma.links.start)
            end = int(vma.links.end)

            if rw_no_file:
                if vma.get_perms() != "rw" or vma.get_path(context, config_prefix) != "":
                    if vma.get_special_path() != "[heap]":
                        continue

            yield (start, end - start)

def get_process_memory_sections ( self , context : interfaces . context . ContextInterface , config_prefix : str , rw_no_file : bool = False ) -> Generator [ Tuple [ int , int ] , None , None ] : for vma in self . get_map_iter ( ) : start = int ( vma . links . start ) end = int ( vma . links . end ) if rw_no_file : if vma . get_perms ( ) != "rw" or vma . get_path ( context , config_prefix ) != "" : if vma . get_special_path ( ) != "[heap]" : continue yield ( start , end - start )


Flags memory regions that are mapped rwx or that map an executable not back from a file on disk .

def is_suspicious(self, context, config_prefix):
        """Flags memory regions that are mapped rwx or that map an executable
        not back from a file on disk."""
        ret = False

        perms = self.get_perms()

        if perms == "rwx":
            ret = True

        elif perms == "r-x" and self.get_path(context, config_prefix) == "":
            ret = True

        return ret

def is_suspicious ( self , context , config_prefix ) : ret = False perms = self . get_perms ( ) if perms == "rwx" : ret = True elif perms == "r-x" and self . get_path ( context , config_prefix ) == "" : ret = True return ret


Walks a queue in a smear - aware and smear - resistant manner

def walk_list(self,
                  list_head: interfaces.objects.ObjectInterface,
                  member_name: str,
                  type_name: str,
                  max_size: int = 4096) -> Iterable[interfaces.objects.ObjectInterface]:
        """
        Walks a queue in a smear-aware and smear-resistant manner

        smear is detected by:
            - the max_size parameter sets an upper bound
            - each seen entry is only allowed once

        attempts to work around smear:
            - the list is walked in both directions to help find as many elements as possible

        Args:
            list_head   - the head of the list
            member_name - the name of the embedded list member
            type_name   - the type of each element in the list
            max_size    - the maximum amount of elements that will be returned

        Returns:
            Each instance of the queue cast as "type_name" type
        """

        yielded = 0

        seen = set()

        for attr in ['next', 'prev']:
            try:
                n = getattr(self, attr).dereference().cast(type_name)

                while n is not None and n.vol.offset != list_head:
                    if n.vol.offset in seen:
                        break

                    yield n

                    seen.add(n.vol.offset)

                    yielded = yielded + 1
                    if yielded == max_size:
                        return

                    n = getattr(n.member(attr = member_name), attr).dereference().cast(type_name)

            except exceptions.InvalidAddressException:
                pass

def walk_list ( self , list_head : interfaces . objects . ObjectInterface , member_name : str , type_name : str , max_size : int = 4096 ) -> Iterable [ interfaces . objects . ObjectInterface ] : yielded = 0 seen = set ( ) for attr in [ 'next' , 'prev' ] : try : n = getattr ( self , attr ) . dereference ( ) . cast ( type_name ) while n is not None and n . vol . offset != list_head : if n . vol . offset in seen : break yield n seen . add ( n . vol . offset ) yielded = yielded + 1 if yielded == max_size : return n = getattr ( n . member ( attr = member_name ) , attr ) . dereference ( ) . cast ( type_name ) except exceptions . InvalidAddressException : pass


def get_perms(self) -> str:
        """
        Returns the actions allowed on the node

        Args: None

        Returns:
            A combination of:
                R - readable
                W - writeable
                L - self handles locking
        """
        ret = ""

        checks = [0x80000000, 0x40000000, 0x00800000]
        perms = ["R", "W", "L"]

        for (i, c) in enumerate(checks):
            if c & self.oid_kind:
                ret = ret + perms[i]
            else:
                ret = ret + "-"

        return ret

def get_perms ( self ) -> str : ret = "" checks = [ 0x80000000 , 0x40000000 , 0x00800000 ] perms = [ "R" , "W" , "L" ] for ( i , c ) in enumerate ( checks ) : if c & self . oid_kind : ret = ret + perms [ i ] else : ret = ret + "-" return ret


def get_ctltype(self) -> str:
        """
        Returns the type of the sysctl node

        Args: None

        Returns:
            One of:
                CTLTYPE_NODE
                CTLTYPE_INT
                CTLTYPE_STRING
                CTLTYPE_QUAD
                CTLTYPE_OPAQUE
                an empty string for nodes not in the above types

        Based on sysctl_sysctl_debug_dump_node
        """
        types = {1: 'CTLTYPE_NODE', 2: 'CTLTYPE_INT', 3: 'CTLTYPE_STRING', 4: 'CTLTYPE_QUAD', 5: 'CTLTYPE_OPAQUE'}

        ctltype = self.oid_kind & 0xf

        if 0 < ctltype < 6:
            ret = types[ctltype]
        else:
            ret = ""

        return ret

def get_ctltype ( self ) -> str : types = { 1 : 'CTLTYPE_NODE' , 2 : 'CTLTYPE_INT' , 3 : 'CTLTYPE_STRING' , 4 : 'CTLTYPE_QUAD' , 5 : 'CTLTYPE_OPAQUE' } ctltype = self . oid_kind & 0xf if 0 < ctltype < 6 : ret = types [ ctltype ] else : ret = "" return ret


def mask_mods_list(cls, context: interfaces.context.ContextInterface, layer_name: str,
                       mods: Iterator[interfaces.objects.ObjectInterface]) -> List[Tuple[str, int, int]]:
        """
        A helper function to mask the starting and end address of kernel modules
        """
        mask = context.layers[layer_name].address_mask

        return [(utility.array_to_string(mod.name), mod.get_module_base() & mask,
                 (mod.get_module_base() & mask) + mod.get_core_size()) for mod in mods]

def mask_mods_list ( cls , context : interfaces . context . ContextInterface , layer_name : str , mods : Iterator [ interfaces . objects . ObjectInterface ] ) -> List [ Tuple [ str , int , int ] ] : mask = context . layers [ layer_name ] . address_mask return [ ( utility . array_to_string ( mod . name ) , mod . get_module_base ( ) & mask , ( mod . get_module_base ( ) & mask ) + mod . get_core_size ( ) ) for mod in mods ]


def generate_kernel_handler_info(
            cls, context: interfaces.context.ContextInterface, layer_name: str, kernel_name: str,
            mods_list: Iterator[interfaces.objects.ObjectInterface]) -> List[Tuple[str, int, int]]:
        """
        A helper function that gets the beginning and end address of the kernel module
        """

        kernel = contexts.Module(context, kernel_name, layer_name, 0)

        mask = context.layers[layer_name].address_mask

        start_addr = kernel.object_from_symbol("_text")
        start_addr = start_addr.vol.offset & mask

        end_addr = kernel.object_from_symbol("_etext")
        end_addr = end_addr.vol.offset & mask

        return [(constants.linux.KERNEL_NAME, start_addr, end_addr)] + \
               LinuxUtilities.mask_mods_list(context, layer_name, mods_list)

def generate_kernel_handler_info ( cls , context : interfaces . context . ContextInterface , layer_name : str , kernel_name : str , mods_list : Iterator [ interfaces . objects . ObjectInterface ] ) -> List [ Tuple [ str , int , int ] ] : kernel = contexts . Module ( context , kernel_name , layer_name , 0 ) mask = context . layers [ layer_name ] . address_mask start_addr = kernel . object_from_symbol ( "_text" ) start_addr = start_addr . vol . offset & mask end_addr = kernel . object_from_symbol ( "_etext" ) end_addr = end_addr . vol . offset & mask return [ ( constants . linux . KERNEL_NAME , start_addr , end_addr ) ] + LinuxUtilities . mask_mods_list ( context , layer_name , mods_list )


Searches between the start and end address of the kernel module using target_address . Returns the module and symbol name of the address provided .

def lookup_module_address(cls, context: interfaces.context.ContextInterface, handlers: List[Tuple[str, int, int]],
                              target_address: int):
        """
        Searches between the start and end address of the kernel module using target_address.
        Returns the module and symbol name of the address provided.
        """

        mod_name = "UNKNOWN"
        symbol_name = "N/A"

        for name, start, end in handlers:
            if start <= target_address <= end:
                mod_name = name
                if name == constants.linux.KERNEL_NAME:
                    symbols = list(context.symbol_space.get_symbols_by_location(target_address))

                    if len(symbols):
                        symbol_name = symbols[0].split(constants.BANG)[1] if constants.BANG in symbols[0] else \
                            symbols[0]

                break

        return mod_name, symbol_name

def lookup_module_address ( cls , context : interfaces . context . ContextInterface , handlers : List [ Tuple [ str , int , int ] ] , target_address : int ) : mod_name = "UNKNOWN" symbol_name = "N/A" for name , start , end in handlers : if start <= target_address <= end : mod_name = name if name == constants . linux . KERNEL_NAME : symbols = list ( context . symbol_space . get_symbols_by_location ( target_address ) ) if len ( symbols ) : symbol_name = symbols [ 0 ] . split ( constants . BANG ) [ 1 ] if constants . BANG in symbols [ 0 ] else symbols [ 0 ] break return mod_name , symbol_name


def is_valid(self):
        '''
        Determine whether it is a valid object
        '''
        return self._type_prefix is not None and self._hdr is not None

def is_valid ( self ) : return self . _type_prefix is not None and self . _hdr is not None


def get_name(self):
        """ Get the name of the module as a string """
        return utility.array_to_string(self.name)

def get_name ( self ) : return utility . array_to_string ( self . name )


def _get_sect_count(self, grp):
        """ Try to determine the number of valid sections """
        arr = self._context.object(
            self.get_symbol_table().name + constants.BANG + "array",
            layer_name = self.vol.layer_name,
            offset = grp.attrs,
            subtype = self._context.symbol_space.get_type(self.get_symbol_table().name + constants.BANG + "pointer"),
            count = 25)

        idx = 0
        while arr[idx]:
            idx = idx + 1

        return idx

def _get_sect_count ( self , grp ) : arr = self . _context . object ( self . get_symbol_table ( ) . name + constants . BANG + "array" , layer_name = self . vol . layer_name , offset = grp . attrs , subtype = self . _context . symbol_space . get_type ( self . get_symbol_table ( ) . name + constants . BANG + "pointer" ) , count = 25 ) idx = 0 while arr [ idx ] : idx = idx + 1 return idx


def get_sections(self):
        """ Get sections of the module """
        if self.sect_attrs.has_member("nsections"):
            num_sects = self.sect_attrs.nsections
        else:
            num_sects = self._get_sect_count(self.sect_attrs.grp)

        arr = self._context.object(self.get_symbol_table().name + constants.BANG + "array",
                                   layer_name = self.vol.layer_name,
                                   offset = self.sect_attrs.attrs.vol.offset,
                                   subtype = self._context.symbol_space.get_type(self.get_symbol_table().name +
                                                                                constants.BANG + 'module_sect_attr'),
                                   count = num_sects)

        for attr in arr:
            yield attr

def get_sections ( self ) : if self . sect_attrs . has_member ( "nsections" ) : num_sects = self . sect_attrs . nsections else : num_sects = self . _get_sect_count ( self . sect_attrs . grp ) arr = self . _context . object ( self . get_symbol_table ( ) . name + constants . BANG + "array" , layer_name = self . vol . layer_name , offset = self . sect_attrs . attrs . vol . offset , subtype = self . _context . symbol_space . get_type ( self . get_symbol_table ( ) . name + constants . BANG + 'module_sect_attr' ) , count = num_sects ) for attr in arr : yield attr


def get_symbol(self, wanted_sym_name):
        """ Get value for a given symbol name """
        for sym in self.get_symbols():
            sym_name = sym.get_name()
            sym_addr = sym.st_value
            if wanted_sym_name == sym_name:
                return sym_addr

def get_symbol ( self , wanted_sym_name ) : for sym in self . get_symbols ( ) : sym_name = sym . get_name ( ) sym_addr = sym . st_value if wanted_sym_name == sym_name : return sym_addr


def add_process_layer(self, config_prefix: str = None, preferred_name: str = None) -> Optional[str]:
        """Constructs a new layer based on the process's DTB.

        Returns the name of the Layer or None.
        """

        parent_layer = self._context.layers[self.vol.layer_name]
        try:
            pgd = self.mm.pgd
        except exceptions.InvalidAddressException:
            return None

        if not isinstance(parent_layer, linear.LinearlyMappedLayer):
            raise TypeError("Parent layer is not a translation layer, unable to construct process layer")

        dtb, layer_name = parent_layer.translate(pgd)
        if not dtb:
            return None

        if preferred_name is None:
            preferred_name = self.vol.layer_name + "_Process{}".format(self.pid)

        # Add the constructed layer and return the name
        return self._add_process_layer(self._context, dtb, config_prefix, preferred_name)


def get_process_memory_sections(self, heap_only: bool = False) -> Generator[Tuple[int, int], None, None]:
        """Returns a list of sections based on the memory manager's view of
        this task's virtual memory."""
        for vma in self.mm.get_mmap_iter():
            start = int(vma.vm_start)
            end = int(vma.vm_end)

            if heap_only and not (start <= self.mm.brk and end >= self.mm.start_brk):
                continue
            else:
                # FIXME: Check if this actually needs to be printed out or not
                vollog.info("adding vma: {:x} {:x} | {:x} {:x}".format(start, self.mm.brk, end, self.mm.start_brk))

            yield (start, end - start)


Returns an iterator for the mmap list member of an mm_struct .

def get_mmap_iter(self) -> Iterable[interfaces.objects.ObjectInterface]:
        """Returns an iterator for the mmap list member of an mm_struct."""

        if not self.mmap:
            return

        yield self.mmap

        seen = {self.mmap.vol.offset}
        link = self.mmap.vm_next

        while link != 0 and link.vol.offset not in seen:
            yield link
            seen.add(link.vol.offset)
            link = link.vm_next

def get_mmap_iter ( self ) -> Iterable [ interfaces . objects . ObjectInterface ] : if not self . mmap : return yield self . mmap seen = { self . mmap . vol . offset } link = self . mmap . vm_next while link != 0 and link . vol . offset not in seen : yield link seen . add ( link . vol . offset ) link = link . vm_next


Returns an string representation of the flags in a vm_area_struct .

def _parse_flags(self, vm_flags, parse_flags) -> str:
        """Returns an string representation of the flags in a
        vm_area_struct."""

        retval = ""

        for mask, char in parse_flags.items():
            if (vm_flags & mask) == mask:
                retval = retval + char
            else:
                retval = retval + '-'

        return retval

def _parse_flags ( self , vm_flags , parse_flags ) -> str : retval = "" for mask , char in parse_flags . items ( ) : if ( vm_flags & mask ) == mask : retval = retval + char else : retval = retval + '-' return retval


Returns an iterator of the entries in the list .

def to_list(self,
                symbol_type: str,
                member: str,
                forward: bool = True,
                sentinel: bool = True,
                layer: Optional[str] = None) -> Iterator[interfaces.objects.ObjectInterface]:
        """Returns an iterator of the entries in the list."""
        layer = layer or self.vol.layer_name

        relative_offset = self._context.symbol_space.get_type(symbol_type).relative_child_offset(member)

        direction = 'prev'
        if forward:
            direction = 'next'
        try:
            link = getattr(self, direction).dereference()
        except exceptions.InvalidAddressException:
            return

        if not sentinel:
            yield self._context.object(symbol_type, layer, offset = self.vol.offset - relative_offset)

        seen = {self.vol.offset}
        while link.vol.offset not in seen:

            obj = self._context.object(symbol_type, layer, offset = link.vol.offset - relative_offset)
            yield obj

            seen.add(link.vol.offset)
            try:
                link = getattr(link, direction).dereference()
            except exceptions.InvalidAddressException:
                break

def to_list ( self , symbol_type : str , member : str , forward : bool = True , sentinel : bool = True , layer : Optional [ str ] = None ) -> Iterator [ interfaces . objects . ObjectInterface ] : layer = layer or self . vol . layer_name relative_offset = self . _context . symbol_space . get_type ( symbol_type ) . relative_child_offset ( member ) direction = 'prev' if forward : direction = 'next' try : link = getattr ( self , direction ) . dereference ( ) except exceptions . InvalidAddressException : return if not sentinel : yield self . _context . object ( symbol_type , layer , offset = self . vol . offset - relative_offset ) seen = { self . vol . offset } while link . vol . offset not in seen : obj = self . _context . object ( symbol_type , layer , offset = link . vol . offset - relative_offset ) yield obj seen . add ( link . vol . offset ) try : link = getattr ( link , direction ) . dereference ( ) except exceptions . InvalidAddressException : break


Constructs a new layer based on the process s DirectoryTableBase .

def _add_process_layer(self,
                           context: interfaces.context.ContextInterface,
                           dtb: Union[int, interfaces.objects.ObjectInterface],
                           config_prefix: str = None,
                           preferred_name: str = None) -> str:
        """Constructs a new layer based on the process's DirectoryTableBase."""

        if config_prefix is None:
            # TODO: Ensure collisions can't happen by verifying the config_prefix is empty
            random_prefix = ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits)
                                    for _ in range(8))
            config_prefix = interfaces.configuration.path_join("temporary", "_" + random_prefix)

        # Figure out a suitable name we can use for the new layer
        if preferred_name is None:
            preferred_name = context.layers.free_layer_name(prefix = self.vol.layer_name + "_Process")
        else:
            if preferred_name in context.layers:
                preferred_name = context.layers.free_layer_name(prefix = preferred_name)

        # Copy the parent's config and then make suitable changes
        parent_layer = context.layers[self.vol.layer_name]
        parent_config = parent_layer.build_configuration()
        # It's an intel layer, because we hardwire the "memory_layer" config option
        # FIXME: this could be for other architectures if we don't hardwire this/these values
        parent_config['memory_layer'] = parent_layer.config['memory_layer']
        parent_config['page_map_offset'] = dtb

        # Set the new configuration and construct the layer
        config_path = interfaces.configuration.path_join(config_prefix, preferred_name)
        context.config.splice(config_path, parent_config)
        new_layer = parent_layer.__class__(context, config_path = config_path, name = preferred_name)

        # Add the constructed layer and return the name
        context.layers.add_layer(new_layer)
        return preferred_name


Loads up the list of successfully cached json objects so we don t need to revalidate them .

def load_cached_validations() -> Set[str]:
    """Loads up the list of successfully cached json objects, so we don't need
    to revalidate them."""
    validhashes = set()  # type: Set
    if os.path.exists(cached_validation_filepath):
        with open(cached_validation_filepath, "r") as f:
            validhashes.update(json.load(f))
    return validhashes

def
load_cached_validations
(
)
->
Set
[
str
]
:
validhashes
=
set
(
)
# type: Set
if
os
.
path
.
exists
(
cached_validation_filepath
)
:
with
open
(
cached_validation_filepath
,
"r"
)
as
f
:
validhashes
.
update
(
json
.
load
(
f
)
)
return
validhashes


Record the cached validations so we don t need to revalidate them in future .

def record_cached_validations(validations):
    """Record the cached validations, so we don't need to revalidate them in
    future."""
    with open(cached_validation_filepath, "w") as f:
        json.dump(list(validations), f)

def record_cached_validations ( validations ) : with open ( cached_validation_filepath , "w" ) as f : json . dump ( list ( validations ) , f )


Validates an input JSON file based upon .

def validate(input: Dict[str, Any], use_cache: bool = True) -> bool:
    """Validates an input JSON file based upon."""
    format = input.get('metadata', {}).get('format', None)
    if not format:
        vollog.debug("No schema format defined")
        return False
    basepath = os.path.abspath(os.path.dirname(__file__))
    schema_path = os.path.join(basepath, 'schema-' + format + '.json')
    if not os.path.exists(schema_path):
        vollog.debug("Schema for format not found: {}".format(schema_path))
        return False
    with open(schema_path, 'r') as s:
        schema = json.load(s)
    return valid(input, schema, use_cache)

def
validate
(
input
:
Dict
[
str
,
Any
]
,
use_cache
:
bool
=
True
)
->
bool
:
format
=
input
.
get
(
'metadata'
,
{
}
)
.
get
(
'format'
,
)
if
not
format
:
vollog
.
debug
(
"No schema format defined"
)
return
False
basepath
=
os
.
path
.
abspath
(
os
.
path
.
dirname
(
__file__
)
)
schema_path
=
os
.
path
.
join
(
basepath
,
'schema-'
+
format
+
'.json'
)
if
not
os
.
path
.
exists
(
schema_path
)
:
vollog
.
debug
(
"Schema for format not found: {}"
.
format
(
schema_path
)
)
return
False
with
open
(
schema_path
,
'r'
)
as
s
:
schema
=
json
.
load
(
s
)
return
valid
(
input
,
schema
,
use_cache
)


Constructs the hash of the input and schema to create a unique indentifier for a particular JSON file .

def create_json_hash(input: Dict[str, Any], schema: Dict[str, Any]) -> str:
    """Constructs the hash of the input and schema to create a unique
    indentifier for a particular JSON file."""
    return hashlib.sha1(bytes(json.dumps((input, schema), sort_keys = True), 'utf-8')).hexdigest()

def create_json_hash ( input : Dict [ str , Any ] , schema : Dict [ str , Any ] ) -> str : return hashlib . sha1 ( bytes ( json . dumps ( ( input , schema ) , sort_keys = True ) , 'utf-8' ) ) . hexdigest ( )


def valid(input: Dict[str, Any], schema: Dict[str, Any], use_cache: bool = True) -> bool:
    """Validates a json schema."""
    input_hash = create_json_hash(input, schema)
    if input_hash in cached_validations and use_cache:
        return True
    try:
        import jsonschema
    except ImportError:
        vollog.info("Dependency for validation unavailable: jsonschema")
        vollog.debug("All validations will report success, even with malformed input")
        return True

    try:
        vollog.debug("Validating JSON against schema...")
        jsonschema.validate(input, schema)
        cached_validations.add(input_hash)
        vollog.debug("JSON validated against schema (result cached)")
    except jsonschema.exceptions.SchemaError:
        vollog.debug("Schema validation error", exc_info = True)
        return False

    record_cached_validations(cached_validations)
    return True


def process_files(self, named_files: Dict[str, str]):
        """Runs the dwarf2json binary across the files"""
        print("Processing Files...")
        for i in named_files:
            if named_files[i] is None:
                print("FAILURE: None encountered for {}".format(i))
                return
        args = [DWARF2JSON, 'linux']
        output_filename = 'unknown-kernel.json'
        for named_file in named_files:
            prefix = '--system-map'
            if not 'System' in named_files[named_file]:
                prefix = '--elf'
                output_filename = './' + '-'.join((named_file.split('/')[-1]).split('-')[2:])[:-4] + '.json.xz'
            args += [prefix, named_files[named_file]]
        print(" - Running {}".format(args))
        proc = subprocess.run(args, capture_output = True)

        print(" - Writing to {}".format(output_filename))
        with lzma.open(output_filename, 'w') as f:
            f.write(proc.stdout)

def
process_files
(
self
,
named_files
:
Dict
[
str
,
str
]
)
:
print
(
"Processing Files..."
)
for
i
in
named_files
:
if
named_files
[
i
]
is
:
print
(
"FAILURE: None encountered for {}"
.
format
(
i
)
)
return
args
=
[
DWARF2JSON
,
'linux'
]
output_filename
=
'unknown-kernel.json'
for
named_file
in
named_files
:
prefix
=
'--system-map'
if
not
'System'
in
named_files
[
named_file
]
:
prefix
=
'--elf'
output_filename
=
'./'
+
'-'
.
join
(
(
named_file
.
split
(
'/'
)
[
1
]
)
.
split
(
'-'
)
[
2
:
]
)
[
:
4
]
+
'.json.xz'
args
+=
[
prefix
,
named_files
[
named_file
]
]
print
(
" - Running {}"
.
format
(
args
)
)
proc
=
subprocess
.
run
(
args
,
capture_output
=
True
)
print
(
" - Writing to {}"
.
format
(
output_filename
)
)
with
lzma
.
open
(
output_filename
,
'w'
)
as
f
:
f
.
write
(
proc
.
stdout
)


def read_pdb(self) -> Dict:
        """Reads in the PDB file and forms essentially a python dictionary of necessary data"""
        output = {
            "user_types": self.read_usertypes(),
            "enums": self.read_enums(),
            "metadata": self.generate_metadata(),
            "symbols": self.read_symbols(),
            "base_types": self.read_basetypes()
        }
        return output

def read_pdb ( self ) -> Dict : output = { "user_types" : self . read_usertypes ( ) , "enums" : self . read_enums ( ) , "metadata" : self . generate_metadata ( ) , "symbols" : self . read_symbols ( ) , "base_types" : self . read_basetypes ( ) } return output


def generate_metadata(self) -> Dict[str, Any]:
        """Generates the metadata necessary for this object"""
        dbg = self._pdb.STREAM_DBI
        last_bytes = str(binascii.hexlify(self._pdb.STREAM_PDB.GUID.Data4), 'ascii')[-16:]
        guidstr = u'{:08x}{:04x}{:04x}{}'.format(self._pdb.STREAM_PDB.GUID.Data1, self._pdb.STREAM_PDB.GUID.Data2,
                                                 self._pdb.STREAM_PDB.GUID.Data3, last_bytes)
        pdb_data = {
            "GUID": guidstr.upper(),
            "age": self._pdb.STREAM_PDB.Age,
            "database": "ntkrnlmp.pdb",
            "machine_type": int(dbg.machine)
        }
        result = {
            "format": "6.0.0",
            "producer": {
                "datetime": datetime.datetime.now().isoformat(),
                "name": "pdbconv",
                "version": "0.1.0"
            },
            "windows": {
                "pdb": pdb_data
            }
        }
        return result

def generate_metadata ( self ) -> Dict [ str , Any ] : dbg = self . _pdb . STREAM_DBI last_bytes = str ( binascii . hexlify ( self . _pdb . STREAM_PDB . GUID . Data4 ) , 'ascii' ) [ - 16 : ] guidstr = u'{:08x}{:04x}{:04x}{}' . format ( self . _pdb . STREAM_PDB . GUID . Data1 , self . _pdb . STREAM_PDB . GUID . Data2 , self . _pdb . STREAM_PDB . GUID . Data3 , last_bytes ) pdb_data = { "GUID" : guidstr . upper ( ) , "age" : self . _pdb . STREAM_PDB . Age , "database" : "ntkrnlmp.pdb" , "machine_type" : int ( dbg . machine ) } result = { "format" : "6.0.0" , "producer" : { "datetime" : datetime . datetime . now ( ) . isoformat ( ) , "name" : "pdbconv" , "version" : "0.1.0" } , "windows" : { "pdb" : pdb_data } } return result


def read_enums(self) -> Dict:
        """Reads the Enumerations from the PDB file"""
        logger.info("Reading enums...")
        output = {}  # type: Dict[str, Any]
        stream = self._pdb.STREAM_TPI
        for type_index in stream.types:
            user_type = stream.types[type_index]
            if (user_type.leaf_type == "LF_ENUM" and not user_type.prop.fwdref):
                output.update(self._format_enum(user_type))
        return output

def
read_enums
(
self
)
->
Dict
:
logger
.
info
(
"Reading enums..."
)
output
=
{
}
# type: Dict[str, Any]
stream
=
self
.
_pdb
.
STREAM_TPI
for
type_index
in
stream
.
types
:
user_type
=
stream
.
types
[
type_index
]
if
(
user_type
.
leaf_type
==
"LF_ENUM"
and
not
user_type
.
prop
.
fwdref
)
:
output
.
update
(
self
.
_format_enum
(
user_type
)
)
return
output


def read_symbols(self) -> Dict:
        """Reads the symbols from the PDB file"""
        logger.info("Reading symbols...")
        output = {}

        try:
            sects = self._pdb.STREAM_SECT_HDR_ORIG.sections
            omap = self._pdb.STREAM_OMAP_FROM_SRC
        except AttributeError as e:
            # In this case there is no OMAP, so we use the given section
            # headers and use the identity function for omap.remap
            sects = self._pdb.STREAM_SECT_HDR.sections
            omap = None

        for sym in self._pdb.STREAM_GSYM.globals:
            if not hasattr(sym, 'offset'):
                continue
            try:
                virt_base = sects[sym.segment - 1].VirtualAddress
            except IndexError:
                continue
            name, _, _ = pdbparse.undecorate.undecorate(sym.name)
            if omap:
                output[name] = {"address": omap.remap(sym.offset + virt_base)}
            else:
                output[name] = {"address": sym.offset + virt_base}

        return output


def read_usertypes(self) -> Dict:
        """Reads the user types from the PDB file"""
        logger.info("Reading usertypes...")
        output = {}
        stream = self._pdb.STREAM_TPI
        for type_index in stream.types:
            user_type = stream.types[type_index]
            if (user_type.leaf_type == "LF_STRUCTURE" and not user_type.prop.fwdref):
                output.update(self._format_usertype(user_type, "struct"))
            elif (user_type.leaf_type == "LF_UNION" and not user_type.prop.fwdref):
                output.update(self._format_usertype(user_type, "union"))
        return output

def
read_usertypes
(
self
)
->
Dict
:
logger
.
info
(
"Reading usertypes..."
)
output
=
{
}
stream
=
self
.
_pdb
.
STREAM_TPI
for
type_index
in
stream
.
types
:
user_type
=
stream
.
types
[
type_index
]
if
(
user_type
.
leaf_type
==
"LF_STRUCTURE"
and
not
user_type
.
prop
.
fwdref
)
:
output
.
update
(
self
.
_format_usertype
(
user_type
,
"struct"
)
)
elif
(
user_type
.
leaf_type
==
"LF_UNION"
and
not
user_type
.
prop
.
fwdref
)
:
output
.
update
(
self
.
_format_usertype
(
user_type
,
"union"
)
)
return
output


def _format_usertype(self, usertype, kind) -> Dict:
        """Produces a single usertype"""
        fields = {}  # type: Dict[str, Dict[str, Any]]
        [fields.update(self._format_field(s)) for s in usertype.fieldlist.substructs]
        return {usertype.name: {'fields': fields, 'kind': kind, 'size': usertype.size}}

def
_format_usertype
(
self
,
usertype
,
kind
)
->
Dict
:
fields
=
{
}
# type: Dict[str, Dict[str, Any]]
[
fields
.
update
(
self
.
_format_field
(
s
)
)
for
s
in
usertype
.
fieldlist
.
substructs
]
return
{
usertype
.
name
:
{
'fields'
:
fields
,
'kind'
:
kind
,
'size'
:
usertype
.
size
}
}


def read_basetypes(self) -> Dict:
        """Reads the base types from the PDB file"""
        ptr_size = 4
        if "64" in self._pdb.STREAM_DBI.machine:
            ptr_size = 8

        output = {"pointer": {"endian": "little", "kind": "int", "signed": False, "size": ptr_size}}
        for index in self._seen_ctypes:
            output[self.ctype[index]] = {
                "endian": "little",
                "kind": self.ctype_python_types.get(self.ctype[index], "int"),
                "signed": False if "_U" in index else True,
                "size": self.base_type_size[index]
            }
        return output

def read_basetypes ( self ) -> Dict : ptr_size = 4 if "64" in self . _pdb . STREAM_DBI . machine : ptr_size = 8 output = { "pointer" : { "endian" : "little" , "kind" : "int" , "signed" : False , "size" : ptr_size } } for index in self . _seen_ctypes : output [ self . ctype [ index ] ] = { "endian" : "little" , "kind" : self . ctype_python_types . get ( self . ctype [ index ] , "int" ) , "signed" : False if "_U" in index else True , "size" : self . base_type_size [ index ] } return output


def main(file):
    """Main function for everything!"""

    #print("\n    Enumerating FilterToConsumerBindings...")

    #Read objects.data 4 lines at a time to look for bindings
    objects_file = open(file , "rb")
    current_line = objects_file.readline()
    lines_list = [current_line]
    current_line = objects_file.readline()
    lines_list.append(current_line)
    current_line = objects_file.readline()
    lines_list.append(current_line)
    current_line = objects_file.readline()
    lines_list.append(current_line)

    #Precompiled match objects to search each line with
    event_consumer_mo = re.compile(r"([\w\_]*EventConsumer\.Name\=\")([\w\s]*)(\")")
    event_filter_mo = re.compile(r"(_EventFilter\.Name\=\")([\w\s]*)(\")")

    #Dictionaries that will store bindings, consumers, and filters
    bindings_dict = {}
    consumer_dict = {}
    filter_dict = {}

    while current_line:
        # Join all the read lines together (should always be 4) to look for bindings spread over
        #   multiple lines that may have been one page
        potential_page = " ".join(lines_list)

        # Look for FilterToConsumerBindings
        if "_FilterToConsumerBinding" in potential_page:
            if (
                    re.search(event_consumer_mo, potential_page) and
                    re.search(event_filter_mo, potential_page)):
                event_consumer_name = re.search(event_consumer_mo, potential_page).groups(0)[1]
                event_filter_name = re.search(event_filter_mo, potential_page).groups(0)[1]

                #Add the consumers and filters to their dicts if they don't already exist
                #set() is used to avoid duplicates as we go through the lines
                if event_consumer_name not in consumer_dict:
                    consumer_dict[event_consumer_name] = set()
                if event_filter_name not in filter_dict:
                    filter_dict[event_filter_name] = set()

                #Give the binding a name and add it to the dict
                binding_id = "{}-{}".format(event_consumer_name, event_filter_name)
                if binding_id not in bindings_dict:
                    bindings_dict[binding_id] = {
                        "event_consumer_name":event_consumer_name,
                        "event_filter_name":event_filter_name}

        # Increment lines and look again
        current_line = objects_file.readline()
        lines_list.append(current_line)
        lines_list.pop(0)

    # Close the file and look for consumers and filters
    objects_file.close()
    #print("    {} FilterToConsumerBinding(s) Found. Enumerating Filters and Consumers..."
    #      .format(len(bindings_dict)))

    #print( bindings_dict )
    # Read objects.data 4 lines at a time to look for filters and consumers
    objects_file = open(file, "rb")
    current_line = objects_file.readline()
    lines_list = [current_line]
    current_line = objects_file.readline()
    lines_list.append(current_line)
    current_line = objects_file.readline()
    lines_list.append(current_line)
    current_line = objects_file.readline()
    lines_list.append(current_line)

    while current_line:
        potential_page = " ".join(lines_list).replace("\n", "")

        # Check each potential page for the consumers we are looking for
        if "EventConsumer" in potential_page:
            for event_consumer_name, event_consumer_details in consumer_dict.iteritems():
                # Can't precompile regex because it is dynamically created with each consumer name
                if "CommandLineEventConsumer" in potential_page:
                    consumer_mo = re.compile("(CommandLineEventConsumer)(\x00\x00)(.*?)(\x00)(.*?)"
                                             "({})(\x00\x00)?([^\x00]*)?"
                                             .format(event_consumer_name))
                    consumer_match = re.search(consumer_mo, potential_page)
                    if consumer_match:
                        noisy_string = consumer_match.groups()[2]
                        consumer_details = "\n\t\tConsumer Type: {}\n\t\tArguments:     {}".format(
                            consumer_match.groups()[0],
                            filter(lambda event_consumer_name: event_consumer_name in
                                   PRINTABLE_CHARS, noisy_string))
                        if consumer_match.groups()[5]:
                            consumer_details += "\n\t\tConsumer Name: {}".format(consumer_match.groups()[5])
                        if consumer_match.groups()[7]:
                            consumer_details += "\n\t\tOther:         {}".format(consumer_match.groups()[7])
                        consumer_dict[event_consumer_name].add(consumer_details)

                else:
                    consumer_mo = re.compile(
                        r"(\w*EventConsumer)(.*?)({})(\x00\x00)([^\x00]*)(\x00\x00)([^\x00]*)"
                        .format(event_consumer_name))
                    consumer_match = re.search(consumer_mo, potential_page)
                    if consumer_match:
                        consumer_details = "{} ~ {} ~ {} ~ {}".format(
                            consumer_match.groups()[0],
                            consumer_match.groups()[2],
                            consumer_match.groups()[4],
                            consumer_match.groups()[6])
                        consumer_dict[event_consumer_name].add(consumer_details)

        # Check each potential page for the filters we are looking for
        for event_filter_name, event_filter_details in filter_dict.iteritems():
            if event_filter_name in potential_page:
                # Can't precompile regex because it is dynamically created with each filter name
                filter_mo = re.compile(
                    r"({})(\x00\x00)([^\x00]*)(\x00\x00)".format(event_filter_name))
                filter_match = re.search(filter_mo, potential_page)
                if filter_match:
                    filter_details = "\n\t\tFilter name:  {}\n\t\tFilter Query: {}".format(
                        filter_match.groups()[0],
                        filter_match.groups()[2])
                    filter_dict[event_filter_name].add(filter_details)

        current_line = objects_file.readline()
        lines_list.append(current_line)
        lines_list.pop(0)
    objects_file.close()

    # Print results to stdout. CSV will be in future version.
    #print("\n    Bindings:\n")
    res = [  ]

    for binding_name, binding_details in bindings_dict.iteritems():
        temp_res = {}
        temp_res['binding_name'] = binding_name

        event_filter_name = binding_details["event_filter_name"]
        event_consumer_name = binding_details["event_consumer_name"]

        temp_res['filter_name'] = event_filter_name
        temp_res['consumer_name'] = event_consumer_name

        # Print binding details if available
        if consumer_dict[event_consumer_name]:
            for event_consumer_details in consumer_dict[event_consumer_name]:
                #print("            Consumer: {}".format(event_consumer_details))
                temp_res['consumer_dict'] = event_consumer_details

            temp_consumer_dict = {}
            for i in temp_res['consumer_dict'].split('\n\t'):
                v = ':'.join( i.split(':')[1:]).strip()
                k = i.split(':')[0]
                if k == "":
                    continue
                if v == "":
                    temp_consumer_dict['Consumer'] = k
                else:
                    temp_consumer_dict[k.strip('\t').replace(' ' , '_')] = v
            temp_res['consumer_dict'] = temp_consumer_dict
            #print(temp_res['consumer_dict'])

        else:
            #print("            Consumer: {}".format(event_consumer_name))
            temp_res['consumer_dict'] = event_consumer_name

        # Print details for each filter found for this filter name
        for event_filter_details in filter_dict[event_filter_name]:
            #print("\n            Filter: {}".format(event_filter_details))
            #print()
            temp_res['filter_details'] = event_filter_details

        temp_filter_details = {}
        for i in temp_res['filter_details'].split('\n\t'):
            k = i.split(':')[0].strip('\t').replace(' ' , '_')
            if k == "":
                    continue
            temp_filter_details[k] = ':'.join( i.split(':')[1:]).strip()
        temp_res['filter_details'] = temp_filter_details

        res.append(temp_res)
    # Print closing message
    #print("\n    Thanks for using PyWMIPersistenceFinder! Please contact @DavidPany with "
    #      "questions, bugs, or suggestions.\n\n    Please review FireEye's whitepaper "
    #      "for additional WMI persistence details:\n        https://www.fireeye.com/content/dam"
    #      "/fireeye-www/global/en/current-threats/pdfs/wp-windows-management-instrumentation.pdf")

    #for i in res:
    #    print(str(i))
    return res