Ejemplo n.º 1
0
    def _generator(self, procs):
        # determine if we're on a 32 or 64 bit kernel
        is_32bit_arch = not symbols.symbol_table_is_64bit(
            self.context, self.config["nt_symbols"])

        for proc in procs:
            process_name = utility.array_to_string(proc.ImageFileName)

            for vad, data in self.list_injections(self.context,
                                                  self.config["nt_symbols"],
                                                  proc):

                # if we're on a 64 bit kernel, we may still need 32 bit disasm due to wow64
                if is_32bit_arch or proc.get_is_wow64():
                    architecture = "intel"
                else:
                    architecture = "intel64"

                disasm = interfaces.renderers.Disassembly(
                    data, vad.get_start(), architecture)

                yield (0, (proc.UniqueProcessId, process_name,
                           format_hints.Hex(vad.get_start()),
                           format_hints.Hex(vad.get_end()), vad.get_tag(),
                           vad.get_protection(
                               vadinfo.VadInfo.protect_values(
                                   self.context, proc.vol.layer_name,
                                   self.config["nt_symbols"]),
                               vadinfo.winnt_protections),
                           vad.get_commit_charge(), vad.get_private_memory(),
                           format_hints.HexBytes(data), disasm))
Ejemplo n.º 2
0
    def _generator(self, tasks):
        result = []
        is_32bit = not symbols.symbol_table_is_64bit(self.context,
                                                     self.config["vmlinux"])
        if is_32bit:
            openssh_json_file = "openssh32"
        else:
            openssh_json_file = "openssh64"

        openssh_table_name = intermed.IntermediateSymbolTable.create(
            self.context, self.config_path, 'linux', openssh_json_file)

        for task in tasks:
            if not task.mm:
                continue

            name = utility.array_to_string(task.comm)
            vollog.info("Scanning process #{} ({} {})".format(
                task.pid, name, ""))
            proc_layer_name = task.add_process_layer()
            extractor = SSHKeyExtractor(self.context, openssh_table_name,
                                        proc_layer_name, task,
                                        self._progress_callback)
            keys = extractor.extract()
            result.extend(keys)
        return result
Ejemplo n.º 3
0
    def get_symbols(self):
        ret_syms = []

        if symbols.symbol_table_is_64bit(self._context,
                                         self.get_symbol_table().name):
            prefix = "Elf64_"
        else:
            prefix = "Elf32_"

        elf_table_name = intermed.IntermediateSymbolTable.create(
            self.context,
            self.config_path,
            "linux",
            "elf",
            native_types=None,
            class_types=extensions.elf.class_types)

        syms = self._context.object(
            self.get_symbol_table().name + constants.BANG + "array",
            layer_name=self.vol.layer_name,
            offset=self.section_symtab,
            subtype=self._context.symbol_space.get_type(elf_table_name +
                                                        constants.BANG +
                                                        prefix + "Sym"),
            count=self.num_symtab + 1)
        if self.section_strtab:
            for sym in syms:
                sym.set_cached_strtab(self.section_strtab)
                yield sym
Ejemplo n.º 4
0
    def create_callback_table(context: interfaces.context.ContextInterface, symbol_table: str, config_path: str) -> str:
        """Creates a symbol table for a set of callbacks.

        Args:
            context: The context to retrieve required elements (layers, symbol tables) from
            symbol_table: The name of an existing symbol table containing the kernel symbols
            config_path: The configuration path within the context of the symbol table to create

        Returns:
            The name of the constructed callback table
        """
        native_types = context.symbol_space[symbol_table].natives
        is_64bit = symbols.symbol_table_is_64bit(context, symbol_table)
        table_mapping = {"nt_symbols": symbol_table}

        if is_64bit:
            symbol_filename = "callbacks-x64"
        else:
            symbol_filename = "callbacks-x86"

        return intermed.IntermediateSymbolTable.create(context,
                                                       config_path,
                                                       "windows",
                                                       symbol_filename,
                                                       native_types = native_types,
                                                       table_mapping = table_mapping)
Ejemplo n.º 5
0
    def _get_pool_header_module(cls, context, layer_name, symbol_table):
        # Setup the pool header and offset differential
        try:
            module = context.module(symbol_table, layer_name, offset=0)
            module.get_type("_POOL_HEADER")
        except exceptions.SymbolError:
            # We have to manually load a symbol table

            if symbols.symbol_table_is_64bit(context, symbol_table):
                is_win_7 = cls.is_windows_7(context, symbol_table)
                if is_win_7:
                    pool_header_json_filename = "poolheader-x64-win7"
                else:
                    pool_header_json_filename = "poolheader-x64"
            else:
                pool_header_json_filename = "poolheader-x86"

            new_table_name = intermed.IntermediateSymbolTable.create(
                context=context,
                config_path=configuration.path_join(
                    context.symbol_space[symbol_table].config_path,
                    "poolheader"),
                sub_path="windows",
                filename=pool_header_json_filename,
                table_mapping={'nt_symbols': symbol_table},
                class_types={'_POOL_HEADER': extensions.pool.POOL_HEADER})
            module = context.module(new_table_name, layer_name, offset=0)
        return module
