From 500bb9478034969374a7ba0c20846e019642535a Mon Sep 17 00:00:00 2001 From: nacayoshi00 Date: Mon, 24 May 2021 22:07:42 +0900 Subject: [PATCH 01/14] Fix bug: DLL address still use ImageBase address in spite of changing lib_base. --- qiling/loader/pe.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qiling/loader/pe.py b/qiling/loader/pe.py index 1fbe91728..c5f48c53b 100644 --- a/qiling/loader/pe.py +++ b/qiling/loader/pe.py @@ -99,6 +99,9 @@ def load_dll(self, dll_name, driver=False): data = bytearray(dll.get_memory_mapped_image()) cmdlines = [] + dll_base = self.dll_last_address + dll.relocate_image(dll_base) + import_symbols = {} import_table = {} for entry in dll.DIRECTORY_ENTRY_EXPORT.symbols: @@ -129,7 +132,6 @@ def load_dll(self, dll_name, driver=False): except Exception as ex: self.ql.log.exception(f'Unable to add {dll_name} import symbols') - dll_base = self.dll_last_address dll_len = self.ql.mem.align(len(bytes(data)), 0x1000) self.dll_size += dll_len self.ql.mem.map(dll_base, dll_len, info=dll_name) From 4b840c9c0f8e9eca96161615779be0ca9b2048f1 Mon Sep 17 00:00:00 2001 From: prayer Date: Tue, 1 Jun 2021 22:49:59 +0900 Subject: [PATCH 02/14] Fix: Dll address realocation, Cover execution with API Set Dll --- examples/scripts/dllscollector.bat | 31 +++--- qiling/loader/pe.py | 162 ++++++++++++++++++++++++----- qiling/os/windows/dlls/wsock32.py | 11 +- tests/test_pe.py | 10 ++ 4 files changed, 172 insertions(+), 42 deletions(-) diff --git a/examples/scripts/dllscollector.bat b/examples/scripts/dllscollector.bat index 40c9e307c..b6380c709 100644 --- a/examples/scripts/dllscollector.bat +++ b/examples/scripts/dllscollector.bat @@ -55,15 +55,7 @@ if exist %WINDIR%\SysWOW64\winhttp.dll xcopy /f /y %WINDIR%\SysWOW64\winhttp.dll if exist %WINDIR%\SysWOW64\wininet.dll xcopy /f /y %WINDIR%\SysWOW64\wininet.dll "examples\rootfs\x86_windows\Windows\System32\" if exist %WINDIR%\SysWOW64\ws2_32.dll xcopy /f /y %WINDIR%\SysWOW64\ws2_32.dll "examples\rootfs\x86_windows\Windows\System32\" if exist %WINDIR%\SysWOW64\msvcr120_clr0400.dll echo f | xcopy /f /y %WINDIR%\SysWOW64\msvcr120_clr0400.dll "examples\rootfs\x86_windows\Windows\System32\msvcr110.dll" -if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-stdio-l1-1-0.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-stdio-l1-1-0.dll "examples\rootfs\x86_windows\Windows\System32\" -if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-runtime-l1-1-0.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-runtime-l1-1-0.dll "examples\rootfs\x86_windows\Windows\System32\" -if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-math-l1-1-0.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-math-l1-1-0.dll "examples\rootfs\x86_windows\Windows\System32\" -if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-locale-l1-1-0.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-locale-l1-1-0.dll "examples\rootfs\x86_windows\Windows\System32\" -if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-heap-l1-1-0.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-heap-l1-1-0.dll "examples\rootfs\x86_windows\Windows\System32\" -if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-core-synch-l1-2-0.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-core-synch-l1-2-0.dll "examples\rootfs\x86_windows\Windows\System32\" -if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-core-fibers-l1-1-1.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-core-fibers-l1-1-1.dll "examples\rootfs\x86_windows\Windows\System32\" -if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-core-localization-l1-2-1.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-core-localization-l1-2-1.dll "examples\rootfs\x86_windows\Windows\System32\" -if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-core-sysinfo-l1-2-1.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-core-sysinfo-l1-2-1.dll "examples\rootfs\x86_windows\Windows\System32\" +xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-*.dll "examples\rootfs\x86_windows\Windows\System32\" if exist %WINDIR%\SysWOW64\shlwapi.dll xcopy /f /y %WINDIR%\SysWOW64\shlwapi.dll "examples\rootfs\x86_windows\Windows\System32\" if exist %WINDIR%\SysWOW64\setupapi.dll xcopy /f /y %WINDIR%\SysWOW64\setupapi.dll "examples\rootfs\x86_windows\Windows\System32\" if exist %WINDIR%\System32\ntoskrnl.exe xcopy /f /y %WINDIR%\System32\ntoskrnl.exe "examples\rootfs\x86_windows\Windows\System32\" @@ -79,12 +71,23 @@ if exist %WINDIR%\System32\urlmon.dll xcopy /f /y %WINDIR%\System32\urlmon.dll " if exist %WINDIR%\System32\user32.dll xcopy /f /y %WINDIR%\System32\user32.dll "examples\rootfs\x8664_windows\Windows\System32\" if exist %WINDIR%\System32\ws2_32.dll xcopy /f /y %WINDIR%\System32\ws2_32.dll "examples\rootfs\x8664_windows\Windows\System32\" if exist %WINDIR%\System32\vcruntime140.dll xcopy /f /y %WINDIR%\System32\vcruntime140.dll "examples\rootfs\x8664_windows\Windows\System32\" -if exist %WINDIR%\System32\downlevel\api-ms-win-crt-stdio-l1-1-0.dll xcopy /f /y %WINDIR%\System32\downlevel\api-ms-win-crt-stdio-l1-1-0.dll "examples\rootfs\x8664_windows\Windows\System32\" -if exist %WINDIR%\System32\downlevel\api-ms-win-crt-runtime-l1-1-0.dll xcopy /f /y %WINDIR%\System32\downlevel\api-ms-win-crt-runtime-l1-1-0.dll "examples\rootfs\x8664_windows\Windows\System32\" -if exist %WINDIR%\System32\downlevel\api-ms-win-crt-math-l1-1-0.dll xcopy /f /y %WINDIR%\System32\downlevel\api-ms-win-crt-math-l1-1-0.dll "examples\rootfs\x8664_windows\Windows\System32\" -if exist %WINDIR%\System32\downlevel\api-ms-win-crt-locale-l1-1-0.dll xcopy /f /y %WINDIR%\System32\downlevel\api-ms-win-crt-locale-l1-1-0.dll "examples\rootfs\x8664_windows\Windows\System32\" -if exist %WINDIR%\System32\downlevel\api-ms-win-crt-heap-l1-1-0.dll xcopy /f /y %WINDIR%\System32\downlevel\api-ms-win-crt-heap-l1-1-0.dll "examples\rootfs\x8664_windows\Windows\System32\" +xcopy /f /y %WINDIR%\System32\downlevel\api-*.dll "examples\rootfs\x8664_windows\Windows\System32\" if exist %WINDIR%\System32\vcruntime140d.dll xcopy /f /y %WINDIR%\System32\vcruntime140d.dll "examples\rootfs\x8664_windows\Windows\System32\" if exist %WINDIR%\System32\ucrtbased.dll xcopy /f /y %WINDIR%\System32\ucrtbased.dll "examples\rootfs\x8664_windows\Windows\System32\" +if exist %WINDIR%\System32\sechost.dll xcopy /f /y %WINDIR%\System32\sechost.dll "examples\rootfs\x8664_windows\Windows\System32\" +if exist %WINDIR%\System32\normaliz.dll xcopy /f /y %WINDIR%\System32\normaliz.dll "examples\rootfs\x8664_windows\Windows\System32\" +if exist %WINDIR%\System32\win32u.dll xcopy /f /y %WINDIR%\System32\win32u.dll "examples\rootfs\x8664_windows\Windows\System32\" +if exist %WINDIR%\System32\gdi32.dll xcopy /f /y %WINDIR%\System32\gdi32.dll "examples\rootfs\x8664_windows\Windows\System32\" +if exist %WINDIR%\System32\rpcrt4.dll xcopy /f /y %WINDIR%\System32\rpcrt4.dll "examples\rootfs\x8664_windows\Windows\System32\" +if exist %WINDIR%\System32\ucrtbase.dll xcopy /f /y %WINDIR%\System32\ucrtbase.dll "examples\rootfs\x8664_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\ucrtbase.dll xcopy /f /y %WINDIR%\SysWOW64\ucrtbase.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\win32u.dll xcopy /f /y %WINDIR%\SysWOW64\win32u.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\sechost.dll xcopy /f /y %WINDIR%\SysWOW64\sechost.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\msvcp_win.dll xcopy /f /y %WINDIR%\SysWOW64\msvcp_win.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\combase.dll xcopy /f /y %WINDIR%\SysWOW64\combase.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\cfgmgr32.dll xcopy /f /y %WINDIR%\SysWOW64\cfgmgr32.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\normaliz.dll xcopy /f /y %WINDIR%\SysWOW64\normaliz.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\bcrypt.dll xcopy /f /y %WINDIR%\SysWOW64\bcrypt.dll "examples\rootfs\x86_windows\Windows\System32\" + exit /b diff --git a/qiling/loader/pe.py b/qiling/loader/pe.py index c5f48c53b..8a5d92739 100644 --- a/qiling/loader/pe.py +++ b/qiling/loader/pe.py @@ -96,12 +96,14 @@ def load_dll(self, dll_name, driver=False): self.ql.log.warning(f'Warnings while loading {path}:') for warning in warnings: self.ql.log.warning(f' - {warning}') + + # [Room for Improvement] too much time when kernelbase.dll is loaded. + self.ql.log.debug('relocate {}, {:x}'.format(dll_name, self.dll_last_address)) + dll.relocate_image(self.dll_last_address) + data = bytearray(dll.get_memory_mapped_image()) cmdlines = [] - dll_base = self.dll_last_address - dll.relocate_image(dll_base) - import_symbols = {} import_table = {} for entry in dll.DIRECTORY_ENTRY_EXPORT.symbols: @@ -132,6 +134,7 @@ def load_dll(self, dll_name, driver=False): except Exception as ex: self.ql.log.exception(f'Unable to add {dll_name} import symbols') + dll_base = self.dll_last_address dll_len = self.ql.mem.align(len(bytes(data)), 0x1000) self.dll_size += dll_len self.ql.mem.map(dll_base, dll_len, info=dll_name) @@ -378,7 +381,7 @@ def init_ki_user_shared_data(self): shared_user_data_len = self.align(ctypes.sizeof(KUSER_SHARED_DATA), 0x1000) self.ql.uc.mem_map(KI_USER_SHARED_DATA, shared_user_data_len) self.ql.mem.write(KI_USER_SHARED_DATA, bytes(shared_user_data)) - + class QlLoaderPE(QlLoader, Process): def __init__(self, ql): @@ -577,28 +580,93 @@ def load(self): sys_dlls = self.sys_dlls for each in sys_dlls: super().load_dll(each, self.is_driver) + # 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: - dll_name = str(entry.dll.lower(), 'utf-8', 'ignore') - super().load_dll(entry.dll, self.is_driver) - for imp in entry.imports: - # fix IAT - # ql.log.info(imp.name) - # ql.log.info(self.import_address_table[imp.name]) - if imp.name: - try: - addr = self.import_address_table[dll_name][imp.name] - except KeyError: - self.ql.log.debug("Error in loading function %s" % imp.name.decode()) - else: - addr = self.import_address_table[dll_name][imp.ordinal] - - if self.ql.archtype == QL_ARCH.X86: - address = self.ql.pack32(addr) + # load all DLL files linked from the target PE file recursively and make IAT of all dlls. + load_pe_dll_list = ['target_pe'] + list(filter(lambda x: '.dll' in x, list(map(lambda x: x[3], self.ql.mem.map_info)))) + load_failed_dll_list = [] + + while len(load_pe_dll_list) != 0: + pe_dll_name = load_pe_dll_list.pop(0) + if pe_dll_name == 'target_pe': + pe_dll = self.pe + else: + path = os.path.join(self.ql.rootfs, self.ql.dlls, pe_dll_name) + pe_dll = pefile.PE(path) + + if pe_dll.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT']].VirtualAddress != 0: + for entry in pe_dll.DIRECTORY_ENTRY_IMPORT: + export_dll_name = str(entry.dll.lower(), 'utf-8', 'ignore') + if export_dll_name in load_failed_dll_list: + continue + + # check if the dll is not API Set dll + if (export_dll_name[0:4] != 'api-') and (export_dll_name[0:4] != 'ext-'): + # check if the dll is not already loaded and the dll is not already in load_pe_dll_list + loaded_dll_list = list(map(lambda x: x[3], self.ql.mem.map_info)) + if (export_dll_name not in load_pe_dll_list) and (export_dll_name not in loaded_dll_list): + super().load_dll(entry.dll, self.is_driver) + load_pe_dll_list.append(export_dll_name) else: - address = self.ql.pack64(addr) - self.ql.mem.write(imp.address, address) + for imp in entry.imports: + if export_dll_name in load_failed_dll_list: + continue + export_real_dll_name, import_name = self._get_export_symbol_from_api_dll(export_dll_name, imp.name.decode('utf-8')) + # if _get_export_symbol_from_api_dll can't load API Set dll, dll_name=None, import_name=None + if (export_real_dll_name == None) and (import_name == None): + load_failed_dll_list.append(export_dll_name) + continue + loaded_dll_list = list(map(lambda x: x[3], self.ql.mem.map_info)) + if (export_real_dll_name not in load_pe_dll_list) and (export_real_dll_name not in loaded_dll_list): + super().load_dll(export_real_dll_name.encode('utf-8'), self.is_driver) + load_pe_dll_list.append(export_real_dll_name) + + # set import address of target PE and dll file from the IAT + loaded_pe_dll_list = ['target_pe'] + list(filter(lambda x: '.dll' in x, list(map(lambda x: x[3], self.ql.mem.map_info)))) + + for pe_dll_name in loaded_pe_dll_list: + if pe_dll_name == 'target_pe': + pe_dll = self.pe + lib_image_base = pe_dll.OPTIONAL_HEADER.ImageBase + lib_base = list(filter(lambda x: x[3] == '[PE]', self.ql.mem.map_info))[0][0] + else: + path = os.path.join(self.ql.rootfs, self.ql.dlls, pe_dll_name) + pe_dll = pefile.PE(path) + lib_image_base = pe_dll.OPTIONAL_HEADER.ImageBase + lib_base = list(filter(lambda x: x[3] == pe_dll_name, self.ql.mem.map_info))[0][0] + + if pe_dll.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT']].VirtualAddress != 0: + for entry in pe_dll.DIRECTORY_ENTRY_IMPORT: + for imp in entry.imports: + if imp.name == None: + continue + import_name = imp.name.decode('utf-8') + dll_name = str(entry.dll.lower(), 'utf-8', 'ignore') + if dll_name in load_failed_dll_list: + continue + + if (dll_name[0:4] == 'api-') or (dll_name[0:4] == 'ext-'): + dll_name, import_name = self._get_export_symbol_from_api_dll(dll_name, import_name) + if import_name != None: + try: + # encode('utf-8') because import name of import_address_table is byte type (not string) + addr = self.import_address_table[dll_name][import_name.encode('utf-8')] + except KeyError: + self.ql.log.debug("Error in loading function %s" % import_name) + else: + if dll_name != None: + addr = self.import_address_table[dll_name][imp.ordinal] + # if _get_export_symbol_from_api_dll can't load API Set dll, dll_name=None, import_name=None + else: + load_failed_dll_list.append(dll_name) + continue + + if self.ql.archtype == QL_ARCH.X86: + address = self.ql.pack32(addr) + else: + address = self.ql.pack64(addr) + + self.ql.mem.write(imp.address-lib_image_base+lib_base, address) self.ql.log.debug("Done with loading %s" % self.path) self.ql.os.entry_point = self.entry_point @@ -629,3 +697,49 @@ def load(self): # move entry_point to ql.os self.ql.os.entry_point = self.entry_point self.init_sp = self.ql.reg.arch_sp + + def _get_export_symbol_from_api_dll(self, api_dll_name, target_symbol): + """ + API set dll (https://docs.microsoft.com/en-us/windows/win32/apiindex/windows-apisets) loader. + The function extract actual export symbol and DLL name from API set dll(likes 'api-ms-xxxx.dll'). + + Args: + api_dll_name (str): API Set DLL name + target_symbol (str): The symbol looking for. + Return: + dll_name (str): actual DLL name. + export_symbol (str): Actual symbol name. + """ + + def _get_string_from_pe(api_dll, target_symbol): + offset = 0 + string = '' + dll_base = api_dll.OPTIONAL_HEADER.ImageBase + + export_symbol_list = list(filter(lambda x: x.name == target_symbol.encode('utf-8'), api_dll.DIRECTORY_ENTRY_EXPORT.symbols)) + if len(export_symbol_list) == 0: + self.ql.log.debug("Error: can't find symbol from API Set dll (symbol: %s, apiset dll: %s" % (api_dll, target_symbol)) + return '' + + while True: + char = api_dll.get_data(export_symbol_list[0].address+offset, 1) + if char == b'\x00': + break + + string += char.decode('utf-8') + offset += 1 + + return string + + try: + api_dll = pefile.PE(os.path.join(self.ql.rootfs, self.ql.dlls, api_dll_name)) + except: + self.ql.log.warning('Failed to load API dll %s' % (api_dll_name)) + return None, None + + # result of _get_string_from_pe has 2 types, "kernel32.GetPriorityClass" and "advapi32.dll.OpenProcessToken" + # therefore, both types need to be supported. + dll_name, export_symbol = _get_string_from_pe(api_dll, target_symbol).rsplit('.', 1) + if dll_name[-4:] == '.dll': + return dll_name, export_symbol + return dll_name+'.dll', export_symbol \ No newline at end of file diff --git a/qiling/os/windows/dlls/wsock32.py b/qiling/os/windows/dlls/wsock32.py index 351960b87..4e7d2f2f1 100644 --- a/qiling/os/windows/dlls/wsock32.py +++ b/qiling/os/windows/dlls/wsock32.py @@ -60,10 +60,13 @@ def hook_connect(ql, address, params): ql.log.debug("sockaddr sin_family unhandled variant") return 0 - ql.log.debug(f"0x{params['name']:08x}: sockaddr_in{6 if sin_family == 0x17 else ''}", - f"{{sin_family=0x{sin_family:02x}, sin_port={sin_port}, sin_addr={sin_addr}}}", - sep="", - ) + ql.log.debug("0x{:08x}: sockaddr_in{} sin_family=0x{:02x}, sin_port={}, sin_addr={}".format( + params['name'], + ('6' if sin_family == 0x17 else ''), + sin_family, + sin_port, + sin_addr + )) return 0 diff --git a/tests/test_pe.py b/tests/test_pe.py index 7bb6cdbf2..20532d207 100644 --- a/tests/test_pe.py +++ b/tests/test_pe.py @@ -475,5 +475,15 @@ def test_pe_win_x8664_libcache(self): del ql + def test_pe_win_x8664_relocate_dll_image_and_api_set_dii(self): + # First force the cache to be recreated + ql = Qiling(["../examples/rootfs/x8664_windows/bin/api_set_dll_demo.exe"], + "../examples/rootfs/x8664_windows", + verbose=QL_VERBOSE.DEFAULT) + ql.run() + del ql + + + if __name__ == "__main__": unittest.main() From af6bc8a96e58b6dbae85d27b08a37f44f9ddf5eb Mon Sep 17 00:00:00 2001 From: prayer Date: Wed, 2 Jun 2021 14:33:37 +0900 Subject: [PATCH 03/14] Fix for run test error --- examples/scripts/dllscollector.bat | 1 + qiling/loader/pe.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/examples/scripts/dllscollector.bat b/examples/scripts/dllscollector.bat index b6380c709..175cdad67 100644 --- a/examples/scripts/dllscollector.bat +++ b/examples/scripts/dllscollector.bat @@ -89,5 +89,6 @@ if exist %WINDIR%\SysWOW64\combase.dll xcopy /f /y %WINDIR%\SysWOW64\combase.dll if exist %WINDIR%\SysWOW64\cfgmgr32.dll xcopy /f /y %WINDIR%\SysWOW64\cfgmgr32.dll "examples\rootfs\x86_windows\Windows\System32\" if exist %WINDIR%\SysWOW64\normaliz.dll xcopy /f /y %WINDIR%\SysWOW64\normaliz.dll "examples\rootfs\x86_windows\Windows\System32\" if exist %WINDIR%\SysWOW64\bcrypt.dll xcopy /f /y %WINDIR%\SysWOW64\bcrypt.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\bcrypt.dll xcopy /f /y %WINDIR%\SysWOW64\sspicli.dll "examples\rootfs\x86_windows\Windows\System32\" exit /b diff --git a/qiling/loader/pe.py b/qiling/loader/pe.py index 8a5d92739..8c32a8c77 100644 --- a/qiling/loader/pe.py +++ b/qiling/loader/pe.py @@ -611,6 +611,8 @@ def load(self): for imp in entry.imports: if export_dll_name in load_failed_dll_list: continue + if imp.name == None: + continue export_real_dll_name, import_name = self._get_export_symbol_from_api_dll(export_dll_name, imp.name.decode('utf-8')) # if _get_export_symbol_from_api_dll can't load API Set dll, dll_name=None, import_name=None if (export_real_dll_name == None) and (import_name == None): From 5210c5537cc11a2000bf0de98bc4a0bb867ee88d Mon Sep 17 00:00:00 2001 From: prayer Date: Wed, 2 Jun 2021 22:56:05 +0900 Subject: [PATCH 04/14] add test exe source, add dll that need for the test --- examples/scripts/dllscollector.bat | 4 +- .../api_set_dll_demo/api_set_dll_demo.sln | 31 ++++ .../api_set_dll_demo/api_set_dll_demo.cpp | 44 ++++++ .../api_set_dll_demo/api_set_dll_demo.vcxproj | 149 ++++++++++++++++++ .../api_set_dll_demo.vcxproj.filters | 22 +++ .../api_set_dll_demo.vcxproj.user | 4 + qiling/loader/pe.py | 7 +- 7 files changed, 258 insertions(+), 3 deletions(-) create mode 100644 examples/src/windows/api_set_dll_demo/api_set_dll_demo.sln create mode 100644 examples/src/windows/api_set_dll_demo/api_set_dll_demo/api_set_dll_demo.cpp create mode 100644 examples/src/windows/api_set_dll_demo/api_set_dll_demo/api_set_dll_demo.vcxproj create mode 100644 examples/src/windows/api_set_dll_demo/api_set_dll_demo/api_set_dll_demo.vcxproj.filters create mode 100644 examples/src/windows/api_set_dll_demo/api_set_dll_demo/api_set_dll_demo.vcxproj.user diff --git a/examples/scripts/dllscollector.bat b/examples/scripts/dllscollector.bat index 175cdad67..e322a3b93 100644 --- a/examples/scripts/dllscollector.bat +++ b/examples/scripts/dllscollector.bat @@ -89,6 +89,8 @@ if exist %WINDIR%\SysWOW64\combase.dll xcopy /f /y %WINDIR%\SysWOW64\combase.dll if exist %WINDIR%\SysWOW64\cfgmgr32.dll xcopy /f /y %WINDIR%\SysWOW64\cfgmgr32.dll "examples\rootfs\x86_windows\Windows\System32\" if exist %WINDIR%\SysWOW64\normaliz.dll xcopy /f /y %WINDIR%\SysWOW64\normaliz.dll "examples\rootfs\x86_windows\Windows\System32\" if exist %WINDIR%\SysWOW64\bcrypt.dll xcopy /f /y %WINDIR%\SysWOW64\bcrypt.dll "examples\rootfs\x86_windows\Windows\System32\" -if exist %WINDIR%\SysWOW64\bcrypt.dll xcopy /f /y %WINDIR%\SysWOW64\sspicli.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\sspicli.dll xcopy /f /y %WINDIR%\SysWOW64\sspicli.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\bcryptprimitives.dll xcopy /f /y %WINDIR%\SysWOW64\bcryptprimitives.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\cryptbase.dll xcopy /f /y %WINDIR%\SysWOW64\cryptbase.dll "examples\rootfs\x86_windows\Windows\System32\" exit /b diff --git a/examples/src/windows/api_set_dll_demo/api_set_dll_demo.sln b/examples/src/windows/api_set_dll_demo/api_set_dll_demo.sln new file mode 100644 index 000000000..a78a901da --- /dev/null +++ b/examples/src/windows/api_set_dll_demo/api_set_dll_demo.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31129.286 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "api_set_dll_demo", "api_set_dll_demo\api_set_dll_demo.vcxproj", "{A8CD06FD-0C9F-4A64-85C1-305F8A52F427}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A8CD06FD-0C9F-4A64-85C1-305F8A52F427}.Debug|x64.ActiveCfg = Debug|x64 + {A8CD06FD-0C9F-4A64-85C1-305F8A52F427}.Debug|x64.Build.0 = Debug|x64 + {A8CD06FD-0C9F-4A64-85C1-305F8A52F427}.Debug|x86.ActiveCfg = Debug|Win32 + {A8CD06FD-0C9F-4A64-85C1-305F8A52F427}.Debug|x86.Build.0 = Debug|Win32 + {A8CD06FD-0C9F-4A64-85C1-305F8A52F427}.Release|x64.ActiveCfg = Release|x64 + {A8CD06FD-0C9F-4A64-85C1-305F8A52F427}.Release|x64.Build.0 = Release|x64 + {A8CD06FD-0C9F-4A64-85C1-305F8A52F427}.Release|x86.ActiveCfg = Release|Win32 + {A8CD06FD-0C9F-4A64-85C1-305F8A52F427}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4B512553-FA45-431F-AF2F-A8ED03607A35} + EndGlobalSection +EndGlobal diff --git a/examples/src/windows/api_set_dll_demo/api_set_dll_demo/api_set_dll_demo.cpp b/examples/src/windows/api_set_dll_demo/api_set_dll_demo/api_set_dll_demo.cpp new file mode 100644 index 000000000..82cce6116 --- /dev/null +++ b/examples/src/windows/api_set_dll_demo/api_set_dll_demo/api_set_dll_demo.cpp @@ -0,0 +1,44 @@ +#define _CRT_SECURE_NO_WARNINGS +#include +#include +#include + +int main() { + + char server_ip_addr[32] = "127.0.0.1"; + int port_number = 19090; + char send_buf[256] = "send test text\n"; + char recv_buf[256]; + + WSADATA wsa_data; + if (WSAStartup(MAKEWORD(2, 0), &wsa_data) != 0) { + printf("Initialize winsock failed (WSAStartup)\n"); + } + + struct sockaddr_in dst_addr; + memset(&dst_addr, 0, sizeof(dst_addr)); + dst_addr.sin_port = htons(port_number); + dst_addr.sin_family = AF_INET; + + inet_pton(dst_addr.sin_family, server_ip_addr, &dst_addr.sin_addr.s_addr); + + int dst_socket = socket(AF_INET, SOCK_STREAM, 0); + + if (connect(dst_socket, (struct sockaddr*)&dst_addr, sizeof(dst_addr))) { + printf("error: serverIP\n"); + exit(0); + } + + printf("accepted: serverIP\n");; + + send(dst_socket, send_buf, 256, 0); + recv(dst_socket, recv_buf, 256, 0); + + printf("recv: %s\n", recv_buf); + + + closesocket(dst_socket); + + WSACleanup(); + return 0; +} \ No newline at end of file diff --git a/examples/src/windows/api_set_dll_demo/api_set_dll_demo/api_set_dll_demo.vcxproj b/examples/src/windows/api_set_dll_demo/api_set_dll_demo/api_set_dll_demo.vcxproj new file mode 100644 index 000000000..a4530c265 --- /dev/null +++ b/examples/src/windows/api_set_dll_demo/api_set_dll_demo/api_set_dll_demo.vcxproj @@ -0,0 +1,149 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {a8cd06fd-0c9f-4a64-85c1-305f8a52f427} + apisetdlldemo + 10.0 + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + ws2_32.lib;%(AdditionalDependencies) + + + + + + + + + \ No newline at end of file diff --git a/examples/src/windows/api_set_dll_demo/api_set_dll_demo/api_set_dll_demo.vcxproj.filters b/examples/src/windows/api_set_dll_demo/api_set_dll_demo/api_set_dll_demo.vcxproj.filters new file mode 100644 index 000000000..9645a0a69 --- /dev/null +++ b/examples/src/windows/api_set_dll_demo/api_set_dll_demo/api_set_dll_demo.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/examples/src/windows/api_set_dll_demo/api_set_dll_demo/api_set_dll_demo.vcxproj.user b/examples/src/windows/api_set_dll_demo/api_set_dll_demo/api_set_dll_demo.vcxproj.user new file mode 100644 index 000000000..0f14913f3 --- /dev/null +++ b/examples/src/windows/api_set_dll_demo/api_set_dll_demo/api_set_dll_demo.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/qiling/loader/pe.py b/qiling/loader/pe.py index 8c32a8c77..b6e3f547d 100644 --- a/qiling/loader/pe.py +++ b/qiling/loader/pe.py @@ -620,8 +620,11 @@ def load(self): continue loaded_dll_list = list(map(lambda x: x[3], self.ql.mem.map_info)) if (export_real_dll_name not in load_pe_dll_list) and (export_real_dll_name not in loaded_dll_list): - super().load_dll(export_real_dll_name.encode('utf-8'), self.is_driver) - load_pe_dll_list.append(export_real_dll_name) + if os.path.exists(os.path.join(self.ql.rootfs, self.ql.dlls, export_real_dll_name)): + super().load_dll(export_real_dll_name.encode('utf-8'), self.is_driver) + load_pe_dll_list.append(export_real_dll_name) + else: + self.ql.log.warning('Failed to load dll %s' % (export_real_dll_name)) # set import address of target PE and dll file from the IAT loaded_pe_dll_list = ['target_pe'] + list(filter(lambda x: '.dll' in x, list(map(lambda x: x[3], self.ql.mem.map_info)))) From b50f3eca5d1662bc5c7aaa508f4676df79c77add Mon Sep 17 00:00:00 2001 From: prayer Date: Fri, 4 Jun 2021 23:45:12 +0900 Subject: [PATCH 05/14] limit copy dll file. --- examples/scripts/dllscollector.bat | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/examples/scripts/dllscollector.bat b/examples/scripts/dllscollector.bat index e322a3b93..81ac1d113 100644 --- a/examples/scripts/dllscollector.bat +++ b/examples/scripts/dllscollector.bat @@ -55,7 +55,15 @@ if exist %WINDIR%\SysWOW64\winhttp.dll xcopy /f /y %WINDIR%\SysWOW64\winhttp.dll if exist %WINDIR%\SysWOW64\wininet.dll xcopy /f /y %WINDIR%\SysWOW64\wininet.dll "examples\rootfs\x86_windows\Windows\System32\" if exist %WINDIR%\SysWOW64\ws2_32.dll xcopy /f /y %WINDIR%\SysWOW64\ws2_32.dll "examples\rootfs\x86_windows\Windows\System32\" if exist %WINDIR%\SysWOW64\msvcr120_clr0400.dll echo f | xcopy /f /y %WINDIR%\SysWOW64\msvcr120_clr0400.dll "examples\rootfs\x86_windows\Windows\System32\msvcr110.dll" -xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-*.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-stdio-l1-1-0.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-stdio-l1-1-0.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-runtime-l1-1-0.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-runtime-l1-1-0.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-math-l1-1-0.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-math-l1-1-0.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-locale-l1-1-0.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-locale-l1-1-0.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-heap-l1-1-0.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-crt-heap-l1-1-0.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-core-synch-l1-2-0.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-core-synch-l1-2-0.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-core-fibers-l1-1-1.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-core-fibers-l1-1-1.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-core-localization-l1-2-1.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-core-localization-l1-2-1.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\downlevel\api-ms-win-core-sysinfo-l1-2-1.dll xcopy /f /y %WINDIR%\SysWOW64\downlevel\api-ms-win-core-sysinfo-l1-2-1.dll "examples\rootfs\x86_windows\Windows\System32\" if exist %WINDIR%\SysWOW64\shlwapi.dll xcopy /f /y %WINDIR%\SysWOW64\shlwapi.dll "examples\rootfs\x86_windows\Windows\System32\" if exist %WINDIR%\SysWOW64\setupapi.dll xcopy /f /y %WINDIR%\SysWOW64\setupapi.dll "examples\rootfs\x86_windows\Windows\System32\" if exist %WINDIR%\System32\ntoskrnl.exe xcopy /f /y %WINDIR%\System32\ntoskrnl.exe "examples\rootfs\x86_windows\Windows\System32\" @@ -71,10 +79,13 @@ if exist %WINDIR%\System32\urlmon.dll xcopy /f /y %WINDIR%\System32\urlmon.dll " if exist %WINDIR%\System32\user32.dll xcopy /f /y %WINDIR%\System32\user32.dll "examples\rootfs\x8664_windows\Windows\System32\" if exist %WINDIR%\System32\ws2_32.dll xcopy /f /y %WINDIR%\System32\ws2_32.dll "examples\rootfs\x8664_windows\Windows\System32\" if exist %WINDIR%\System32\vcruntime140.dll xcopy /f /y %WINDIR%\System32\vcruntime140.dll "examples\rootfs\x8664_windows\Windows\System32\" -xcopy /f /y %WINDIR%\System32\downlevel\api-*.dll "examples\rootfs\x8664_windows\Windows\System32\" +if exist %WINDIR%\System32\downlevel\api-ms-win-crt-stdio-l1-1-0.dll xcopy /f /y %WINDIR%\System32\downlevel\api-ms-win-crt-stdio-l1-1-0.dll "examples\rootfs\x8664_windows\Windows\System32\" +if exist %WINDIR%\System32\downlevel\api-ms-win-crt-runtime-l1-1-0.dll xcopy /f /y %WINDIR%\System32\downlevel\api-ms-win-crt-runtime-l1-1-0.dll "examples\rootfs\x8664_windows\Windows\System32\" +if exist %WINDIR%\System32\downlevel\api-ms-win-crt-math-l1-1-0.dll xcopy /f /y %WINDIR%\System32\downlevel\api-ms-win-crt-math-l1-1-0.dll "examples\rootfs\x8664_windows\Windows\System32\" +if exist %WINDIR%\System32\downlevel\api-ms-win-crt-locale-l1-1-0.dll xcopy /f /y %WINDIR%\System32\downlevel\api-ms-win-crt-locale-l1-1-0.dll "examples\rootfs\x8664_windows\Windows\System32\" +if exist %WINDIR%\System32\downlevel\api-ms-win-crt-heap-l1-1-0.dll xcopy /f /y %WINDIR%\System32\downlevel\api-ms-win-crt-heap-l1-1-0.dll "examples\rootfs\x8664_windows\Windows\System32\" if exist %WINDIR%\System32\vcruntime140d.dll xcopy /f /y %WINDIR%\System32\vcruntime140d.dll "examples\rootfs\x8664_windows\Windows\System32\" if exist %WINDIR%\System32\ucrtbased.dll xcopy /f /y %WINDIR%\System32\ucrtbased.dll "examples\rootfs\x8664_windows\Windows\System32\" - if exist %WINDIR%\System32\sechost.dll xcopy /f /y %WINDIR%\System32\sechost.dll "examples\rootfs\x8664_windows\Windows\System32\" if exist %WINDIR%\System32\normaliz.dll xcopy /f /y %WINDIR%\System32\normaliz.dll "examples\rootfs\x8664_windows\Windows\System32\" if exist %WINDIR%\System32\win32u.dll xcopy /f /y %WINDIR%\System32\win32u.dll "examples\rootfs\x8664_windows\Windows\System32\" @@ -93,4 +104,7 @@ if exist %WINDIR%\SysWOW64\sspicli.dll xcopy /f /y %WINDIR%\SysWOW64\sspicli.dll if exist %WINDIR%\SysWOW64\bcryptprimitives.dll xcopy /f /y %WINDIR%\SysWOW64\bcryptprimitives.dll "examples\rootfs\x86_windows\Windows\System32\" if exist %WINDIR%\SysWOW64\cryptbase.dll xcopy /f /y %WINDIR%\SysWOW64\cryptbase.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\System32\downlevel\api-ms-win-core-errorhandling-l1-1-0.dll xcopy /f /y %WINDIR%\System32\downlevel\api-ms-win-core-errorhandling-l1-1-0.dll "examples\rootfs\x8664_windows\Windows\System32\" +if exist %WINDIR%\System32\downlevel\api-ms-win-core-synch-l1-1-0.dll xcopy /f /y %WINDIR%\System32\downlevel\api-ms-win-core-synch-l1-1-0.dll "examples\rootfs\x8664_windows\Windows\System32\" + exit /b From bb1b99bb2fe4b701cbd59ad18c61714dabe0a1d0 Mon Sep 17 00:00:00 2001 From: prayer Date: Wed, 14 Jul 2021 00:10:50 +0900 Subject: [PATCH 06/14] add dll resolve hook --- qiling/os/windows/resolve_dll.py | 372 +++++++++++++++++++++++++++++++ qiling/os/windows/windows.py | 4 + 2 files changed, 376 insertions(+) create mode 100644 qiling/os/windows/resolve_dll.py diff --git a/qiling/os/windows/resolve_dll.py b/qiling/os/windows/resolve_dll.py new file mode 100644 index 000000000..d02cd8a59 --- /dev/null +++ b/qiling/os/windows/resolve_dll.py @@ -0,0 +1,372 @@ +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + +from qiling import Qiling +from qiling.const import QL_ARCH, QL_INTERCEPT +from qiling.exception import QlErrorSyscallError, QlErrorSyscallNotFound + +import pefile +from capstone import* +from unicorn import * +from lark import Lark, ast_utils, Transformer, v_args + + +def resolve_symbol(ql: Qiling, address: int, size): + reg = ql.reg.save() + + # Check the address to jump is in memory map. If not, check if it needs to load additional dll. + buf = ql.mem.read(address, size) + if ql.archtype == QL_ARCH.X8664: + md = Cs(CS_ARCH_X86, CS_MODE_64) + else: + md = Cs(CS_ARCH_X86, CS_MODE_32) + + + op = list(md.disasm(buf, address))[0] + + jump_address = -1 + jump_pointer_address = -1 + if op.mnemonic in ['jmp', 'call']: + print(op.mnemonic, op.op_str) + _arch_utils.parser.parse("{} {}".format(op.mnemonic, op.op_str)) + jump_pointer = _arch_utils.parser.calculate(ql, size)['operand'] + + if jump_pointer != None: + if "address" in jump_pointer.keys(): + jump_address = jump_pointer['address'] + elif "pointer" in jump_pointer.keys(): + jump_pointer_address = jump_pointer['pointer'] + jump_address = jump_pointer['value'] + + # check if library is already imported or not. + if (not is_in_allocated_memory_address(ql, jump_address)) and (jump_address != -1) and (jump_pointer_address != -1): + #print('{:016x}: _is_in_allocated_memory_address'.format(jump_pointer_address)) + load_additional_dll(ql, jump_pointer_address) + + + + +def is_in_allocated_memory_address(ql, address): + for mi in ql.mem.map_info: + if (mi[0] < address) and (address < mi[1]): + return True + return False + + +def load_additional_dll(ql, import_address): + """ + Load additional dll that is not loaded at the begining of qiling run. + The function may be used for the dll, which is called from other dll that have been already loaded. + + Args: + ql(obj): qiling object. + import_address(int): external address of the function called by unloaded dll.\ + Return: + boolean: True if Add dll to memory successfully, otherwise False. + """ + + dll_list = {} + # ql.mem.map_info example: [140737221971968, 140737222746112, 7, 'kernel32.dll'] + map_info = ql.mem.map_info + + dll_last_address = 0x0 + + for mi in map_info: + dll_name = mi[3] + if '.dll' in dll_name: + dll_list[dll_name] = { + 'dll': dll_name, + 'base': mi[0], + 'end': mi[1], + } + dll_last_address = mi[1] + + + for dll_name in dll_list.keys(): + if (dll_list[dll_name]['base'] < import_address) and (import_address < dll_list[dll_name]['end']): + target_dll_name = dll_name + break + else: + return + + print('Windows/system32/{}'.format(target_dll_name)) + + target_dll_bin = pefile.PE(ql.rootfs+'/Windows/system32/{}'.format(target_dll_name)) + target_dll_image_base = target_dll_bin.OPTIONAL_HEADER.ImageBase + + target_symbol = None + for entry_import in target_dll_bin.DIRECTORY_ENTRY_IMPORT: + for entry_import_symbol in entry_import.imports: + if (entry_import_symbol.address - target_dll_image_base + dll_list[dll_name]['base']) == import_address: + target_symbol = entry_import_symbol.name.decode('utf-8') + + # Go to proccess of loading additional dll from import_address if the import symbol exists. + export_dll_name = entry_import.dll.decode('utf-8') + + # The case of API Set dll + # ref: https://docs.microsoft.com/en-us/windows/win32/apiindex/windows-apisets + if (export_dll_name[0:4] == 'api-') or (export_dll_name[0:4] == 'ext-'): + export_dll_name, target_symbol = get_export_symbol_from_api_dll(ql, export_dll_name, target_symbol) + + # *Additional dll must not be loaded because import symbol is not resolved, but the case of API set dll, export_dll might be loaded.* + if (export_dll_name not in dll_list.keys()): + export_dll_bin = pefile.PE(ql.rootfs+'/Windows/system32/{}'.format(export_dll_name)) + export_dll_base = dll_last_address + + export_dll_bin.parse_data_directories() + export_dll_bin.relocate_image(export_dll_base) + export_dll_data = bytearray(export_dll_bin.get_memory_mapped_image()) + + export_dll_len = ql.mem.align(len(bytes(export_dll_data)), 0x1000) + ql.mem.map(export_dll_base, export_dll_len, info=export_dll_name) + ql.mem.write(export_dll_base, bytes(export_dll_data)) + + resolve_import_dll_address(ql, target_dll_name, export_dll_name, target_symbol, import_address) + + return True + + return False + +def resolve_import_dll_address(ql, import_dll_name, export_dll_name, target_symbol, import_address): + """ + Make IAT of import address. + + Args: + ql(obj): qiling object. + import_dll_name(str): Dll to import. it must be loaded already. + export_dll_name(str): Dll to export. + Return: + boolean: True if Add dll to memory successfully, otherwise False. + """ + + dll_list = {} + # ql.mem.map_info example: [140737221971968, 140737222746112, 7, 'kernel32.dll'] + map_info = ql.mem.map_info + + for mi in map_info: + dll_name = mi[3] + if '.dll' in dll_name: + dll_list[dll_name] = { + 'dll': dll_name, + 'base': mi[0], + } + + if import_dll_name not in dll_list.keys(): + return False + + # dll_name is the dll imported by the binary + print('[+] {}'.format(import_dll_name)) + import_dll_bin = pefile.PE(os.path.join(ql.rootfs, 'Windows/system32/{}'.format(import_dll_name))) + import_dll_base = dll_list[import_dll_name]['base'] + import_dll_image_base = import_dll_bin.OPTIONAL_HEADER.ImageBase + + # split \x00 since section.Name sometime like this b'.text\x00\x00\x00' + rdata_section = list(filter(lambda x: x.Name.decode('utf-8').split('\x00')[0] == '.rdata', import_dll_bin.sections)) + + # entry_import_dll_name is the dll described as import dll in dll_name + if import_dll_bin.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT']].VirtualAddress == 0: + return False + + # export dll is already loaded at _load_additional_dll. + export_dll_bin = pefile.PE(os.path.join(ql.rootfs, 'Windows/system32/{}'.format(export_dll_name))) + export_dll_base = dll_list[export_dll_name]['base'] + + export_symbol_list = list(filter(lambda x: x.name.decode('utf-8') == target_symbol, export_dll_bin.DIRECTORY_ENTRY_EXPORT.symbols)) + if len(export_symbol_list) == 0: + return False + + print('[+] import address: {:016x}, import_dll_base: {:016x}, import_dll_image_base: {:016x}'.format(import_address, import_dll_base, import_dll_image_base)) + print('[+] memory write {:016x} -> {}'.format( + import_address, + (export_symbol_list[0].address+export_dll_base).to_bytes(8,'little'))) + + ql.mem.write( + import_address, + (export_symbol_list[0].address+export_dll_base).to_bytes(8,'little') + ) + + return True + +def get_export_symbol_from_api_dll(ql, api_dll_name, target_symbol): + def _get_string_from_pe(api_dll, target_symbol): + offset = 0 + string = '' + dll_base = api_dll.OPTIONAL_HEADER.ImageBase + + export_symbol_list = list(filter(lambda x: x.name.decode('utf-8') == target_symbol, api_dll.DIRECTORY_ENTRY_EXPORT.symbols)) + if len(export_symbol_list) == 0: + return '' + + while True: + print(hex(export_symbol_list[0].address)) + char = api_dll.get_data(export_symbol_list[0].address+offset, 1) + if char == b'\x00': + break + + string += char.decode('utf-8') + offset += 1 + return string + + print(os.path.join(ql.rootfs, 'Windows/system32/{}'.format(api_dll_name))) + api_dll = pefile.PE(os.path.join(ql.rootfs, 'Windows/system32/{}'.format(api_dll_name))) + + dll_name, export_symbol = _get_string_from_pe(api_dll, target_symbol).split('.') + + print(dll_name, export_symbol) + + return dll_name+'.dll', export_symbol + + +class OpcodeCalculateTransformer(Transformer): + def __init__(self, ql, op_size): + self.ql = ql + self.op_size = op_size + + self.reg = self.ql.reg.save() + self.pointer_address = [] + + self.do_calculate = False + return + + # def start(self, args): + # #args example: {'opcode': 'mov', 'operand': 0} + # if isinstance(args[0], int): + # return args, self.pointer_address + # else: + # return args[0], self.pointer_address + + def start(self, args): + # opcode only + if len(args) == 1: + return args[0] + else: + return {"opcode": args[0]["opcode"], "operand":args[1]["operand"]} + + def operand(self, args): + return {"operand": args[0]} + + def connection(self, args): + return args + + def element(self, args): + return args[0] + + def cast(self, args): + if args[0].value == 'byte': + return args[1] & 0xff + elif args[0].value == 'dword': + return args[1] & 0xffff + elif args[0].value == 'qword': + return args[1] + + return + + def pointer(self, args): + reg = self.ql.reg.save() + + # args example: [{'address': 140737488474144}] + pointer_address = args[0]['address'] + + try: + pointer_value = int.from_bytes( + self.ql.mem.read( + pointer_address, + 8 + ), + "little" + ) + return {"pointer": pointer_address, "value": pointer_value} + except: + return None + + def address(self, args): + return {"address": args[0]} + + def add_op(self, args): + return args[0] + args[1] + + def sub_op(self, args): + return args[0] - args[1] + + def mul_op(self, args): + return args[0] * args[1] + + def primitive(self, args): + return args[0] + + def OPCODE(self, args): + return {"opcode": args.value} + + def HEX(self, args): + return int(args.value, 16) + + def NUMBER(self, args): + return int(args.value) + + def REGISTER(self, args): + if args.value == "rip": + # rip is not increment, need to add opcode size + return self.reg[args.value] + self.op_size + else: + return self.reg[args.value] + + def STRING(self, args): + return args.value + +class OpcodeParser(): + def __init__(self): + parser_grammer = r""" + start : OPCODE (operand)? + + operand : connection + | element + + connection : element "," element + element : cast + | pointer + | address + + cast : TYPE ( pointer | address ) + pointer : "ptr" "[" address "]" + | "[" address "]" + + address : (primitive | add_op | sub_op | mul_op) + + + + add_op : (primitive) "+" (primitive | add_op | sub_op | mul_op) + sub_op : (primitive) "-" (primitive | add_op | sub_op | mul_op) + mul_op : (primitive) "*" (primitive | add_op | sub_op | mul_op) + + primitive : REGISTER + | HEX + | NUMBER + | STRING + + // . + OPCODE.2: /[0-9a-z]+/ + HEX.2 : /0x[0-9a-f]+/ + NUMBER.2 : /-?[0-9]+/ + TYPE.2 : /(([qd]?|xmm)word|byte)/ + REGISTER.2 : /(r|e)?ax|a(l|h)|(r|e)?bx|b(l|h)|(r|e)?cx|c(l|h)|(r|e)?dx|d(l|h)|(r|e)?bp(l)?|(r|e)?sp(l)?|(r|e)?ip(l)?|(r|e)si(l)?|(r|e)di(l)?|r8(d|w|b)?|r9(d|w|b)?|r10(d|w|b)?|r11(d|w|b)?|r12(d|w|b)?|r13(d|w|b)?|r14(d|w|b)?|r15(d|w|b)?|xmm[01]/ + STRING : /[0-9a-zA-Z._:]+/ + + %ignore " " + """ + self.parser = Lark(parser_grammer, parser="lalr", propagate_positions=True) + + def parse(self, opcode): + self.tree = self.parser.parse(opcode) + return self.tree + + def calculate(self, ql, op_size): + return OpcodeCalculateTransformer(ql, op_size).transform(self.tree) + + +if __name__ == "__main__": + ql = Qiling(["../examples/rootfs/x8664_windows/bin/x8664_return_main.exe"], "../examples/rootfs/x8664_windows") + ql.hook_code(resolve_symbol) + + ql.run() \ No newline at end of file diff --git a/qiling/os/windows/windows.py b/qiling/os/windows/windows.py index c7827e89a..7dbe77379 100644 --- a/qiling/os/windows/windows.py +++ b/qiling/os/windows/windows.py @@ -24,6 +24,7 @@ from . import fiber from . import registry from . import utils +from . import resolve_dll import qiling.os.windows.dlls as api @@ -113,6 +114,9 @@ def setupComponents(self): # hook WinAPI in PE EMU def hook_winapi(self, ql: Qiling, address: int, size: int): + # resolve dll address if not + resolve_dll.resolve_symbol(ql, address, size) + if address in ql.loader.import_symbols: entry = ql.loader.import_symbols[address] api_name = entry['name'] From 171d0c477612516217de1df0f50cb71fc3509ba5 Mon Sep 17 00:00:00 2001 From: prayer Date: Wed, 14 Jul 2021 18:02:54 +0900 Subject: [PATCH 07/14] update resolve_dll --- examples/scripts/dllscollector.bat | 22 +++++++++++++++- qiling/loader/pe.py | 7 +++++- qiling/os/windows/resolve_dll.py | 40 ++++++++++++++++++------------ tests/test_pe.py | 7 ++++++ 4 files changed, 58 insertions(+), 18 deletions(-) diff --git a/examples/scripts/dllscollector.bat b/examples/scripts/dllscollector.bat index 40c9e307c..76ae16af4 100644 --- a/examples/scripts/dllscollector.bat +++ b/examples/scripts/dllscollector.bat @@ -86,5 +86,25 @@ if exist %WINDIR%\System32\downlevel\api-ms-win-crt-locale-l1-1-0.dll xcopy /f / if exist %WINDIR%\System32\downlevel\api-ms-win-crt-heap-l1-1-0.dll xcopy /f /y %WINDIR%\System32\downlevel\api-ms-win-crt-heap-l1-1-0.dll "examples\rootfs\x8664_windows\Windows\System32\" if exist %WINDIR%\System32\vcruntime140d.dll xcopy /f /y %WINDIR%\System32\vcruntime140d.dll "examples\rootfs\x8664_windows\Windows\System32\" if exist %WINDIR%\System32\ucrtbased.dll xcopy /f /y %WINDIR%\System32\ucrtbased.dll "examples\rootfs\x8664_windows\Windows\System32\" +if exist %WINDIR%\System32\sechost.dll xcopy /f /y %WINDIR%\System32\sechost.dll "examples\rootfs\x8664_windows\Windows\System32\" +if exist %WINDIR%\System32\normaliz.dll xcopy /f /y %WINDIR%\System32\normaliz.dll "examples\rootfs\x8664_windows\Windows\System32\" +if exist %WINDIR%\System32\win32u.dll xcopy /f /y %WINDIR%\System32\win32u.dll "examples\rootfs\x8664_windows\Windows\System32\" +if exist %WINDIR%\System32\gdi32.dll xcopy /f /y %WINDIR%\System32\gdi32.dll "examples\rootfs\x8664_windows\Windows\System32\" +if exist %WINDIR%\System32\rpcrt4.dll xcopy /f /y %WINDIR%\System32\rpcrt4.dll "examples\rootfs\x8664_windows\Windows\System32\" +if exist %WINDIR%\System32\ucrtbase.dll xcopy /f /y %WINDIR%\System32\ucrtbase.dll "examples\rootfs\x8664_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\ucrtbase.dll xcopy /f /y %WINDIR%\SysWOW64\ucrtbase.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\win32u.dll xcopy /f /y %WINDIR%\SysWOW64\win32u.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\sechost.dll xcopy /f /y %WINDIR%\SysWOW64\sechost.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\msvcp_win.dll xcopy /f /y %WINDIR%\SysWOW64\msvcp_win.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\combase.dll xcopy /f /y %WINDIR%\SysWOW64\combase.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\cfgmgr32.dll xcopy /f /y %WINDIR%\SysWOW64\cfgmgr32.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\normaliz.dll xcopy /f /y %WINDIR%\SysWOW64\normaliz.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\bcrypt.dll xcopy /f /y %WINDIR%\SysWOW64\bcrypt.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\sspicli.dll xcopy /f /y %WINDIR%\SysWOW64\sspicli.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\bcryptprimitives.dll xcopy /f /y %WINDIR%\SysWOW64\bcryptprimitives.dll "examples\rootfs\x86_windows\Windows\System32\" +if exist %WINDIR%\SysWOW64\cryptbase.dll xcopy /f /y %WINDIR%\SysWOW64\cryptbase.dll "examples\rootfs\x86_windows\Windows\System32\" -exit /b +if exist %WINDIR%\System32\downlevel\api-ms-win-core-errorhandling-l1-1-0.dll xcopy /f /y %WINDIR%\System32\downlevel\api-ms-win-core-errorhandling-l1-1-0.dll "examples\rootfs\x8664_windows\Windows\System32\" +if exist %WINDIR%\System32\downlevel\api-ms-win-core-synch-l1-1-0.dll xcopy /f /y %WINDIR%\System32\downlevel\api-ms-win-core-synch-l1-1-0.dll "examples\rootfs\x8664_windows\Windows\System32\" + +exit /b \ No newline at end of file diff --git a/qiling/loader/pe.py b/qiling/loader/pe.py index 1fbe91728..d27a01335 100644 --- a/qiling/loader/pe.py +++ b/qiling/loader/pe.py @@ -96,6 +96,11 @@ def load_dll(self, dll_name, driver=False): self.ql.log.warning(f'Warnings while loading {path}:') for warning in warnings: self.ql.log.warning(f' - {warning}') + + # [Room for Improvement] too much time when kernelbase.dll is loaded. + #self.ql.log.debug('relocate {}, {:x}'.format(dll_name, self.dll_last_address)) + #dll.relocate_image(self.dll_last_address) + data = bytearray(dll.get_memory_mapped_image()) cmdlines = [] @@ -466,7 +471,7 @@ def load(self): if self.pe_image_address + self.pe_image_address_size > self.ql.os.heap_base_address: # pe reloc self.pe_image_address = self.image_address - self.pe.relocate_image(self.image_address) + #self.pe.relocate_image(self.image_address) self.entry_point = self.pe_entry_point = self.pe_image_address + self.pe.OPTIONAL_HEADER.AddressOfEntryPoint self.sizeOfStackReserve = self.pe.OPTIONAL_HEADER.SizeOfStackReserve diff --git a/qiling/os/windows/resolve_dll.py b/qiling/os/windows/resolve_dll.py index d02cd8a59..4a3efcd5f 100644 --- a/qiling/os/windows/resolve_dll.py +++ b/qiling/os/windows/resolve_dll.py @@ -7,6 +7,7 @@ from qiling.const import QL_ARCH, QL_INTERCEPT from qiling.exception import QlErrorSyscallError, QlErrorSyscallNotFound +import os import pefile from capstone import* from unicorn import * @@ -29,16 +30,16 @@ def resolve_symbol(ql: Qiling, address: int, size): jump_address = -1 jump_pointer_address = -1 if op.mnemonic in ['jmp', 'call']: - print(op.mnemonic, op.op_str) - _arch_utils.parser.parse("{} {}".format(op.mnemonic, op.op_str)) - jump_pointer = _arch_utils.parser.calculate(ql, size)['operand'] + #print(op.mnemonic, op.op_str) + parser = OpcodeParser() + jump_pointer = parser.calculate(ql, "{} {}".format(op.mnemonic, op.op_str), size)['operand'] if jump_pointer != None: if "address" in jump_pointer.keys(): jump_address = jump_pointer['address'] elif "pointer" in jump_pointer.keys(): jump_pointer_address = jump_pointer['pointer'] - jump_address = jump_pointer['value'] + jump_address = jump_pointer['address'] # check if library is already imported or not. if (not is_in_allocated_memory_address(ql, jump_address)) and (jump_address != -1) and (jump_pointer_address != -1): @@ -109,6 +110,9 @@ def load_additional_dll(ql, import_address): # ref: https://docs.microsoft.com/en-us/windows/win32/apiindex/windows-apisets if (export_dll_name[0:4] == 'api-') or (export_dll_name[0:4] == 'ext-'): export_dll_name, target_symbol = get_export_symbol_from_api_dll(ql, export_dll_name, target_symbol) + # export dll is not exist + if (export_dll_name is None): + continue # *Additional dll must not be loaded because import symbol is not resolved, but the case of API set dll, export_dll might be loaded.* if (export_dll_name not in dll_list.keys()): @@ -173,7 +177,8 @@ def resolve_import_dll_address(ql, import_dll_name, export_dll_name, target_symb export_dll_bin = pefile.PE(os.path.join(ql.rootfs, 'Windows/system32/{}'.format(export_dll_name))) export_dll_base = dll_list[export_dll_name]['base'] - export_symbol_list = list(filter(lambda x: x.name.decode('utf-8') == target_symbol, export_dll_bin.DIRECTORY_ENTRY_EXPORT.symbols)) + # insert if statement since sometimes DIRECTORY_ENTRY_EXPORT.symbols.name is None + export_symbol_list = list(filter(lambda x: x.name.decode('utf-8') == target_symbol if x.name != None else False, export_dll_bin.DIRECTORY_ENTRY_EXPORT.symbols)) if len(export_symbol_list) == 0: return False @@ -210,6 +215,9 @@ def _get_string_from_pe(api_dll, target_symbol): return string print(os.path.join(ql.rootfs, 'Windows/system32/{}'.format(api_dll_name))) + if not os.path.exists(os.path.join(ql.rootfs, 'Windows/system32/{}'.format(api_dll_name))): + return None, None + api_dll = pefile.PE(os.path.join(ql.rootfs, 'Windows/system32/{}'.format(api_dll_name))) dll_name, export_symbol = _get_string_from_pe(api_dll, target_symbol).split('.') @@ -254,14 +262,15 @@ def element(self, args): return args[0] def cast(self, args): + if args[1] == None: + return None + if args[0].value == 'byte': - return args[1] & 0xff + args[1]['address'] &= 0xff elif args[0].value == 'dword': - return args[1] & 0xffff - elif args[0].value == 'qword': - return args[1] + args[1]['address'] &= 0xffff - return + return args[1] def pointer(self, args): reg = self.ql.reg.save() @@ -277,7 +286,7 @@ def pointer(self, args): ), "little" ) - return {"pointer": pointer_address, "value": pointer_value} + return {"pointer": pointer_address, "address": pointer_value} except: return None @@ -357,16 +366,15 @@ def __init__(self): """ self.parser = Lark(parser_grammer, parser="lalr", propagate_positions=True) - def parse(self, opcode): + def calculate(self, ql, opcode, op_size): self.tree = self.parser.parse(opcode) - return self.tree - - def calculate(self, ql, op_size): return OpcodeCalculateTransformer(ql, op_size).transform(self.tree) if __name__ == "__main__": - ql = Qiling(["../examples/rootfs/x8664_windows/bin/x8664_return_main.exe"], "../examples/rootfs/x8664_windows") + ql = Qiling(["../examples/rootfs/x8664_windows/bin/api_set_dll_demo.exe"], + "../examples/rootfs/x8664_windows", + verbose=QL_VERBOSE.DEFAULT) ql.hook_code(resolve_symbol) ql.run() \ No newline at end of file diff --git a/tests/test_pe.py b/tests/test_pe.py index 7bb6cdbf2..6334ebd3a 100644 --- a/tests/test_pe.py +++ b/tests/test_pe.py @@ -474,6 +474,13 @@ def test_pe_win_x8664_libcache(self): ql.run() del ql + def test_pe_win_x8664_relocate_dll_image_and_api_set_dii(self): + # First force the cache to be recreated + ql = Qiling(["../examples/rootfs/x8664_windows/bin/api_set_dll_demo.exe"], + "../examples/rootfs/x8664_windows", + verbose=QL_VERBOSE.DEFAULT) + ql.run() + del ql if __name__ == "__main__": unittest.main() From 011d882407e1ea2f372e6132031719f74ec7d35a Mon Sep 17 00:00:00 2001 From: prayer Date: Thu, 15 Jul 2021 13:44:31 +0900 Subject: [PATCH 08/14] update test_pe to use libcache in all test --- qiling/loader/pe.py | 4 +- qiling/os/windows/resolve_dll.py | 301 ++++++++++++++++--------------- tests/test_pe.py | 41 +++-- 3 files changed, 177 insertions(+), 169 deletions(-) diff --git a/qiling/loader/pe.py b/qiling/loader/pe.py index d27a01335..929baa3dd 100644 --- a/qiling/loader/pe.py +++ b/qiling/loader/pe.py @@ -99,7 +99,7 @@ def load_dll(self, dll_name, driver=False): # [Room for Improvement] too much time when kernelbase.dll is loaded. #self.ql.log.debug('relocate {}, {:x}'.format(dll_name, self.dll_last_address)) - #dll.relocate_image(self.dll_last_address) + dll.relocate_image(self.dll_last_address) data = bytearray(dll.get_memory_mapped_image()) cmdlines = [] @@ -471,7 +471,7 @@ def load(self): if self.pe_image_address + self.pe_image_address_size > self.ql.os.heap_base_address: # pe reloc self.pe_image_address = self.image_address - #self.pe.relocate_image(self.image_address) + self.pe.relocate_image(self.image_address) self.entry_point = self.pe_entry_point = self.pe_image_address + self.pe.OPTIONAL_HEADER.AddressOfEntryPoint self.sizeOfStackReserve = self.pe.OPTIONAL_HEADER.SizeOfStackReserve diff --git a/qiling/os/windows/resolve_dll.py b/qiling/os/windows/resolve_dll.py index 4a3efcd5f..c3e88f9a2 100644 --- a/qiling/os/windows/resolve_dll.py +++ b/qiling/os/windows/resolve_dll.py @@ -14,7 +14,153 @@ from lark import Lark, ast_utils, Transformer, v_args +class OpcodeCalculateTransformer(Transformer): + def __init__(self, ql, op_size): + self.ql = ql + self.op_size = op_size + + self.reg = self.ql.reg.save() + self.pointer_address = [] + + self.do_calculate = False + return + + # def start(self, args): + # #args example: {'opcode': 'mov', 'operand': 0} + # if isinstance(args[0], int): + # return args, self.pointer_address + # else: + # return args[0], self.pointer_address + + def start(self, args): + # opcode only + if len(args) == 1: + return args[0] + else: + return {"opcode": args[0]["opcode"], "operand":args[1]["operand"]} + + def operand(self, args): + return {"operand": args[0]} + + def connection(self, args): + return args + + def element(self, args): + return args[0] + + def cast(self, args): + if args[1] == None: + return None + + if args[0].value == 'byte': + args[1]['address'] &= 0xff + elif args[0].value == 'dword': + args[1]['address'] &= 0xffff + + return args[1] + + def pointer(self, args): + reg = self.ql.reg.save() + + # args example: [{'address': 140737488474144}] + pointer_address = args[0]['address'] + + try: + pointer_value = int.from_bytes( + self.ql.mem.read( + pointer_address, + 8 + ), + "little" + ) + return {"pointer": pointer_address, "address": pointer_value} + except: + return None + + def address(self, args): + return {"address": args[0]} + + def add_op(self, args): + return args[0] + args[1] + + def sub_op(self, args): + return args[0] - args[1] + + def mul_op(self, args): + return args[0] * args[1] + + def primitive(self, args): + return args[0] + + def OPCODE(self, args): + return {"opcode": args.value} + + def HEX(self, args): + return int(args.value, 16) + + def NUMBER(self, args): + return int(args.value) + + def REGISTER(self, args): + if args.value == "rip": + # rip is not increment, need to add opcode size + return self.reg[args.value] + self.op_size + else: + return self.reg[args.value] + + def STRING(self, args): + return args.value + +class OpcodeParser(): + def __init__(self): + parser_grammer = r""" + start : OPCODE (operand)? + + operand : connection + | element + + connection : element "," element + element : cast + | pointer + | address + + cast : TYPE ( pointer | address ) + pointer : "ptr" "[" address "]" + | "[" address "]" + + address : (primitive | add_op | sub_op | mul_op) + + + + add_op : (primitive) "+" (primitive | add_op | sub_op | mul_op) + sub_op : (primitive) "-" (primitive | add_op | sub_op | mul_op) + mul_op : (primitive) "*" (primitive | add_op | sub_op | mul_op) + + primitive : REGISTER + | HEX + | NUMBER + | STRING + + // . + OPCODE.2: /[0-9a-z]+/ + HEX.2 : /0x[0-9a-f]+/ + NUMBER.2 : /-?[0-9]+/ + TYPE.2 : /(([qd]?|xmm)word|byte)/ + REGISTER.2 : /(r|e)?ax|a(l|h)|(r|e)?bx|b(l|h)|(r|e)?cx|c(l|h)|(r|e)?dx|d(l|h)|(r|e)?bp(l)?|(r|e)?sp(l)?|(r|e)?ip(l)?|(r|e)si(l)?|(r|e)di(l)?|r8(d|w|b)?|r9(d|w|b)?|r10(d|w|b)?|r11(d|w|b)?|r12(d|w|b)?|r13(d|w|b)?|r14(d|w|b)?|r15(d|w|b)?|xmm[01]/ + STRING : /[0-9a-zA-Z._:]+/ + + %ignore " " + """ + self.parser = Lark(parser_grammer, parser="lalr", propagate_positions=True) + + def calculate(self, ql, opcode, op_size): + self.tree = self.parser.parse(opcode) + return OpcodeCalculateTransformer(ql, op_size).transform(self.tree) + +parser = OpcodeParser() + def resolve_symbol(ql: Qiling, address: int, size): + global parser reg = ql.reg.save() # Check the address to jump is in memory map. If not, check if it needs to load additional dll. @@ -29,18 +175,20 @@ def resolve_symbol(ql: Qiling, address: int, size): jump_address = -1 jump_pointer_address = -1 + #print("0x{:08x}".format(address), op.mnemonic, op.op_str) if op.mnemonic in ['jmp', 'call']: - #print(op.mnemonic, op.op_str) - parser = OpcodeParser() + jump_pointer = parser.calculate(ql, "{} {}".format(op.mnemonic, op.op_str), size)['operand'] if jump_pointer != None: - if "address" in jump_pointer.keys(): - jump_address = jump_pointer['address'] - elif "pointer" in jump_pointer.keys(): + if "pointer" in jump_pointer.keys(): jump_pointer_address = jump_pointer['pointer'] jump_address = jump_pointer['address'] + elif "address" in jump_pointer.keys(): + jump_address = jump_pointer['address'] + + # check if library is already imported or not. if (not is_in_allocated_memory_address(ql, jump_address)) and (jump_address != -1) and (jump_pointer_address != -1): #print('{:016x}: _is_in_allocated_memory_address'.format(jump_pointer_address)) @@ -205,7 +353,7 @@ def _get_string_from_pe(api_dll, target_symbol): return '' while True: - print(hex(export_symbol_list[0].address)) + #print(hex(export_symbol_list[0].address)) char = api_dll.get_data(export_symbol_list[0].address+offset, 1) if char == b'\x00': break @@ -227,148 +375,7 @@ def _get_string_from_pe(api_dll, target_symbol): return dll_name+'.dll', export_symbol -class OpcodeCalculateTransformer(Transformer): - def __init__(self, ql, op_size): - self.ql = ql - self.op_size = op_size - self.reg = self.ql.reg.save() - self.pointer_address = [] - - self.do_calculate = False - return - - # def start(self, args): - # #args example: {'opcode': 'mov', 'operand': 0} - # if isinstance(args[0], int): - # return args, self.pointer_address - # else: - # return args[0], self.pointer_address - - def start(self, args): - # opcode only - if len(args) == 1: - return args[0] - else: - return {"opcode": args[0]["opcode"], "operand":args[1]["operand"]} - - def operand(self, args): - return {"operand": args[0]} - - def connection(self, args): - return args - - def element(self, args): - return args[0] - - def cast(self, args): - if args[1] == None: - return None - - if args[0].value == 'byte': - args[1]['address'] &= 0xff - elif args[0].value == 'dword': - args[1]['address'] &= 0xffff - - return args[1] - - def pointer(self, args): - reg = self.ql.reg.save() - - # args example: [{'address': 140737488474144}] - pointer_address = args[0]['address'] - - try: - pointer_value = int.from_bytes( - self.ql.mem.read( - pointer_address, - 8 - ), - "little" - ) - return {"pointer": pointer_address, "address": pointer_value} - except: - return None - - def address(self, args): - return {"address": args[0]} - - def add_op(self, args): - return args[0] + args[1] - - def sub_op(self, args): - return args[0] - args[1] - - def mul_op(self, args): - return args[0] * args[1] - - def primitive(self, args): - return args[0] - - def OPCODE(self, args): - return {"opcode": args.value} - - def HEX(self, args): - return int(args.value, 16) - - def NUMBER(self, args): - return int(args.value) - - def REGISTER(self, args): - if args.value == "rip": - # rip is not increment, need to add opcode size - return self.reg[args.value] + self.op_size - else: - return self.reg[args.value] - - def STRING(self, args): - return args.value - -class OpcodeParser(): - def __init__(self): - parser_grammer = r""" - start : OPCODE (operand)? - - operand : connection - | element - - connection : element "," element - element : cast - | pointer - | address - - cast : TYPE ( pointer | address ) - pointer : "ptr" "[" address "]" - | "[" address "]" - - address : (primitive | add_op | sub_op | mul_op) - - - - add_op : (primitive) "+" (primitive | add_op | sub_op | mul_op) - sub_op : (primitive) "-" (primitive | add_op | sub_op | mul_op) - mul_op : (primitive) "*" (primitive | add_op | sub_op | mul_op) - - primitive : REGISTER - | HEX - | NUMBER - | STRING - - // . - OPCODE.2: /[0-9a-z]+/ - HEX.2 : /0x[0-9a-f]+/ - NUMBER.2 : /-?[0-9]+/ - TYPE.2 : /(([qd]?|xmm)word|byte)/ - REGISTER.2 : /(r|e)?ax|a(l|h)|(r|e)?bx|b(l|h)|(r|e)?cx|c(l|h)|(r|e)?dx|d(l|h)|(r|e)?bp(l)?|(r|e)?sp(l)?|(r|e)?ip(l)?|(r|e)si(l)?|(r|e)di(l)?|r8(d|w|b)?|r9(d|w|b)?|r10(d|w|b)?|r11(d|w|b)?|r12(d|w|b)?|r13(d|w|b)?|r14(d|w|b)?|r15(d|w|b)?|xmm[01]/ - STRING : /[0-9a-zA-Z._:]+/ - - %ignore " " - """ - self.parser = Lark(parser_grammer, parser="lalr", propagate_positions=True) - - def calculate(self, ql, opcode, op_size): - self.tree = self.parser.parse(opcode) - return OpcodeCalculateTransformer(ql, op_size).transform(self.tree) if __name__ == "__main__": diff --git a/tests/test_pe.py b/tests/test_pe.py index 6334ebd3a..b4771c72a 100644 --- a/tests/test_pe.py +++ b/tests/test_pe.py @@ -31,14 +31,14 @@ class PETest(unittest.TestCase): def test_pe_win_x8664_hello(self): ql = Qiling(["../examples/rootfs/x8664_windows/bin/x8664_hello.exe"], "../examples/rootfs/x8664_windows", - verbose=QL_VERBOSE.DEFAULT) + verbose=QL_VERBOSE.DEFAULT, libcache=True) ql.run() del ql def test_pe_win_x86_hello(self): ql = Qiling(["../examples/rootfs/x86_windows/bin/x86_hello.exe"], "../examples/rootfs/x86_windows", - verbose=QL_VERBOSE.DEFAULT, profile="profiles/append_test.ql") + verbose=QL_VERBOSE.DEFAULT, profile="profiles/append_test.ql", libcache=True) ql.run() del ql @@ -62,7 +62,7 @@ def close(self): return 0 ql = Qiling(["../examples/rootfs/x86_windows/bin/UselessDisk.bin"], "../examples/rootfs/x86_windows", - verbose=QL_VERBOSE.DEBUG) + verbose=QL_VERBOSE.DEBUG, libcache=True) ql.add_fs_mapper(r"\\.\PHYSICALDRIVE0", Fake_Drive()) ql.run() del ql @@ -114,7 +114,7 @@ def randomize_config_value(ql, key, subkey): raise QlErrorNotImplemented("API not implemented") ql = Qiling(["../examples/rootfs/x86_windows/bin/GandCrab502.bin"], "../examples/rootfs/x86_windows", - verbose=QL_VERBOSE.DEBUG, profile="profiles/windows_gandcrab_admin.ql") + verbose=QL_VERBOSE.DEBUG, profile="profiles/windows_gandcrab_admin.ql", libcache=True) default_user = ql.os.profile["USER"]["username"] default_computer = ql.os.profile["SYSTEM"]["computername"] @@ -128,7 +128,7 @@ def randomize_config_value(ql, key, subkey): # RUN AS USER ql = Qiling(["../examples/rootfs/x86_windows/bin/GandCrab502.bin"], "../examples/rootfs/x86_windows", - verbose=QL_VERBOSE.DEBUG, profile="profiles/windows_gandcrab_user.ql") + verbose=QL_VERBOSE.DEBUG, profile="profiles/windows_gandcrab_user.ql", libcache=True) ql.run() num_syscalls_user = ql.os.utils.syscalls_counter @@ -136,7 +136,7 @@ def randomize_config_value(ql, key, subkey): del ql ql = Qiling(["../examples/rootfs/x86_windows/bin/GandCrab502.bin"], "../examples/rootfs/x86_windows", - verbose=QL_VERBOSE.DEBUG, profile="profiles/windows_gandcrab_russian_keyboard.ql") + verbose=QL_VERBOSE.DEBUG, profile="profiles/windows_gandcrab_russian_keyboard.ql", libcache=True) num_syscalls_russ = ql.os.utils.syscalls_counter ql.run() @@ -149,7 +149,7 @@ def ThreadId_onEnter(ql, address, params): self.thread_id = ql.os.thread_manager.cur_thread.id return address, params - ql = Qiling(["../examples/rootfs/x86_windows/bin/MultiThread.exe"], "../examples/rootfs/x86_windows") + ql = Qiling(["../examples/rootfs/x86_windows/bin/MultiThread.exe"], "../examples/rootfs/x86_windows", libcache=True) ql.set_api("GetCurrentThreadId", ThreadId_onEnter, QL_INTERCEPT.ENTER) ql.run() @@ -161,31 +161,31 @@ def ThreadId_onEnter(ql, address, params): def test_pe_win_x86_clipboard(self): - ql = Qiling(["../examples/rootfs/x8664_windows/bin//x8664_clipboard_test.exe"], "../examples/rootfs/x8664_windows") + ql = Qiling(["../examples/rootfs/x8664_windows/bin//x8664_clipboard_test.exe"], "../examples/rootfs/x8664_windows", libcache=True) ql.run() del ql def test_pe_win_x86_tls(self): - ql = Qiling(["../examples/rootfs/x8664_windows/bin/x8664_tls.exe"], "../examples/rootfs/x8664_windows") + ql = Qiling(["../examples/rootfs/x8664_windows/bin/x8664_tls.exe"], "../examples/rootfs/x8664_windows", libcache=True) ql.run() del ql def test_pe_win_x86_getlasterror(self): - ql = Qiling(["../examples/rootfs/x86_windows/bin/GetLastError.exe"], "../examples/rootfs/x86_windows") + ql = Qiling(["../examples/rootfs/x86_windows/bin/GetLastError.exe"], "../examples/rootfs/x86_windows", libcache=True) ql.run() del ql def test_pe_win_x86_regdemo(self): - ql = Qiling(["../examples/rootfs/x86_windows/bin/RegDemo.exe"], "../examples/rootfs/x86_windows") + ql = Qiling(["../examples/rootfs/x86_windows/bin/RegDemo.exe"], "../examples/rootfs/x86_windows", libcache=True) ql.run() del ql def test_pe_win_x8664_fls(self): - ql = Qiling(["../examples/rootfs/x8664_windows/bin/Fls.exe"], "../examples/rootfs/x8664_windows", verbose=QL_VERBOSE.DEFAULT) + ql = Qiling(["../examples/rootfs/x8664_windows/bin/Fls.exe"], "../examples/rootfs/x8664_windows", verbose=QL_VERBOSE.DEFAULT, libcache=True) ql.run() del ql @@ -223,7 +223,7 @@ def stop(ql): ql.log.info("No Print") ql.emu_stop() - ql = Qiling(["../examples/rootfs/x86_windows/bin/wannacry.bin"], "../examples/rootfs/x86_windows") + ql = Qiling(["../examples/rootfs/x86_windows/bin/wannacry.bin"], "../examples/rootfs/x86_windows", libcache=True) ql.hook_address(stop, 0x40819a) ql.run() del ql @@ -232,7 +232,7 @@ def stop(ql): def test_pe_win_x86_NtQueryInformationSystem(self): ql = Qiling( ["../examples/rootfs/x86_windows/bin/NtQuerySystemInformation.exe"], - "../examples/rootfs/x86_windows") + "../examples/rootfs/x86_windows", libcache=True) ql.run() del ql @@ -240,7 +240,7 @@ def test_pe_win_x86_NtQueryInformationSystem(self): def test_pe_win_al_khaser(self): if 'QL_FAST_TEST' in os.environ: return - ql = Qiling(["../examples/rootfs/x86_windows/bin/al-khaser.bin"], "../examples/rootfs/x86_windows") + ql = Qiling(["../examples/rootfs/x86_windows/bin/al-khaser.bin"], "../examples/rootfs/x86_windows", libcache=True) # The hooks are to remove the prints to file. It crashes. will debug why in the future def results(ql): @@ -292,7 +292,7 @@ def my_onexit(ql, address, params, retval): self.set_api_onexit = self.set_api = len( params["str"]) def my_sandbox(path, rootfs): - ql = Qiling(path, rootfs, verbose=QL_VERBOSE.DEBUG) + ql = Qiling(path, rootfs, libcache=True, verbose=QL_VERBOSE.DEBUG) ql.set_api("puts", my_onenter, QL_INTERCEPT.ENTER) ql.set_api("puts", my_puts64) ql.set_api("puts", my_onexit, QL_INTERCEPT.EXIT) @@ -337,7 +337,7 @@ def check_print(ql: Qiling, address: int, params): return address, params - ql = Qiling(["../examples/rootfs/x86_windows/bin/argv.exe"], "../examples/rootfs/x86_windows") + ql = Qiling(["../examples/rootfs/x86_windows/bin/argv.exe"], "../examples/rootfs/x86_windows", libcache=True) ql.set_api('__stdio_common_vfprintf', check_print, QL_INTERCEPT.ENTER) ql.run() @@ -386,7 +386,7 @@ def force_call_dialog_func(ql): ql.reg.eip = lpDialogFunc def our_sandbox(path, rootfs): - ql = Qiling(path, rootfs) + ql = Qiling(path, rootfs, libcache=True) ql.patch(0x004010B5, b'\x90\x90') ql.patch(0x004010CD, b'\x90\x90') ql.patch(0x0040110B, b'\x90\x90') @@ -403,7 +403,7 @@ def our_sandbox(path, rootfs): def test_pe_win_x86_cmdln(self): ql = Qiling( ["../examples/rootfs/x86_windows/bin/cmdln32.exe", 'arg1', 'arg2 with spaces'], - "../examples/rootfs/x86_windows") + "../examples/rootfs/x86_windows", libcache=True) ql.stdout = TestOut() ql.run() expected_string = b'\n' @@ -417,7 +417,7 @@ def test_pe_win_x86_cmdln(self): def test_pe_win_x8664_cmdln(self): ql = Qiling( ["../examples/rootfs/x8664_windows/bin/cmdln64.exe", 'arg1', 'arg2 with spaces'], - "../examples/rootfs/x8664_windows") + "../examples/rootfs/x8664_windows", libcache=True) ql.stdout = TestOut() ql.run() expected_string = b'\n' @@ -478,6 +478,7 @@ def test_pe_win_x8664_relocate_dll_image_and_api_set_dii(self): # First force the cache to be recreated ql = Qiling(["../examples/rootfs/x8664_windows/bin/api_set_dll_demo.exe"], "../examples/rootfs/x8664_windows", + libcache=True, verbose=QL_VERBOSE.DEFAULT) ql.run() del ql From 2ff1283dc67de67bbb1c0b11c464517410c9b79c Mon Sep 17 00:00:00 2001 From: prayer Date: Thu, 15 Jul 2021 18:46:25 +0900 Subject: [PATCH 09/14] save --- qiling/loader/pe.py | 46 ++++++++++++++++-------------- qiling/os/windows/resolve_dll.py | 48 ++++++++++++-------------------- 2 files changed, 43 insertions(+), 51 deletions(-) diff --git a/qiling/loader/pe.py b/qiling/loader/pe.py index 929baa3dd..d0fee4ed5 100644 --- a/qiling/loader/pe.py +++ b/qiling/loader/pe.py @@ -581,27 +581,31 @@ def load(self): for each in sys_dlls: super().load_dll(each, self.is_driver) # 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: - dll_name = str(entry.dll.lower(), 'utf-8', 'ignore') - super().load_dll(entry.dll, self.is_driver) - for imp in entry.imports: - # fix IAT - # ql.log.info(imp.name) - # ql.log.info(self.import_address_table[imp.name]) - if imp.name: - try: - addr = self.import_address_table[dll_name][imp.name] - except KeyError: - self.ql.log.debug("Error in loading function %s" % imp.name.decode()) - else: - addr = self.import_address_table[dll_name][imp.ordinal] - - if self.ql.archtype == QL_ARCH.X86: - address = self.ql.pack32(addr) - else: - address = self.ql.pack64(addr) - self.ql.mem.write(imp.address, address) + # if self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT']].VirtualAddress != 0: + # for entry in self.pe.DIRECTORY_ENTRY_IMPORT: + # dll_name = str(entry.dll.lower(), 'utf-8', 'ignore') + # # skip windows API dll + # if (dll_name[0:4] == 'api-') or (dll_name[0:4] == 'ext-'): + # continue + + # super().load_dll(entry.dll, self.is_driver) + # for imp in entry.imports: + # # fix IAT + # # ql.log.info(imp.name) + # # ql.log.info(self.import_address_table[imp.name]) + # if imp.name: + # try: + # addr = self.import_address_table[dll_name][imp.name] + # except KeyError: + # self.ql.log.debug("Error in loading function %s" % imp.name.decode()) + # else: + # addr = self.import_address_table[dll_name][imp.ordinal] + + # if self.ql.archtype == QL_ARCH.X86: + # address = self.ql.pack32(addr) + # else: + # address = self.ql.pack64(addr) + # self.ql.mem.write(imp.address, address) self.ql.log.debug("Done with loading %s" % self.path) self.ql.os.entry_point = self.entry_point diff --git a/qiling/os/windows/resolve_dll.py b/qiling/os/windows/resolve_dll.py index c3e88f9a2..f3be1d358 100644 --- a/qiling/os/windows/resolve_dll.py +++ b/qiling/os/windows/resolve_dll.py @@ -175,7 +175,7 @@ def resolve_symbol(ql: Qiling, address: int, size): jump_address = -1 jump_pointer_address = -1 - #print("0x{:08x}".format(address), op.mnemonic, op.op_str) + print("0x{:08x}".format(address), op.mnemonic, op.op_str) if op.mnemonic in ['jmp', 'call']: jump_pointer = parser.calculate(ql, "{} {}".format(op.mnemonic, op.op_str), size)['operand'] @@ -188,14 +188,13 @@ def resolve_symbol(ql: Qiling, address: int, size): elif "address" in jump_pointer.keys(): jump_address = jump_pointer['address'] - + print(jump_pointer) # check if library is already imported or not. if (not is_in_allocated_memory_address(ql, jump_address)) and (jump_address != -1) and (jump_pointer_address != -1): - #print('{:016x}: _is_in_allocated_memory_address'.format(jump_pointer_address)) + print('{:016x}: _is_in_allocated_memory_address'.format(jump_pointer_address)) load_additional_dll(ql, jump_pointer_address) - def is_in_allocated_memory_address(ql, address): for mi in ql.mem.map_info: @@ -224,7 +223,7 @@ def load_additional_dll(ql, import_address): for mi in map_info: dll_name = mi[3] - if '.dll' in dll_name: + if ('.dll' in dll_name) or ('[PE]' == dll_name): dll_list[dll_name] = { 'dll': dll_name, 'base': mi[0], @@ -232,18 +231,24 @@ def load_additional_dll(ql, import_address): } dll_last_address = mi[1] + print(dll_list.keys()) for dll_name in dll_list.keys(): if (dll_list[dll_name]['base'] < import_address) and (import_address < dll_list[dll_name]['end']): target_dll_name = dll_name + target_dll_base = dll_list[dll_name]['base'] break else: return print('Windows/system32/{}'.format(target_dll_name)) - target_dll_bin = pefile.PE(ql.rootfs+'/Windows/system32/{}'.format(target_dll_name)) - target_dll_image_base = target_dll_bin.OPTIONAL_HEADER.ImageBase + if dll_name == '[PE]': + target_dll_bin = ql.loader.pe + target_dll_image_base = target_dll_bin.OPTIONAL_HEADER.ImageBase + else: + target_dll_bin = pefile.PE(ql.rootfs+'/Windows/system32/{}'.format(target_dll_name)) + target_dll_image_base = target_dll_bin.OPTIONAL_HEADER.ImageBase target_symbol = None for entry_import in target_dll_bin.DIRECTORY_ENTRY_IMPORT: @@ -275,13 +280,17 @@ def load_additional_dll(ql, import_address): ql.mem.map(export_dll_base, export_dll_len, info=export_dll_name) ql.mem.write(export_dll_base, bytes(export_dll_data)) - resolve_import_dll_address(ql, target_dll_name, export_dll_name, target_symbol, import_address) + else: + export_dll_bin = pefile.PE(ql.rootfs+'/Windows/system32/{}'.format(export_dll_name)) + export_dll_base = dll_list[export_dll_name]['base'] + + resolve_import_dll_address(ql, target_dll_bin, target_dll_base, export_dll_bin, export_dll_base, target_symbol, import_address) return True return False -def resolve_import_dll_address(ql, import_dll_name, export_dll_name, target_symbol, import_address): +def resolve_import_dll_address(ql, import_dll_bin, import_dll_base, export_dll_bin, export_dll_base, target_symbol, import_address): """ Make IAT of import address. @@ -293,25 +302,8 @@ def resolve_import_dll_address(ql, import_dll_name, export_dll_name, target_symb boolean: True if Add dll to memory successfully, otherwise False. """ - dll_list = {} - # ql.mem.map_info example: [140737221971968, 140737222746112, 7, 'kernel32.dll'] - map_info = ql.mem.map_info - - for mi in map_info: - dll_name = mi[3] - if '.dll' in dll_name: - dll_list[dll_name] = { - 'dll': dll_name, - 'base': mi[0], - } - - if import_dll_name not in dll_list.keys(): - return False # dll_name is the dll imported by the binary - print('[+] {}'.format(import_dll_name)) - import_dll_bin = pefile.PE(os.path.join(ql.rootfs, 'Windows/system32/{}'.format(import_dll_name))) - import_dll_base = dll_list[import_dll_name]['base'] import_dll_image_base = import_dll_bin.OPTIONAL_HEADER.ImageBase # split \x00 since section.Name sometime like this b'.text\x00\x00\x00' @@ -321,10 +313,6 @@ def resolve_import_dll_address(ql, import_dll_name, export_dll_name, target_symb if import_dll_bin.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT']].VirtualAddress == 0: return False - # export dll is already loaded at _load_additional_dll. - export_dll_bin = pefile.PE(os.path.join(ql.rootfs, 'Windows/system32/{}'.format(export_dll_name))) - export_dll_base = dll_list[export_dll_name]['base'] - # insert if statement since sometimes DIRECTORY_ENTRY_EXPORT.symbols.name is None export_symbol_list = list(filter(lambda x: x.name.decode('utf-8') == target_symbol if x.name != None else False, export_dll_bin.DIRECTORY_ENTRY_EXPORT.symbols)) if len(export_symbol_list) == 0: From d1491266b66edc33c094f45cddebc663f47c778b Mon Sep 17 00:00:00 2001 From: prayer Date: Sat, 17 Jul 2021 21:12:02 +0900 Subject: [PATCH 10/14] check mov opcode pointer --- qiling/os/windows/resolve_dll.py | 113 +++++++++++++++++++++---------- 1 file changed, 77 insertions(+), 36 deletions(-) diff --git a/qiling/os/windows/resolve_dll.py b/qiling/os/windows/resolve_dll.py index f3be1d358..2dcfcec85 100644 --- a/qiling/os/windows/resolve_dll.py +++ b/qiling/os/windows/resolve_dll.py @@ -49,13 +49,15 @@ def element(self, args): return args[0] def cast(self, args): - if args[1] == None: + if (args[1] == None) or ('address' not in args[1].keys()): return None if args[0].value == 'byte': args[1]['address'] &= 0xff - elif args[0].value == 'dword': + elif args[0].value == 'word': args[1]['address'] &= 0xffff + elif args[0].value == 'dword': + args[1]['address'] &= 0xffffffff return args[1] @@ -77,6 +79,9 @@ def pointer(self, args): except: return None + def gs_pointer(self, args): + return {} + def address(self, args): return {"address": args[0]} @@ -122,12 +127,16 @@ def __init__(self): connection : element "," element element : cast | pointer + | gs_pointer | address - cast : TYPE ( pointer | address ) + cast : TYPE ( pointer | address | gs_pointer) pointer : "ptr" "[" address "]" | "[" address "]" + gs_pointer : "ptr" "gs:[" address "]" + | "ptr" "fs:[" address "]" + address : (primitive | add_op | sub_op | mul_op) @@ -175,10 +184,12 @@ def resolve_symbol(ql: Qiling, address: int, size): jump_address = -1 jump_pointer_address = -1 - print("0x{:08x}".format(address), op.mnemonic, op.op_str) - if op.mnemonic in ['jmp', 'call']: - - jump_pointer = parser.calculate(ql, "{} {}".format(op.mnemonic, op.op_str), size)['operand'] + #print("0x{:08x}".format(address), op.mnemonic, op.op_str) + if op.mnemonic in ['jmp', 'call', 'mov']: + if op.mnemonic in ['jmp', 'call']: + jump_pointer = parser.calculate(ql, "{} {}".format(op.mnemonic, op.op_str), size)['operand'] + if op.mnemonic in ['mov']: + jump_pointer = parser.calculate(ql, "{} {}".format(op.mnemonic, op.op_str), size)['operand'][1] if jump_pointer != None: if "pointer" in jump_pointer.keys(): @@ -188,19 +199,37 @@ def resolve_symbol(ql: Qiling, address: int, size): elif "address" in jump_pointer.keys(): jump_address = jump_pointer['address'] - print(jump_pointer) # check if library is already imported or not. - if (not is_in_allocated_memory_address(ql, jump_address)) and (jump_address != -1) and (jump_pointer_address != -1): - print('{:016x}: _is_in_allocated_memory_address'.format(jump_pointer_address)) - load_additional_dll(ql, jump_pointer_address) + if(is_in_executable_memory_address(ql, jump_pointer_address)): + if (not is_in_executable_memory_address(ql, jump_address)) and (jump_address != -1) and (jump_pointer_address != -1): + #print('is_in_executable_memory_address: pointer:{:016x}, address:{:016x}'.format(jump_pointer_address, jump_address)) + load_additional_dll(ql, jump_pointer_address) + #input() +def get_base_address(ql, map_name): + map_info = ql.mem.map_info -def is_in_allocated_memory_address(ql, address): - for mi in ql.mem.map_info: + for mi in map_info: + _map_name = mi[3] + if ('.dll' in _map_name) or ('[PE]' == _map_name): + if _map_name == map_name: + return mi[0] + + return None + +def is_in_executable_memory_address(ql, address): + for mi in ql.mem.map_info: + if ('.dll' in mi[3]) or ('[PE]' == mi[3]): if (mi[0] < address) and (address < mi[1]): return True - return False + return False + +def is_in_allocated_memory_address(ql, address): + for mi in ql.mem.map_info: + if (mi[0] < address) and (address < mi[1]): + return True + return False def load_additional_dll(ql, import_address): @@ -231,7 +260,7 @@ def load_additional_dll(ql, import_address): } dll_last_address = mi[1] - print(dll_list.keys()) + #print(dll_list.keys()) for dll_name in dll_list.keys(): if (dll_list[dll_name]['base'] < import_address) and (import_address < dll_list[dll_name]['end']): @@ -241,7 +270,7 @@ def load_additional_dll(ql, import_address): else: return - print('Windows/system32/{}'.format(target_dll_name)) + #print('Windows/system32/{}'.format(target_dll_name)) if dll_name == '[PE]': target_dll_bin = ql.loader.pe @@ -250,6 +279,11 @@ def load_additional_dll(ql, import_address): target_dll_bin = pefile.PE(ql.rootfs+'/Windows/system32/{}'.format(target_dll_name)) target_dll_image_base = target_dll_bin.OPTIONAL_HEADER.ImageBase + #print(target_dll_bin) + # Sometimes pe file don't have DIRECTORY_ENTRY_IMPORT + if not hasattr(target_dll_bin, 'DIRECTORY_ENTRY_IMPORT'): + return False + target_symbol = None for entry_import in target_dll_bin.DIRECTORY_ENTRY_IMPORT: for entry_import_symbol in entry_import.imports: @@ -257,37 +291,44 @@ def load_additional_dll(ql, import_address): target_symbol = entry_import_symbol.name.decode('utf-8') # Go to proccess of loading additional dll from import_address if the import symbol exists. - export_dll_name = entry_import.dll.decode('utf-8') - + export_dll_name = entry_import.dll.decode('utf-8').lower() + #print('export_dll_name: {}'.format(export_dll_name)) # The case of API Set dll # ref: https://docs.microsoft.com/en-us/windows/win32/apiindex/windows-apisets if (export_dll_name[0:4] == 'api-') or (export_dll_name[0:4] == 'ext-'): export_dll_name, target_symbol = get_export_symbol_from_api_dll(ql, export_dll_name, target_symbol) + + #print('export_dll_name: {}'.format(export_dll_name)) # export dll is not exist if (export_dll_name is None): continue # *Additional dll must not be loaded because import symbol is not resolved, but the case of API set dll, export_dll might be loaded.* if (export_dll_name not in dll_list.keys()): - export_dll_bin = pefile.PE(ql.rootfs+'/Windows/system32/{}'.format(export_dll_name)) - export_dll_base = dll_last_address + ql.loader.load_dll(export_dll_name.encode('utf-8')) - export_dll_bin.parse_data_directories() - export_dll_bin.relocate_image(export_dll_base) - export_dll_data = bytearray(export_dll_bin.get_memory_mapped_image()) + # export_dll_bin = pefile.PE(ql.rootfs+'/Windows/system32/{}'.format(export_dll_name)) + # export_dll_base = dll_last_address - export_dll_len = ql.mem.align(len(bytes(export_dll_data)), 0x1000) - ql.mem.map(export_dll_base, export_dll_len, info=export_dll_name) - ql.mem.write(export_dll_base, bytes(export_dll_data)) + # export_dll_bin.parse_data_directories() + # export_dll_bin.relocate_image(export_dll_base) + # export_dll_data = bytearray(export_dll_bin.get_memory_mapped_image()) - else: - export_dll_bin = pefile.PE(ql.rootfs+'/Windows/system32/{}'.format(export_dll_name)) - export_dll_base = dll_list[export_dll_name]['base'] + # export_dll_len = ql.mem.align(len(bytes(export_dll_data)), 0x1000) + # ql.mem.map(export_dll_base, export_dll_len, info=export_dll_name) + # ql.mem.write(export_dll_base, bytes(export_dll_data)) + + + + + export_dll_bin = pefile.PE(ql.rootfs+'/Windows/system32/{}'.format(export_dll_name)) + export_dll_base = get_base_address(ql, export_dll_name) resolve_import_dll_address(ql, target_dll_bin, target_dll_base, export_dll_bin, export_dll_base, target_symbol, import_address) return True + #print('no dll: ', target_dll_name) return False def resolve_import_dll_address(ql, import_dll_bin, import_dll_base, export_dll_bin, export_dll_base, target_symbol, import_address): @@ -302,7 +343,7 @@ def resolve_import_dll_address(ql, import_dll_bin, import_dll_base, export_dll_b boolean: True if Add dll to memory successfully, otherwise False. """ - + #print(import_dll_base, export_dll_base, target_symbol, import_address) # dll_name is the dll imported by the binary import_dll_image_base = import_dll_bin.OPTIONAL_HEADER.ImageBase @@ -318,10 +359,10 @@ def resolve_import_dll_address(ql, import_dll_bin, import_dll_base, export_dll_b if len(export_symbol_list) == 0: return False - print('[+] import address: {:016x}, import_dll_base: {:016x}, import_dll_image_base: {:016x}'.format(import_address, import_dll_base, import_dll_image_base)) - print('[+] memory write {:016x} -> {}'.format( - import_address, - (export_symbol_list[0].address+export_dll_base).to_bytes(8,'little'))) + #print('[+] import address: {:016x}, import_dll_base: {:016x}, import_dll_image_base: {:016x}'.format(import_address, import_dll_base, import_dll_image_base)) + # print('[+] memory write {:016x} -> {}'.format( + # import_address, + # (export_symbol_list[0].address+export_dll_base).to_bytes(8,'little'))) ql.mem.write( import_address, @@ -350,7 +391,7 @@ def _get_string_from_pe(api_dll, target_symbol): offset += 1 return string - print(os.path.join(ql.rootfs, 'Windows/system32/{}'.format(api_dll_name))) + #print(os.path.join(ql.rootfs, 'Windows/system32/{}'.format(api_dll_name))) if not os.path.exists(os.path.join(ql.rootfs, 'Windows/system32/{}'.format(api_dll_name))): return None, None @@ -358,7 +399,7 @@ def _get_string_from_pe(api_dll, target_symbol): dll_name, export_symbol = _get_string_from_pe(api_dll, target_symbol).split('.') - print(dll_name, export_symbol) + #print(dll_name, export_symbol) return dll_name+'.dll', export_symbol From 1183a5b534b1894ccee264526aa9783f42947c38 Mon Sep 17 00:00:00 2001 From: prayer Date: Sun, 18 Jul 2021 09:50:40 +0900 Subject: [PATCH 11/14] delete lark-parser function --- qiling/os/windows/resolve_dll.py | 248 ++++++++----------------------- 1 file changed, 61 insertions(+), 187 deletions(-) diff --git a/qiling/os/windows/resolve_dll.py b/qiling/os/windows/resolve_dll.py index 2dcfcec85..ec220b92d 100644 --- a/qiling/os/windows/resolve_dll.py +++ b/qiling/os/windows/resolve_dll.py @@ -10,175 +10,29 @@ import os import pefile from capstone import* +from capstone.x86 import * from unicorn import * -from lark import Lark, ast_utils, Transformer, v_args -class OpcodeCalculateTransformer(Transformer): - def __init__(self, ql, op_size): - self.ql = ql - self.op_size = op_size +def calibrate_reg_name(reg_name): + if reg_name not in ['sil', 'dil']: + return reg_name - self.reg = self.ql.reg.save() - self.pointer_address = [] - - self.do_calculate = False - return - - # def start(self, args): - # #args example: {'opcode': 'mov', 'operand': 0} - # if isinstance(args[0], int): - # return args, self.pointer_address - # else: - # return args[0], self.pointer_address - - def start(self, args): - # opcode only - if len(args) == 1: - return args[0] - else: - return {"opcode": args[0]["opcode"], "operand":args[1]["operand"]} - - def operand(self, args): - return {"operand": args[0]} - - def connection(self, args): - return args - - def element(self, args): - return args[0] - - def cast(self, args): - if (args[1] == None) or ('address' not in args[1].keys()): - return None - - if args[0].value == 'byte': - args[1]['address'] &= 0xff - elif args[0].value == 'word': - args[1]['address'] &= 0xffff - elif args[0].value == 'dword': - args[1]['address'] &= 0xffffffff - - return args[1] - - def pointer(self, args): - reg = self.ql.reg.save() - - # args example: [{'address': 140737488474144}] - pointer_address = args[0]['address'] - - try: - pointer_value = int.from_bytes( - self.ql.mem.read( - pointer_address, - 8 - ), - "little" - ) - return {"pointer": pointer_address, "address": pointer_value} - except: - return None - - def gs_pointer(self, args): - return {} - - def address(self, args): - return {"address": args[0]} - - def add_op(self, args): - return args[0] + args[1] - - def sub_op(self, args): - return args[0] - args[1] - - def mul_op(self, args): - return args[0] * args[1] - - def primitive(self, args): - return args[0] - - def OPCODE(self, args): - return {"opcode": args.value} - - def HEX(self, args): - return int(args.value, 16) - - def NUMBER(self, args): - return int(args.value) - - def REGISTER(self, args): - if args.value == "rip": - # rip is not increment, need to add opcode size - return self.reg[args.value] + self.op_size - else: - return self.reg[args.value] - - def STRING(self, args): - return args.value - -class OpcodeParser(): - def __init__(self): - parser_grammer = r""" - start : OPCODE (operand)? - - operand : connection - | element - - connection : element "," element - element : cast - | pointer - | gs_pointer - | address - - cast : TYPE ( pointer | address | gs_pointer) - pointer : "ptr" "[" address "]" - | "[" address "]" - - gs_pointer : "ptr" "gs:[" address "]" - | "ptr" "fs:[" address "]" - - address : (primitive | add_op | sub_op | mul_op) - - - - add_op : (primitive) "+" (primitive | add_op | sub_op | mul_op) - sub_op : (primitive) "-" (primitive | add_op | sub_op | mul_op) - mul_op : (primitive) "*" (primitive | add_op | sub_op | mul_op) - - primitive : REGISTER - | HEX - | NUMBER - | STRING - - // . - OPCODE.2: /[0-9a-z]+/ - HEX.2 : /0x[0-9a-f]+/ - NUMBER.2 : /-?[0-9]+/ - TYPE.2 : /(([qd]?|xmm)word|byte)/ - REGISTER.2 : /(r|e)?ax|a(l|h)|(r|e)?bx|b(l|h)|(r|e)?cx|c(l|h)|(r|e)?dx|d(l|h)|(r|e)?bp(l)?|(r|e)?sp(l)?|(r|e)?ip(l)?|(r|e)si(l)?|(r|e)di(l)?|r8(d|w|b)?|r9(d|w|b)?|r10(d|w|b)?|r11(d|w|b)?|r12(d|w|b)?|r13(d|w|b)?|r14(d|w|b)?|r15(d|w|b)?|xmm[01]/ - STRING : /[0-9a-zA-Z._:]+/ - - %ignore " " - """ - self.parser = Lark(parser_grammer, parser="lalr", propagate_positions=True) - - def calculate(self, ql, opcode, op_size): - self.tree = self.parser.parse(opcode) - return OpcodeCalculateTransformer(ql, op_size).transform(self.tree) - -parser = OpcodeParser() + if reg_name == 'sil': + return 'si' + elif reg_name == 'dil': + return 'di' def resolve_symbol(ql: Qiling, address: int, size): - global parser - reg = ql.reg.save() + #reg = ql.reg.save() # Check the address to jump is in memory map. If not, check if it needs to load additional dll. buf = ql.mem.read(address, size) if ql.archtype == QL_ARCH.X8664: - md = Cs(CS_ARCH_X86, CS_MODE_64) + md = Cs(CS_ARCH_X86, CS_MODE_64) else: md = Cs(CS_ARCH_X86, CS_MODE_32) - + md.detail = True op = list(md.disasm(buf, address))[0] @@ -187,25 +41,57 @@ def resolve_symbol(ql: Qiling, address: int, size): #print("0x{:08x}".format(address), op.mnemonic, op.op_str) if op.mnemonic in ['jmp', 'call', 'mov']: if op.mnemonic in ['jmp', 'call']: - jump_pointer = parser.calculate(ql, "{} {}".format(op.mnemonic, op.op_str), size)['operand'] - if op.mnemonic in ['mov']: - jump_pointer = parser.calculate(ql, "{} {}".format(op.mnemonic, op.op_str), size)['operand'][1] - - if jump_pointer != None: - if "pointer" in jump_pointer.keys(): - jump_pointer_address = jump_pointer['pointer'] - jump_address = jump_pointer['address'] - - elif "address" in jump_pointer.keys(): - jump_address = jump_pointer['address'] - - # check if library is already imported or not. - if(is_in_executable_memory_address(ql, jump_pointer_address)): + pointer_operand = op.operands[0] + elif op.mnemonic in ['mov']: + pointer_operand = op.operands[1] + + if pointer_operand.type == X86_OP_MEM: + #print("\t\toperands.type: MEM") + jump_pointer_address = 0 + if pointer_operand.value.mem.base != 0: + #print("\t\t\toperands.mem.base: REG = %s" % (op.reg_name(pointer_operand.value.mem.base))) + reg_name = calibrate_reg_name(op.reg_name(pointer_operand.value.mem.base)) + jump_pointer_address += ql.reg.read(reg_name) + if 'ip' in reg_name: + jump_pointer_address += size + + if pointer_operand.value.mem.index != 0: + #print("\t\t\toperands.mem.index: REG = %s" % (op.reg_name(pointer_operand.value.mem.index))) + reg_name = calibrate_reg_name(op.reg_name(pointer_operand.value.mem.index)) + jump_pointer_address += ql.reg.read(reg_name) + if 'ip' in reg_name: + jump_pointer_address += size + + if pointer_operand.value.mem.disp != 0: + #print("\t\t\toperands.mem.disp: 0x%x" % (pointer_operand.value.mem.disp)) + jump_pointer_address += pointer_operand.value.mem.disp + + #print(' pointer:{:016x}, address:{:016x}'.format(jump_pointer_address, jump_address)) + # check if library is already imported or not. + if(is_in_executable_memory_address(ql, jump_pointer_address)): + if ql.archtype == QL_ARCH.X8664: + jump_address = int.from_bytes( + ql.mem.read( + jump_pointer_address, + 8 + ), + 'little' + ) + else: + jump_address = int.from_bytes( + ql.mem.read( + jump_pointer_address, + 4 + ), + 'little' + ) + + if (not is_in_executable_memory_address(ql, jump_address)) and (jump_address != -1) and (jump_pointer_address != -1): - #print('is_in_executable_memory_address: pointer:{:016x}, address:{:016x}'.format(jump_pointer_address, jump_address)) + print('is_in_executable_memory_address: pointer:{:016x}, address:{:016x}'.format(jump_pointer_address, jump_address)) load_additional_dll(ql, jump_pointer_address) - #input() + return def get_base_address(ql, map_name): map_info = ql.mem.map_info @@ -250,6 +136,8 @@ def load_additional_dll(ql, import_address): dll_last_address = 0x0 + #print(ql.loader.import_address_table) + for mi in map_info: dll_name = mi[3] if ('.dll' in dll_name) or ('[PE]' == dll_name): @@ -307,20 +195,6 @@ def load_additional_dll(ql, import_address): if (export_dll_name not in dll_list.keys()): ql.loader.load_dll(export_dll_name.encode('utf-8')) - # export_dll_bin = pefile.PE(ql.rootfs+'/Windows/system32/{}'.format(export_dll_name)) - # export_dll_base = dll_last_address - - # export_dll_bin.parse_data_directories() - # export_dll_bin.relocate_image(export_dll_base) - # export_dll_data = bytearray(export_dll_bin.get_memory_mapped_image()) - - # export_dll_len = ql.mem.align(len(bytes(export_dll_data)), 0x1000) - # ql.mem.map(export_dll_base, export_dll_len, info=export_dll_name) - # ql.mem.write(export_dll_base, bytes(export_dll_data)) - - - - export_dll_bin = pefile.PE(ql.rootfs+'/Windows/system32/{}'.format(export_dll_name)) export_dll_base = get_base_address(ql, export_dll_name) From 6e46c7651f3ae355166416aed88449327ac8e3dc Mon Sep 17 00:00:00 2001 From: prayer Date: Sun, 18 Jul 2021 15:36:26 +0900 Subject: [PATCH 12/14] Use entry_import_table --- qiling/loader/pe.py | 40 +++++++++++- qiling/os/windows/resolve_dll.py | 105 +++++++++++++++++++++---------- 2 files changed, 109 insertions(+), 36 deletions(-) diff --git a/qiling/loader/pe.py b/qiling/loader/pe.py index d0fee4ed5..92a5f99a2 100644 --- a/qiling/loader/pe.py +++ b/qiling/loader/pe.py @@ -17,11 +17,12 @@ class QlPeCacheEntry: - def __init__(self, data, cmdlines, import_symbols, import_table): + def __init__(self, data, cmdlines, import_symbols, import_table, entry_import_list): self.data = data self.cmdlines = cmdlines self.import_symbols = import_symbols self.import_table = import_table + self.entry_import_list = entry_import_list # A default simple cache implementation class QlPeCache: @@ -38,7 +39,7 @@ def restore(self, path, address): def save(self, path, address, entry): fcache = self.create_filename(path, address) - data = (entry.data, entry.cmdlines, entry.import_symbols, entry.import_table) + data = (entry.data, entry.cmdlines, entry.import_symbols, entry.import_table, entry.entry_import_list) # cache this dll file with open(fcache, "wb") as fcache_file: pickle.dump(data, fcache_file) @@ -85,6 +86,7 @@ def load_dll(self, dll_name, driver=False): data = cached.data import_symbols = cached.import_symbols import_table = cached.import_table + entry_import_list = cached.entry_import_list for entry in cached.cmdlines: self.set_cmdline(entry['name'], entry['address'], data) self.ql.log.info("Loaded %s from cache" % path) @@ -100,6 +102,19 @@ def load_dll(self, dll_name, driver=False): # [Room for Improvement] too much time when kernelbase.dll is loaded. #self.ql.log.debug('relocate {}, {:x}'.format(dll_name, self.dll_last_address)) dll.relocate_image(self.dll_last_address) + + # make entry import table for resolving dll address + entry_import_list = {} + if hasattr(dll, 'DIRECTORY_ENTRY_IMPORT'): + for entry_import in dll.DIRECTORY_ENTRY_IMPORT: + for entry_import_symbol in entry_import.imports: + if entry_import_symbol.name == None: + continue + entry_import_list[entry_import_symbol.address] = { + 'symbol': entry_import_symbol.name.decode('utf-8'), + 'dll': entry_import.dll.decode('utf-8').lower() + } + data = bytearray(dll.get_memory_mapped_image()) cmdlines = [] @@ -119,7 +134,7 @@ def load_dll(self, dll_name, driver=False): cmdlines.append(cmdline_entry) if self.libcache: - cached = QlPeCacheEntry(data, cmdlines, import_symbols, import_table) + cached = QlPeCacheEntry(data, cmdlines, import_symbols, import_table, entry_import_list) self.libcache.save(path, self.dll_last_address, cached) self.ql.log.info("Cached %s" % path) @@ -129,6 +144,11 @@ def load_dll(self, dll_name, driver=False): except Exception as ex: self.ql.log.exception(f'Unable to add {dll_name} to IAT') + try: + self.entry_import_table[dll_name] = entry_import_list + except Exception as ex: + self.ql.log.exception(f'Unable to add {dll_name} to entry_import_table') + try: self.import_symbols.update(import_symbols) except Exception as ex: @@ -430,6 +450,7 @@ def run(self): self.import_symbols = {} self.export_symbols = {} self.import_address_table = {} + self.entry_import_table = {} self.ldr_list = [] self.pe_image_address = 0 self.pe_image_address_size = 0 @@ -479,6 +500,19 @@ def load(self): self.ql.log.info("PE entry point at 0x%x" % self.entry_point) self.images.append(Image(self.pe_image_address, self.pe_image_address + self.pe.NT_HEADERS.OPTIONAL_HEADER.SizeOfImage, self.path)) + # make entry import table for resolving dll address + entry_import_list = {} + if hasattr(self.pe, 'DIRECTORY_ENTRY_IMPORT'): + for entry_import in self.pe.DIRECTORY_ENTRY_IMPORT: + for entry_import_symbol in entry_import.imports: + #print(entry_import_symbol.address, self.pe_image_address, self.image_address) + entry_import_list[entry_import_symbol.address] = { + 'symbol': entry_import_symbol.name.decode('utf-8'), + 'dll': entry_import.dll.decode('utf-8').lower() + } + self.entry_import_table['[PE]'] = entry_import_list + + # Stack should not init at the very bottom. Will cause errors with Dlls sp = self.stack_address + self.stack_size - 0x1000 diff --git a/qiling/os/windows/resolve_dll.py b/qiling/os/windows/resolve_dll.py index ec220b92d..3a2d7213c 100644 --- a/qiling/os/windows/resolve_dll.py +++ b/qiling/os/windows/resolve_dll.py @@ -88,7 +88,7 @@ def resolve_symbol(ql: Qiling, address: int, size): if (not is_in_executable_memory_address(ql, jump_address)) and (jump_address != -1) and (jump_pointer_address != -1): - print('is_in_executable_memory_address: pointer:{:016x}, address:{:016x}'.format(jump_pointer_address, jump_address)) + #print('is_in_executable_memory_address: pointer:{:016x}, address:{:016x}'.format(jump_pointer_address, jump_address)) load_additional_dll(ql, jump_pointer_address) return @@ -136,8 +136,6 @@ def load_additional_dll(ql, import_address): dll_last_address = 0x0 - #print(ql.loader.import_address_table) - for mi in map_info: dll_name = mi[3] if ('.dll' in dll_name) or ('[PE]' == dll_name): @@ -160,47 +158,88 @@ def load_additional_dll(ql, import_address): #print('Windows/system32/{}'.format(target_dll_name)) - if dll_name == '[PE]': - target_dll_bin = ql.loader.pe - target_dll_image_base = target_dll_bin.OPTIONAL_HEADER.ImageBase - else: - target_dll_bin = pefile.PE(ql.rootfs+'/Windows/system32/{}'.format(target_dll_name)) - target_dll_image_base = target_dll_bin.OPTIONAL_HEADER.ImageBase + # if dll_name == '[PE]': + # target_dll_bin = ql.loader.pe + # target_dll_image_base = target_dll_bin.OPTIONAL_HEADER.ImageBase + # else: + # target_dll_bin = pefile.PE(ql.rootfs+'/Windows/system32/{}'.format(target_dll_name)) + # target_dll_image_base = target_dll_bin.OPTIONAL_HEADER.ImageBase #print(target_dll_bin) # Sometimes pe file don't have DIRECTORY_ENTRY_IMPORT - if not hasattr(target_dll_bin, 'DIRECTORY_ENTRY_IMPORT'): + # if not hasattr(target_dll_bin, 'DIRECTORY_ENTRY_IMPORT'): + # return False + #print(ql.loader.entry_import_table[target_dll_name].keys()) + + if import_address not in ql.loader.entry_import_table[target_dll_name].keys(): + #print('not in table: {}'.format(import_address)) return False - target_symbol = None - for entry_import in target_dll_bin.DIRECTORY_ENTRY_IMPORT: - for entry_import_symbol in entry_import.imports: - if (entry_import_symbol.address - target_dll_image_base + dll_list[dll_name]['base']) == import_address: - target_symbol = entry_import_symbol.name.decode('utf-8') + entry_import = ql.loader.entry_import_table[target_dll_name][import_address] + target_symbol, export_dll_name = entry_import['symbol'], entry_import['dll'] - # Go to proccess of loading additional dll from import_address if the import symbol exists. - export_dll_name = entry_import.dll.decode('utf-8').lower() - #print('export_dll_name: {}'.format(export_dll_name)) - # The case of API Set dll - # ref: https://docs.microsoft.com/en-us/windows/win32/apiindex/windows-apisets - if (export_dll_name[0:4] == 'api-') or (export_dll_name[0:4] == 'ext-'): - export_dll_name, target_symbol = get_export_symbol_from_api_dll(ql, export_dll_name, target_symbol) + #print(export_dll_name, target_symbol) - #print('export_dll_name: {}'.format(export_dll_name)) - # export dll is not exist - if (export_dll_name is None): - continue + if entry_import['dll'] not in ql.loader.import_address_table.keys(): + #print('export_dll_name: {}'.format(export_dll_name)) + # The case of API Set dll + # ref: https://docs.microsoft.com/en-us/windows/win32/apiindex/windows-apisets + if (export_dll_name[0:4] == 'api-') or (export_dll_name[0:4] == 'ext-'): + export_dll_name, target_symbol = get_export_symbol_from_api_dll(ql, export_dll_name, target_symbol) - # *Additional dll must not be loaded because import symbol is not resolved, but the case of API set dll, export_dll might be loaded.* - if (export_dll_name not in dll_list.keys()): - ql.loader.load_dll(export_dll_name.encode('utf-8')) + #print('export_dll_name: {}'.format(export_dll_name)) + # export dll is not exist + if (export_dll_name is None): + return False - export_dll_bin = pefile.PE(ql.rootfs+'/Windows/system32/{}'.format(export_dll_name)) - export_dll_base = get_base_address(ql, export_dll_name) + # *Additional dll must not be loaded because import symbol is not resolved, but the case of API set dll, export_dll might be loaded.* + if (export_dll_name not in dll_list.keys()): + ql.loader.load_dll(export_dll_name.encode('utf-8')) - resolve_import_dll_address(ql, target_dll_bin, target_dll_base, export_dll_bin, export_dll_base, target_symbol, import_address) - return True + + #print('[+] import address: {:016x}, import_dll_base: {:016x}, import_dll_image_base: {:016x}'.format(import_address, import_dll_base, import_dll_image_base)) + # print('[+] memory write {:016x} -> {}'.format( + # import_address, + # (export_symbol_list[0].address+export_dll_base).to_bytes(8,'little'))) + + #print(ql.loader.import_address_table[export_dll_name][target_symbol.encode('utf-8')]) + + export_dll_base = get_base_address(ql, export_dll_name) + ql.mem.write( + import_address, + (ql.loader.import_address_table[export_dll_name][target_symbol.encode('utf-8')]).to_bytes(8,'little') + ) + + # target_symbol = None + # for entry_import in target_dll_bin.DIRECTORY_ENTRY_IMPORT: + # for entry_import_symbol in entry_import.imports: + # if (entry_import_symbol.address - target_dll_image_base + dll_list[dll_name]['base']) == import_address: + # target_symbol = entry_import_symbol.name.decode('utf-8') + + # # Go to proccess of loading additional dll from import_address if the import symbol exists. + # export_dll_name = entry_import.dll.decode('utf-8').lower() + # #print('export_dll_name: {}'.format(export_dll_name)) + # # The case of API Set dll + # # ref: https://docs.microsoft.com/en-us/windows/win32/apiindex/windows-apisets + # if (export_dll_name[0:4] == 'api-') or (export_dll_name[0:4] == 'ext-'): + # export_dll_name, target_symbol = get_export_symbol_from_api_dll(ql, export_dll_name, target_symbol) + + # #print('export_dll_name: {}'.format(export_dll_name)) + # # export dll is not exist + # if (export_dll_name is None): + # continue + + # # *Additional dll must not be loaded because import symbol is not resolved, but the case of API set dll, export_dll might be loaded.* + # if (export_dll_name not in dll_list.keys()): + # ql.loader.load_dll(export_dll_name.encode('utf-8')) + + # export_dll_bin = pefile.PE(ql.rootfs+'/Windows/system32/{}'.format(export_dll_name)) + # export_dll_base = get_base_address(ql, export_dll_name) + + # resolve_import_dll_address(ql, target_dll_bin, target_dll_base, export_dll_bin, export_dll_base, target_symbol, import_address) + + # return True #print('no dll: ', target_dll_name) return False From 6fe5e2941256bc894299a8933ad33db14d7cf01e Mon Sep 17 00:00:00 2001 From: prayer Date: Sun, 18 Jul 2021 16:53:08 +0900 Subject: [PATCH 13/14] update dynamic resolve dll address --- qiling/loader/pe.py | 26 ----- qiling/os/windows/resolve_dll.py | 194 +++++++------------------------ 2 files changed, 42 insertions(+), 178 deletions(-) diff --git a/qiling/loader/pe.py b/qiling/loader/pe.py index 92a5f99a2..837cc96e5 100644 --- a/qiling/loader/pe.py +++ b/qiling/loader/pe.py @@ -614,32 +614,6 @@ def load(self): sys_dlls = self.sys_dlls for each in sys_dlls: super().load_dll(each, self.is_driver) - # 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: - # dll_name = str(entry.dll.lower(), 'utf-8', 'ignore') - # # skip windows API dll - # if (dll_name[0:4] == 'api-') or (dll_name[0:4] == 'ext-'): - # continue - - # super().load_dll(entry.dll, self.is_driver) - # for imp in entry.imports: - # # fix IAT - # # ql.log.info(imp.name) - # # ql.log.info(self.import_address_table[imp.name]) - # if imp.name: - # try: - # addr = self.import_address_table[dll_name][imp.name] - # except KeyError: - # self.ql.log.debug("Error in loading function %s" % imp.name.decode()) - # else: - # addr = self.import_address_table[dll_name][imp.ordinal] - - # if self.ql.archtype == QL_ARCH.X86: - # address = self.ql.pack32(addr) - # else: - # address = self.ql.pack64(addr) - # self.ql.mem.write(imp.address, address) self.ql.log.debug("Done with loading %s" % self.path) self.ql.os.entry_point = self.entry_point diff --git a/qiling/os/windows/resolve_dll.py b/qiling/os/windows/resolve_dll.py index 3a2d7213c..5c907c85e 100644 --- a/qiling/os/windows/resolve_dll.py +++ b/qiling/os/windows/resolve_dll.py @@ -15,13 +15,13 @@ def calibrate_reg_name(reg_name): - if reg_name not in ['sil', 'dil']: + if reg_name not in ["sil", "dil"]: return reg_name - if reg_name == 'sil': - return 'si' - elif reg_name == 'dil': - return 'di' + if reg_name == "sil": + return "si" + elif reg_name == "dil": + return "di" def resolve_symbol(ql: Qiling, address: int, size): #reg = ql.reg.save() @@ -38,35 +38,29 @@ def resolve_symbol(ql: Qiling, address: int, size): jump_address = -1 jump_pointer_address = -1 - #print("0x{:08x}".format(address), op.mnemonic, op.op_str) - if op.mnemonic in ['jmp', 'call', 'mov']: - if op.mnemonic in ['jmp', 'call']: + if op.mnemonic in ["jmp", "call", "mov"]: + if op.mnemonic in ["jmp", "call"]: pointer_operand = op.operands[0] - elif op.mnemonic in ['mov']: + elif op.mnemonic in ["mov"]: pointer_operand = op.operands[1] if pointer_operand.type == X86_OP_MEM: - #print("\t\toperands.type: MEM") jump_pointer_address = 0 if pointer_operand.value.mem.base != 0: - #print("\t\t\toperands.mem.base: REG = %s" % (op.reg_name(pointer_operand.value.mem.base))) reg_name = calibrate_reg_name(op.reg_name(pointer_operand.value.mem.base)) jump_pointer_address += ql.reg.read(reg_name) - if 'ip' in reg_name: + if "ip" in reg_name: jump_pointer_address += size if pointer_operand.value.mem.index != 0: - #print("\t\t\toperands.mem.index: REG = %s" % (op.reg_name(pointer_operand.value.mem.index))) reg_name = calibrate_reg_name(op.reg_name(pointer_operand.value.mem.index)) jump_pointer_address += ql.reg.read(reg_name) - if 'ip' in reg_name: + if "ip" in reg_name: jump_pointer_address += size if pointer_operand.value.mem.disp != 0: - #print("\t\t\toperands.mem.disp: 0x%x" % (pointer_operand.value.mem.disp)) jump_pointer_address += pointer_operand.value.mem.disp - #print(' pointer:{:016x}, address:{:016x}'.format(jump_pointer_address, jump_address)) # check if library is already imported or not. if(is_in_executable_memory_address(ql, jump_pointer_address)): if ql.archtype == QL_ARCH.X8664: @@ -75,7 +69,7 @@ def resolve_symbol(ql: Qiling, address: int, size): jump_pointer_address, 8 ), - 'little' + "little" ) else: jump_address = int.from_bytes( @@ -83,12 +77,11 @@ def resolve_symbol(ql: Qiling, address: int, size): jump_pointer_address, 4 ), - 'little' + "little" ) if (not is_in_executable_memory_address(ql, jump_address)) and (jump_address != -1) and (jump_pointer_address != -1): - #print('is_in_executable_memory_address: pointer:{:016x}, address:{:016x}'.format(jump_pointer_address, jump_address)) load_additional_dll(ql, jump_pointer_address) return @@ -98,7 +91,7 @@ def get_base_address(ql, map_name): for mi in map_info: _map_name = mi[3] - if ('.dll' in _map_name) or ('[PE]' == _map_name): + if (".dll" in _map_name) or ("[PE]" == _map_name): if _map_name == map_name: return mi[0] @@ -106,7 +99,7 @@ def get_base_address(ql, map_name): def is_in_executable_memory_address(ql, address): for mi in ql.mem.map_info: - if ('.dll' in mi[3]) or ('[PE]' == mi[3]): + if (".dll" in mi[3]) or ("[PE]" == mi[3]): if (mi[0] < address) and (address < mi[1]): return True return False @@ -131,190 +124,87 @@ def load_additional_dll(ql, import_address): """ dll_list = {} - # ql.mem.map_info example: [140737221971968, 140737222746112, 7, 'kernel32.dll'] + # ql.mem.map_info example: [140737221971968, 140737222746112, 7, "kernel32.dll"] map_info = ql.mem.map_info - dll_last_address = 0x0 - for mi in map_info: dll_name = mi[3] - if ('.dll' in dll_name) or ('[PE]' == dll_name): + if (".dll" in dll_name) or ("[PE]" == dll_name): dll_list[dll_name] = { - 'dll': dll_name, - 'base': mi[0], - 'end': mi[1], + "dll": dll_name, + "base": mi[0], + "end": mi[1], } - dll_last_address = mi[1] - - #print(dll_list.keys()) for dll_name in dll_list.keys(): - if (dll_list[dll_name]['base'] < import_address) and (import_address < dll_list[dll_name]['end']): + if (dll_list[dll_name]["base"] < import_address) and (import_address < dll_list[dll_name]["end"]): target_dll_name = dll_name - target_dll_base = dll_list[dll_name]['base'] break else: return - #print('Windows/system32/{}'.format(target_dll_name)) - - # if dll_name == '[PE]': - # target_dll_bin = ql.loader.pe - # target_dll_image_base = target_dll_bin.OPTIONAL_HEADER.ImageBase - # else: - # target_dll_bin = pefile.PE(ql.rootfs+'/Windows/system32/{}'.format(target_dll_name)) - # target_dll_image_base = target_dll_bin.OPTIONAL_HEADER.ImageBase - - #print(target_dll_bin) - # Sometimes pe file don't have DIRECTORY_ENTRY_IMPORT - # if not hasattr(target_dll_bin, 'DIRECTORY_ENTRY_IMPORT'): - # return False - #print(ql.loader.entry_import_table[target_dll_name].keys()) - if import_address not in ql.loader.entry_import_table[target_dll_name].keys(): - #print('not in table: {}'.format(import_address)) return False entry_import = ql.loader.entry_import_table[target_dll_name][import_address] - target_symbol, export_dll_name = entry_import['symbol'], entry_import['dll'] + target_symbol, export_dll_name = entry_import["symbol"], entry_import["dll"] - #print(export_dll_name, target_symbol) - - if entry_import['dll'] not in ql.loader.import_address_table.keys(): - #print('export_dll_name: {}'.format(export_dll_name)) + if entry_import["dll"] not in ql.loader.import_address_table.keys(): # The case of API Set dll # ref: https://docs.microsoft.com/en-us/windows/win32/apiindex/windows-apisets - if (export_dll_name[0:4] == 'api-') or (export_dll_name[0:4] == 'ext-'): + if (export_dll_name[0:4] == "api-") or (export_dll_name[0:4] == "ext-"): export_dll_name, target_symbol = get_export_symbol_from_api_dll(ql, export_dll_name, target_symbol) - #print('export_dll_name: {}'.format(export_dll_name)) # export dll is not exist if (export_dll_name is None): return False # *Additional dll must not be loaded because import symbol is not resolved, but the case of API set dll, export_dll might be loaded.* if (export_dll_name not in dll_list.keys()): - ql.loader.load_dll(export_dll_name.encode('utf-8')) - - - - #print('[+] import address: {:016x}, import_dll_base: {:016x}, import_dll_image_base: {:016x}'.format(import_address, import_dll_base, import_dll_image_base)) - # print('[+] memory write {:016x} -> {}'.format( - # import_address, - # (export_symbol_list[0].address+export_dll_base).to_bytes(8,'little'))) - - #print(ql.loader.import_address_table[export_dll_name][target_symbol.encode('utf-8')]) + ql.loader.load_dll(export_dll_name.encode("utf-8")) - export_dll_base = get_base_address(ql, export_dll_name) ql.mem.write( import_address, - (ql.loader.import_address_table[export_dll_name][target_symbol.encode('utf-8')]).to_bytes(8,'little') + (ql.loader.import_address_table[export_dll_name][target_symbol.encode("utf-8")]).to_bytes(8,"little") ) - # target_symbol = None - # for entry_import in target_dll_bin.DIRECTORY_ENTRY_IMPORT: - # for entry_import_symbol in entry_import.imports: - # if (entry_import_symbol.address - target_dll_image_base + dll_list[dll_name]['base']) == import_address: - # target_symbol = entry_import_symbol.name.decode('utf-8') - - # # Go to proccess of loading additional dll from import_address if the import symbol exists. - # export_dll_name = entry_import.dll.decode('utf-8').lower() - # #print('export_dll_name: {}'.format(export_dll_name)) - # # The case of API Set dll - # # ref: https://docs.microsoft.com/en-us/windows/win32/apiindex/windows-apisets - # if (export_dll_name[0:4] == 'api-') or (export_dll_name[0:4] == 'ext-'): - # export_dll_name, target_symbol = get_export_symbol_from_api_dll(ql, export_dll_name, target_symbol) - - # #print('export_dll_name: {}'.format(export_dll_name)) - # # export dll is not exist - # if (export_dll_name is None): - # continue - - # # *Additional dll must not be loaded because import symbol is not resolved, but the case of API set dll, export_dll might be loaded.* - # if (export_dll_name not in dll_list.keys()): - # ql.loader.load_dll(export_dll_name.encode('utf-8')) - - # export_dll_bin = pefile.PE(ql.rootfs+'/Windows/system32/{}'.format(export_dll_name)) - # export_dll_base = get_base_address(ql, export_dll_name) - - # resolve_import_dll_address(ql, target_dll_bin, target_dll_base, export_dll_bin, export_dll_base, target_symbol, import_address) - - # return True - - #print('no dll: ', target_dll_name) - return False - -def resolve_import_dll_address(ql, import_dll_bin, import_dll_base, export_dll_bin, export_dll_base, target_symbol, import_address): - """ - Make IAT of import address. - - Args: - ql(obj): qiling object. - import_dll_name(str): Dll to import. it must be loaded already. - export_dll_name(str): Dll to export. - Return: - boolean: True if Add dll to memory successfully, otherwise False. - """ - - #print(import_dll_base, export_dll_base, target_symbol, import_address) - # dll_name is the dll imported by the binary - import_dll_image_base = import_dll_bin.OPTIONAL_HEADER.ImageBase - - # split \x00 since section.Name sometime like this b'.text\x00\x00\x00' - rdata_section = list(filter(lambda x: x.Name.decode('utf-8').split('\x00')[0] == '.rdata', import_dll_bin.sections)) - - # entry_import_dll_name is the dll described as import dll in dll_name - if import_dll_bin.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT']].VirtualAddress == 0: - return False - - # insert if statement since sometimes DIRECTORY_ENTRY_EXPORT.symbols.name is None - export_symbol_list = list(filter(lambda x: x.name.decode('utf-8') == target_symbol if x.name != None else False, export_dll_bin.DIRECTORY_ENTRY_EXPORT.symbols)) - if len(export_symbol_list) == 0: - return False - - #print('[+] import address: {:016x}, import_dll_base: {:016x}, import_dll_image_base: {:016x}'.format(import_address, import_dll_base, import_dll_image_base)) - # print('[+] memory write {:016x} -> {}'.format( - # import_address, - # (export_symbol_list[0].address+export_dll_base).to_bytes(8,'little'))) + return True - ql.mem.write( - import_address, - (export_symbol_list[0].address+export_dll_base).to_bytes(8,'little') - ) - return True +api_dll_list = {} def get_export_symbol_from_api_dll(ql, api_dll_name, target_symbol): + global api_dll_list + def _get_string_from_pe(api_dll, target_symbol): offset = 0 - string = '' - dll_base = api_dll.OPTIONAL_HEADER.ImageBase + string = "" - export_symbol_list = list(filter(lambda x: x.name.decode('utf-8') == target_symbol, api_dll.DIRECTORY_ENTRY_EXPORT.symbols)) + export_symbol_list = list(filter(lambda x: x.name.decode("utf-8") == target_symbol, api_dll.DIRECTORY_ENTRY_EXPORT.symbols)) if len(export_symbol_list) == 0: - return '' + return "" while True: - #print(hex(export_symbol_list[0].address)) char = api_dll.get_data(export_symbol_list[0].address+offset, 1) - if char == b'\x00': + if char == b"\x00": break - string += char.decode('utf-8') + string += char.decode("utf-8") offset += 1 return string - #print(os.path.join(ql.rootfs, 'Windows/system32/{}'.format(api_dll_name))) - if not os.path.exists(os.path.join(ql.rootfs, 'Windows/system32/{}'.format(api_dll_name))): + if not os.path.exists(os.path.join(ql.rootfs, "Windows/system32/{}".format(api_dll_name))): return None, None - api_dll = pefile.PE(os.path.join(ql.rootfs, 'Windows/system32/{}'.format(api_dll_name))) + if api_dll_name not in api_dll_list.keys(): + api_dll = pefile.PE(os.path.join(ql.rootfs, "Windows/system32/{}".format(api_dll_name))) + api_dll_list[api_dll_name] = api_dll + else: + api_dll = api_dll_list[api_dll_name] - dll_name, export_symbol = _get_string_from_pe(api_dll, target_symbol).split('.') - - #print(dll_name, export_symbol) + dll_name, export_symbol = _get_string_from_pe(api_dll, target_symbol).split(".") - return dll_name+'.dll', export_symbol + return dll_name+".dll", export_symbol From 87229f6005899db4f0e7d4a3203858b56a604c2e Mon Sep 17 00:00:00 2001 From: prayer Date: Mon, 19 Jul 2021 22:38:07 +0900 Subject: [PATCH 14/14] delete Unnecessary description --- qiling/loader/pe.py | 48 +------------------------------- qiling/os/windows/resolve_dll.py | 4 +++ 2 files changed, 5 insertions(+), 47 deletions(-) diff --git a/qiling/loader/pe.py b/qiling/loader/pe.py index 49da62f4d..f8a39e0c7 100644 --- a/qiling/loader/pe.py +++ b/qiling/loader/pe.py @@ -480,6 +480,7 @@ def init_thread_information_block(self): super().init_exports() def load(self): + self.ql.log.info("Qiling dynamic loader start") # set stack pointer self.ql.log.info("Initiate stack address at 0x%x " % self.stack_address) self.ql.mem.map(self.stack_address, self.stack_size, info="[stack]") @@ -505,7 +506,6 @@ def load(self): if hasattr(self.pe, 'DIRECTORY_ENTRY_IMPORT'): for entry_import in self.pe.DIRECTORY_ENTRY_IMPORT: for entry_import_symbol in entry_import.imports: - #print(entry_import_symbol.address, self.pe_image_address, self.image_address) entry_import_list[entry_import_symbol.address] = { 'symbol': entry_import_symbol.name.decode('utf-8'), 'dll': entry_import.dll.decode('utf-8').lower() @@ -644,49 +644,3 @@ def load(self): # move entry_point to ql.os self.ql.os.entry_point = self.entry_point self.init_sp = self.ql.reg.arch_sp - - def _get_export_symbol_from_api_dll(self, api_dll_name, target_symbol): - """ - API set dll (https://docs.microsoft.com/en-us/windows/win32/apiindex/windows-apisets) loader. - The function extract actual export symbol and DLL name from API set dll(likes 'api-ms-xxxx.dll'). - - Args: - api_dll_name (str): API Set DLL name - target_symbol (str): The symbol looking for. - Return: - dll_name (str): actual DLL name. - export_symbol (str): Actual symbol name. - """ - - def _get_string_from_pe(api_dll, target_symbol): - offset = 0 - string = '' - dll_base = api_dll.OPTIONAL_HEADER.ImageBase - - export_symbol_list = list(filter(lambda x: x.name == target_symbol.encode('utf-8'), api_dll.DIRECTORY_ENTRY_EXPORT.symbols)) - if len(export_symbol_list) == 0: - self.ql.log.debug("Error: can't find symbol from API Set dll (symbol: %s, apiset dll: %s" % (api_dll, target_symbol)) - return '' - - while True: - char = api_dll.get_data(export_symbol_list[0].address+offset, 1) - if char == b'\x00': - break - - string += char.decode('utf-8') - offset += 1 - - return string - - try: - api_dll = pefile.PE(os.path.join(self.ql.rootfs, self.ql.dlls, api_dll_name)) - except: - self.ql.log.warning('Failed to load API dll %s' % (api_dll_name)) - return None, None - - # result of _get_string_from_pe has 2 types, "kernel32.GetPriorityClass" and "advapi32.dll.OpenProcessToken" - # therefore, both types need to be supported. - dll_name, export_symbol = _get_string_from_pe(api_dll, target_symbol).rsplit('.', 1) - if dll_name[-4:] == '.dll': - return dll_name, export_symbol - return dll_name+'.dll', export_symbol \ No newline at end of file diff --git a/qiling/os/windows/resolve_dll.py b/qiling/os/windows/resolve_dll.py index 5c907c85e..5c34a8ee8 100644 --- a/qiling/os/windows/resolve_dll.py +++ b/qiling/os/windows/resolve_dll.py @@ -163,6 +163,8 @@ def load_additional_dll(ql, import_address): if (export_dll_name not in dll_list.keys()): ql.loader.load_dll(export_dll_name.encode("utf-8")) + ql.log.debug("load additional DLL (%s -> %s)" % (export_dll_name, target_symbol)) + ql.mem.write( import_address, (ql.loader.import_address_table[export_dll_name][target_symbol.encode("utf-8")]).to_bytes(8,"little") @@ -193,6 +195,8 @@ def _get_string_from_pe(api_dll, target_symbol): offset += 1 return string + ql.log.debug("load windos API (%s -> %s)" % (api_dll_name, target_symbol)) + if not os.path.exists(os.path.join(ql.rootfs, "Windows/system32/{}".format(api_dll_name))): return None, None