Пример #1
0
    def init_exports(self, pe: pefile.PE):
        if not Process.directory_exists(pe, 'IMAGE_DIRECTORY_ENTRY_EXPORT'):
            return

        # Do a full load if IMAGE_DIRECTORY_ENTRY_EXPORT is present so we can load the exports
        pe.full_load()

        iat = {}

        # parse directory entry export
        for entry in pe.DIRECTORY_ENTRY_EXPORT.symbols:
            ea = self.pe_image_address + entry.address

            self.export_symbols[ea] = {
                'name': entry.name,
                'ordinal': entry.ordinal
            }

            if entry.name:
                iat[entry.name] = ea

            iat[entry.ordinal] = ea

        dll_name = os.path.basename(self.path)
        self.import_address_table[dll_name.casefold()] = iat
Пример #2
0
    def init_imports(self, pe: pefile.PE, is_driver: bool):
        if not Process.directory_exists(pe, 'IMAGE_DIRECTORY_ENTRY_IMPORT'):
            return

        pe.full_load()

        for entry in pe.DIRECTORY_ENTRY_IMPORT:
            dll_name = entry.dll.decode().casefold()
            self.ql.log.debug(f'Requesting imports from {dll_name}')

            orig_dll_name = dll_name
            redirected = False

            if dll_name.startswith('api-ms-win-'):
                # DLLs starting with this prefix contain no actual code. Instead, the windows loader loads the actual
                # code from one of the main windows dlls.
                # see https://github.com/lucasg/Dependencies for correct replacement dlls
                #
                # The correct way to find the dll that replaces all symbols from this dll involves using the hashmap
                # inside of apisetschema.dll (see https://lucasg.github.io/2017/10/15/Api-set-resolution/ ).
                #
                # Currently, we use a simpler, more hacky approach, that seems to work in a lot of cases: we just scan
                # through some key dlls and hope that we find the requested symbols there. some symbols may appear on
                # more than one dll though; in that case we proceed to the next symbol to see which key dll includes it.
                #
                # Note: You might be tempted to load the actual dll (dll_name), because they also contain a reference to
                # the replacement dll. However, chances are, that these dlls do not exist in the rootfs and maybe they
                # don't even exist on windows. Therefore this approach is a bad idea.

                # DLLs that seem to contain most of the requested symbols
                key_dlls = ('ntdll.dll', 'kernelbase.dll', 'ucrtbase.dll')

                imports = iter(entry.imports)
                failed = False
                fallback = None

                while not redirected and not failed:
                    # find all possible redirection options by scanning key dlls for the current imported symbol
                    imp = next(imports, None)
                    redirection_options = [fallback] if imp is None else [
                        filename for filename in key_dlls
                        if filename in self.import_address_table
                        and imp.name in self.import_address_table[filename]
                    ]

                    # no redirection options: failed to redirect dll
                    if not redirection_options:
                        failed = True

                    # exactly one redirection options: use it
                    elif len(redirection_options) == 1:
                        key_dll = redirection_options[0]
                        redirected = True

                    # more than one redirection options: remember one of them and proceed to next symbol
                    else:
                        fallback = redirection_options[-1]

                if not redirected:
                    self.ql.log.warning(f'Failed to resolve {dll_name}')
                    continue

                self.ql.log.debug(f'Redirecting {dll_name} to {key_dll}')
                dll_name = key_dll

            unbound_imports = [imp for imp in entry.imports if not imp.bound]

            if unbound_imports:
                # Only load dll if encountered unbound symbol
                if not redirected:
                    dll_base = self.load_dll(entry.dll.decode(), is_driver)

                    if not dll_base:
                        continue

                for imp in unbound_imports:
                    iat = self.import_address_table[dll_name]

                    if imp.name:
                        if imp.name not in iat:
                            self.ql.log.debug(
                                f'Error in loading function {imp.name.decode()} ({orig_dll_name}){", probably misdirected" if redirected else ""}'
                            )
                            continue

                        addr = iat[imp.name]
                    else:
                        addr = iat[imp.ordinal]

                    self.ql.mem.write_ptr(imp.address, addr)