Skip to content

Qiling doesn't load imports imported by DLLs #1094

@srobertjames

Description

@srobertjames

*Describe the bug
On Windows PE, an executable can import functions from a DLL. Qiling handles that well.

However, DLLs can themselves import functions from other DLLs. Qiling does not handle these. Two things are missing:

  1. If alib.dll imports funcB from blib.dll, then Qiling will not load blib.dll or map into the address space.
  2. If the main executable happens to also load blib.dll, Qiling will load and map it. Even so, however, the import address table of alib.dll will be unmodified, causing an error when code in alib.dll tries to call funcB.

Sample Code
Load and run an executable which loads a DLL which in turns loads another DLL. This is very common; here's a simple example: https://github.com/utcoalition/utcctf-19/blob/master/rev-hactivate/rev.exe .

Expected behavior
In my example, rev.exe loads msvcp140.dll, which in turn loads api-ms-win-crt-heap-l1-1-0.dll and imports malloc from it. However, the IAT entry for malloc in msvcp140.dll is left empty.

Screenshots
Below is an excerpted trace of the problem:

:: 0x1003e7d9:	call	0x1003f54a
    :: 0x1003f54a:	jmp	dword ptr [0x10069188]
    [+]	ERROR: unmapped memory access at 0x69a06
    [+]	ERROR: unmapped memory access at 0x69a07
    ...
    [x]	eip	: 0x69a06
    ...
    [=]	Memory map:
    [=]	Start      End        Perm    Label          Image
    [=]	00006000 - 0000c000   rwx     [FS/GS]        
    [=]	00030000 - 00031000   rwx     [GDT]          
    [=]	00400000 - 0040a000   rwx     [PE]           rootfs/x86_windows/bin/rev.exe
    [=]	05000000 - 05001000   rwx     [heap]         
    [=]	06000000 - 0c000000   rwx     [FS/GS]        
    [=]	10000000 - 10071000   rwx     msvcp140.dll   rootfs/x86_windows/Windows/System32/msvcp140.dll
    ...
    [=]	100d0000 - 100d3000   rwx     api-ms-win-crt-heap-l1-1-0.dll   rootfs/x86_windows/Windows/

    $ r2 -A rootfs/x86_windows/Windows/System32/msvcp140.dll
    ; CODE XREF from sub.api_ms_win_crt_heap_l1_1_0.dll_malloc @ 0x1003f54a
    0x10069188      .dword 0x00069a06 ; reloc.api_ms_win_crt_heap_l1_1_0.dll_malloc

    $ r2 -A api-ms-win-crt-heap-l1-1-0.dll
    26  0x000009fd 0x100015fd GLOBAL FUNC 0    api-ms-win-crt-heap-l1-1-0.dll malloc

The relocations for msvcp140.dll are not being fixed up, but are being left in their initial state (note that unlike ELF imports, which are initially all NULL, 32 bit PE imports start with two NULLs followed by, in this case, if the Ordinal/Name flag is set to 0, bits 0-30 bits are used to hold an RVA of a Hint/Name table).

In this case, this import can be manually fixed up by simply doing ql.patch(0x10069188, p32(0x100d15fd)), since api-ms-win-crt-heap-l1-1-0.dll is anyways loaded. But note that DLLs loaded by DLLs themselves, such as ucrtbase.dll, aren't even loaded in.

Additional context
The root cause seems to be https://github.com/qilingframework/qiling/blob/master/qiling/loader/pe.py#L638 is not recursive:

 # parse directory entry import
            if self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT']].VirtualAddress != 0:
                for entry in self.pe.DIRECTORY_ENTRY_IMPORT:

To solve this, similar code to the above needs to be not only in load() but in load_dll() itself, near https://github.com/qilingframework/qiling/blob/master/qiling/loader/pe.py#L95 .

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions