Пример #1
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 = versions.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
Пример #2
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
Пример #3
0
    def parse_partitions(
        cls, context: interfaces.context.ContextInterface, layer_name: str,
        net_symbol_table: str, tcpip_symbol_table: str,
        tcpip_module_offset: int
    ) -> Iterable[interfaces.objects.ObjectInterface]:
        """Parses tcpip.sys's PartitionTable containing established TCP connections.
        The amount of Partition depends on the value of the symbol `PartitionCount` and correlates with
        the maximum processor count (refer to Art of Memory Forensics, chapter 11).

        Args:
            context: The context to retrieve required elements (layers, symbol tables) from
            layer_name: The name of the layer on which to operate
            net_symbol_table: The name of the table containing the tcpip types
            tcpip_symbol_table: The name of the table containing the tcpip driver symbols
            tcpip_module_offset: The offset of the tcpip module

        Returns:
            The list of TCP endpoint objects from the `layer_name` layer's `PartitionTable`
        """
        if symbols.symbol_table_is_64bit(context, net_symbol_table):
            alignment = 0x10
        else:
            alignment = 8

        obj_name = net_symbol_table + constants.BANG + "_TCP_ENDPOINT"
        # part_table_symbol is the offset within tcpip.sys which contains the address of the partition table itself
        part_table_symbol = context.symbol_space.get_symbol(
            tcpip_symbol_table + constants.BANG + "PartitionTable").address
        part_count_symbol = context.symbol_space.get_symbol(
            tcpip_symbol_table + constants.BANG + "PartitionCount").address

        part_table_addr = context.object(
            net_symbol_table + constants.BANG + "pointer",
            layer_name=layer_name,
            offset=tcpip_module_offset + part_table_symbol)

        # part_table is the actual partition table offset and consists out of a dynamic amount of _PARTITION objects
        part_table = context.object(net_symbol_table + constants.BANG +
                                    "_PARTITION_TABLE",
                                    layer_name=layer_name,
                                    offset=part_table_addr)
        part_count = int.from_bytes(
            context.layers[layer_name].read(
                tcpip_module_offset + part_count_symbol, 1), "little")
        part_table.Partitions.count = part_count

        vollog.debug(
            "Found TCP connection PartitionTable @ 0x{:x} (partition count: {})"
            .format(part_table_addr, part_count))
        entry_offset = context.symbol_space.get_type(
            obj_name).relative_child_offset("ListEntry")
        for ctr, partition in enumerate(part_table.Partitions):
            vollog.debug(f"Parsing partition {ctr}")
            if partition.Endpoints.NumEntries > 0:
                for endpoint_entry in cls.parse_hashtable(
                        context, layer_name, partition.Endpoints.Directory,
                        partition.Endpoints.TableSize, alignment,
                        net_symbol_table):

                    endpoint = context.object(obj_name,
                                              layer_name=layer_name,
                                              offset=endpoint_entry -
                                              entry_offset)
                    yield endpoint
