Exemplo n.º 1
0
 def get_record_tuple(service_record: interfaces.objects.ObjectInterface):
     return (format_hints.Hex(service_record.vol.offset),
             service_record.Order, service_record.get_pid(),
             service_record.Start.description,
             service_record.State.description, service_record.get_type(),
             service_record.get_name(), service_record.get_display(),
             service_record.get_binary())
Exemplo n.º 2
0
 def parse_string(structure: interfaces.objects.ObjectInterface,
                  parse_as_pascal: bool = False,
                  size: int = 0) -> str:
     """Consumes either a c-string or a pascal string depending on the
     leaf_type."""
     if not parse_as_pascal:
         name = structure.cast("string",
                               max_length=size,
                               encoding="latin-1")
     else:
         name = structure.cast("pascal_string")
         name = name.string.cast("string",
                                 max_length=name.length,
                                 encoding="latin-1")
     return str(name)
Exemplo n.º 3
0
    def _walk_iterable(
        cls,
        queue: interfaces.objects.ObjectInterface,
        list_head_member: str,
        list_next_member: str,
        next_member: str,
        max_elements: int = 4096
    ) -> Iterable[interfaces.objects.ObjectInterface]:
        seen: Set[int] = set()

        try:
            current = queue.member(attr=list_head_member)
        except exceptions.InvalidAddressException:
            return

        while current:
            if current.vol.offset in seen:
                break

            seen.add(current.vol.offset)

            if len(seen) == max_elements:
                break

            if current.is_readable():
                yield current

            try:
                current = current.member(attr=next_member).member(
                    attr=list_next_member)
            except exceptions.InvalidAddressException:
                break
Exemplo n.º 4
0
    def process_dump(
            cls, context: interfaces.context.ContextInterface, kernel_table_name: str, pe_table_name: str,
            proc: interfaces.objects.ObjectInterface,
            open_method: Type[interfaces.plugins.FileHandlerInterface]) -> interfaces.plugins.FileHandlerInterface:
        """Extracts the complete data for a process as a FileHandlerInterface

        Args:
            context: the context to operate upon
            kernel_table_name: the name for the symbol table containing the kernel's symbols
            pe_table_name: the name for the symbol table containing the PE format symbols
            proc: the process object whose memory should be output
            open_method: class to provide context manager for opening the file

        Returns:
            An open FileHandlerInterface object containing the complete data for the process or None in the case of failure
        """

        file_handle = None
        try:
            proc_layer_name = proc.add_process_layer()
            peb = context.object(kernel_table_name + constants.BANG + "_PEB",
                                 layer_name = proc_layer_name,
                                 offset = proc.Peb)

            dos_header = context.object(pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER",
                                        offset = peb.ImageBaseAddress,
                                        layer_name = proc_layer_name)
            file_handle = open_method("pid.{0}.{1:#x}.dmp".format(proc.UniqueProcessId, peb.ImageBaseAddress))
            for offset, data in dos_header.reconstruct():
                file_handle.seek(offset)
                file_handle.write(data)
        except Exception as excp:
            vollog.debug("Unable to dump PE with pid {}: {}".format(proc.UniqueProcessId, excp))

        return file_handle
Exemplo n.º 5
0
    def list_injections(
        cls, context: interfaces.context.ContextInterface,
        kernel_layer_name: str, symbol_table: str,
        proc: interfaces.objects.ObjectInterface
    ) -> Iterable[Tuple[interfaces.objects.ObjectInterface, bytes]]:
        """Generate memory regions for a process that may contain injected
        code.

        Args:
            context: The context to retrieve required elements (layers, symbol tables) from
            kernel_layer_name: The name of the kernel layer from which to read the VAD protections
            symbol_table: The name of the table containing the kernel symbols
            proc: an _EPROCESS instance

        Returns:
            An iterable of VAD instances and the first 64 bytes of data containing in that region
        """
        proc_id = "Unknown"
        try:
            proc_id = proc.UniqueProcessId
            proc_layer_name = proc.add_process_layer()
        except exceptions.InvalidAddressException as excp:
            vollog.debug("Process {}: invalid address {} in layer {}".format(
                proc_id, excp.invalid_address, excp.layer_name))
            return

        proc_layer = context.layers[proc_layer_name]

        for vad in proc.get_vad_root().traverse():
            protection_string = vad.get_protection(
                vadinfo.VadInfo.protect_values(context, kernel_layer_name,
                                               symbol_table),
                vadinfo.winnt_protections)
            write_exec = "EXECUTE" in protection_string and "WRITE" in protection_string

            # the write/exec check applies to everything
            if not write_exec:
                continue

            if (vad.get_private_memory() == 1 and vad.get_tag() == "VadS") or (
                    vad.get_private_memory() == 0
                    and protection_string != "PAGE_EXECUTE_WRITECOPY"):
                if cls.is_vad_empty(proc_layer, vad):
                    continue

                data = proc_layer.read(vad.get_start(), 64, pad=True)
                yield vad, data
