Ejemplo n.º 1
0
    def _enum_apis(self, all_mods):
        """Enumerate all exported functions from kernel
        or process space.

        @param all_mods: list of _LDR_DATA_TABLE_ENTRY

        To enum kernel APIs, all_mods is a list of drivers.
        To enum process APIs, all_mods is a list of DLLs.

        The function name is used if available, otherwise
        we take the ordinal value.
        """
        exports = {}

        for i, mod in enumerate(all_mods):
            self.session.report_progress("Scanning imports %s/%s" %
                                         (i, len(all_mods)))

            pe = pe_vtypes.PE(address_space=mod.obj_vm,
                              session=self.session,
                              image_base=mod.DllBase)

            for _, func_pointer, func_name, ordinal in pe.ExportDirectory():
                function_name = func_name or ordinal or ''

                exports[func_pointer.v()] = (mod, func_pointer, function_name)

        return exports
Ejemplo n.º 2
0
    def detect_IAT_hooks(self):
        """Detect Import Address Table hooks.

        An IAT hook is where malware changes the IAT entry for a dll after its
        loaded so that when it is called from within the DLL, flow control is
        directed to the malware instead.

        We determine the IAT entry is hooked if the address is outside the dll
        which is imported.
        """
        pe = pe_vtypes.PE(image_base=self.image_base, session=self.session)

        # First try to find all the names of the imported functions.
        imports = [(dll, func_name)
                   for dll, func_name, _ in pe.ImportDirectory()]

        resolver = self.session.address_resolver

        for idx, (dll, func_address, _) in enumerate(pe.IAT()):
            target_dll, target_func_name = imports[idx]
            target_dll = self.session.address_resolver.NormalizeModuleName(
                target_dll)

            self.session.report_progress("Checking function %s!%s", target_dll,
                                         target_func_name)

            # We only want the containing module.
            _, _, name = resolver.FindContainingModule(func_address)
            if target_dll == name:
                continue

            function_name = "%s:%s" % (target_dll, target_func_name)

            yield function_name, func_address
Ejemplo n.º 3
0
    def render(self, renderer):
        # The filename is an executable
        if self.guid is None:
            self.pe = pe_vtypes.PE(filename=self.filename,
                                   session=self.session)
            data_directory = self.pe.nt_header.OptionalHeader.DataDirectory[
                "IMAGE_DIRECTORY_ENTRY_DEBUG"].VirtualAddress.dereference_as(
                    "_IMAGE_DEBUG_DIRECTORY")

            # We only support the more recent RSDS format.
            debug = data_directory.AddressOfRawData.dereference_as(
                "CV_RSDS_HEADER")

            if debug.Signature != "RSDS":
                logging.error("PDB stream %s not supported.", debug.Signature)
                return

            self.pdb_filename = ntpath.basename(str(debug.Filename))
            self.guid = self.pe.RSDS.GUID_AGE

        elif self.filename is None:
            raise RuntimeError(
                "Filename must be provided when GUI is specified.")

        else:
            self.pdb_filename = self.filename
            self.guid = self.guid.upper()

        for url in self.SYM_URLS:
            try:
                basename = ntpath.splitext(self.pdb_filename)[0]
                url += "/%s/%s/%s.pd_" % (self.pdb_filename, self.guid,
                                          basename)

                renderer.format("Trying to fetch {0}\n", url)
                request = urllib2.Request(
                    url, None, headers={'User-Agent': self.USER_AGENT})

                data = urllib2.urlopen(request).read()
                renderer.format("Received {0} bytes\n", len(data))

                output_file = os.path.join(self.dump_dir, "%s.pd_" % basename)
                with open(output_file, "wb") as fd:
                    fd.write(data)

                try:
                    subprocess.check_call(
                        ["cabextract",
                         os.path.basename(output_file)],
                        cwd=self.dump_dir)
                except subprocess.CalledProcessError:
                    renderer.format(
                        "Failed to decompress output file {0}. "
                        "Ensure cabextract is installed.\n", output_file)

                break

            except IOError as e:
                logging.error(e)
                continue
Ejemplo n.º 4
0
    def LoadProfileForDll(self, module_base, module_name):
        self._EnsureInitialized()

        if module_name in self.profiles:
            return self.profiles[module_name]

        # Try to determine the DLL's GUID.
        pe_helper = pe_vtypes.PE(
            address_space=self.session.GetParameter("default_address_space"),
            image_base=module_base,
            session=self.session)

        # TODO: Apply profile index to detect the profile.
        guid_age = pe_helper.RSDS.GUID_AGE
        if guid_age:
            profile = self.session.LoadProfile("%s/GUID/%s" % (
                module_name, guid_age))

            profile.name = module_name
            profile.image_base = module_base
            if profile:
                self.profiles[module_name] = profile
                return profile

        result = self._build_profile_from_exports(module_base, module_name)
        self.profiles[module_name] = result
        return result