Пример #4
0
    def _generator(self, procs):
        """
        Finds instances of the RC4 HMAC CSystem structure

        Returns whether the instances are hooked as well as the function handler addresses

        Args:
            procs: the process list filtered to lsass.exe instances
        """
        kernel = self.context.modules[self.config['kernel']]

        if not symbols.symbol_table_is_64bit(self.context,
                                             kernel.symbol_table_name):
            vollog.info(
                "This plugin only supports 64bit Windows memory samples")
            return

        lsass_proc, proc_layer_name = self._find_lsass_proc(procs)
        if not lsass_proc:
            vollog.info(
                "Unable to find a valid lsass.exe process in the process list. This should never happen. Analysis cannot proceed."
            )
            return

        cryptdll_base, cryptdll_size = self._find_cryptdll(lsass_proc)
        if not cryptdll_base:
            vollog.info(
                "Unable to find the location of cryptdll.dll inside of lsass.exe. Analysis cannot proceed."
            )
            return

        # the custom type information from binary analysis
        cryptdll_types = self._get_cryptdll_types(self.context, self.config,
                                                  self.config_path,
                                                  proc_layer_name,
                                                  cryptdll_base)

        # attempt to find the array and symbols directly from the PDB
        csystems, rc4HmacInitialize, rc4HmacDecrypt = \
            self._find_csystems_with_symbols(proc_layer_name,
                                             cryptdll_types,
                                             cryptdll_base,
                                             cryptdll_size)

        # if we can't find cSystems through the PDB then
        # we fall back to export analysis and scanning
        # we keep the address of the rc4 functions from the PDB
        # though as its our only source to get them
        if csystems is None:
            fallback_sources = [
                self._find_csystems_with_export,
                self._find_csystems_with_scanning
            ]

            for source in fallback_sources:
                csystems = source(proc_layer_name, cryptdll_types,
                                  cryptdll_base, cryptdll_size)

                if csystems is not None:
                    break

        if csystems is None:
            vollog.info(
                "Unable to find CSystems inside of cryptdll.dll. Analysis cannot proceed."
            )
            return

        for csystem in csystems:
            if not self.context.layers[proc_layer_name].is_valid(
                    csystem.vol.offset, csystem.vol.size):
                continue

            # filter for RC4 HMAC
            if csystem.EncryptionType != 0x17:
                continue

            # use the specific symbols if present, otherwise use the vad start and size
            if rc4HmacInitialize and rc4HmacDecrypt:
                skeleton_key_present = self._check_for_skeleton_key_symbols(
                    csystem, rc4HmacInitialize, rc4HmacDecrypt)
            else:
                skeleton_key_present = self._check_for_skeleton_key_vad(
                    csystem, cryptdll_base, cryptdll_size)

            yield 0, (lsass_proc.UniqueProcessId, "lsass.exe", skeleton_key_present, \
                      format_hints.Hex(csystem.Initialize), format_hints.Hex(csystem.Decrypt))