Exemplo n.º 6
0
    def _vnode_name(cls, vnode: interfaces.objects.ObjectInterface) -> Optional[str]:
        # roots of mount points have special name handling
        if vnode.v_flag & 1 == 1:
            v_name = vnode.full_path()
        else:
            try:
                v_name = utility.pointer_to_string(vnode.v_name, 255)
            except exceptions.InvalidAddressException:
                v_name = None

        return v_name
Exemplo n.º 7
0
def array_of_pointers(array: interfaces.objects.ObjectInterface, count: int,
                      subtype: Union[str, interfaces.objects.Template],
                      context: interfaces.context.ContextInterface) -> interfaces.objects.ObjectInterface:
    """Takes an object, and recasts it as an array of pointers to subtype."""
    symbol_table = array.vol.type_name.split(constants.BANG)[0]
    if isinstance(subtype, str) and context is not None:
        subtype = context.symbol_space.get_type(subtype)
    if not isinstance(subtype, interfaces.objects.Template) or subtype is None:
        raise TypeError("Subtype must be a valid template (or string name of an object template)")
    subtype_pointer = context.symbol_space.get_type(symbol_table + constants.BANG + "pointer")
    subtype_pointer.update_vol(subtype = subtype)
    return array.cast("array", count = count, subtype = subtype_pointer)
Exemplo n.º 8
0
 def get_vad(task: interfaces.objects.ObjectInterface, address: int):  # vad
     """Creates a map of start/end addresses within a virtual address
     descriptor tree.
     Args:
         task: The EPROCESS object of which to traverse the vad tree
     Returns:
         An iterable of tuples containing start and end addresses for each descriptor
     """
     vad_root = task.get_vad_root()
     for vad in vad_root.traverse():
         end = vad.get_end()
         start = vad.get_start()
         if end > address >= start:
             return vad, start, end
     return None, None, None
Exemplo n.º 9
0
    def list_vads(cls, proc: interfaces.objects.ObjectInterface,
                  filter_func: Callable[[interfaces.objects.ObjectInterface], bool] = lambda _: False) -> \
            Generator[interfaces.objects.ObjectInterface, None, None]:
        """Lists the Virtual Address Descriptors of a specific process.

        Args:
            proc: _EPROCESS object from which to list the VADs
            filter_func: Function to take a virtual address descriptor value and return True if it should be filtered out

        Returns:
            A list of virtual address descriptors based on the process and filtered based on the filter function
        """
        for vad in proc.get_vad_root().traverse():
            if not filter_func(vad):
                yield vad
Exemplo n.º 10
0
    def get_user_name(cls, user: interfaces.objects.ObjectInterface,
                      samhive: registry.RegistryHive) -> Optional[bytes]:
        V = None
        for v in user.get_values():
            if v.get_name() == 'V':
                V = samhive.read(v.Data + 4, v.DataLength)
        if not V:
            return None

        name_offset = unpack("<L", V[0x0c:0x10])[0] + 0xCC
        name_length = unpack("<L", V[0x10:0x14])[0]
        if name_length > len(V):
            return None

        username = V[name_offset:name_offset + name_length]
        return username
Exemplo n.º 11
0
    def get_vad_maps(
            task: interfaces.objects.ObjectInterface
    ) -> Iterable[Tuple[int, int]]:
        """Creates a map of start/end addresses within a virtual address
        descriptor tree.

        Args:
            task: The EPROCESS object of which to traverse the vad tree

        Returns:
            An iterable of tuples containing start and end addresses for each descriptor
        """
        vad_root = task.get_vad_root()
        for vad in vad_root.traverse():
            end = vad.get_end()
            start = vad.get_start()
            yield (start, end - start)