Ejemplo n.º 6
0
    def _generator(self):
        vmlinux = contexts.Module(self.context, self.config['vmlinux'], self.config['primary'], 0)

        modules = lsmod.Lsmod.list_modules(self.context, self.config['primary'], self.config['vmlinux'])

        handlers = linux.LinuxUtilities.generate_kernel_handler_info(self.context, self.config['primary'],
                                                                     self.config['vmlinux'], modules)

        is_32bit = not symbols.symbol_table_is_64bit(self.context, self.config["vmlinux"])

        idt_table_size = 256

        address_mask = self.context.layers[self.config['primary']].address_mask

        # hw handlers + system call
        check_idxs = list(range(0, 20)) + [128]

        if is_32bit:
            if vmlinux.has_type("gate_struct"):
                idt_type = "gate_struct"
            else:
                idt_type = "desc_struct"
        else:
            if vmlinux.has_type("gate_struct64"):
                idt_type = "gate_struct64"
            elif vmlinux.has_type("gate_struct"):
                idt_type = "gate_struct"
            else:
                idt_type = "idt_desc"

        addrs = vmlinux.object_from_symbol("idt_table")

        table = vmlinux.object(object_type = 'array', offset = addrs.vol.offset, subtype = vmlinux.get_type(idt_type),
                               count = idt_table_size)

        for i in check_idxs:
            ent = table[i]

            if not ent:
                continue

            if hasattr(ent, "Address"):
                idt_addr = ent.Address
            else:
                low = ent.offset_low
                middle = ent.offset_middle

                if hasattr(ent, "offset_high"):
                    high = ent.offset_high
                else:
                    high = 0

                idt_addr = (high << 32) | (middle << 16) | low

                idt_addr = idt_addr & address_mask

            module_name, symbol_name = linux.LinuxUtilities.lookup_module_address(self.context, handlers, idt_addr)

            yield (0, [format_hints.Hex(i), format_hints.Hex(idt_addr), module_name, symbol_name])
Ejemplo n.º 7
0
    def _generator(self, tasks):
        is_32bit = not symbols.symbol_table_is_64bit(self.context,
                                                     self.config["darwin"])
        if is_32bit:
            pack_format = "I"
            bash_json_file = "bash32"
        else:
            pack_format = "Q"
            bash_json_file = "bash64"

        bash_table_name = BashIntermedSymbols.create(self.context,
                                                     self.config_path, "linux",
                                                     bash_json_file)

        ts_offset = self.context.symbol_space.get_type(
            bash_table_name + constants.BANG +
            "hist_entry").relative_child_offset("timestamp")

        for task in tasks:
            task_name = utility.array_to_string(task.p_comm)
            if task_name not in ["bash", "sh", "dash"]:
                continue

            proc_layer_name = task.add_process_layer()
            if proc_layer_name is None:
                continue

            proc_layer = self.context.layers[proc_layer_name]

            bang_addrs = []

            # find '#' values on the heap
            for address in proc_layer.scan(
                    self.context,
                    scanners.BytesScanner(b"#"),
                    sections=task.get_process_memory_sections(
                        self.context, self.config['darwin'], rw_no_file=True)):
                bang_addrs.append(struct.pack(pack_format, address))

            history_entries = []

            for address, _ in proc_layer.scan(
                    self.context,
                    scanners.MultiStringScanner(bang_addrs),
                    sections=task.get_process_memory_sections(
                        self.context, self.config['darwin'], rw_no_file=True)):
                hist = self.context.object(bash_table_name + constants.BANG +
                                           "hist_entry",
                                           offset=address - ts_offset,
                                           layer_name=proc_layer_name)

                if hist.is_valid():
                    history_entries.append(hist)

            for hist in sorted(history_entries,
                               key=lambda x: x.get_time_as_integer()):
                yield (0, (int(task.p_pid), task_name, hist.get_time_object(),
                           hist.get_command()))
Ejemplo n.º 8
0
    def create_service_table(context: interfaces.context.ContextInterface,
                             symbol_table: str, config_path: str) -> str:
        """Constructs a symbol table containing the symbols for services
        depending upon the operating system in use.

        Args:
            context: The context to retrieve required elements (layers, symbol tables) from
            symbol_table: The name of the table containing the kernel symbols
            config_path: The configuration path for any settings required by the new table

        Returns:
            A symbol table containing the symbols necessary for services
        """
        native_types = context.symbol_space[symbol_table].natives
        is_64bit = symbols.symbol_table_is_64bit(context, symbol_table)

        if SvcScan.is_windows_xp(context=context,
                                 symbol_table=symbol_table) and not is_64bit:
            symbol_filename = "services-xp-x86"
        elif SvcScan.is_xp_or_2003(context=context,
                                   symbol_table=symbol_table) and is_64bit:
            symbol_filename = "services-xp-2003-x64"
        elif poolscanner.PoolScanner.is_windows_8_or_later(
                context=context, symbol_table=symbol_table) and is_64bit:
            symbol_filename = "services-win8-x64"
        elif poolscanner.PoolScanner.is_windows_8_or_later(
                context=context, symbol_table=symbol_table) and not is_64bit:
            symbol_filename = "services-win8-x86"
        elif SvcScan.is_win10_up_to_15063(
                context=context, symbol_table=symbol_table) and is_64bit:
            symbol_filename = "services-win10-15063-x64"
        elif SvcScan.is_win10_up_to_15063(
                context=context, symbol_table=symbol_table) and not is_64bit:
            symbol_filename = "services-win10-15063-x86"
        elif SvcScan.is_win10_16299_or_later(
                context=context, symbol_table=symbol_table) and is_64bit:
            symbol_filename = "services-win10-16299-x64"
        elif SvcScan.is_win10_16299_or_later(
                context=context, symbol_table=symbol_table) and not is_64bit:
            symbol_filename = "services-win10-16299-x86"
        elif SvcScan.is_vista_or_later(context=context,
                                       symbol_table=symbol_table) and is_64bit:
            symbol_filename = "services-vista-x64"
        elif SvcScan.is_vista_or_later(
                context=context, symbol_table=symbol_table) and not is_64bit:
            symbol_filename = "services-vista-x86"
        else:
            raise NotImplementedError(
                "This version of Windows is not supported!")

        return intermed.IntermediateSymbolTable.create(
            context,
            config_path,
            "windows",
            symbol_filename,
            class_types=services.class_types,
            native_types=native_types)