Пример #5
0
    def get_object(
        self,
        constraint: PoolConstraint,
        use_top_down: bool,
        kernel_symbol_table: Optional[str] = None,
        native_layer_name: Optional[str] = None
    ) -> Optional[interfaces.objects.ObjectInterface]:
        """Carve an object or data structure from a kernel pool allocation

        Args:
            constraint: a PoolConstraint object used to get the pool allocation header object 
            use_top_down: for delineating how a windows version finds the size of the object body
            kernel_symbol_table: in case objects of a different symbol table are scanned for
            native_layer_name: the name of the layer where the data originally lived

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

        type_name = constraint.type_name
        executive = constraint.object_type is not None

        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]

        # when checking for symbols from a table other than nt_symbols grab _OBJECT_HEADER from the kernel
        # because symbol_table_name will be different from kernel_symbol_table.
        if kernel_symbol_table:
            object_header_type = self._context.symbol_space.get_type(
                kernel_symbol_table + constants.BANG + "_OBJECT_HEADER")
        else:
            # otherwise symbol_table_name *is* the kernel symbol table, so just use that.
            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)
            yield 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')
                pointercount_offset = object_header_type.relative_child_offset(
                    'PointerCount')
                pointercount_size = object_header_type.members['PointerCount'][
                    1].size
                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]
                    pointercount_value = int.from_bytes(
                        infomask_data[addr + pointercount_offset:addr +
                                      pointercount_offset + pointercount_size],
                        byteorder='little',
                        signed=True)
                    if not 0x1000000 > pointercount_value >= 0:
                        continue

                    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])
                        padding_length -= lengths_of_optional_headers[
                            padding_available or 0]

                    # 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():
                            yield 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
                if constraint.additional_structures:
                    for additional_structure in constraint.additional_structures:
                        type_size += self._context.symbol_space.get_type(
                            symbol_table_name + constants.BANG +
                            additional_structure).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():
                        yield mem_object
                except (TypeError, exceptions.InvalidAddressException):
                    pass
Пример #6
0
    def determine_tcpip_version(cls,
                                context: interfaces.context.ContextInterface,
                                layer_name: str,
                                nt_symbol_table: str) -> Tuple[str, Type]:
        """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)

        is_18363_or_later = versions.is_win10_18363_or_later(
            context=context, symbol_table=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, 0): "netscan-vista-x86",
                (6, 0, 6001, 0): "netscan-vista-x86",
                (6, 0, 6002, 0): "netscan-vista-x86",
                (6, 0, 6003, 0): "netscan-vista-x86",
                (6, 1, 7600, 0): "netscan-win7-x86",
                (6, 1, 7601, 0): "netscan-win7-x86",
                (6, 1, 8400, 0): "netscan-win7-x86",
                (6, 2, 9200, 0): "netscan-win8-x86",
                (6, 3, 9600, 0): "netscan-win81-x86",
                (10, 0, 10240, 0): "netscan-win10-10240-x86",
                (10, 0, 10586, 0): "netscan-win10-10586-x86",
                (10, 0, 14393, 0): "netscan-win10-14393-x86",
                (10, 0, 15063, 0): "netscan-win10-15063-x86",
                (10, 0, 16299, 0): "netscan-win10-15063-x86",
                (10, 0, 17134, 0): "netscan-win10-17134-x86",
                (10, 0, 17763, 0): "netscan-win10-17134-x86",
                (10, 0, 18362, 0): "netscan-win10-17134-x86",
                (10, 0, 18363, 0): "netscan-win10-17134-x86"
            }
        else:
            version_dict = {
                (6, 0, 6000, 0): "netscan-vista-x64",
                (6, 0, 6001, 0): "netscan-vista-sp12-x64",
                (6, 0, 6002, 0): "netscan-vista-sp12-x64",
                (6, 0, 6003, 0): "netscan-vista-sp12-x64",
                (6, 1, 7600, 0): "netscan-win7-x64",
                (6, 1, 7601, 0): "netscan-win7-x64",
                (6, 1, 8400, 0): "netscan-win7-x64",
                (6, 2, 9200, 0): "netscan-win8-x64",
                (6, 3, 9600, 0): "netscan-win81-x64",
                (6, 3, 9600, 19935): "netscan-win81-19935-x64",
                (10, 0, 10240, 0): "netscan-win10-x64",
                (10, 0, 10586, 0): "netscan-win10-x64",
                (10, 0, 14393, 0): "netscan-win10-x64",
                (10, 0, 15063, 0): "netscan-win10-15063-x64",
                (10, 0, 16299, 0): "netscan-win10-16299-x64",
                (10, 0, 17134, 0): "netscan-win10-17134-x64",
                (10, 0, 17763, 0): "netscan-win10-17763-x64",
                (10, 0, 18362, 0): "netscan-win10-18362-x64",
                (10, 0, 18363, 0): "netscan-win10-18363-x64",
                (10, 0, 19041, 0): "netscan-win10-19041-x64"
            }

        # we do not need to check for tcpip's specific FileVersion in every case
        tcpip_mod_version = 0  # keep it 0 as a default

        # special use cases

        # Win10_18363 is not recognized by windows.info as 18363
        # because all kernel file headers and debug structures report 18363 as
        # "10.0.18362.1198" with the last part being incremented. However, we can use
        # os_distinguisher to differentiate between 18362 and 18363
        if vers_minor_version == 18362 and is_18363_or_later:
            vollog.debug(
                "Detected 18363 data structures: working with 18363 symbol table."
            )
            vers_minor_version = 18363

        # we need to define additional version numbers (which are then found via tcpip.sys's FileVersion header) in case there is
        # ambiguity _within_ an OS version. If such a version number (last number of the tuple) is defined for the current OS
        # we need to inspect tcpip.sys's headers to see if we can grab the precise version
        if [(a, b, c, d) for a, b, c, d in version_dict
                if (a, b, c) == (nt_major_version, nt_minor_version,
                                 vers_minor_version) and d != 0]:
            vollog.debug(
                "Requiring further version inspection due to OS version by checking tcpip.sys's FileVersion header"
            )
            # the following is IntelLayer specific and might need to be adapted to other architectures.
            physical_layer_name = context.layers[layer_name].config.get(
                'memory_layer', None)
            if physical_layer_name:
                ver = verinfo.VerInfo.find_version_info(
                    context, physical_layer_name, "tcpip.sys")
                if ver:
                    tcpip_mod_version = ver[3]
                    vollog.debug(
                        "Determined tcpip.sys's FileVersion: {}".format(
                            tcpip_mod_version))
                else:
                    vollog.debug(
                        "Could not determine tcpip.sys's FileVersion.")
            else:
                vollog.debug(
                    "Unable to retrieve physical memory layer, skipping FileVersion check."
                )

        # 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, tcpip_mod_version))
        if not filename:
            # no match on filename means that we possibly have a version newer than those listed here.
            # try to grab the latest supported version of the current image NT version. If that symbol
            # version does not work, support has to be added manually.
            current_versions = [
                (nt_maj, nt_min, vers_min, tcpip_ver)
                for nt_maj, nt_min, vers_min, tcpip_ver in version_dict
                if nt_maj == nt_major_version and nt_min == nt_minor_version
                and tcpip_ver <= tcpip_mod_version
            ]
            current_versions.sort()

            if current_versions:
                latest_version = current_versions[-1]

                filename = version_dict.get(latest_version)

                vollog.debug(
                    f"Unable to find exact matching symbol file, going with latest: {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(f"Determined symbol filename: {filename}")

        return filename, class_types
Пример #7
0
    def _generator(self) -> Iterator[Tuple[int, Tuple[int, int, Any, Any]]]:

        kernel = self.context.modules[self.config['kernel']]

        layer_name = kernel.layer_name
        collection = self.build_module_collection(self.context, layer_name,
                                                  kernel.symbol_table_name)

        kvo = self.context.layers[layer_name].config['kernel_virtual_offset']
        ntkrnlmp = self.context.module(kernel.symbol_table_name,
                                       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,
                                                     kernel.symbol_table_name)
        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()))