Exemplo n.º 12
0
    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)
Exemplo n.º 13
0
    def dump_file_producer(
        cls, file_object: interfaces.objects.ObjectInterface,
        memory_object: interfaces.objects.ObjectInterface,
        open_method: Type[interfaces.plugins.FileHandlerInterface],
        layer: interfaces.layers.DataLayerInterface, desired_file_name: str
    ) -> Optional[interfaces.plugins.FileHandlerInterface]:
        """Produce a file from the memory object's get_available_pages() interface.

        :param file_object: the parent _FILE_OBJECT
        :param memory_object: the _CONTROL_AREA or _SHARED_CACHE_MAP
        :param open_method: class for constructing output files
        :param layer: the memory layer to read from
        :param desired_file_name: name of the output file
        :return: result status
        """
        filedata = open_method(desired_file_name)
        try:
            # Description of these variables:
            #   memoffset: offset in the specified layer where the page begins
            #   fileoffset: write to this offset in the destination file
            #   datasize: size of the page

            # track number of bytes written so we don't write empty files to disk
            bytes_written = 0
            for memoffset, fileoffset, datasize in memory_object.get_available_pages(
            ):
                data = layer.read(memoffset, datasize, pad=True)
                bytes_written += len(data)
                filedata.seek(fileoffset)
                filedata.write(data)

            if not bytes_written:
                vollog.debug(
                    f"No data is cached for the file at {file_object.vol.offset:#x}"
                )
                return None
            else:
                vollog.debug(f"Stored {filedata.preferred_filename}")
                return filedata
        except exceptions.InvalidAddressException:
            vollog.debug(f"Unable to dump file at {file_object.vol.offset:#x}")
            return None
Exemplo n.º 14
0
 def filter_function(x: interfaces.objects.ObjectInterface) -> bool:
     return x.get_start() not in [self.config['address']]
Exemplo n.º 15
0
    def vad_dump(
        cls,
        context: interfaces.context.ContextInterface,
        proc: interfaces.objects.ObjectInterface,
        vad: interfaces.objects.ObjectInterface,
        open_method: Type[interfaces.plugins.FileHandlerInterface],
        maxsize: int = MAXSIZE_DEFAULT
    ) -> Optional[interfaces.plugins.FileHandlerInterface]:
        """Extracts the complete data for Vad as a FileInterface.

        Args:
            context: The context to retrieve required elements (layers, symbol tables) from
            proc: an _EPROCESS instance
            vad: The suspected VAD to extract (ObjectInterface)
            open_method: class to provide context manager for opening the file
            maxsize: Max size of VAD section (default MAXSIZE_DEFAULT)

        Returns:
            An open FileInterface object containing the complete data for the process or None in the case of failure
        """

        try:
            vad_start = vad.get_start()
            vad_end = vad.get_end()
        except AttributeError:
            vollog.debug("Unable to find the starting/ending VPN member")
            return None

        if maxsize > 0 and (vad_end - vad_start) > maxsize:
            vollog.debug(
                f"Skip VAD dump {vad_start:#x}-{vad_end:#x} due to maxsize limit"
            )
            return None

        proc_id = "Unknown"
        try:
            proc_id = proc.UniqueProcessId
            proc_layer_name = proc.add_process_layer()
        except exceptions.InvalidAddressException as excp:
            vollog.debug("Process {}: invalid address {} in layer {}".format(
                proc_id, excp.invalid_address, excp.layer_name))
            return None

        proc_layer = context.layers[proc_layer_name]
        file_name = f"pid.{proc_id}.vad.{vad_start:#x}-{vad_end:#x}.dmp"
        try:
            file_handle = open_method(file_name)
            chunk_size = 1024 * 1024 * 10
            offset = vad_start
            while offset < vad_end:
                to_read = min(chunk_size, vad_end - offset)
                data = proc_layer.read(offset, to_read, pad=True)
                if not data:
                    break
                file_handle.write(data)
                offset += to_read

        except Exception as excp:
            vollog.debug(f"Unable to dump VAD {file_name}: {excp}")
            return None

        return file_handle