Ejemplo n.º 5
0
    def _enum_apis(self, all_mods):
        """Enumerate all exported functions from process space.

        @param all_mods: list of _LDR_DATA_TABLE_ENTRY

        To enum process APIs, all_mods is a list of DLLs.

        The function name is used if available, otherwise
        we take the ordinal value.
        """
        exports = {}

        for i, mod in enumerate(all_mods):
            self.session.report_progress("Scanning exports %s/%s" %
                                         (i, len(all_mods)))

            pe = pe_vtypes.PE(address_space=mod.obj_vm,
                              session=self.session,
                              image_base=mod.DllBase)

            export_directory = pe.nt_header.OptionalHeader.DataDirectory[
                'IMAGE_DIRECTORY_ENTRY_EXPORT'].dereference()

            dll = export_directory.Name.dereference()
            function_table = export_directory.AddressOfFunctions.dereference()

            for func_index, pointer in enumerate(function_table):
                exports[pointer.v()] = (mod, pointer, export_directory,
                                        func_index)

        return exports
Ejemplo n.º 6
0
    def detect_inline_hooks(self):
        """A Generator of hooked exported functions from this PE file.

        Yields:
          A tuple of (function, name, jump_destination)
        """
        # Inspect the export directory for inline hooks.
        pe = pe_vtypes.PE(
            image_base=self.plugin_args.image_base,
            address_space=self.session.GetParameter("default_address_space"),
            session=self.session)
        pfn_db = self.session.profile.get_constant_object("MmPfnDatabase")
        heuristic = HookHeuristic(session=self.session)

        ok_pages = set()

        for _, function, name, _ in pe.ExportDirectory():
            # Dereference the function pointer.
            function_address = function.deref().obj_offset

            self.session.report_progress("Checking function %#x (%s)",
                                         function, name)

            # Check if the page is private or a file mapping. Usually if a
            # mapped page is modified it will be converted to a private page due
            # to Windows copy on write semantics. We assume that hooks are only
            # placed in memory, and therefore functions which are still mapped
            # to disk files are not hooked and can be safely skipped.
            if not self.plugin_args.thorough:
                # We must do the vtop in the process address space. This is the
                # physical page backing the function preamble.
                phys_address = function.obj_vm.vtop(function_address)

                # Page not mapped.
                if phys_address == None:
                    continue

                phys_page = phys_address >> 12

                # We determined this page is ok before - we can skip it.
                if phys_page in ok_pages:
                    continue

                # Get the PFN DB record.
                pfn_obj = pfn_db[phys_page]

                # The page is controlled by a prototype PTE which means it is
                # still a file mapping. It has not been changed.
                if pfn_obj.IsPrototype:
                    ok_pages.add(phys_page)
                    continue

            # Try to detect an inline hook.
            destination = heuristic.Inspect(function, instructions=3) or ""

            # If we did not detect a hook we skip this function.
            if destination:
                yield function, name, destination
Ejemplo n.º 7
0
    def __init__(self, **kwargs):
        super(PEAddressResolver, self).__init__(**kwargs)
        self.address_map = utils.SortedCollection(key=lambda x: x[0])
        self.section_map = utils.SortedCollection(key=lambda x: x[0])
        self.image_base = self.kernel_address_space.image_base
        self.pe_helper = pe_vtypes.PE(
            address_space=self.session.kernel_address_space,
            image_base=self.image_base,
            session=self.session)

        # Delay initialization until we need it.
        self._initialized = False