Пример #8
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:
                    # TODO: Fix appropriately in a future release.
                    # Currently just a temporary workaround to deal with custom bit flag
                    # in the PFN field for pages in transition state.
                    # See https://github.com/volatilityfoundation/volatility3/pull/475
                    physoffset = (mmpte.u.Trans.PageFrameNumber &
                                  ((1 << 33) - 1)) << 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
Пример #9
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])
Пример #10
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 versions.is_windows_xp(context=context,
                                  symbol_table=symbol_table) and not is_64bit:
            symbol_filename = "services-xp-x86"
        elif versions.is_xp_or_2003(context=context,
                                    symbol_table=symbol_table) and is_64bit:
            symbol_filename = "services-xp-2003-x64"
        elif versions.is_win10_16299_or_later(
                context=context, symbol_table=symbol_table) and is_64bit:
            symbol_filename = "services-win10-16299-x64"
        elif versions.is_win10_16299_or_later(
                context=context, symbol_table=symbol_table) and not is_64bit:
            symbol_filename = "services-win10-16299-x86"
        elif versions.is_win10_up_to_15063(
                context=context, symbol_table=symbol_table) and is_64bit:
            symbol_filename = "services-win8-x64"
        elif versions.is_win10_up_to_15063(
                context=context, symbol_table=symbol_table) and not is_64bit:
            symbol_filename = "services-win8-x86"
        elif versions.is_win10_15063(context=context,
                                     symbol_table=symbol_table) and is_64bit:
            symbol_filename = "services-win10-15063-x64"
        elif versions.is_win10_15063(
                context=context, symbol_table=symbol_table) and not is_64bit:
            symbol_filename = "services-win10-15063-x86"
        elif versions.is_windows_8_or_later(
                context=context, symbol_table=symbol_table) and is_64bit:
            symbol_filename = "services-win8-x64"
        elif versions.is_windows_8_or_later(
                context=context, symbol_table=symbol_table) and not is_64bit:
            symbol_filename = "services-win8-x86"
        elif versions.is_vista_or_later(
                context=context, symbol_table=symbol_table) and is_64bit:
            symbol_filename = "services-vista-x64"
        elif versions.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,
            os.path.join("windows", "services"),
            symbol_filename,
            class_types=services.class_types,
            native_types=native_types)