Exemplo n.º 16
0
 def filter_function(x: interfaces.objects.ObjectInterface) -> bool:
     try:
         return not (x.get_private_memory() == 0 and x.ControlArea)
     except AttributeError:
         return False
Exemplo n.º 17
0
 def save_vacb(self, vacb_obj: interfaces.objects.ObjectInterface,
               vacb_list: List):
     data = (int(vacb_obj.BaseAddress), int(vacb_obj.get_file_offset()),
             self.VACB_BLOCK)
     vacb_list.append(data)
Exemplo n.º 18
0
    def process_file_object(
        cls, context: interfaces.context.ContextInterface,
        primary_layer_name: str,
        open_method: Type[interfaces.plugins.FileHandlerInterface],
        file_obj: interfaces.objects.ObjectInterface
    ) -> Generator[Tuple, None, None]:
        """Given a FILE_OBJECT, dump data to separate files for each of the three file caches.

        :param context: the context to operate upon
        :param primary_layer_name: primary/virtual layer to operate on
        :param open_method: class for constructing output files
        :param file_obj: the FILE_OBJECT
        """

        # Filtering by these types of devices prevents us from processing other types of devices that
        # use the "File" object type, such as \Device\Tcp and \Device\NamedPipe.
        if file_obj.DeviceObject.DeviceType not in [
                FILE_DEVICE_DISK, FILE_DEVICE_NETWORK_FILE_SYSTEM
        ]:
            vollog.log(
                constants.LOGLEVEL_VVV,
                f"The file object at {file_obj.vol.offset:#x} is not a file on disk"
            )
            return

        # Depending on the type of object (DataSection, ImageSection, SharedCacheMap) we may need to
        # read from the memory layer or the primary layer.
        memory_layer_name = context.layers[primary_layer_name].config[
            'memory_layer']
        memory_layer = context.layers[memory_layer_name]
        primary_layer = context.layers[primary_layer_name]

        obj_name = file_obj.file_name_with_device()

        # This stores a list of tuples, describing what to dump and how to dump it.
        # Ex: (
        #     memory_object with get_available_pages() API (either CONTROL_AREA or SHARED_CACHE_MAP),
        #     layer to read from,
        #     file extension to apply,
        #     )
        dump_parameters = []

        # The DataSectionObject and ImageSectionObject caches are handled in basically the same way.
        # We carve these "pages" from the memory_layer.
        for member_name, extension in [("DataSectionObject", "dat"),
                                       ("ImageSectionObject", "img")]:
            try:
                section_obj = getattr(file_obj.SectionObjectPointer,
                                      member_name)
                control_area = section_obj.dereference().cast("_CONTROL_AREA")
                if control_area.is_valid():
                    dump_parameters.append(
                        (control_area, memory_layer, extension))
            except exceptions.InvalidAddressException:
                vollog.log(
                    constants.LOGLEVEL_VVV,
                    f"{member_name} is unavailable for file {file_obj.vol.offset:#x}"
                )

        # The SharedCacheMap is handled differently than the caches above.
        # We carve these "pages" from the primary_layer.
        try:
            scm_pointer = file_obj.SectionObjectPointer.SharedCacheMap
            shared_cache_map = scm_pointer.dereference().cast(
                "_SHARED_CACHE_MAP")
            if shared_cache_map.is_valid():
                dump_parameters.append(
                    (shared_cache_map, primary_layer, "vacb"))
        except exceptions.InvalidAddressException:
            vollog.log(
                constants.LOGLEVEL_VVV,
                f"SharedCacheMap is unavailable for file {file_obj.vol.offset:#x}"
            )

        for memory_object, layer, extension in dump_parameters:
            cache_name = EXTENSION_CACHE_MAP[extension]
            desired_file_name = "file.{0:#x}.{1:#x}.{2}.{3}.{4}".format(
                file_obj.vol.offset, memory_object.vol.offset, cache_name,
                ntpath.basename(obj_name), extension)

            file_handle = DumpFiles.dump_file_producer(file_obj, memory_object,
                                                       open_method, layer,
                                                       desired_file_name)

            file_output = "Error dumping file"
            if file_handle:
                file_handle.close()
                file_output = file_handle.preferred_filename

            yield (
                cache_name,
                format_hints.Hex(file_obj.vol.offset),
                ntpath.basename(
                    obj_name),  # temporary, so its easier to visualize output
                file_output)