Ejemplo n.º 9
0
    def dereference(self) -> interfaces.objects.ObjectInterface:

        if constants.BANG not in self.vol.type_name:
            raise ValueError("Invalid symbol table name syntax (no {} found)".format(constants.BANG))

        # the mask value is different on 32 and 64 bits
        symbol_table_name = self.vol.type_name.split(constants.BANG)[0]
        if not symbols.symbol_table_is_64bit(self._context, symbol_table_name):
            max_fast_ref = 7
        else:
            max_fast_ref = 15

        return self._context.object(symbol_table_name + constants.BANG + "pointer",
                                    layer_name = self.vol.layer_name,
                                    offset = self.Object & ~max_fast_ref,
                                    native_layer_name = self.vol.native_layer_name)
Ejemplo n.º 10
0
    def _generator(self, procs):
        # determine if we're on a 32 or 64 bit kernel
        is_32bit_arch = not symbols.symbol_table_is_64bit(
            self.context, self.config["nt_symbols"])

        for proc in procs:
            process_name = utility.array_to_string(proc.ImageFileName)

            for vad, data in self.list_injections(self.context,
                                                  self.config["primary"],
                                                  self.config["nt_symbols"],
                                                  proc):

                # if we're on a 64 bit kernel, we may still need 32 bit disasm due to wow64
                if is_32bit_arch or proc.get_is_wow64():
                    architecture = "intel"
                else:
                    architecture = "intel64"

                disasm = interfaces.renderers.Disassembly(
                    data, vad.get_start(), architecture)

                file_output = "Disabled"
                if self.config['dump']:
                    file_output = "Error outputting to file"
                    try:
                        file_handle = vadinfo.VadInfo.vad_dump(
                            self.context, proc, vad, self.open)
                        file_handle.close()
                        file_output = file_handle.preferred_filename
                    except (exceptions.InvalidAddressException,
                            OverflowError) as excp:
                        vollog.debug(
                            "Unable to dump PE with pid {0}.{1:#x}: {2}".
                            format(proc.UniqueProcessId, vad.get_start(),
                                   excp))

                yield (0, (proc.UniqueProcessId, process_name,
                           format_hints.Hex(vad.get_start()),
                           format_hints.Hex(vad.get_end()), vad.get_tag(),
                           vad.get_protection(
                               vadinfo.VadInfo.protect_values(
                                   self.context, self.config["primary"],
                                   self.config["nt_symbols"]),
                               vadinfo.winnt_protections),
                           vad.get_commit_charge(), vad.get_private_memory(),
                           file_output, format_hints.HexBytes(data), disasm))
Ejemplo n.º 11
0
    def _generator(self, procs):
        # determine if we're on a 32 or 64 bit kernel
        is_32bit_arch = not symbols.symbol_table_is_64bit(
            self.context, self.config["nt_symbols"])

        for proc in procs:
            process_name = utility.array_to_string(proc.ImageFileName)

            for vad, data in self.list_injections(self.context,
                                                  self.config["primary"],
                                                  self.config["nt_symbols"],
                                                  proc):

                # if we're on a 64 bit kernel, we may still need 32 bit disasm due to wow64
                if is_32bit_arch or proc.get_is_wow64():
                    architecture = "intel"
                else:
                    architecture = "intel64"

                disasm = interfaces.renderers.Disassembly(
                    data, vad.get_start(), architecture)

                dumped = False
                if self.config['dump']:
                    filedata = vadinfo.VadInfo.vad_dump(
                        self.context, proc, vad)
                    if filedata:
                        try:
                            self.produce_file(filedata)
                            dumped = True
                        except Exception as excp:
                            vollog.debug(
                                "Unable to dump PE with pid {0}.{1:#x}: {2}".
                                format(proc.UniqueProcessId, vad.get_start(),
                                       excp))

                yield (0, (proc.UniqueProcessId, process_name,
                           format_hints.Hex(vad.get_start()),
                           format_hints.Hex(vad.get_end()), vad.get_tag(),
                           vad.get_protection(
                               vadinfo.VadInfo.protect_values(
                                   self.context, self.config["primary"],
                                   self.config["nt_symbols"]),
                               vadinfo.winnt_protections),
                           vad.get_commit_charge(), vad.get_private_memory(),
                           dumped, format_hints.HexBytes(data), disasm))