Пример #11
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 = versions.is_windows_10(context, symbol_table)
        is_windows_8_or_later = versions.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_objects = header.get_object(constraint = constraint,
                                           use_top_down = is_windows_8_or_later,
                                           native_layer_name = layer_name,
                                           kernel_symbol_table = symbol_table)

            for mem_object in mem_objects:
                if mem_object is None:
                    vollog.log(constants.LOGLEVEL_VVV, f"Cannot create an instance of {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,
                                   f"Cannot test instance type check for {constraint.type_name}")
                        continue

                yield constraint, mem_object, header
Пример #12
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=os.path.join("windows", "bigpools"),
                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
Пример #13
0
    def _generator(self) -> Iterator[Tuple]:
        kernel = self.context.modules[self.config['kernel']]
        physical_layer_name = self.context.layers[
            kernel.layer_name].config.get('memory_layer', None)

        # Decide of Memory Dump Architecture
        layer = self.context.layers[physical_layer_name]
        architecture = "intel" if not symbols.symbol_table_is_64bit(
            self.context, kernel.symbol_table_name) else "intel64"

        # Read in the Symbol File
        symbol_table = intermed.IntermediateSymbolTable.create(
            context=self.context,
            config_path=self.config_path,
            sub_path="windows",
            filename="mbr",
            class_types={
                'PARTITION_TABLE': mbr.PARTITION_TABLE,
                'PARTITION_ENTRY': mbr.PARTITION_ENTRY
            })

        partition_table_object = symbol_table + constants.BANG + "PARTITION_TABLE"

        # Define Signature and Data Length
        mbr_signature = b"\x55\xAA"
        mbr_length = 0x200
        bootcode_length = 0x1B8

        # Scan the Layer for Raw Master Boot Record (MBR) and parse the fields
        for offset, _value in layer.scan(
                context=self.context,
                scanner=scanners.MultiStringScanner(patterns=[mbr_signature])):
            try:
                mbr_start_offset = offset - (mbr_length - len(mbr_signature))
                partition_table = self.context.object(partition_table_object,
                                                      offset=mbr_start_offset,
                                                      layer_name=layer.name)

                # Extract only BootCode
                full_mbr = layer.read(mbr_start_offset, mbr_length, pad=True)
                bootcode = full_mbr[:bootcode_length]

                all_zeros = None

                if bootcode:
                    all_zeros = bootcode.count(b"\x00") == len(bootcode)

                if not all_zeros:

                    partition_entries = [
                        partition_table.FirstEntry,
                        partition_table.SecondEntry,
                        partition_table.ThirdEntry, partition_table.FourthEntry
                    ]

                    if not self.config.get("full", True):
                        yield (0, (format_hints.Hex(offset),
                                   partition_table.get_disk_signature(),
                                   self.get_hash(bootcode),
                                   self.get_hash(full_mbr),
                                   renderers.NotApplicableValue(),
                                   renderers.NotApplicableValue(),
                                   renderers.NotApplicableValue(),
                                   renderers.NotApplicableValue(),
                                   interfaces.renderers.Disassembly(
                                       bootcode, 0, architecture)))
                    else:
                        yield (0, (format_hints.Hex(offset),
                                   partition_table.get_disk_signature(),
                                   self.get_hash(bootcode),
                                   self.get_hash(full_mbr),
                                   renderers.NotApplicableValue(),
                                   renderers.NotApplicableValue(),
                                   renderers.NotApplicableValue(),
                                   renderers.NotApplicableValue(),
                                   renderers.NotApplicableValue(),
                                   renderers.NotApplicableValue(),
                                   renderers.NotApplicableValue(),
                                   renderers.NotApplicableValue(),
                                   renderers.NotApplicableValue(),
                                   renderers.NotApplicableValue(),
                                   renderers.NotApplicableValue(),
                                   renderers.NotApplicableValue(),
                                   renderers.NotApplicableValue(),
                                   interfaces.renderers.Disassembly(
                                       bootcode, 0, architecture),
                                   format_hints.HexBytes(bootcode)))

                    for partition_index, partition_entry_object in enumerate(
                            partition_entries, start=1):

                        if not self.config.get("full", True):
                            yield (1, (
                                format_hints.Hex(offset),
                                partition_table.get_disk_signature(),
                                self.get_hash(bootcode),
                                self.get_hash(full_mbr), partition_index,
                                partition_entry_object.is_bootable(),
                                partition_entry_object.get_partition_type(),
                                format_hints.Hex(partition_entry_object.
                                                 get_size_in_sectors()),
                                renderers.NotApplicableValue()))
                        else:
                            yield (1, (
                                format_hints.Hex(offset),
                                partition_table.get_disk_signature(),
                                self.get_hash(bootcode),
                                self.get_hash(full_mbr), partition_index,
                                partition_entry_object.is_bootable(),
                                format_hints.Hex(partition_entry_object.
                                                 get_bootable_flag()),
                                partition_entry_object.get_partition_type(),
                                format_hints.Hex(
                                    partition_entry_object.PartitionType),
                                format_hints.Hex(
                                    partition_entry_object.get_starting_lba()),
                                partition_entry_object.get_starting_cylinder(),
                                partition_entry_object.get_starting_chs(),
                                partition_entry_object.get_starting_sector(),
                                partition_entry_object.get_ending_cylinder(),
                                partition_entry_object.get_ending_chs(),
                                partition_entry_object.get_ending_sector(),
                                format_hints.Hex(partition_entry_object.
                                                 get_size_in_sectors()),
                                renderers.NotApplicableValue(),
                                renderers.NotApplicableValue()))
                else:
                    vollog.log(
                        constants.LOGLEVEL_VVVV,
                        f"Not a valid MBR: Data all zeroed out : {format_hints.Hex(offset)}"
                    )
                    continue

            except exceptions.PagedInvalidAddressException as excp:
                vollog.log(
                    constants.LOGLEVEL_VVVV,
                    f"Invalid address identified in guessed MBR: {hex(excp.invalid_address)}"
                )
                continue
