(self, pe: pefile.PE, is_driver: bool)
| 614 | return pe.OPTIONAL_HEADER.DATA_DIRECTORY[ent].VirtualAddress != 0 |
| 615 | |
| 616 | def init_imports(self, pe: pefile.PE, is_driver: bool): |
| 617 | if not Process.directory_exists(pe, 'IMAGE_DIRECTORY_ENTRY_IMPORT'): |
| 618 | return |
| 619 | |
| 620 | pe.full_load() |
| 621 | |
| 622 | for entry in pe.DIRECTORY_ENTRY_IMPORT: |
| 623 | dll_name = entry.dll.decode().casefold() |
| 624 | self.ql.log.debug(f'Requesting imports from {dll_name}') |
| 625 | |
| 626 | orig_dll_name = dll_name |
| 627 | redirected = False |
| 628 | |
| 629 | if dll_name.startswith('api-ms-win-'): |
| 630 | # DLLs starting with this prefix contain no actual code. Instead, the windows loader loads the actual |
| 631 | # code from one of the main windows dlls. |
| 632 | # see https://github.com/lucasg/Dependencies for correct replacement dlls |
| 633 | # |
| 634 | # The correct way to find the dll that replaces all symbols from this dll involves using the hashmap |
| 635 | # inside of apisetschema.dll (see https://lucasg.github.io/2017/10/15/Api-set-resolution/ ). |
| 636 | # |
| 637 | # Currently, we use a simpler, more hacky approach, that seems to work in a lot of cases: we just scan |
| 638 | # through some key dlls and hope that we find the requested symbols there. some symbols may appear on |
| 639 | # more than one dll though; in that case we proceed to the next symbol to see which key dll includes it. |
| 640 | # |
| 641 | # Note: You might be tempted to load the actual dll (dll_name), because they also contain a reference to |
| 642 | # the replacement dll. However, chances are, that these dlls do not exist in the rootfs and maybe they |
| 643 | # don't even exist on windows. Therefore this approach is a bad idea. |
| 644 | |
| 645 | # DLLs that seem to contain most of the requested symbols |
| 646 | key_dlls = ( |
| 647 | 'kernel32.dll', |
| 648 | 'ntdll.dll', |
| 649 | 'kernelbase.dll', |
| 650 | 'ucrtbase.dll' |
| 651 | ) |
| 652 | |
| 653 | imports = iter(entry.imports) |
| 654 | failed = False |
| 655 | fallback = None |
| 656 | |
| 657 | while not redirected and not failed: |
| 658 | # find all possible redirection options by scanning key dlls for the current imported symbol |
| 659 | imp = next(imports, None) |
| 660 | 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]] |
| 661 | |
| 662 | # no redirection options: failed to redirect dll |
| 663 | if not redirection_options: |
| 664 | failed = True |
| 665 | |
| 666 | # exactly one redirection options: use it |
| 667 | elif len(redirection_options) == 1: |
| 668 | key_dll = redirection_options[0] |
| 669 | redirected = True |
| 670 | |
| 671 | # more than one redirection options: remember one of them and proceed to next symbol |
| 672 | else: |
| 673 | fallback = redirection_options[-1] |
no test coverage detected