Ejemplo n.º 12
0
    def get_pool_header_table(cls,
                              context: interfaces.context.ContextInterface,
                              symbol_table: str) -> str:
        """Returns the appropriate symbol_table containing a _POOL_HEADER type, even if the original symbol table
        doesn't contain one.

        Args:
            context: The context that the symbol tables does (or will) reside in
            symbol_table: The expected symbol_table to contain the _POOL_HEADER type
        """
        # Setup the pool header and offset differential
        try:
            context.symbol_space.get_type(symbol_table + constants.BANG +
                                          "_POOL_HEADER")
            table_name = symbol_table
        except exceptions.SymbolError:
            # We have to manually load a symbol table

            if symbols.symbol_table_is_64bit(context, symbol_table):
                is_win_7 = versions.is_windows_7(context, symbol_table)
                if is_win_7:
                    pool_header_json_filename = "poolheader-x64-win7"
                else:
                    pool_header_json_filename = "poolheader-x64"
            else:
                pool_header_json_filename = "poolheader-x86"

            # set the class_type to match the normal WindowsKernelIntermedSymbols
            is_vista_or_later = versions.is_vista_or_later(
                context, symbol_table)
            if is_vista_or_later:
                class_type = extensions.pool.POOL_HEADER_VISTA
            else:
                class_type = extensions.pool.POOL_HEADER

            table_name = intermed.IntermediateSymbolTable.create(
                context=context,
                config_path=configuration.path_join(
                    context.symbol_space[symbol_table].config_path,
                    "poolheader"),
                sub_path="windows",
                filename=pool_header_json_filename,
                table_mapping={'nt_symbols': symbol_table},
                class_types={'_POOL_HEADER': class_type})
        return table_name
Ejemplo n.º 13
0
    def scan_hives(cls,
                   context: interfaces.context.ContextInterface,
                   layer_name: str,
                   symbol_table: str) -> \
            Iterable[interfaces.objects.ObjectInterface]:
        """Scans for hives using the poolscanner module and constraints or bigpools module with tag.

        Args:
            context: The context to retrieve required elements (layers, symbol tables) from
            layer_name: The name of the layer on which to operate
            symbol_table: The name of the table containing the kernel symbols

        Returns:
            A list of Hive objects as found from the `layer_name` layer based on Hive pool signatures
        """

        is_64bit = symbols.symbol_table_is_64bit(context, symbol_table)
        is_windows_8_1_or_later = HiveScan.is_windows_8_1_or_later(
            context=context, symbol_table=symbol_table)

        if is_windows_8_1_or_later and is_64bit:
            kvo = context.layers[layer_name].config['kernel_virtual_offset']
            ntkrnlmp = context.module(symbol_table,
                                      layer_name=layer_name,
                                      offset=kvo)

            for pool in bigpools.BigPools.list_big_pools(
                    context,
                    layer_name=layer_name,
                    symbol_table=symbol_table,
                    tags=["CM10"]):
                cmhive = ntkrnlmp.object(object_type="_CMHIVE",
                                         offset=pool.Va,
                                         absolute=True)
                yield cmhive

        else:
            constraints = poolscanner.PoolScanner.builtin_constraints(
                symbol_table, [b'CM10'])

            for result in poolscanner.PoolScanner.generate_pool_scan(
                    context, layer_name, symbol_table, constraints):
                _constraint, mem_object, _header = result
                yield mem_object
Ejemplo n.º 14
0
    def get_tag(self):
        vad_address = self.vol.offset

        # the offset is different on 32 and 64 bits
        symbol_table_name = self.vol.type_name.split(constants.BANG)[0]
        if not symbols.symbol_table_is_64bit(self._context, symbol_table_name):
            vad_address -= 4
        else:
            vad_address -= 12

        try:
            # TODO: instantiate a _POOL_HEADER and return PoolTag
            bytesobj = self._context.object(symbol_table_name + constants.BANG + "bytes",
                                            layer_name = self.vol.layer_name,
                                            offset = vad_address,
                                            native_layer_name = self.vol.native_layer_name,
                                            length = 4)

            return bytesobj.decode()
        except exceptions.InvalidAddressException:
            return None
        except UnicodeDecodeError:
            return None