Пример #14
0
    def populate_processes(self):
        """Populate the tree view with processes"""
        root = self.model.invisibleRootItem()

        for proc in self.window.runtime.get_procs():
            root = self.model.invisibleRootItem()

            # This will hold the export symbol table for an entire process
            exports = {}

            # System process?
            if proc.UniqueProcessId == 4:
                image_base = self.window.runtime.context.config[
                    "plugins.PsList.PsList.primary.kernel_virtual_offset"]

                # Create internal filename
                filename = "{} ({}) - {:#08x}".format("System",
                                                      proc.UniqueProcessId,
                                                      image_base)

                # Create process tree view item
                process_item = QtGui.QStandardItem("System")
                process_item.setData(
                    {
                        "filename": filename,
                        "proc": proc,
                        "entry": None,
                        "exports": exports,
                        "image_base": image_base
                    }, QtCore.Qt.UserRole)
                process_item.setUserTristate(True)
                process_item.setCheckable(True)

                # Determine kernel architecture specifics
                if symbols.symbol_table_is_64bit(
                        self.window.runtime.context, self.window.runtime.
                        context.config["plugins.PsList.PsList.nt_symbols"]):
                    ptr_mask = 0xffffffffffffffff
                    ptr_width = 16
                else:
                    ptr_mask = 0xffffffff
                    ptr_width = 8

                root.appendRow([
                    process_item,
                    QtGui.QStandardItem("{}".format(proc.UniqueProcessId)),
                    QtGui.QStandardItem("{}".format(
                        proc.InheritedFromUniqueProcessId)),
                    QtGui.QStandardItem("0x{:0{w}x}".format(image_base
                                                            & ptr_mask,
                                                            w=ptr_width)),
                    QtGui.QStandardItem(""),
                    QtGui.QStandardItem(""),
                    QtGui.QStandardItem(""),
                    QtGui.QStandardItem("")
                ])

                root = process_item

                # Iterate over kernel modules
                for module in self.window.runtime.get_modules():
                    try:
                        process_item = self.make_process_item(
                            proc, module, exports)

                        root.appendRow([
                            process_item,
                            QtGui.QStandardItem("{}".format(
                                proc.UniqueProcessId)),
                            QtGui.QStandardItem(""),
                            QtGui.QStandardItem("0x{:0{w}x}".format(
                                int(module.DllBase) & ptr_mask, w=ptr_width)),
                            QtGui.QStandardItem(""),
                            QtGui.QStandardItem(""),
                            QtGui.QStandardItem(""),
                            QtGui.QStandardItem("")
                        ])

                    except framework.exceptions.PagedInvalidAddressException:
                        pass

            # Iterate over load order modules
            for entry in proc.load_order_modules():
                try:
                    process_item = self.make_process_item(
                        proc,
                        entry,
                        exports,
                        expandable=bool(
                            root == self.model.invisibleRootItem()))
                except framework.exceptions.PagedInvalidAddressException:
                    continue

                # Determine process architecture specifics
                if proc.get_is_wow64():
                    ptr_mask = 0xffffffffffffffff
                    ptr_width = 16
                else:
                    ptr_mask = 0xffffffff
                    ptr_width = 8

                if root == self.model.invisibleRootItem():
                    # Include full details for the main process
                    root.appendRow([
                        process_item,
                        QtGui.QStandardItem("{}".format(proc.UniqueProcessId)),
                        QtGui.QStandardItem("{}".format(
                            proc.InheritedFromUniqueProcessId)),
                        QtGui.QStandardItem("0x{:0{w}x}".format(
                            int(entry.DllBase) & ptr_mask, w=ptr_width)),
                        QtGui.QStandardItem("{}".format(proc.ActiveThreads)),
                        QtGui.QStandardItem("{}".format(
                            proc.get_handle_count())),
                        QtGui.QStandardItem("{}".format(
                            proc.get_create_time())),
                        QtGui.QStandardItem("{}".format(proc.get_is_wow64()))
                    ])

                    root = process_item
                else:
                    # Add details for the DLL
                    root.appendRow([
                        process_item,
                        QtGui.QStandardItem("{}".format(proc.UniqueProcessId)),
                        QtGui.QStandardItem("{}".format(
                            proc.InheritedFromUniqueProcessId)),
                        QtGui.QStandardItem("0x{:0{w}x}".format(
                            int(entry.DllBase) & ptr_mask, w=ptr_width)),
                        QtGui.QStandardItem(""),
                        QtGui.QStandardItem(""),
                        QtGui.QStandardItem(""),
                        QtGui.QStandardItem("")
                    ])

            for i in range(0, 7):
                self.treeview.resizeColumnToContents(i)
