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/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 faa347f6a..2a8d5f039 100644 --- a/qiling/loader/pe.py +++ b/qiling/loader/pe.py @@ -17,12 +17,13 @@ from .loader import QlLoader, Image class QlPeCacheEntry: - def __init__(self, ba: int, data: bytearray, cmdlines: Sequence, import_symbols: Mapping, import_table: Mapping): + def __init__(self, ba: int, data: bytearray, cmdlines: Sequence, import_symbols: Mapping, import_table: Mapping, entry_import_list: Mapping): self. ba = ba 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: @@ -42,7 +43,7 @@ def restore(self, path: str) -> Optional[QlPeCacheEntry]: def save(self, path: str, entry: QlPeCacheEntry): fcache = self.create_filename(path) - data = (entry.ba, entry.data, entry.cmdlines, entry.import_symbols, entry.import_table) + data = (entry.ba, 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) @@ -104,6 +105,7 @@ def load_dll(self, name: bytes, driver: bool = False) -> int: if self.ql.mem.is_available(image_base, image_size): 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) @@ -123,6 +125,23 @@ def load_dll(self, name: bytes, driver: bool = False) -> int: 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) + + # 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()) image_base = dll.OPTIONAL_HEADER.ImageBase or self.dll_last_address @@ -160,7 +179,7 @@ def load_dll(self, name: bytes, driver: bool = False) -> int: cmdlines.append(cmdline_entry) if self.libcache: - cached = QlPeCacheEntry(image_base, data, cmdlines, import_symbols, import_table) + cached = QlPeCacheEntry(image_base, data, cmdlines, import_symbols, import_table, entry_import_list) self.libcache.save(path, cached) self.ql.log.info("Cached %s" % path) @@ -170,6 +189,11 @@ def load_dll(self, name: bytes, driver: bool = False) -> int: 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: @@ -427,7 +451,7 @@ def init_ki_user_shared_data(self): shared_user_data_len = self.align(ctypes.sizeof(KUSER_SHARED_DATA), 0x1000) self.ql.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: Qiling): @@ -478,6 +502,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 @@ -507,6 +532,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]") @@ -527,6 +553,18 @@ 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: + 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 @@ -628,28 +666,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') - 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 new file mode 100644 index 000000000..5c34a8ee8 --- /dev/null +++ b/qiling/os/windows/resolve_dll.py @@ -0,0 +1,223 @@ +#!/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 os +import pefile +from capstone import* +from capstone.x86 import * +from unicorn import * + + +def calibrate_reg_name(reg_name): + if reg_name not in ["sil", "dil"]: + return reg_name + + if reg_name == "sil": + return "si" + elif reg_name == "dil": + return "di" + +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) + md.detail = True + + op = list(md.disasm(buf, address))[0] + + jump_address = -1 + jump_pointer_address = -1 + if op.mnemonic in ["jmp", "call", "mov"]: + if op.mnemonic in ["jmp", "call"]: + pointer_operand = op.operands[0] + elif op.mnemonic in ["mov"]: + pointer_operand = op.operands[1] + + if pointer_operand.type == X86_OP_MEM: + jump_pointer_address = 0 + if pointer_operand.value.mem.base != 0: + 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: + 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: + jump_pointer_address += pointer_operand.value.mem.disp + + # 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): + load_additional_dll(ql, jump_pointer_address) + + return + +def get_base_address(ql, map_name): + map_info = 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 + +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 + + for mi in map_info: + dll_name = mi[3] + if (".dll" in dll_name) or ("[PE]" == dll_name): + dll_list[dll_name] = { + "dll": dll_name, + "base": mi[0], + "end": 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 + + if import_address not in ql.loader.entry_import_table[target_dll_name].keys(): + 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"] + + 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-"): + 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): + 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")) + + 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") + ) + + 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 = "" + + 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: + 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 + + 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 + + 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(".") + + return dll_name+".dll", export_symbol + + + + + +if __name__ == "__main__": + 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/qiling/os/windows/windows.py b/qiling/os/windows/windows.py index c7558bde6..04bb07d88 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'] diff --git a/tests/test_pe.py b/tests/test_pe.py index 860b69c54..2b7d32c7e 100644 --- a/tests/test_pe.py +++ b/tests/test_pe.py @@ -32,14 +32,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 @@ -63,7 +63,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 @@ -115,7 +115,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"] @@ -129,7 +129,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 @@ -137,7 +137,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() @@ -150,7 +150,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() @@ -162,31 +162,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 @@ -224,7 +224,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 @@ -233,7 +233,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 @@ -241,7 +241,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): @@ -291,7 +291,7 @@ def my_onexit(ql: Qiling, address: int, params, retval: int): self.set_api_onexit = 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_INTERCEPT.CALL) ql.set_api("puts", my_onexit, QL_INTERCEPT.EXIT) @@ -336,7 +336,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() @@ -363,7 +363,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') @@ -382,7 +382,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.os.stdout = TestOut() ql.run() expected_string = b'\n' @@ -396,7 +396,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.os.stdout = TestOut() ql.run() expected_string = b'\n' @@ -453,6 +453,17 @@ def test_pe_win_x8664_libcache(self): ql.run() del ql + def test_pe_win_x8664_relocate_dll_image_and_api_set_dll(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 + + + if __name__ == "__main__": unittest.main()