Ejemplo n.º 15
0
    def generate_pool_scan(cls,
                           context: interfaces.context.ContextInterface,
                           layer_name: str,
                           symbol_table: str,
                           constraints: List[PoolConstraint]) \
            -> Generator[Tuple[
                             PoolConstraint, interfaces.objects.ObjectInterface, interfaces.objects.ObjectInterface], None, None]:
        """

        Args:
            context: The context to retrieve required elements (layers, symbol tables) from
            layer_name: The name of the layer on which to operate
            symbol_table: The name of the table containing the kernel symbols
            constraints: List of pool constraints used to limit the scan results

        Returns:
            Iterable of tuples, containing the constraint that matched, the object from memory, the object header used to determine the object
        """

        # get the object type map
        type_map = handles.Handles.get_type_map(context=context,
                                                layer_name=layer_name,
                                                symbol_table=symbol_table)

        cookie = handles.Handles.find_cookie(context=context,
                                             layer_name=layer_name,
                                             symbol_table=symbol_table)

        is_windows_10 = cls.is_windows_10(context, symbol_table)
        is_windows_8_or_later = cls.is_windows_8_or_later(
            context, symbol_table)

        # start off with the primary virtual layer
        scan_layer = layer_name

        # switch to a non-virtual layer if necessary
        if not is_windows_10:
            scan_layer = context.layers[scan_layer].config['memory_layer']

        if symbols.symbol_table_is_64bit(context, symbol_table):
            alignment = 0x10
        else:
            alignment = 8

        for constraint, header in cls.pool_scan(context,
                                                scan_layer,
                                                symbol_table,
                                                constraints,
                                                alignment=alignment):

            mem_object = header.get_object(type_name=constraint.type_name,
                                           use_top_down=is_windows_8_or_later,
                                           executive=constraint.object_type
                                           is not None,
                                           native_layer_name='primary')

            if mem_object is None:
                vollog.log(
                    constants.LOGLEVEL_VVV,
                    "Cannot create an instance of {}".format(
                        constraint.type_name))
                continue

            if constraint.object_type is not None and not constraint.skip_type_test:
                try:
                    if mem_object.get_object_header().get_object_type(
                            type_map, cookie) != constraint.object_type:
                        continue
                except exceptions.InvalidAddressException:
                    vollog.log(
                        constants.LOGLEVEL_VVV,
                        "Cannot test instance type check for {}".format(
                            constraint.type_name))
                    continue

            yield constraint, mem_object, header
Ejemplo n.º 16
0
    def _generator(self) -> Iterator[Tuple[int, Tuple[int, int, Any, Any]]]:

        layer_name = self.config['primary']
        collection = self.build_module_collection(self.context,
                                                  self.config["primary"],
                                                  self.config["nt_symbols"])

        kvo = self.context.layers[layer_name].config['kernel_virtual_offset']
        ntkrnlmp = self.context.module(self.config["nt_symbols"],
                                       layer_name=layer_name,
                                       offset=kvo)

        # this is just one way to enumerate the native (NT) service table.
        # to do the same thing for the Win32K service table, we would need Win32K.sys symbol support
        ## we could also find nt!KeServiceDescriptorTable (NT) and KeServiceDescriptorTableShadow (NT, Win32K)
        service_table_address = ntkrnlmp.get_symbol("KiServiceTable").address
        service_limit_address = ntkrnlmp.get_symbol("KiServiceLimit").address
        service_limit = ntkrnlmp.object(object_type="int",
                                        offset=service_limit_address)

        # on 32-bit systems the table indexes are 32-bits and contain pointers (unsigned)
        # on 64-bit systems the indexes are also 32-bits but they're offsets from the
        # base address of the table and can be negative, so we need a signed data type
        is_kernel_64 = symbols.symbol_table_is_64bit(self.context,
                                                     self.config["nt_symbols"])
        if is_kernel_64:
            array_subtype = "long"

            def kvo_calulator(func: int) -> int:
                return kvo + service_table_address + (func >> 4)

            find_address = kvo_calulator
        else:
            array_subtype = "unsigned long"

            def passthrough(func: int) -> int:
                return func

            find_address = passthrough

        functions = ntkrnlmp.object(object_type="array",
                                    offset=service_table_address,
                                    subtype=ntkrnlmp.get_type(array_subtype),
                                    count=service_limit)

        for idx, function_obj in enumerate(functions):

            function = find_address(function_obj)
            module_symbols = collection.get_module_symbols_by_absolute_location(
                function)

            for module_name, symbol_generator in module_symbols:
                symbols_found = False

                for symbol in symbol_generator:
                    symbols_found = True
                    yield (0, (idx, format_hints.Hex(function), module_name,
                               symbol.split(constants.BANG)[1]))

                if not symbols_found:
                    yield (0, (idx, format_hints.Hex(function), module_name,
                               renderers.NotAvailableValue()))