Пример #15
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)

        is_18363_or_later = versions.is_win10_18363_or_later(
            context=context, symbol_table=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-10240-x86",
                (10, 0, 10586): "netscan-win10-10586-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-17134-x86",
                (10, 0, 17763): "netscan-win10-17134-x86",
                (10, 0, 18362): "netscan-win10-17134-x86",
                (10, 0, 18363): "netscan-win10-17134-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-16299-x64",
                (10, 0, 17134): "netscan-win10-17134-x64",
                (10, 0, 17763): "netscan-win10-17763-x64",
                (10, 0, 18362): "netscan-win10-18362-x64",
                (10, 0, 18363): "netscan-win10-18363-x64",
                (10, 0, 19041): "netscan-win10-19041-x64"
            }

        # special use case: Win10_18363 is not recognized by windows.info as 18363
        # because all kernel file headers and debug structures report 18363 as
        # "10.0.18362.1198" with the last part being incremented. However, we can use
        # os_distinguisher to differentiate between 18362 and 18363
        if vers_minor_version == 18362 and is_18363_or_later:
            vollog.debug(
                "Detected 18363 data structures: working with 18363 symbol table."
            )
            vers_minor_version = 18363

        # 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:
            # no match on filename means that we possibly have a version newer than those listed here.
            # try to grab the latest supported version of the current image NT version. If that symbol
            # version does not work, support has to be added manually.
            current_versions = [
                key for key in list(version_dict.keys())
                if key[0] == nt_major_version and key[1] == nt_minor_version
            ]
            current_versions.sort()

            if current_versions:
                latest_version = current_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
Пример #16
0
    def _generator(self):

        kernel = self.context.modules[self.config['kernel']]

        layer_name = kernel.layer_name
        symbol_table = kernel.symbol_table_name
        layer = self.context.layers[layer_name]
        table = self.context.symbol_space[symbol_table]

        kdbg = self.get_kdbg_structure(self.context, self.config_path,
                                       layer_name, symbol_table)

        yield (0, ("Kernel Base", hex(layer.config["kernel_virtual_offset"])))
        yield (0, ("DTB", hex(layer.config["page_map_offset"])))
        yield (0, ("Symbols", table.config["isf_url"]))
        yield (0,
               ("Is64Bit",
                str(symbols.symbol_table_is_64bit(self.context,
                                                  symbol_table))))
        yield (0,
               ("IsPAE",
                str(self.context.layers[layer_name].metadata.get("pae",
                                                                 False))))

        for i, layer in self.get_depends(self.context, layer_name):
            yield (0, (layer.name, f"{i} {layer.__class__.__name__}"))

        if kdbg.Header.OwnerTag == 0x4742444B:

            yield (0, ("KdDebuggerDataBlock", hex(kdbg.vol.offset)))
            yield (0, ("NTBuildLab", kdbg.get_build_lab()))
            yield (0, ("CSDVersion", str(kdbg.get_csdversion())))

        vers = self.get_version_structure(self.context, layer_name,
                                          symbol_table)

        yield (0, ("KdVersionBlock", hex(vers.vol.offset)))
        yield (0, ("Major/Minor", f"{vers.MajorVersion}.{vers.MinorVersion}"))
        yield (0, ("MachineType", str(vers.MachineType)))

        ntkrnlmp = self.get_kernel_module(self.context, layer_name,
                                          symbol_table)

        cpu_count_offset = ntkrnlmp.get_symbol("KeNumberProcessors").address

        cpu_count = ntkrnlmp.object(object_type="unsigned int",
                                    layer_name=layer_name,
                                    offset=cpu_count_offset)

        yield (0, ("KeNumberProcessors", str(cpu_count)))

        kuser = self.get_kuser_structure(self.context, layer_name,
                                         symbol_table)

        yield (0, ("SystemTime", str(kuser.SystemTime.get_time())))
        yield (0, ("NtSystemRoot",
                   str(
                       kuser.NtSystemRoot.cast("string",
                                               encoding="utf-16",
                                               errors="replace",
                                               max_length=260))))
        yield (0, ("NtProductType", str(kuser.NtProductType.description)))
        yield (0, ("NtMajorVersion", str(kuser.NtMajorVersion)))
        yield (0, ("NtMinorVersion", str(kuser.NtMinorVersion)))
        # yield (0, ("KdDebuggerEnabled", "True" if kuser.KdDebuggerEnabled else "False"))
        # yield (0, ("SafeBootMode", "True" if kuser.SafeBootMode else "False"))

        nt_header = self.get_ntheader_structure(self.context, self.config_path,
                                                layer_name)

        yield (0, ("PE MajorOperatingSystemVersion",
                   str(nt_header.OptionalHeader.MajorOperatingSystemVersion)))
        yield (0, ("PE MinorOperatingSystemVersion",
                   str(nt_header.OptionalHeader.MinorOperatingSystemVersion)))

        yield (0, ("PE Machine", str(nt_header.FileHeader.Machine)))
        yield (0,
               ("PE TimeDateStamp",
                time.asctime(time.gmtime(nt_header.FileHeader.TimeDateStamp))))