Ejemplo n.º 8
0
    def LoadProfileForDll(self, module_base, module_name):
        """Loads a profile for the PE file located at the module_base.

        This method gets a profile using the following methods in order:

        1 - Have session load it from the profile repository.
        2 - Build locally from the MS symbols server.
        3 - Look up a the profile using index if an index exists for this
            binary.
        4 - Build it from export tables of the PE binary.
        """
        self._EnsureInitialized()

        if module_name in self.profiles:
            return self.profiles[module_name]

        result = None

        # Try to determine the DLL's GUID.
        pe_helper = pe_vtypes.PE(
            address_space=self.session.GetParameter("default_address_space"),
            image_base=module_base,
            session=self.session)

        guid_age = pe_helper.RSDS.GUID_AGE
        if guid_age:
            profile_name = "%s/GUID/%s" % (module_name, guid_age)
            result = (self.session.LoadProfile(profile_name) or
                      self._build_local_profile(module_name, profile_name))

            if result:
                result.name = module_name
                result.image_base = module_base

        if not result:
            # Try to apply the profile index if possible.
            index_profile = self.session.LoadProfile("%s/index" % module_name)
            if index_profile:
                for test_profile, _ in index_profile.LookupIndex(
                        module_base, minimal_match=1):
                    result = self.session.LoadProfile(test_profile)
                    result.image_base = module_base
                    break

        if not result:
            result = self._build_profile_from_exports(module_base, module_name)

        self.profiles[module_name] = result
        return result
Ejemplo n.º 9
0
    def detect_IAT_hooks(self):
        """Detect Import Address Table hooks.

        An IAT hook is where malware changes the IAT entry for a dll after its
        loaded so that when it is called from within the DLL, flow control is
        directed to the malware instead.

        We determine the IAT entry is hooked if the address is outside the dll
        which is imported.
        """
        pe = pe_vtypes.PE(image_base=self.plugin_args.image_base,
                          session=self.session)

        # First try to find all the names of the imported functions.
        imports = [(dll, func_name)
                   for dll, func_name, _ in pe.ImportDirectory()]

        resolver = self.session.address_resolver

        for idx, (dll, func_address, _) in enumerate(pe.IAT()):

            try:
                target_dll, target_func_name = imports[idx]
                target_dll = self.session.address_resolver.NormalizeModuleName(
                    target_dll)
            except IndexError:
                # We can not retrieve these function's name from the
                # OriginalFirstThunk array - possibly because it is not mapped
                # in.
                target_dll = dll
                target_func_name = ""

            self.session.report_progress("Checking function %s!%s", target_dll,
                                         target_func_name)

            # We only want the containing module.
            module = resolver.GetContainingModule(func_address)
            if module and target_dll == module.name:
                continue

            # Use ordinal if function has no name
            if not len(target_func_name):
                target_func_name = "(%s)" % idx

            function_name = "%s!%s" % (target_dll, target_func_name)

            # Function_name is the name which the PE file want
            yield function_name, func_address
Ejemplo n.º 10
0
def findExportsFromLoadedDLLs(pid):
    p = process(pid)
    eprocess = p['_EPROCESS']
    modules = eprocess.get_load_modules()

    exports = {}
    for i, mod in enumerate(modules):
        pe = pe_vtypes.PE(address_space=mod.obj_vm,
                          session=s,
                          image_base=mod.DllBase)

        for _, func_pointer, func_name, ordinal in pe.ExportDirectory():
            function_name = func_name or ordinal or ''
            exports[func_pointer.v()] = (mod, func_pointer, function_name)

    return exports
Ejemplo n.º 11
0
    def detect_EAT_hooks(self, size=0):
        """Detect Export Address Table hooks.

        An EAT hook is where malware changes the EAT entry for a dll after its
        loaded so that a new DLL wants to link against it, the new DLL will use
        the malware's function instead of the exporting DLL's function.

        We determine the EAT entry is hooked if the address lies outside the
        exporting dll.
        """
        address_space = self.session.GetParameter("default_address_space")
        pe = pe_vtypes.PE(image_base=self.plugin_args.image_base,
                          session=self.session,
                          address_space=address_space)
        start = self.plugin_args.image_base
        end = self.plugin_args.image_base + size

        # If the dll size is not provided we parse it from the PE header.
        if not size:
            for _, _, virtual_address, section_size in pe.Sections():
                # Only count executable sections.
                section_end = (self.plugin_args.image_base + virtual_address +
                               section_size)
                if section_end > end:
                    end = section_end

        resolver = self.session.address_resolver

        for dll, func, name, hint in pe.ExportDirectory():
            self.session.report_progress("Checking export %s!%s", dll, name)

            # Skip zero or invalid addresses.
            if address_space.read(func.v(), 10) == "\x00" * 10:
                continue

            # Report on exports which fall outside the dll.
            if start < func.v() < end:
                continue

            function_name = "%s:%s (%s)" % (resolver.NormalizeModuleName(dll),
                                            name, hint)

            yield function_name, func
Ejemplo n.º 12
0
    def RSDS(self):
        helper = pe_vtypes.PE(address_space=self.obj_vm,
                              image_base=self.DllBase,
                              session=self.obj_session)

        return helper.RSDS