Ejemplo n.º 17
0
    def list_big_pools(cls,
                       context: interfaces.context.ContextInterface,
                       layer_name: str,
                       symbol_table: str,
                       tags: Optional[list] = None):
        """Returns the big page pool objects from the kernel PoolBigPageTable array.

        Args:
            context: The context to retrieve required elements (layers, symbol tables) from
            layer_name: The name of the layer on which to operate
            symbol_table: The name of the table containing the kernel symbols
            tags: An optional list of pool tags to filter big page pool tags by

        Yields:
            A big page pool object
        """
        kvo = context.layers[layer_name].config['kernel_virtual_offset']
        ntkrnlmp = context.module(symbol_table, layer_name = layer_name, offset = kvo)

        big_page_table_offset = ntkrnlmp.get_symbol("PoolBigPageTable").address
        big_page_table = ntkrnlmp.object(object_type = "unsigned long long", offset = big_page_table_offset)

        big_page_table_size_offset = ntkrnlmp.get_symbol("PoolBigPageTableSize").address
        big_page_table_size = ntkrnlmp.object(object_type = "unsigned long", offset = big_page_table_size_offset)

        try:
            big_page_table_type = ntkrnlmp.get_type("_POOL_TRACKER_BIG_PAGES")
        except exceptions.SymbolError:
            # We have to manually load a symbol table
            is_vista_or_later = versions.is_vista_or_later(context, symbol_table)
            is_win10 = versions.is_win10(context, symbol_table)
            if is_win10:
                big_pools_json_filename = "bigpools-win10"
            elif is_vista_or_later:
                big_pools_json_filename = "bigpools-vista"
            else:
                big_pools_json_filename = "bigpools"

            if symbols.symbol_table_is_64bit(context, symbol_table):
                big_pools_json_filename += "-x64"
            else:
                big_pools_json_filename += "-x86"

            new_table_name = intermed.IntermediateSymbolTable.create(
                context = context,
                config_path = configuration.path_join(context.symbol_space[symbol_table].config_path, "bigpools"),
                sub_path = "windows",
                filename = big_pools_json_filename,
                table_mapping = {'nt_symbols': symbol_table},
                class_types = {'_POOL_TRACKER_BIG_PAGES': extensions.pool.POOL_TRACKER_BIG_PAGES})
            module = context.module(new_table_name, layer_name, offset = 0)
            big_page_table_type = module.get_type("_POOL_TRACKER_BIG_PAGES")

        big_pools = ntkrnlmp.object(object_type = "array",
                                    offset = big_page_table,
                                    subtype = big_page_table_type,
                                    count = big_page_table_size,
                                    absolute = True)

        for big_pool in big_pools:
            if big_pool.is_valid():
                if tags is None or big_pool.get_key() in tags:
                    yield big_pool
Ejemplo n.º 18
0
    def get_available_pages(self) -> Iterable[Tuple[int, int, int]]:
        """Get the available pages that correspond to a cached file.

        The tuples generated are (physical_offset, file_offset, page_size).
        """
        symbol_table_name = self.get_symbol_table_name()
        mmpte_type = self._context.symbol_space.get_type(symbol_table_name +
                                                         constants.BANG +
                                                         "_MMPTE")
        mmpte_size = mmpte_type.size
        subsection = self.get_subsection()
        is_64bit = symbols.symbol_table_is_64bit(self._context,
                                                 symbol_table_name)
        is_pae = self._context.layers[self.vol.layer_name].metadata.get(
            "pae", False)

        # This is a null-terminated single-linked list.
        while subsection != 0:
            try:
                if subsection.ControlArea != self.vol.offset:
                    break
            except exceptions.InvalidAddressException:
                break

            # The offset into the file is stored implicitly based on the PTE location within the Subsection.
            starting_sector = subsection.StartingSector
            subsection_offset = starting_sector * 0x200

            # Similar to the check in is_valid(), make sure the SubsectionBase is not page aligned.
            #if subsection.SubsectionBase & self.PAGE_MASK == 0:
            #    break

            ptecount = 0
            while ptecount < subsection.PtesInSubsection:
                pte_offset = subsection.SubsectionBase + (mmpte_size *
                                                          ptecount)
                file_offset = subsection_offset + ptecount * 0x1000

                try:
                    mmpte = self.get_pte(pte_offset)
                except exceptions.InvalidAddressException:
                    ptecount += 1
                    continue

                # First we check if the entry is valid. If so, then we get the physical offset.
                # The valid entries are actually handled by the hardware.
                if mmpte.u.Hard.Valid == 1:
                    physoffset = mmpte.u.Hard.PageFrameNumber << 12
                    yield physoffset, file_offset, self.PAGE_SIZE

                elif mmpte.u.Soft.Prototype == 1:
                    if not is_64bit and not is_pae:
                        subsection_offset = (
                            (mmpte.u.Subsect.SubsectionAddressHigh << 7) |
                            (mmpte.u.Subsect.SubsectionAddressLow << 3))

                # If the entry is not a valid physical address then see if it is in transition.
                elif mmpte.u.Trans.Transition == 1:
                    physoffset = mmpte.u.Trans.PageFrameNumber << 12
                    yield physoffset, file_offset, self.PAGE_SIZE

                # Go to the next PTE entry
                ptecount += 1

            # Go to the next Subsection in the single-linked list
            subsection = subsection.NextSubsection
