*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:
- If
alib.dll imports funcB from blib.dll, then Qiling will not load blib.dll or map into the address space.
- 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 .
*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:
alib.dllimportsfuncBfromblib.dll, then Qiling will not loadblib.dllor map into the address space.blib.dll, Qiling will load and map it. Even so, however, the import address table ofalib.dllwill be unmodified, causing an error when code inalib.dlltries to callfuncB.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.exeloadsmsvcp140.dll, which in turn loadsapi-ms-win-crt-heap-l1-1-0.dlland importsmallocfrom it. However, the IAT entry formallocinmsvcp140.dllis left empty.Screenshots
Below is an excerpted trace of the problem:
The relocations for
msvcp140.dllare 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)), sinceapi-ms-win-crt-heap-l1-1-0.dllis anyways loaded. But note that DLLs loaded by DLLs themselves, such asucrtbase.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:
To solve this, similar code to the above needs to be not only in
load()but inload_dll()itself, near https://github.com/qilingframework/qiling/blob/master/qiling/loader/pe.py#L95 .