Ejemplo n.º 19
0
    def determine_tcpip_version(cls,
                                context: interfaces.context.ContextInterface,
                                layer_name: str, nt_symbol_table: str) -> str:
        """Tries to determine which symbol filename to use for the image's tcpip driver. The logic is partially taken from the info plugin.

        Args:
            context: The context to retrieve required elements (layers, symbol tables) from
            layer_name: The name of the layer on which to operate
            nt_symbol_table: The name of the table containing the kernel symbols

        Returns:
            The filename of the symbol table to use.
        """

        # while the failsafe way to determine the version of tcpip.sys would be to
        # extract the driver and parse its PE header containing the versionstring,
        # unfortunately that header is not guaranteed to persist within memory.
        # therefore we determine the version based on the kernel version as testing
        # with several windows versions has showed this to work out correctly.

        is_64bit = symbols.symbol_table_is_64bit(context, nt_symbol_table)

        if is_64bit:
            arch = "x64"
        else:
            arch = "x86"

        vers = info.Info.get_version_structure(context, layer_name,
                                               nt_symbol_table)

        kuser = info.Info.get_kuser_structure(context, layer_name,
                                              nt_symbol_table)

        try:
            vers_minor_version = int(vers.MinorVersion)
            nt_major_version = int(kuser.NtMajorVersion)
            nt_minor_version = int(kuser.NtMinorVersion)
        except ValueError:
            # vers struct exists, but is not an int anymore?
            raise NotImplementedError(
                "Kernel Debug Structure version format not supported!")
        except:
            # unsure what to raise here. Also, it might be useful to add some kind of fallback,
            # either to a user-provided version or to another method to determine tcpip.sys's version
            raise exceptions.VolatilityException(
                "Kernel Debug Structure missing VERSION/KUSER structure, unable to determine Windows version!"
            )

        vollog.debug("Determined OS Version: {}.{} {}.{}".format(
            kuser.NtMajorVersion, kuser.NtMinorVersion, vers.MajorVersion,
            vers.MinorVersion))

        if nt_major_version == 10 and arch == "x64":
            # win10 x64 has an additional class type we have to include.
            class_types = network.win10_x64_class_types
        else:
            # default to general class types
            class_types = network.class_types

        # these versions are listed explicitly because symbol files differ based on
        # version *and* architecture. this is currently the clearest way to show
        # the differences, even if it introduces a fair bit of redundancy.
        # furthermore, it is easy to append new versions.
        if arch == "x86":
            version_dict = {
                (6, 0, 6000): "netscan-vista-x86",
                (6, 0, 6001): "netscan-vista-x86",
                (6, 0, 6002): "netscan-vista-x86",
                (6, 0, 6003): "netscan-vista-x86",
                (6, 1, 7600): "netscan-win7-x86",
                (6, 1, 7601): "netscan-win7-x86",
                (6, 1, 8400): "netscan-win7-x86",
                (6, 2, 9200): "netscan-win8-x86",
                (6, 3, 9600): "netscan-win81-x86",
                (10, 0, 10240): "netscan-win10-x86",
                (10, 0, 10586): "netscan-win10-x86",
                (10, 0, 14393): "netscan-win10-14393-x86",
                (10, 0, 15063): "netscan-win10-15063-x86",
                (10, 0, 16299): "netscan-win10-15063-x86",
                (10, 0, 17134): "netscan-win10-15063-x86",
                (10, 0, 17763): "netscan-win10-15063-x86",
                (10, 0, 18362): "netscan-win10-15063-x86",
                (10, 0, 18363): "netscan-win10-15063-x86"
            }
        else:
            version_dict = {
                (6, 0, 6000): "netscan-vista-x64",
                (6, 0, 6001): "netscan-vista-sp12-x64",
                (6, 0, 6002): "netscan-vista-sp12-x64",
                (6, 0, 6003): "netscan-vista-sp12-x64",
                (6, 1, 7600): "netscan-win7-x64",
                (6, 1, 7601): "netscan-win7-x64",
                (6, 1, 8400): "netscan-win7-x64",
                (6, 2, 9200): "netscan-win8-x64",
                (6, 3, 9600): "netscan-win81-x64",
                (10, 0, 10240): "netscan-win10-x64",
                (10, 0, 10586): "netscan-win10-x64",
                (10, 0, 14393): "netscan-win10-x64",
                (10, 0, 15063): "netscan-win10-15063-x64",
                (10, 0, 16299): "netscan-win10-15063-x64",
                (10, 0, 17134): "netscan-win10-15063-x64",
                (10, 0, 17763): "netscan-win10-15063-x64",
                (10, 0, 18362): "netscan-win10-15063-x64",
                (10, 0, 18363): "netscan-win10-15063-x64"
            }

        # when determining the symbol file we have to consider the following cases:
        # the determined version's symbol file is found by intermed.create -> proceed
        # the determined version's symbol file is not found by intermed -> intermed will throw an exc and abort
        # the determined version has no mapped symbol file -> if win10 use latest, otherwise throw exc
        # windows version cannot be determined -> throw exc
        filename = version_dict.get(
            (nt_major_version, nt_minor_version, vers_minor_version))
        if not filename:
            if nt_major_version == 10:
                # NtMajorVersion of 10 without a match means a newer version than listed
                # hence try the latest supported version. If this one throws an error,
                # support has to be added manually.
                win_10_versions = sorted(
                    [key for key in list(version_dict.keys()) if key[0] == 10])
                # as win10 MinorVersion counts upwards we can take the last entry
                latest_version = win_10_versions[-1]
                filename = version_dict.get(latest_version)
                vollog.debug(
                    "Unable to find exact matching symbol file, going with latest: {}"
                    .format(filename))
            else:
                raise NotImplementedError(
                    "This version of Windows is not supported: {}.{} {}.{}!".
                    format(nt_major_version, nt_minor_version,
                           vers.MajorVersion, vers_minor_version))

        vollog.debug("Determined symbol filename: {}".format(filename))

        return filename, class_types
Ejemplo n.º 20
0
    def get_object(self,
                   type_name: str,
                   use_top_down: bool,
                   executive: bool = False,
                   native_layer_name: Optional[str] = None) -> Optional[interfaces.objects.ObjectInterface]:
        """Carve an object or data structure from a kernel pool allocation

        Args:
            type_name: the data structure type name
            native_layer_name: the name of the layer where the data originally lived
            object_type: the object type (executive kernel objects only)

        Returns:
            An object as found from a POOL_HEADER
        """

        symbol_table_name = self.vol.type_name.split(constants.BANG)[0]
        if constants.BANG in type_name:
            symbol_table_name, type_name = type_name.split(constants.BANG)[0:2]

        object_header_type = self._context.symbol_space.get_type(symbol_table_name + constants.BANG + "_OBJECT_HEADER")
        pool_header_size = self.vol.size

        # if there is no object type, then just instantiate a structure
        if not executive:
            mem_object = self._context.object(symbol_table_name + constants.BANG + type_name,
                                              layer_name = self.vol.layer_name,
                                              offset = self.vol.offset + pool_header_size,
                                              native_layer_name = native_layer_name)
            return mem_object

        # otherwise we have an executive object in the pool
        else:
            if symbols.symbol_table_is_64bit(self._context, symbol_table_name):
                alignment = 16
            else:
                alignment = 8

            # use the top down approach for windows 8 and later
            if use_top_down:
                body_offset = object_header_type.relative_child_offset('Body')
                infomask_offset = object_header_type.relative_child_offset('InfoMask')
                optional_headers, lengths_of_optional_headers = self._calculate_optional_header_lengths(
                    self._context, symbol_table_name)
                padding_available = None if 'PADDING_INFO' not in optional_headers else optional_headers.index(
                    'PADDING_INFO')
                max_optional_headers_length = sum(lengths_of_optional_headers)

                # define the starting and ending bounds for the scan
                start_offset = self.vol.offset + pool_header_size
                addr_limit = min(max_optional_headers_length, self.BlockSize * alignment)

                # A single read is better than lots of little one-byte reads.
                # We're ok padding this, because the byte we'd check would be 0 which would only be valid if there
                # were no optional headers in the first place (ie, if we read too much for headers that don't exist,
                # but the bit we could read were valid)
                infomask_data = self._context.layers[self.vol.layer_name].read(start_offset,
                                                                               addr_limit + infomask_offset,
                                                                               pad = True)

                # Addr stores the offset to the potential start of the OBJECT_HEADER from just after the POOL_HEADER
                # It will always be aligned to a particular alignment
                for addr in range(0, addr_limit, alignment):
                    infomask_value = infomask_data[addr + infomask_offset]

                    padding_present = False
                    optional_headers_length = 0
                    for i in range(len(lengths_of_optional_headers)):
                        if infomask_value & (1 << i):
                            optional_headers_length += lengths_of_optional_headers[i]
                            if i == padding_available:
                                padding_present = True

                    # PADDING_INFO is a special case (4 bytes that contain the total padding length)
                    padding_length = 0
                    if padding_present:
                        # Read the four bytes from just before the next optional_headers_length minus the padding_info size
                        #
                        #  ---------------
                        #  POOL_HEADER
                        #  ---------------
                        #
                        #  start of PADDING_INFO
                        #  ---------------
                        #  End of other optional headers
                        #  ---------------
                        #  OBJECT_HEADER
                        #  ---------------
                        if addr - optional_headers_length < 0:
                            continue
                        padding_length = struct.unpack(
                            "<I", infomask_data[addr - optional_headers_length:addr - optional_headers_length + 4])[0]
                        padding_length -= lengths_of_optional_headers[padding_available]

                    # Certain versions of windows have PADDING_INFO lengths that are too long
                    # So we now check that the padding length is at a minimum the right length
                    # and that it doesn't go beyond the entirety of the data
                    if addr - optional_headers_length >= padding_length > addr:
                        continue

                    try:
                        mem_object = self._context.object(symbol_table_name + constants.BANG + type_name,
                                                          layer_name = self.vol.layer_name,
                                                          offset = addr + body_offset + start_offset,
                                                          native_layer_name = native_layer_name)

                        if mem_object.is_valid():
                            return mem_object

                    except (TypeError, exceptions.InvalidAddressException):
                        pass

            # use the bottom up approach for windows 7 and earlier
            else:
                type_size = self._context.symbol_space.get_type(symbol_table_name + constants.BANG + type_name).size
                rounded_size = conversion.round(type_size, alignment, up = True)

                mem_object = self._context.object(symbol_table_name + constants.BANG + type_name,
                                                  layer_name = self.vol.layer_name,
                                                  offset = self.vol.offset + self.BlockSize * alignment - rounded_size,
                                                  native_layer_name = native_layer_name)

                try:
                    if mem_object.is_valid():
                        return mem_object
                except (TypeError, exceptions.InvalidAddressException):
                    return None
        return None