From 5db7c5c95401127c3d8d9cc718a2605178c5b86a Mon Sep 17 00:00:00 2001 From: elicn Date: Wed, 24 May 2023 14:01:20 +0300 Subject: [PATCH 01/17] Make sure dllscollector script is ran with admin privs --- examples/scripts/dllscollector.bat | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/scripts/dllscollector.bat b/examples/scripts/dllscollector.bat index cd115093e..2b85a83e9 100644 --- a/examples/scripts/dllscollector.bat +++ b/examples/scripts/dllscollector.bat @@ -4,6 +4,13 @@ :: Create the emulated Windows directory structure and registry :: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:: Test for Admin privileges +NET SESSIONS > NUL 2>&1 +IF %ERRORLEVEL% NEQ 0 ( + ECHO Error: This script requires administrative privileges. + EXIT /B 1 +) + :: Host system directories SET SYSDIR32="%WINDIR%\SysWOW64" SET SYSDIR64="%WINDIR%\System32" From 1947e1c3973c1202b3914df5c03f5466dced61e9 Mon Sep 17 00:00:00 2001 From: elicn Date: Wed, 24 May 2023 14:03:06 +0300 Subject: [PATCH 02/17] Patch string utils to allow specifying maxlength --- qiling/os/utils.py | 57 ++++++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/qiling/os/utils.py b/qiling/os/utils.py index 5e7f58ec2..552f38222 100644 --- a/qiling/os/utils.py +++ b/qiling/os/utils.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # @@ -16,42 +16,61 @@ # TODO: separate windows-specific implementation from qiling.os.windows.structs import make_unicode_string -class QlOsUtils: +class QlOsUtils: ELLIPSIS_PREF = r'__qlva_' def __init__(self, ql: Qiling): self.ql = ql - @staticmethod - def read_string(ql: Qiling, address: int, terminator: bytes) -> str: - result = bytearray() + def read_string(self, address: int, encoding: str, maxlen: int = 0) -> str: + """Read a null-terminated string from memory. + + Args: + address : starting address + encoding: string encoding to use + maxlen : limit number of characters to read before reaching null terminator, + 0 for unlimited length + + Returns: decoded string + """ + + terminator = '\x00'.encode(encoding) + + data = bytearray() charlen = len(terminator) + strlen = 0 - char = ql.mem.read(address, charlen) + while True: + char = self.ql.mem.read(address, charlen) - while char != terminator: - address += charlen - result += char - char = ql.mem.read(address, charlen) + if char == terminator: + break - return result.decode(errors="ignore") + data += char + strlen += 1 - def read_wstring(self, address: int) -> str: - s = QlOsUtils.read_string(self.ql, address, b'\x00\x00') + if strlen == maxlen: + break - # We need to remove \x00 inside the string. Compares do not work otherwise - s = s.replace("\x00", "") + address += charlen + + s = data.decode(encoding, errors='backslashreplace') self.ql.os.stats.log_string(s) return s - def read_cstring(self, address: int) -> str: - s = QlOsUtils.read_string(self.ql, address, b'\x00') + def read_wstring(self, address: int, maxlen: int = 0) -> str: + """Read a null-terminated wide string from memory. + """ - self.ql.os.stats.log_string(s) + return self.read_string(address, 'utf-16le', maxlen) - return s + def read_cstring(self, address: int, maxlen: int = 0) -> str: + """Read a null-terminated ASCII string from memory. + """ + + return self.read_string(address, 'latin1', maxlen) def read_guid(self, address: int) -> UUID: raw_guid = self.ql.mem.read(address, 16) From 26c2aa4551eb2979517b4e254eb9a1b6026640e2 Mon Sep 17 00:00:00 2001 From: elicn Date: Wed, 24 May 2023 14:06:57 +0300 Subject: [PATCH 03/17] Fix PANSI_STRING and PUNICODE_STRING handling --- qiling/os/windows/api.py | 10 +++--- qiling/os/windows/const.py | 4 ++- qiling/os/windows/dlls/ntdll.py | 21 +++++++---- qiling/os/windows/dlls/ntoskrnl.py | 58 +++++++++++++++++------------- qiling/os/windows/structs.py | 6 +++- qiling/os/windows/utils.py | 34 ++++++++++++++++-- 6 files changed, 92 insertions(+), 41 deletions(-) diff --git a/qiling/os/windows/api.py b/qiling/os/windows/api.py index 1960adb8b..2c4587218 100644 --- a/qiling/os/windows/api.py +++ b/qiling/os/windows/api.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # @@ -56,8 +56,6 @@ LPCSTR = STRING LPOSVERSIONINFOA = STRING -PANSI_STRING = STRING -PCANSI_STRING = STRING PCNZCH = STRING PCSTR = STRING PCSZ = STRING @@ -68,9 +66,7 @@ LPOSVERSIONINFOW = WSTRING OLECHAR = WSTRING PCNZWCH = WSTRING -PCUNICODE_STRING = WSTRING PCWSTR = WSTRING -PUNICODE_STRING = WSTRING DLGPROC = POINTER DWORD_PTR = POINTER @@ -123,6 +119,10 @@ LPWSAPROTOCOL_INFOA = POINTER LPWSTR = POINTER MSIHANDLE = POINTER +PANSI_STRING = POINTER +PUNICODE_STRING = POINTER +PCANSI_STRING = POINTER +PCUNICODE_STRING = POINTER PACCESS_STATE = POINTER PBOOL = POINTER PBYTE = POINTER diff --git a/qiling/os/windows/const.py b/qiling/os/windows/const.py index 33e85310b..7168edbe3 100644 --- a/qiling/os/windows/const.py +++ b/qiling/os/windows/const.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # from Registry import Registry @@ -35,6 +35,8 @@ STATUS_INFO_LENGTH_MISMATCH = 0xC0000004 STATUS_INVALID_PARAMETER = 0xC000000D STATUS_INVALID_HANDLE = 0xC0000008 +STATUS_PROCEDURE_NOT_FOUND = 0xC000007A +STATUS_DLL_NOT_FOUND = 0xC0000135 STATUS_PORT_NOT_SET = 0xC0000353 STATUS_NO_YIELD_PERFORMED = 0x40000024 # ... diff --git a/qiling/os/windows/dlls/ntdll.py b/qiling/os/windows/dlls/ntdll.py index 164595496..096473480 100644 --- a/qiling/os/windows/dlls/ntdll.py +++ b/qiling/os/windows/dlls/ntdll.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # @@ -15,6 +15,8 @@ from qiling.os.windows.const import * from qiling.os.windows.handle import * from qiling.os.windows import structs +from qiling.os.windows import utils + # void *memcpy( # void *dest, @@ -397,16 +399,21 @@ def hook_LdrGetProcedureAddress(ql: Qiling, address: int, params): if dll_name is None: ql.log.debug(f'Could not find specified handle {ModuleHandle} in loaded DLL') - return 0 + return STATUS_DLL_NOT_FOUND - identifier = bytes(FunctionName, 'ascii') if FunctionName else Ordinal + identifier = utils.read_pansi_string(ql, FunctionName) if FunctionName else Ordinal iat = ql.loader.import_address_table[dll_name] - if identifier in iat: - ql.mem.write_ptr(FunctionAddress, iat[identifier]) - return 0 + if not identifier: + return STATUS_INVALID_PARAMETER + + if identifier not in iat: + return STATUS_PROCEDURE_NOT_FOUND + + ql.mem.write_ptr(FunctionAddress, iat[identifier]) + + return STATUS_SUCCESS - return 0xFFFFFFFF # NTSYSAPI PVOID RtlAllocateHeap( # PVOID HeapHandle, diff --git a/qiling/os/windows/dlls/ntoskrnl.py b/qiling/os/windows/dlls/ntoskrnl.py index 59d49da6d..af6573b90 100644 --- a/qiling/os/windows/dlls/ntoskrnl.py +++ b/qiling/os/windows/dlls/ntoskrnl.py @@ -11,9 +11,11 @@ from qiling.os.windows.const import * from qiling.os.windows.fncc import * from qiling.os.windows.structs import * +from qiling.os.windows import utils from qiling.os.windows.wdk_const import DO_DEVICE_INITIALIZING, DO_EXCLUSIVE from qiling.utils import verify_ret + # NTSYSAPI NTSTATUS RtlGetVersion( # PRTL_OSVERSIONINFOW lpVersionInformation # ); @@ -431,7 +433,9 @@ def hook_ExFreePoolWithTag(ql: Qiling, address: int, params): ql.os.heap.free(addr) -hook_only_routine_address = [b'IoCreateDeviceSecure'] + +hook_only_routine_address = ['IoCreateDeviceSecure'] + # PVOID MmGetSystemRoutineAddress( # PUNICODE_STRING SystemRoutineName @@ -440,31 +444,35 @@ def hook_ExFreePoolWithTag(ql: Qiling, address: int, params): 'SystemRoutineName' : PUNICODE_STRING }) def hook_MmGetSystemRoutineAddress(ql: Qiling, address: int, params): - SystemRoutineName = bytes(params["SystemRoutineName"], 'ascii') - - # check function name in import table - for dll_name in ('ntoskrnl.exe', 'ntkrnlpa.exe', 'hal.dll'): - if dll_name in ql.loader.import_address_table: - if SystemRoutineName in ql.loader.import_address_table[dll_name]: - return ql.loader.import_address_table[dll_name][SystemRoutineName] - - # function not found! - # we check function name in `hook_only_routine_address`. - if SystemRoutineName in hook_only_routine_address: - index = hook_only_routine_address.index(SystemRoutineName) - # found! + SystemRoutineName = params["SystemRoutineName"] + + routine_name = SystemRoutineName and utils.read_punicode_string(ql, SystemRoutineName) + + if routine_name: + # check function name in import table for dll_name in ('ntoskrnl.exe', 'ntkrnlpa.exe', 'hal.dll'): - image = ql.loader.get_image_by_name(dll_name) - - if image: - # create fake address - new_function_address = image.base + index + 1 - # update import address table - ql.loader.import_symbols[new_function_address] = { - 'name': SystemRoutineName, - 'ordinal': -1 - } - return new_function_address + if dll_name in ql.loader.import_address_table: + if routine_name in ql.loader.import_address_table[dll_name]: + return ql.loader.import_address_table[dll_name][routine_name] + + # function not found! + # we check function name in `hook_only_routine_address`. + if routine_name in hook_only_routine_address: + index = hook_only_routine_address.index(routine_name) + # found! + for dll_name in ('ntoskrnl.exe', 'ntkrnlpa.exe', 'hal.dll'): + image = ql.loader.get_image_by_name(dll_name) + + if image: + # create fake address + new_function_address = image.base + index + 1 + # update import address table + ql.loader.import_symbols[new_function_address] = { + 'name': SystemRoutineName.encode(), + 'ordinal': -1 + } + return new_function_address + return 0 # int _wcsnicmp( diff --git a/qiling/os/windows/structs.py b/qiling/os/windows/structs.py index 71aed2d74..56d685fc6 100644 --- a/qiling/os/windows/structs.py +++ b/qiling/os/windows/structs.py @@ -116,7 +116,7 @@ class PEB(Struct): return PEB -# https://docs.microsoft.com/en-us/windows/win32/api/subauth/ns-subauth-unicode_string +# https://learn.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-_unicode_string @lru_cache(maxsize=2) def make_unicode_string(archbits: int): """Generate a UNICODE_STRING structure class. @@ -135,6 +135,10 @@ class UNICODE_STRING(Struct): return UNICODE_STRING +# https://learn.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-string +make_ansi_string = make_unicode_string + + # https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_driver_object def make_driver_object(archbits: int): """Generate a DRIVER_OBJECT structure class. diff --git a/qiling/os/windows/utils.py b/qiling/os/windows/utils.py index d55c965e3..31915c378 100644 --- a/qiling/os/windows/utils.py +++ b/qiling/os/windows/utils.py @@ -3,7 +3,7 @@ # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # -from typing import Iterable, Tuple, TypeVar +from typing import Iterable, Optional, Tuple, TypeVar from unicorn import UcError @@ -17,6 +17,7 @@ Comparable = TypeVar('Comparable', str, int) + # an alternative to Python2 cmp builtin which no longer exists in Python3 def cmp(a: Comparable, b: Comparable) -> int: return (a > b) - (a < b) @@ -110,7 +111,7 @@ def __free_all(allocations: Iterable[int]) -> None: ql.log.info(f'Executing from {major_func:#x}') try: - # now emulate + # now emulate ql.run(major_func) except UcError as err: verify_ret(ql, err) @@ -124,6 +125,7 @@ def __free_all(allocations: Iterable[int]) -> None: return info + # Emulate DeviceIoControl() of Windows # BOOL DeviceIoControl( # HANDLE hDevice, @@ -264,3 +266,31 @@ def ioctl_code(DeviceType: int, Function: int, Method: int, Access: int) -> int: __free_all(allocations) return status, info, output_data + + +def read_pansi_string(ql: Qiling, ptr: int) -> Optional[str]: + """Read and decode the string referenced by a PANSI_STRING structure. It is + the caller responsibility to make sure the pointer to the structure is accesible. + """ + + astr_obj = make_ansi_string(ql.arch.bits).load_from(ql.mem, ptr) + + if astr_obj.Buffer and astr_obj.Length: + return ql.os.utils.read_cstring(astr_obj.Buffer, maxlen=astr_obj.Length) + + return None + + +def read_punicode_string(ql: Qiling, ptr: int) -> Optional[str]: + """Read and decode the string referenced by a PUNICODE_STRING structure. It is + the caller responsibility to make sure the pointer to the structure is accesible. + """ + + ucstr_obj = make_unicode_string(ql.arch.bits).load_from(ql.mem, ptr) + + if ucstr_obj.Buffer and ucstr_obj.Length: + assert ucstr_obj.Length % 2 == 0, f'wide string size is expected to be a multiplication of 2. got: {ucstr_obj.Length}' + + return ql.os.utils.read_wstring(ucstr_obj.Buffer, maxlen=ucstr_obj.Length // 2) + + return None From 734e23023d4a1030324f1af59dfd77335b8cd45a Mon Sep 17 00:00:00 2001 From: elicn Date: Wed, 24 May 2023 18:41:53 +0300 Subject: [PATCH 04/17] Properly report unsupported arch_prctl code values --- qiling/os/posix/syscall/prctl.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/qiling/os/posix/syscall/prctl.py b/qiling/os/posix/syscall/prctl.py index c4811a5c6..3cc0e4456 100644 --- a/qiling/os/posix/syscall/prctl.py +++ b/qiling/os/posix/syscall/prctl.py @@ -6,25 +6,35 @@ from qiling import Qiling from qiling.arch.x86_const import IA32_FS_BASE_MSR, IA32_GS_BASE_MSR + def ql_syscall_arch_prctl(ql: Qiling, code: int, addr: int): ARCH_SET_GS = 0x1001 ARCH_SET_FS = 0x1002 ARCH_GET_FS = 0x1003 ARCH_GET_GS = 0x1004 + # # definitions related to Intel CET, they are not commonly supported + # ARCH_CET_STATUS = 0x3001 + # ARCH_CET_DISABLE = 0x3002 + # ARCH_CET_LOCK = 0x3003 + # ARCH_CET_ALLOC_SHSTK = 0x3004 + handlers = { - ARCH_SET_GS : lambda : ql.arch.msr.write(IA32_GS_BASE_MSR, addr), - ARCH_SET_FS : lambda : ql.arch.msr.write(IA32_FS_BASE_MSR, addr), - ARCH_GET_FS : lambda : ql.mem.write_ptr(addr, ql.arch.msr.read(IA32_FS_BASE_MSR), 8), - ARCH_GET_GS : lambda : ql.mem.write_ptr(addr, ql.arch.msr.read(IA32_GS_BASE_MSR), 8) + ARCH_SET_GS: lambda: ql.arch.msr.write(IA32_GS_BASE_MSR, addr), + ARCH_SET_FS: lambda: ql.arch.msr.write(IA32_FS_BASE_MSR, addr), + ARCH_GET_FS: lambda: ql.mem.write_ptr(addr, ql.arch.msr.read(IA32_FS_BASE_MSR), 8), + ARCH_GET_GS: lambda: ql.mem.write_ptr(addr, ql.arch.msr.read(IA32_GS_BASE_MSR), 8) } if code not in handlers: - ql.log.warning(f'prctl code {code:#x} not implemented') - else: - handlers[code]() + ql.log.debug(f'prctl code {code:#x} not implemented') + + return -1 # EINVAL + + handlers[code]() return 0 + def ql_syscall_prctl(ql: Qiling, option: int, arg2: int, arg3: int, arg4: int, arg5: int): return 0 From cf61fe6805fa17c8fa1a21e33c3aee5d3cb89ecc Mon Sep 17 00:00:00 2001 From: elicn Date: Wed, 24 May 2023 20:47:43 +0300 Subject: [PATCH 05/17] Correct x86 segment selector and descriptor bits --- qiling/arch/x86_const.py | 52 ++++++++++++++++++++------------------ qiling/arch/x86_utils.py | 18 ++++++------- qiling/os/linux/syscall.py | 4 +-- qiling/os/linux/thread.py | 2 +- 4 files changed, 39 insertions(+), 37 deletions(-) diff --git a/qiling/arch/x86_const.py b/qiling/arch/x86_const.py index d4716ba6e..0bdfe5b46 100644 --- a/qiling/arch/x86_const.py +++ b/qiling/arch/x86_const.py @@ -5,34 +5,38 @@ from unicorn.x86_const import * -QL_X86_F_GRANULARITY = 0x8 -QL_X86_F_PROT_32 = 0x4 -QL_X86_F_LONG = 0x2 -QL_X86_F_AVAILABLE = 0x1 +# segment descriptor bits [47:40] +QL_X86_A_DATA_A = (1 << 0) +QL_X86_A_DATA_W = (1 << 1) +QL_X86_A_DATA_E = (1 << 2) +QL_X86_A_DATA = (0 << 3) -QL_X86_A_PRESENT = 0x80 +QL_X86_A_CODE_A = (1 << 0) +QL_X86_A_CODE_R = (1 << 1) +QL_X86_A_CODE_C = (1 << 2) +QL_X86_A_CODE = (1 << 3) -QL_X86_A_PRIV_3 = 0x60 -QL_X86_A_PRIV_2 = 0x40 -QL_X86_A_PRIV_1 = 0x20 -QL_X86_A_PRIV_0 = 0x0 +QL_X86_A_DESC_SYSTEM = (0 << 4) # S +QL_X86_A_DESC_CODE = (1 << 4) +QL_X86_A_DESC_DATA = (1 << 4) -QL_X86_A_CODE = 0x10 -QL_X86_A_DATA = 0x10 -QL_X86_A_TSS = 0x0 -QL_X86_A_GATE = 0x0 -QL_X86_A_EXEC = 0x8 +QL_X86_A_PRIV_0 = (0b00 << 5) # DPL +QL_X86_A_PRIV_1 = (0b01 << 5) +QL_X86_A_PRIV_2 = (0b10 << 5) +QL_X86_A_PRIV_3 = (0b11 << 5) -QL_X86_A_DATA_WRITABLE = 0x2 -QL_X86_A_CODE_READABLE = 0x2 -QL_X86_A_DIR_CON_BIT = 0x4 +QL_X86_A_PRESENT = (1 << 7) -QL_X86_S_GDT = 0x0 -QL_X86_S_LDT = 0x4 -QL_X86_S_PRIV_3 = 0x3 -QL_X86_S_PRIV_2 = 0x2 -QL_X86_S_PRIV_1 = 0x1 -QL_X86_S_PRIV_0 = 0x0 +# segment descriptor bits [55:52] +QL_X86_F_AVAILABLE = (1 << 0) # AVL +QL_X86_F_LONG = (1 << 1) # L +QL_X86_F_OPSIZE_32 = (1 << 2) # D/B +QL_X86_F_GRANULARITY = (1 << 3) # G + +# segment selector bits +QL_X86_SEGSEL_RPL_MASK = 0b11 +QL_X86_SEGSEL_TI_GDT = (0 << 2) +QL_X86_SEGSEL_TI_LDT = (1 << 2) QL_X86_GDT_ADDR = 0x30000 QL_X86_GDT_LIMIT = 0x1000 @@ -43,8 +47,6 @@ IA32_GS_BASE_MSR = 0xC0000101 IA32_APIC_BASE_MSR = 0x1B -# WINDOWS SETUP VALUE -# Linux also needs these GS_SEGMENT_ADDR = 0x6000000 GS_SEGMENT_SIZE = (20 << 20) # 20 MB diff --git a/qiling/arch/x86_utils.py b/qiling/arch/x86_utils.py index 587477ad7..4726ef745 100644 --- a/qiling/arch/x86_utils.py +++ b/qiling/arch/x86_utils.py @@ -88,12 +88,12 @@ def make_entry(base: int, limit: int, access: int, flags: int) -> bytes: @staticmethod def make_selector(idx: int, rpl: int) -> int: - assert rpl & ~0b11 == 0 + assert rpl & ~QL_X86_SEGSEL_RPL_MASK == 0 - return (idx << 3) | QL_X86_S_GDT | rpl + return (idx << 3) | QL_X86_SEGSEL_TI_GDT | rpl def register_gdt_segment(self, index: int, seg_base: int, seg_limit: int, access: int) -> int: - flags = QL_X86_F_PROT_32 + flags = QL_X86_F_OPSIZE_32 # is this a huge segment? if seg_limit > (1 << 16): @@ -139,7 +139,7 @@ def setup_gs(self, base: int, size: int) -> None: class SegmentManager86(SegmentManager): def setup_cs_ds_ss_es(self, base: int, size: int) -> None: # While debugging the linux kernel segment, the cs segment was found on the third segment of gdt. - access = QL_X86_A_PRESENT | QL_X86_A_CODE | QL_X86_A_CODE_READABLE | QL_X86_A_PRIV_3 | QL_X86_A_EXEC | QL_X86_A_DIR_CON_BIT + access = QL_X86_A_PRESENT | QL_X86_A_PRIV_3 | QL_X86_A_DESC_CODE | QL_X86_A_CODE | QL_X86_A_CODE_C | QL_X86_A_CODE_R selector = self.gdtm.register_gdt_segment(3, base, size - 1, access) self.arch.regs.cs = selector @@ -147,7 +147,7 @@ def setup_cs_ds_ss_es(self, base: int, size: int) -> None: # TODO : The section permission here should be QL_X86_A_PRIV_3, but I do n’t know why it can only be set to QL_X86_A_PRIV_0. # While debugging the Linux kernel segment, I found that the three segments DS, SS, and ES all point to the same location in the GDT table. # This position is the fifth segment table of GDT. - access = QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_0 | QL_X86_A_DIR_CON_BIT + access = QL_X86_A_PRESENT | QL_X86_A_PRIV_0 | QL_X86_A_DESC_DATA | QL_X86_A_DATA | QL_X86_A_DATA_E | QL_X86_A_DATA_W selector = self.gdtm.register_gdt_segment(5, base, size - 1, access) self.arch.regs.ds = selector @@ -155,13 +155,13 @@ def setup_cs_ds_ss_es(self, base: int, size: int) -> None: self.arch.regs.es = selector def setup_fs(self, base: int, size: int) -> None: - access = QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_3 | QL_X86_A_DIR_CON_BIT + access = QL_X86_A_PRESENT | QL_X86_A_PRIV_3 | QL_X86_A_DESC_DATA | QL_X86_A_DATA | QL_X86_A_DATA_E | QL_X86_A_DATA_W selector = self.gdtm.register_gdt_segment(14, base, size - 1, access) self.arch.regs.fs = selector def setup_gs(self, base: int, size: int) -> None: - access = QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_3 | QL_X86_A_DIR_CON_BIT + access = QL_X86_A_PRESENT | QL_X86_A_PRIV_3 | QL_X86_A_DESC_DATA | QL_X86_A_DATA | QL_X86_A_DATA_E | QL_X86_A_DATA_W selector = self.gdtm.register_gdt_segment(15, base, size - 1, access) self.arch.regs.gs = selector @@ -170,14 +170,14 @@ def setup_gs(self, base: int, size: int) -> None: class SegmentManager64(SegmentManager): def setup_cs_ds_ss_es(self, base: int, size: int) -> None: # While debugging the linux kernel segment, the cs segment was found on the sixth segment of gdt. - access = QL_X86_A_PRESENT | QL_X86_A_CODE | QL_X86_A_CODE_READABLE | QL_X86_A_PRIV_3 | QL_X86_A_EXEC | QL_X86_A_DIR_CON_BIT + access = QL_X86_A_PRESENT | QL_X86_A_PRIV_3 | QL_X86_A_DESC_CODE | QL_X86_A_CODE | QL_X86_A_CODE_C | QL_X86_A_CODE_R selector = self.gdtm.register_gdt_segment(6, base, size - 1, access) self.arch.regs.cs = selector # TODO : The section permission here should be QL_X86_A_PRIV_3, but I do n’t know why it can only be set to QL_X86_A_PRIV_0. # When I debug the Linux kernel, I find that only the SS is set to the fifth segment table, and the rest are not set. - access = QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_0 | QL_X86_A_DIR_CON_BIT + access = QL_X86_A_PRESENT | QL_X86_A_PRIV_0 | QL_X86_A_DESC_DATA | QL_X86_A_DATA | QL_X86_A_DATA_E | QL_X86_A_DATA_W selector = self.gdtm.register_gdt_segment(5, base, size - 1, access) # self.arch.regs.ds = selector diff --git a/qiling/os/linux/syscall.py b/qiling/os/linux/syscall.py index c61951c97..6092eefd0 100644 --- a/qiling/os/linux/syscall.py +++ b/qiling/os/linux/syscall.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # @@ -48,7 +48,7 @@ def ql_syscall_set_thread_area(ql: Qiling, u_info_addr: int): index = ql.os.gdtm.get_free_idx(12) if index in (12, 13, 14): - access = QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_3 | QL_X86_A_DIR_CON_BIT + access = QL_X86_A_PRESENT | QL_X86_A_PRIV_3 | QL_X86_A_DESC_DATA | QL_X86_A_DATA | QL_X86_A_DATA_E | QL_X86_A_DATA_W ql.os.gdtm.register_gdt_segment(index, base, limit, access) ql.mem.write_ptr(u_info_addr, index, 4) diff --git a/qiling/os/linux/thread.py b/qiling/os/linux/thread.py index 0c3864bfd..a4162743d 100644 --- a/qiling/os/linux/thread.py +++ b/qiling/os/linux/thread.py @@ -383,7 +383,7 @@ def set_thread_tls(self, tls_addr): index = self.ql.os.gdtm.get_free_idx(12) if index in (12, 13, 14): - access = QL_X86_A_PRESENT | QL_X86_A_DATA | QL_X86_A_DATA_WRITABLE | QL_X86_A_PRIV_3 | QL_X86_A_DIR_CON_BIT + access = QL_X86_A_PRESENT | QL_X86_A_PRIV_3 | QL_X86_A_DESC_DATA | QL_X86_A_DATA | QL_X86_A_DATA_E | QL_X86_A_DATA_W self.ql.os.gdtm.register_gdt_segment(index, base, limit, access) self.ql.mem.write_ptr(tls_addr, index, 4) From 3bd774e23e455e5fec41cb937d766ad76efcf9de Mon Sep 17 00:00:00 2001 From: elicn Date: Sun, 28 May 2023 20:52:59 +0300 Subject: [PATCH 06/17] Support the case of a zero size ELF region --- qiling/loader/elf.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/qiling/loader/elf.py b/qiling/loader/elf.py index f3df83b99..c1d3eb970 100644 --- a/qiling/loader/elf.py +++ b/qiling/loader/elf.py @@ -197,12 +197,16 @@ def load_elf_segments(elffile: ELFFile, load_address: int, info: str): # map the memory regions for lbound, ubound, perms in load_regions: - try: - self.ql.mem.map(lbound, ubound - lbound, perms, os.path.basename(info)) - except QlMemoryMappedError: - self.ql.log.exception(f'Failed to map {lbound:#x}-{ubound:#x}') - else: - self.ql.log.debug(f'Mapped {lbound:#x}-{ubound:#x}') + size = ubound - lbound + + # there might be a region with zero size. in this case, do not mmap it + if size: + try: + self.ql.mem.map(lbound, size, perms, os.path.basename(info)) + except QlMemoryMappedError: + self.ql.log.exception(f'Failed to map {lbound:#x}-{ubound:#x}') + else: + self.ql.log.debug(f'Mapped {lbound:#x}-{ubound:#x}') # load loadable segments contents to memory for seg in load_segments: From 2ce9ff5351b46763065762143f4f0592b45c6261 Mon Sep 17 00:00:00 2001 From: elicn Date: Sun, 28 May 2023 21:46:22 +0300 Subject: [PATCH 07/17] Fix Windows SetInformationProcess --- qiling/os/windows/dlls/ntdll.py | 50 ++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/qiling/os/windows/dlls/ntdll.py b/qiling/os/windows/dlls/ntdll.py index 096473480..b0a6e6352 100644 --- a/qiling/os/windows/dlls/ntdll.py +++ b/qiling/os/windows/dlls/ntdll.py @@ -320,42 +320,38 @@ def hook_NtSetInformationProcess(ql: Qiling, address: int, params): def _SetInformationProcess(ql: Qiling, address: int, params): process = params["ProcessHandle"] flag = params["ProcessInformationClass"] - dst = params["ProcessInformation"] - dst_size = params["ProcessInformationLength"] + ibuf_ptr = params["ProcessInformation"] + ibuf_len = params["ProcessInformationLength"] if flag == ProcessDebugFlags: - value = b"\x01" * 0x4 + flag_name = 'ProcessDebugFlags' + comment = '' + read_len = 4 elif flag == ProcessDebugPort: - value = b"\x00" * 0x4 + flag_name = 'ProcessDebugPort' + comment = '' + read_len = 4 elif flag == ProcessDebugObjectHandle: return STATUS_PORT_NOT_SET elif flag == ProcessBreakOnTermination: - ql.log.debug("The target may be attempting modify a the 'critical' flag of the process") + flag_name = 'ProcessBreakOnTermination' + comment = 'the critical flag of the process' + read_len = 1 # FIXME: is it really a single-byte data? elif flag == ProcessExecuteFlags: - ql.log.debug("The target may be attempting to modify DEP for the process") - - if dst: - ql.mem.write_ptr(dst, 0, 1) + flag_name = 'ProcessExecuteFlags' + comment = 'DEP for the process' + read_len = 1 elif flag == ProcessBasicInformation: - kconf = ql.os.profile['KERNEL'] - pbi_struct = structs.make_process_basic_info(ql.arch.bits) - - pci_obj = pbi_struct( - ExitStatus=0, - PebBaseAddress=ql.loader.TEB.PebAddress, - AffinityMask=0, - BasePriority=0, - UniqueProcessId=kconf.getint('pid'), - InheritedFromUniqueProcessId=kconf.getint('parent_pid') - ) + flag_name = 'ProcessBasicInformation' + comment = 'PEB debug flag for the process' - ql.log.debug("The target may be attempting to modify the PEB debug flag") - value = bytes(pbi_obj) + pbi_struct = structs.make_process_basic_info(ql.arch.bits) + read_len = pbi_struct.sizeof() else: # TODO: support more info class ("flag") values @@ -363,7 +359,15 @@ def _SetInformationProcess(ql: Qiling, address: int, params): return STATUS_UNSUCCESSFUL - # TODO: value is never used after assignment + if ibuf_len >= read_len: + data = (ql.mem.read_ptr if read_len in (1, 2, 4, 8) else ql.mem.read)(ibuf_ptr, read_len) + + ql.log.debug(f'SetInformationProcess: {flag_name} was set to {data}') + + if comment: + ql.log.debug(f'The target may be attempting modify {comment}') + + # NOTE: we don't actually change anything return STATUS_SUCCESS From df329e8afb9098071c26f1aa9f0ebe6d81113019 Mon Sep 17 00:00:00 2001 From: elicn Date: Mon, 29 May 2023 11:38:56 +0300 Subject: [PATCH 08/17] Fix typo --- qiling/os/windows/dlls/ntdll.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiling/os/windows/dlls/ntdll.py b/qiling/os/windows/dlls/ntdll.py index b0a6e6352..92ba4a84b 100644 --- a/qiling/os/windows/dlls/ntdll.py +++ b/qiling/os/windows/dlls/ntdll.py @@ -70,7 +70,7 @@ def _QueryInformationProcess(ql: Qiling, address: int, params): else: # TODO: support more info class ("flag") values - ql.log.info(f'SetInformationProcess: no implementation for info class {flag:#04x}') + ql.log.info(f'QueryInformationProcess: no implementation for info class {flag:#04x}') return STATUS_UNSUCCESSFUL From 86b8b6fee740f0f868a1698ac2f6fc693ae0d591 Mon Sep 17 00:00:00 2001 From: elicn Date: Mon, 29 May 2023 12:09:14 +0300 Subject: [PATCH 09/17] Add tracing example --- examples/extensions/trace/trace.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 examples/extensions/trace/trace.py diff --git a/examples/extensions/trace/trace.py b/examples/extensions/trace/trace.py new file mode 100644 index 000000000..2caf7c270 --- /dev/null +++ b/examples/extensions/trace/trace.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + +from qiling import Qiling +from qiling.const import QL_VERBOSE +from qiling.extensions import trace + +ROOTFS = r'examples/rootfs/x8664_linux' + +if __name__ == '__main__': + # qiling verbosity should be set to DEBUG to show the trace records + ql = Qiling([fr'{ROOTFS}/bin/x8664_hello'], ROOTFS, verbose=QL_VERBOSE.DEBUG) + + # enable full tracing. since full tracing significantly slows down the emulation, + # it may be enabled on demand from a hook, instead + trace.enable_full_trace(ql) + + # sometimes all we need is to see the last operations that led to a crash. the history + # method uses less resources compared to full trace, and emits trace records only when + # a crash occurs. + # + # for example, showing last 32 trace records before the crash: + # trace.enable_history_trace(ql, 32) + + ql.run() From 38a8622f9145a544b21c9e5f720d642a4152479a Mon Sep 17 00:00:00 2001 From: elicn Date: Fri, 16 Jun 2023 17:08:27 +0300 Subject: [PATCH 10/17] archtype and ostype no longer support string values --- examples/adcache_x86_windows_debug.py | 4 +- examples/evm/evm_Hexagon_overflow.py | 3 +- examples/evm/evm_debugger.py | 6 ++- examples/evm/evm_reentrancy.py | 3 +- examples/evm/evm_reentrancy_vol.py | 3 +- examples/evm/evm_simple_sc.py | 5 +- examples/evm/fuzzing/underflow_test.py | 4 +- examples/extensions/r2/hello_r2.py | 4 +- examples/fuzzing/stm32f429/fuzz.py | 9 ++-- examples/hello_arm_uboot.py | 4 +- examples/mcu/gd32vf103_blink.py | 5 +- examples/mcu/stm32f407_gpio_hook.py | 3 +- examples/mcu/stm32f407_hack_lock.py | 8 +-- examples/mcu/stm32f407_mnist_oled.py | 4 +- examples/mcu/stm32f411_dma_logger.py | 6 +-- examples/mcu/stm32f411_freertos.py | 6 +-- examples/mcu/stm32f411_gpio_hook.py | 6 +-- examples/mcu/stm32f411_i2c_lcd.py | 4 +- examples/mcu/stm32f411_interact_usart.py | 6 +-- examples/mcu/stm32f411_spi_oled12864.py | 7 +-- qiling/core.py | 19 +++---- qltool | 35 ++++++++++--- tests/test_blob.py | 4 +- tests/test_debugger.py | 4 +- tests/test_elf.py | 4 +- tests/test_evm.py | 7 +-- tests/test_mcu.py | 64 ++++++++++++------------ tests/test_peshellcode.py | 9 ++-- tests/test_r2.py | 4 +- tests/test_shellcode.py | 32 +++++------- 30 files changed, 149 insertions(+), 133 deletions(-) diff --git a/examples/adcache_x86_windows_debug.py b/examples/adcache_x86_windows_debug.py index 49486aeca..b2ddd9f25 100644 --- a/examples/adcache_x86_windows_debug.py +++ b/examples/adcache_x86_windows_debug.py @@ -8,12 +8,12 @@ from zipfile import ZipFile from qiling import Qiling -from qiling.const import QL_VERBOSE +from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE if __name__ == "__main__": with ZipFile("shellcodes/win32_https_download.zip") as zip_reader: with zip_reader.open('win32_https_download.bin', 'r', b'infected') as f: sc = f.read() - ql = Qiling(code=sc, archtype="x86", ostype="windows", rootfs="rootfs/x86_windows", verbose=QL_VERBOSE.DEBUG) + ql = Qiling(code=sc, archtype=QL_ARCH.X86, ostype=QL_OS.WINDOWS, rootfs="rootfs/x86_windows", verbose=QL_VERBOSE.DEBUG) ql.run() diff --git a/examples/evm/evm_Hexagon_overflow.py b/examples/evm/evm_Hexagon_overflow.py index 7bf4f50e0..2dfdcc6e3 100644 --- a/examples/evm/evm_Hexagon_overflow.py +++ b/examples/evm/evm_Hexagon_overflow.py @@ -10,11 +10,12 @@ sys.path.append("../..") from qiling import * +from qiling.const import QL_ARCH def example_run_evm(): contract = '0x606060405266017dfcdece4000600055341561001a57600080fd5b600160a060020a033316600090815260016020526040902066017dfcdece400090556106eb8061004b6000396000f3006060604052600436106100c45763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde0381146100c9578063095ea7b31461015357806318160ddd1461018957806323b872dd146101ae57806327edf097146101d6578063313ce567146101ff578063378dc3dc1461021257806342966c681461022557806370a082311461023b578063771282f61461025a57806395d89b411461026d578063a9059cbb14610280578063dd62ed3e146102a2575b600080fd5b34156100d457600080fd5b6100dc6102c7565b60405160208082528190810183818151815260200191508051906020019080838360005b83811015610118578082015183820152602001610100565b50505050905090810190601f1680156101455780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561015e57600080fd5b610175600160a060020a03600435166024356102fe565b604051901515815260200160405180910390f35b341561019457600080fd5b61019c6103a4565b60405190815260200160405180910390f35b34156101b957600080fd5b610175600160a060020a03600435811690602435166044356103aa565b34156101e157600080fd5b6101e9610422565b60405160ff909116815260200160405180910390f35b341561020a57600080fd5b6101e9610427565b341561021d57600080fd5b61019c61042c565b341561023057600080fd5b610175600435610437565b341561024657600080fd5b61019c600160a060020a03600435166104ea565b341561026557600080fd5b61019c6104fc565b341561027857600080fd5b6100dc610502565b341561028b57600080fd5b610175600160a060020a0360043516602435610539565b34156102ad57600080fd5b61019c600160a060020a036004358116906024351661054f565b60408051908101604052600781527f48657861676f6e00000000000000000000000000000000000000000000000000602082015281565b60008115806103305750600160a060020a03338116600090815260026020908152604080832093871683529290522054155b151561033b57600080fd5b600160a060020a03338116600081815260026020908152604080832094881680845294909152908190208590557f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259085905190815260200160405180910390a350600192915050565b60005490565b600160a060020a03808416600090815260026020908152604080832033909416835292905290812054829010156103e057600080fd5b600160a060020a038085166000908152600260209081526040808320339094168352929052208054839003905561041884848461056c565b5060019392505050565b600281565b600481565b66017dfcdece400081565b600160a060020a0333166000908152600160205260408120548290101561045d57600080fd5b600160a060020a033316600081815260016020526040808220805486900390558180527fa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb4980548601905581548590039091557fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca59084905190815260200160405180910390a2506001919050565b60016020526000908152604090205481565b60005481565b60408051908101604052600381527f4858470000000000000000000000000000000000000000000000000000000000602082015281565b600061054633848461056c565b50600192915050565b600260209081526000928352604080842090915290825290205481565b600160a060020a038216151561058157600080fd5b600160a060020a038316600090815260016020526040902054600282019010156105aa57600080fd5b600160a060020a038216600090815260016020526040902054818101116105d057600080fd5b600160a060020a03808416600081815260016020526040808220805460011990879003810190915593861682528082208054860190558180527fa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb4980546002908101909155825490940190915590917fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5915160ff909116815260200160405180910390a281600160a060020a031683600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405190815260200160405180910390a35050505600a165627a7a72305820fbef5b10322242b8659b5de8e24ec1cf5e809831f6f7c08e52112f76daa31aef0029' - ql = Qiling(code=contract, archtype="evm") + ql = Qiling(code=contract, archtype=QL_ARCH.EVM) user1 = ql.arch.evm.create_account(balance=100*10**18) user2 = ql.arch.evm.create_account(balance=100*10**18) diff --git a/examples/evm/evm_debugger.py b/examples/evm/evm_debugger.py index 068aedad9..0d593cca7 100644 --- a/examples/evm/evm_debugger.py +++ b/examples/evm/evm_debugger.py @@ -6,11 +6,13 @@ import sys sys.path.append("../..") -from qiling import * +from qiling import Qiling +from qiling.const import QL_ARCH + if __name__ == '__main__': contract = '0x6060604052341561000f57600080fd5b60405160208061031c833981016040528080519060200190919050508060018190556000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555050610299806100836000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806318160ddd1461005c57806370a0823114610085578063a9059cbb146100d2575b600080fd5b341561006757600080fd5b61006f61012c565b6040518082815260200191505060405180910390f35b341561009057600080fd5b6100bc600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610132565b6040518082815260200191505060405180910390f35b34156100dd57600080fd5b610112600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061017a565b604051808215151515815260200191505060405180910390f35b60015481565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080826000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205403101515156101cb57600080fd5b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555060019050929150505600a165627a7a7230582098f1551a391a3e65b3ce45cfa2b3fa5f91eea9a3e7181a81454e025ea0d7151c0029' - ql = Qiling(code=contract, archtype="evm") + ql = Qiling(code=contract, archtype=QL_ARCH.EVM) ql.debugger = True # Add Balance Var to the contract diff --git a/examples/evm/evm_reentrancy.py b/examples/evm/evm_reentrancy.py index 8c9259562..73f63237a 100644 --- a/examples/evm/evm_reentrancy.py +++ b/examples/evm/evm_reentrancy.py @@ -10,13 +10,14 @@ from qiling.arch.evm.vm.utils import bytecode_to_bytes, runtime_code_detector from qiling.arch.evm.vm.vm import BaseVM from qiling.arch.evm.constants import CREATE_CONTRACT_ADDRESS +from qiling.const import QL_ARCH if __name__ == '__main__': # Attack_contract = '0x608060405234801561001057600080fd5b5060405160208061046d83398101806040528101908080519060200190929190505050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506103ea806100836000396000f300608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680636289d38514610152578063acd2e6e51461015c578063ff11e1db146101b3575b670de0b6b3a76400006000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16311115610150576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663155dd5ee670de0b6b3a76400006040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561013757600080fd5b505af115801561014b573d6000803e3d6000fd5b505050505b005b61015a6101ca565b005b34801561016857600080fd5b50610171610339565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156101bf57600080fd5b506101c861035e565b005b670de0b6b3a764000034101515156101e157600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663e2c41dbc670de0b6b3a76400006040518263ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004016000604051808303818588803b15801561026e57600080fd5b505af1158015610282573d6000803e3d6000fd5b50505050506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663155dd5ee670de0b6b3a76400006040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561031f57600080fd5b505af1158015610333573d6000803e3d6000fd5b50505050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b3373ffffffffffffffffffffffffffffffffffffffff166108fc3073ffffffffffffffffffffffffffffffffffffffff16319081150290604051600060405180830381858888f193505050501580156103bb573d6000803e3d6000fd5b505600a165627a7a723058204ad3139b1085c12112b76e9eab70c6589942d6e84eb3d8329a644eca757c19d00029' Attack_contract = '0x608060405234801561001057600080fd5b506040516020806104b883398101806040528101908080519060200190929190505050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050610435806100836000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063a75e462514610179578063ff11e1db146101e1575b670de0b6b3a76400006000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16311115610177576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600060149054906101000a90047c0100000000000000000000000000000000000000000000000000000000027c01000000000000000000000000000000000000000000000000000000009004670de0b6b3a76400006040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808267ffffffffffffffff1681526020019150506000604051808303816000875af192505050505b005b6101df60048036038101908080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690602001909291905050506101f8565b005b3480156101ed57600080fd5b506101f66103a9565b005b80600060146101000a81548163ffffffff02191690837c010000000000000000000000000000000000000000000000000000000090040217905550670de0b6b3a7640000341015151561024a57600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16670de0b6b3a7640000837c01000000000000000000000000000000000000000000000000000000009004906040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038185885af19350505050506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16817c01000000000000000000000000000000000000000000000000000000009004670de0b6b3a76400006040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808267ffffffffffffffff1681526020019150506000604051808303816000875af192505050505050565b3373ffffffffffffffffffffffffffffffffffffffff166108fc3073ffffffffffffffffffffffffffffffffffffffff16319081150290604051600060405180830381858888f19350505050158015610406573d6000803e3d6000fd5b505600a165627a7a723058205aacb19a5864d2c460aed6c844f2aca575d87de6477ac757a72511bb2975b3f80029' - ql = Qiling(code=Attack_contract, archtype="evm") + ql = Qiling(code=Attack_contract, archtype=QL_ARCH.EVM) vm:BaseVM = ql.arch.evm.vm C1 = b'\xaa' * 20 diff --git a/examples/evm/evm_reentrancy_vol.py b/examples/evm/evm_reentrancy_vol.py index ae732cc25..c7be400c2 100644 --- a/examples/evm/evm_reentrancy_vol.py +++ b/examples/evm/evm_reentrancy_vol.py @@ -10,12 +10,13 @@ from qiling.arch.evm.vm.utils import bytecode_to_bytes, runtime_code_detector from qiling.arch.evm.vm.vm import BaseVM from qiling.arch.evm.constants import CREATE_CONTRACT_ADDRESS +from qiling.const import QL_ARCH def template(vic_contract, deposit, withdraw): Attack_contract = '0x608060405234801561001057600080fd5b506040516020806104b883398101806040528101908080519060200190929190505050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050610435806100836000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063a75e462514610179578063ff11e1db146101e1575b670de0b6b3a76400006000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16311115610177576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600060149054906101000a90047c0100000000000000000000000000000000000000000000000000000000027c01000000000000000000000000000000000000000000000000000000009004670de0b6b3a76400006040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808267ffffffffffffffff1681526020019150506000604051808303816000875af192505050505b005b6101df60048036038101908080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690602001909291905050506101f8565b005b3480156101ed57600080fd5b506101f66103a9565b005b80600060146101000a81548163ffffffff02191690837c010000000000000000000000000000000000000000000000000000000090040217905550670de0b6b3a7640000341015151561024a57600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16670de0b6b3a7640000837c01000000000000000000000000000000000000000000000000000000009004906040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038185885af19350505050506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16817c01000000000000000000000000000000000000000000000000000000009004670de0b6b3a76400006040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808267ffffffffffffffff1681526020019150506000604051808303816000875af192505050505050565b3373ffffffffffffffffffffffffffffffffffffffff166108fc3073ffffffffffffffffffffffffffffffffffffffff16319081150290604051600060405180830381858888f19350505050158015610406573d6000803e3d6000fd5b505600a165627a7a723058205aacb19a5864d2c460aed6c844f2aca575d87de6477ac757a72511bb2975b3f80029' - ql = Qiling(code=Attack_contract, archtype="evm") + ql = Qiling(code=Attack_contract, archtype=QL_ARCH.EVM) vm:BaseVM = ql.arch.evm.vm C1 = b'\xaa' * 20 diff --git a/examples/evm/evm_simple_sc.py b/examples/evm/evm_simple_sc.py index fc99438cb..54eed51b4 100644 --- a/examples/evm/evm_simple_sc.py +++ b/examples/evm/evm_simple_sc.py @@ -6,13 +6,14 @@ import sys sys.path.append("../..") -from qiling import * +from qiling import Qiling +from qiling.const import QL_ARCH def example_run_evm(): contract = '0x6060604052341561000f57600080fd5b60405160208061031c833981016040528080519060200190919050508060018190556000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555050610299806100836000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806318160ddd1461005c57806370a0823114610085578063a9059cbb146100d2575b600080fd5b341561006757600080fd5b61006f61012c565b6040518082815260200191505060405180910390f35b341561009057600080fd5b6100bc600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610132565b6040518082815260200191505060405180910390f35b34156100dd57600080fd5b610112600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061017a565b604051808215151515815260200191505060405180910390f35b60015481565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080826000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205403101515156101cb57600080fd5b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555060019050929150505600a165627a7a7230582098f1551a391a3e65b3ce45cfa2b3fa5f91eea9a3e7181a81454e025ea0d7151c0029' - ql = Qiling(code=contract, archtype="evm") + ql = Qiling(code=contract, archtype=QL_ARCH.EVM) # Add Balance Var to the contract bal = ql.arch.evm.abi.convert(['uint256'], [20]) diff --git a/examples/evm/fuzzing/underflow_test.py b/examples/evm/fuzzing/underflow_test.py index b9ad9a2cc..10fa8ed7e 100644 --- a/examples/evm/fuzzing/underflow_test.py +++ b/examples/evm/fuzzing/underflow_test.py @@ -8,10 +8,12 @@ sys.path.append("../../../..") from qiling import * +from qiling import Qiling +from qiling.const import QL_ARCH def underflow(fuzz_balance): code = '0x6060604052341561000f57600080fd5b60405160208061031c833981016040528080519060200190919050508060018190556000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555050610299806100836000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806318160ddd1461005c57806370a0823114610085578063a9059cbb146100d2575b600080fd5b341561006757600080fd5b61006f61012c565b6040518082815260200191505060405180910390f35b341561009057600080fd5b6100bc600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610132565b6040518082815260200191505060405180910390f35b34156100dd57600080fd5b610112600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061017a565b604051808215151515815260200191505060405180910390f35b60015481565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080826000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205403101515156101cb57600080fd5b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555060019050929150505600a165627a7a7230582098f1551a391a3e65b3ce45cfa2b3fa5f91eea9a3e7181a81454e025ea0d7151c0029' - ql = Qiling(code=code, archtype="evm") + ql = Qiling(code=code, archtype=QL_ARCH.EVM) # Add Balance Var to the contract bal = ql.arch.evm.abi.convert(['uint256'], [20]) diff --git a/examples/extensions/r2/hello_r2.py b/examples/extensions/r2/hello_r2.py index ebd54c452..e2919da6f 100644 --- a/examples/extensions/r2/hello_r2.py +++ b/examples/extensions/r2/hello_r2.py @@ -7,7 +7,7 @@ sys.path.append('..') from qiling import Qiling -from qiling.const import QL_VERBOSE +from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE from qiling.extensions.r2 import R2 @@ -43,7 +43,7 @@ def my_sandbox(path, rootfs): # test shellcode mode ARM64_LIN = bytes.fromhex('420002ca210080d2400080d2c81880d2010000d4e60300aa01020010020280d2681980d2010000d4410080d2420002cae00306aa080380d2010000d4210400f165ffff54e0000010420002ca210001caa81b80d2010000d4020004d27f0000012f62696e2f736800') print("\nLinux ARM 64bit Shellcode") - ql = Qiling(code=ARM64_LIN, archtype="arm64", ostype="linux", verbose=QL_VERBOSE.DEBUG) + ql = Qiling(code=ARM64_LIN, archtype=QL_ARCH.ARM64, ostype=QL_OS.LINUX, verbose=QL_VERBOSE.DEBUG) r2 = R2(ql) # disassemble 32 instructions print(r2._cmd('pd 32')) diff --git a/examples/fuzzing/stm32f429/fuzz.py b/examples/fuzzing/stm32f429/fuzz.py index c4da71397..a2be712e7 100644 --- a/examples/fuzzing/stm32f429/fuzz.py +++ b/examples/fuzzing/stm32f429/fuzz.py @@ -10,7 +10,7 @@ sys.path.append("../../..") from qiling.core import Qiling -from qiling.const import QL_VERBOSE +from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE from qiling.extensions.afl import ql_afl_fuzz_custom from qiling.extensions.mcu.stm32f4 import stm32f429 @@ -18,11 +18,8 @@ from unicorn import UC_ERR_OK, UcError def main(input_file: str): - ql = Qiling(["../../rootfs/mcu/stm32f429/bof.elf"], - archtype="cortex_m", - env=stm32f429, - ostype='mcu', - verbose=QL_VERBOSE.DISABLED) + ql = Qiling(["../../rootfs/mcu/stm32f429/bof.elf"], + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f429, verbose=QL_VERBOSE.DISABLED) ql.hw.create('rcc') ql.hw.create('usart2') diff --git a/examples/hello_arm_uboot.py b/examples/hello_arm_uboot.py index 90087f3d6..0cdb70a2b 100644 --- a/examples/hello_arm_uboot.py +++ b/examples/hello_arm_uboot.py @@ -7,7 +7,7 @@ sys.path.append("..") from qiling.core import Qiling -from qiling.const import QL_VERBOSE +from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE from qiling.os.const import STRING def get_kaimendaji_password(): @@ -60,7 +60,7 @@ def partial_run_init(ql: Qiling): with open("../examples/rootfs/blob/u-boot.bin.img", "rb") as f: uboot_code = f.read() - ql = Qiling(code=uboot_code[0x40:], archtype="arm", ostype="blob", profile="uboot_bin.ql", verbose=QL_VERBOSE.OFF) + ql = Qiling(code=uboot_code[0x40:], archtype=QL_ARCH.ARM, ostype=QL_OS.BLOB, profile="uboot_bin.ql", verbose=QL_VERBOSE.OFF) image_base_addr = ql.loader.load_address ql.hook_address(my_getenv, image_base_addr + 0x13AC0) diff --git a/examples/mcu/gd32vf103_blink.py b/examples/mcu/gd32vf103_blink.py index 003e5a1c5..6ffbaab84 100644 --- a/examples/mcu/gd32vf103_blink.py +++ b/examples/mcu/gd32vf103_blink.py @@ -9,9 +9,10 @@ from qiling.core import Qiling from qiling.const import QL_VERBOSE from qiling.extensions.mcu.gd32vf1 import gd32vf103 +from qiling.const import QL_ARCH, QL_OS -ql = Qiling(['../rootfs/mcu/gd32vf103/blink.hex'], archtype="riscv64", ostype="mcu", - env=gd32vf103, verbose=QL_VERBOSE.DEBUG) +ql = Qiling(['../rootfs/mcu/gd32vf103/blink.hex'], archtype=QL_ARCH.RISCV64, ostype=QL_OS.MCU, + env=gd32vf103, verbose=QL_VERBOSE.DEBUG) ql.hw.create('rcu') ql.hw.create('gpioa').watch() diff --git a/examples/mcu/stm32f407_gpio_hook.py b/examples/mcu/stm32f407_gpio_hook.py index 5d0c9dbe9..e2c845e96 100644 --- a/examples/mcu/stm32f407_gpio_hook.py +++ b/examples/mcu/stm32f407_gpio_hook.py @@ -10,10 +10,11 @@ from qiling.const import QL_VERBOSE from qiling.extensions.mcu.stm32f4 import stm32f407 from qiling.hw.external_device.oled.ssd1306 import PyGameSSD1306Spi +from qiling.const import QL_ARCH, QL_OS ql = Qiling(["../rootfs/mcu/stm32f407/ai-sine-test.elf"], - archtype="cortex_m", ostype="mcu", env=stm32f407, verbose=QL_VERBOSE.DEFAULT) + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f407, verbose=QL_VERBOSE.DEFAULT) ql.hw.create('rcc') ql.hw.create('pwr') diff --git a/examples/mcu/stm32f407_hack_lock.py b/examples/mcu/stm32f407_hack_lock.py index de41dad44..b6a2bbfc6 100644 --- a/examples/mcu/stm32f407_hack_lock.py +++ b/examples/mcu/stm32f407_hack_lock.py @@ -11,7 +11,7 @@ sys.path.append("../..") from qiling.core import Qiling -from qiling.const import QL_VERBOSE +from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE from qiling.extensions.mcu.stm32f4 import stm32f407 @@ -26,9 +26,9 @@ def dicts(): # Cracking the passwd of lock def crack(passwd): - ql = Qiling(["../../examples/rootfs/mcu/stm32f407/backdoorlock.hex"], - archtype="cortex_m", ostype="mcu", env=stm32f407, verbose=QL_VERBOSE.DISABLED) - + ql = Qiling(["../../examples/rootfs/mcu/stm32f407/backdoorlock.hex"], + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f407, verbose=QL_VERBOSE.DISABLED) + ql.hw.create('spi2') ql.hw.create('gpioe') ql.hw.create('gpiof') diff --git a/examples/mcu/stm32f407_mnist_oled.py b/examples/mcu/stm32f407_mnist_oled.py index 6055518d8..bc4eb9b28 100644 --- a/examples/mcu/stm32f407_mnist_oled.py +++ b/examples/mcu/stm32f407_mnist_oled.py @@ -7,13 +7,13 @@ sys.path.append("../..") from qiling.core import Qiling -from qiling.const import QL_VERBOSE +from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE from qiling.extensions.mcu.stm32f4 import stm32f407 from qiling.hw.external_device.oled.ssd1306 import PyGameSSD1306Spi ql = Qiling(["../rootfs/mcu/stm32f407/mnist.bin", 0x8000000], - archtype="cortex_m", ostype="mcu", env=stm32f407, verbose=QL_VERBOSE.DEFAULT) + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f407, verbose=QL_VERBOSE.DEFAULT) ql.hw.create('rcc') ql.hw.create('gpiod') diff --git a/examples/mcu/stm32f411_dma_logger.py b/examples/mcu/stm32f411_dma_logger.py index 5ab10c515..0e129b58d 100644 --- a/examples/mcu/stm32f411_dma_logger.py +++ b/examples/mcu/stm32f411_dma_logger.py @@ -7,12 +7,12 @@ sys.path.append("../..") from qiling.core import Qiling -from qiling.const import QL_VERBOSE +from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE from qiling.extensions.mcu.stm32f4 import stm32f411 def stm32f411_dma(): - ql = Qiling(["../rootfs/mcu/stm32f411/dma-clock.hex"], - archtype="cortex_m", ostype="mcu", env=stm32f411, verbose=QL_VERBOSE.DEBUG) + ql = Qiling(["../rootfs/mcu/stm32f411/dma-clock.hex"], + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f411, verbose=QL_VERBOSE.DEBUG) ql.hw.create('usart2').watch() ql.hw.create('dma1').watch() diff --git a/examples/mcu/stm32f411_freertos.py b/examples/mcu/stm32f411_freertos.py index ee003f141..c5c2ed460 100644 --- a/examples/mcu/stm32f411_freertos.py +++ b/examples/mcu/stm32f411_freertos.py @@ -7,13 +7,13 @@ sys.path.append("../..") from qiling.core import Qiling -from qiling.const import QL_VERBOSE +from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE from qiling.extensions.mcu.stm32f4 import stm32f411 def stm32f411_freertos(): - ql = Qiling(["../rootfs/mcu/stm32f411/os-demo.hex"], - archtype="cortex_m", ostype="mcu", env=stm32f411, verbose=QL_VERBOSE.DEBUG) + ql = Qiling(["../rootfs/mcu/stm32f411/os-demo.hex"], + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f411, verbose=QL_VERBOSE.DEBUG) ql.hw.create('usart2').watch() ql.hw.create('gpioa').watch() diff --git a/examples/mcu/stm32f411_gpio_hook.py b/examples/mcu/stm32f411_gpio_hook.py index 5fbb23979..22f369003 100644 --- a/examples/mcu/stm32f411_gpio_hook.py +++ b/examples/mcu/stm32f411_gpio_hook.py @@ -7,12 +7,12 @@ sys.path.append("../..") from qiling.core import Qiling -from qiling.const import QL_VERBOSE +from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE from qiling.extensions.mcu.stm32f4 import stm32f411 def test_mcu_gpio_stm32f411(): - ql = Qiling(["../../examples/rootfs/mcu/stm32f411/hello_gpioA.hex"], - archtype="cortex_m", ostype="mcu", env=stm32f411, verbose=QL_VERBOSE.DEBUG) + ql = Qiling(["../../examples/rootfs/mcu/stm32f411/hello_gpioA.hex"], + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f411, verbose=QL_VERBOSE.DEBUG) ql.hw.create('usart2').watch() ql.hw.create('rcc').watch() diff --git a/examples/mcu/stm32f411_i2c_lcd.py b/examples/mcu/stm32f411_i2c_lcd.py index 48b143834..1e98722e3 100644 --- a/examples/mcu/stm32f411_i2c_lcd.py +++ b/examples/mcu/stm32f411_i2c_lcd.py @@ -8,12 +8,12 @@ sys.path.append("../..") from qiling.core import Qiling -from qiling.const import QL_VERBOSE +from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE from qiling.hw.external_device.lcd.lcd1602 import PyGameLCD1602 from qiling.extensions.mcu.stm32f4 import stm32f411 def create(path, lcd): - ql = Qiling([path], archtype="cortex_m", ostype="mcu", env=stm32f411, verbose=QL_VERBOSE.DEBUG) + ql = Qiling([path], archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f411, verbose=QL_VERBOSE.DEBUG) ql.hw.create('i2c1') ql.hw.create('rcc') diff --git a/examples/mcu/stm32f411_interact_usart.py b/examples/mcu/stm32f411_interact_usart.py index 19f5ba839..ab1dcabad 100644 --- a/examples/mcu/stm32f411_interact_usart.py +++ b/examples/mcu/stm32f411_interact_usart.py @@ -11,12 +11,12 @@ import threading from qiling.core import Qiling -from qiling.const import QL_VERBOSE +from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE from qiling.extensions.mcu.stm32f4 import stm32f411 -ql = Qiling(["../../examples/rootfs/mcu/stm32f411/md5_server.hex"], - archtype="cortex_m", ostype="mcu", env=stm32f411, verbose=QL_VERBOSE.OFF) +ql = Qiling(["../../examples/rootfs/mcu/stm32f411/md5_server.hex"], + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f411, verbose=QL_VERBOSE.OFF) ql.hw.create('usart2') ql.hw.create('rcc') diff --git a/examples/mcu/stm32f411_spi_oled12864.py b/examples/mcu/stm32f411_spi_oled12864.py index 69fdcb707..c8877f209 100644 --- a/examples/mcu/stm32f411_spi_oled12864.py +++ b/examples/mcu/stm32f411_spi_oled12864.py @@ -7,12 +7,13 @@ sys.path.append("../..") from qiling.core import Qiling -from qiling.const import QL_VERBOSE +from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE from qiling.extensions.mcu.stm32f4 import stm32f411 from qiling.hw.external_device.oled.ssd1306 import PyGameSSD1306Spi -ql = Qiling(['../rootfs/mcu/stm32f411/oled12864.hex'], - archtype="cortex_m", ostype="mcu", env=stm32f411, verbose=QL_VERBOSE.DEFAULT) + +ql = Qiling(['../rootfs/mcu/stm32f411/oled12864.hex'], + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f411, verbose=QL_VERBOSE.DEFAULT) ql.hw.create('rcc') ql.hw.create('gpioa') diff --git a/qiling/core.py b/qiling/core.py index 6cc5c4706..ab85be0f9 100644 --- a/qiling/core.py +++ b/qiling/core.py @@ -36,8 +36,8 @@ def __init__( rootfs: str = r'.', env: MutableMapping[AnyStr, AnyStr] = {}, code: Optional[bytes] = None, - ostype: Union[str, QL_OS] = None, - archtype: Union[str, QL_ARCH] = None, + ostype: Optional[QL_OS] = None, + archtype: Optional[QL_ARCH] = None, verbose: QL_VERBOSE = QL_VERBOSE.DEFAULT, profile: str = None, console: bool = True, @@ -117,13 +117,8 @@ def __init__( ################# # arch os setup # ################# - if type(archtype) is str: - archtype = arch_convert(archtype) - if type(ostype) is str: - ostype = os_convert(ostype) - - # if provided arch was invalid or not provided, guess arch and os + # if arch was not provided, guess arch and os if archtype is None: guessed_archtype, guessed_ostype, guessed_archendian = ql_guess_emu_env(self.path) @@ -140,12 +135,12 @@ def __init__( ostype = arch_os_convert(archtype) # arch should have been determined by now; fail if not - if type(archtype) is not QL_ARCH: - raise QlErrorArch(f'Unknown or unsupported architecture: "{archtype}"') + if archtype is None: + raise QlErrorArch(f'Unknown or unsupported architecture') # os should have been determined by now; fail if not - if type(ostype) is not QL_OS: - raise QlErrorOsType(f'Unknown or unsupported operating system: "{ostype}"') + if ostype is None: + raise QlErrorOsType(f'Unknown or unsupported operating system') # if endianess is still undetermined, set it to little-endian. # this setting is ignored for architectures with predefined endianess diff --git a/qltool b/qltool index be6a52d3f..e949d985f 100755 --- a/qltool +++ b/qltool @@ -9,6 +9,7 @@ import sys import ast import pickle from pprint import pprint +from typing import TYPE_CHECKING, Mapping, Type from unicorn import __version__ as uc_ver from qiling import __version__ as ql_ver @@ -16,11 +17,15 @@ from qiling import __version__ as ql_ver from qiling import Qiling from qiling.arch import utils as arch_utils from qiling.debugger.qdb import QlQdb -from qiling.utils import arch_convert from qiling.const import QL_VERBOSE, QL_ENDIAN, os_map, arch_map, verbose_map from qiling.extensions.coverage import utils as cov_utils from qiling.extensions import report + +if TYPE_CHECKING: + from enum import Enum + + # read code from file def read_file(fname: str): with open(fname, "rb") as f: @@ -38,9 +43,24 @@ class __arg_env(argparse.Action): setattr(namespace, self.dest, env or {}) -class __arg_verbose(argparse.Action): - def __call__(self, parser, namespace, values, option_string): - setattr(namespace, self.dest, verbose_map[values]) + +def __make_enum_arg(enum_rmap: Mapping[str, 'Enum'], aliases: Mapping[str, str] = {}) -> Type[argparse.Action]: + class __enum_arg(argparse.Action): + def __call__(self, parser, namespace, values: str, option_string): + values = values.casefold() + + if values in aliases: + values = aliases[values] + + setattr(namespace, self.dest, enum_rmap[values]) + + return __enum_arg + + +__arg_archtype = __make_enum_arg(arch_map, {'x86_64': 'x8664', 'riscv32': 'riscv'}) +__arg_ostype = __make_enum_arg(os_map, {'darwin': 'macos'}) +__arg_verbose = __make_enum_arg(verbose_map) + def handle_code(options: argparse.Namespace): archendian = { @@ -67,9 +87,8 @@ def handle_code(options: argparse.Namespace): elif options.format == 'asm': print ("Load ASM from FILE") assembly = read_file(options.filename) - archtype = arch_convert(options.arch) - assembler = arch_utils.assembler(archtype, archendian, options.thumb) + assembler = arch_utils.assembler(options.arch, archendian, options.thumb) code, _ = assembler.asm(assembly) code = bytes(code) @@ -185,10 +204,10 @@ if __name__ == '__main__': code_parser = commands.add_parser('code', help='execute a shellcode') code_parser.add_argument('-f', '--filename', metavar="FILE", help="filename") code_parser.add_argument('-i', '--input', metavar="INPUT", dest="input", help='input hex value') - code_parser.add_argument('--arch', required=True, choices=arch_map) + code_parser.add_argument('--arch', required=True, choices=arch_map, action=__arg_archtype) code_parser.add_argument('--thumb', action='store_true', default=False, help='specify thumb mode for ARM') code_parser.add_argument('--endian', choices=('little', 'big'), default='little', help='specify endianess for bi-endian archs') - code_parser.add_argument('--os', required=True, choices=os_map) + code_parser.add_argument('--os', required=True, choices=os_map, action=__arg_ostype) code_parser.add_argument('--rootfs', default='.', help='emulated root filesystem, that is where all libraries reside') code_parser.add_argument('--format', choices=('asm', 'hex', 'bin'), default='bin', help='input file format') diff --git a/tests/test_blob.py b/tests/test_blob.py index 1a702f01c..26bd5f7f2 100644 --- a/tests/test_blob.py +++ b/tests/test_blob.py @@ -7,7 +7,7 @@ sys.path.append("..") from qiling.core import Qiling -from qiling.const import QL_VERBOSE +from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE from qiling.os.const import STRING class BlobTest(unittest.TestCase): @@ -51,7 +51,7 @@ def partial_run_init(ql): with open("../examples/rootfs/blob/u-boot.bin.img", "rb") as f: uboot_code = f.read() - ql = Qiling(code=uboot_code[0x40:], archtype="arm", ostype="blob", profile="profiles/uboot_bin.ql", verbose=QL_VERBOSE.DEBUG) + ql = Qiling(code=uboot_code[0x40:], archtype=QL_ARCH.ARM, ostype=QL_OS.BLOB, profile="profiles/uboot_bin.ql", verbose=QL_VERBOSE.DEBUG) image_base_addr = ql.loader.load_address ql.hook_address(my_getenv, image_base_addr + 0x13AC0) diff --git a/tests/test_debugger.py b/tests/test_debugger.py index 9003fe1cb..ca7ca33e9 100644 --- a/tests/test_debugger.py +++ b/tests/test_debugger.py @@ -7,7 +7,7 @@ sys.path.append("..") from qiling import Qiling -from qiling.const import QL_VERBOSE +from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE class SimpleGdbClient: DELAY = 0.6 @@ -165,7 +165,7 @@ def gdb_test_client(): def test_gdbdebug_shellcode_server(self): X8664_LIN = bytes.fromhex('31c048bbd19d9691d08c97ff48f7db53545f995257545eb03b0f05') - ql = Qiling(code=X8664_LIN, archtype='x8664', ostype='linux') + ql = Qiling(code=X8664_LIN, archtype=QL_ARCH.X8664, ostype=QL_OS.LINUX) ql.debugger = 'gdb:127.0.0.1:9998' def gdb_test_client(): diff --git a/tests/test_elf.py b/tests/test_elf.py index 48d071865..dc9f187b2 100644 --- a/tests/test_elf.py +++ b/tests/test_elf.py @@ -7,7 +7,7 @@ sys.path.append("..") from qiling import Qiling -from qiling.const import QL_OS, QL_INTERCEPT, QL_STOP, QL_VERBOSE +from qiling.const import QL_ARCH, QL_OS, QL_INTERCEPT, QL_STOP, QL_VERBOSE from qiling.exception import * from qiling.extensions import pipe from qiling.os.const import STRING @@ -1113,7 +1113,7 @@ def test_elf_linux_x86_getdents64(self): del ql def test_memory_search(self): - ql = Qiling(code = b"\xCC", archtype = "x8664", ostype = "linux", verbose=QL_VERBOSE.DEBUG) + ql = Qiling(code=b"\xCC", archtype=QL_ARCH.X8664, ostype=QL_OS.LINUX, verbose=QL_VERBOSE.DEBUG) ql.mem.map(0x1000, 0x1000) ql.mem.map(0x2000, 0x1000) diff --git a/tests/test_evm.py b/tests/test_evm.py index 4e8c1d4f9..f7cc193bc 100644 --- a/tests/test_evm.py +++ b/tests/test_evm.py @@ -4,6 +4,7 @@ sys.path.append("..") from qiling import Qiling +from qiling.const import QL_ARCH, QL_VERBOSE SECRET_KEY = os.environ.get('AM_I_IN_A_DOCKER_CONTAINER', False) @@ -27,7 +28,7 @@ def __init__(self) -> None: class EVMTest(unittest.TestCase): def test_underflow_code(self): - ql = Qiling(code="0x6060604052341561000f57600080fd5b60405160208061031c833981016040528080519060200190919050508060018190556000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555050610299806100836000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806318160ddd1461005c57806370a0823114610085578063a9059cbb146100d2575b600080fd5b341561006757600080fd5b61006f61012c565b6040518082815260200191505060405180910390f35b341561009057600080fd5b6100bc600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610132565b6040518082815260200191505060405180910390f35b34156100dd57600080fd5b610112600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061017a565b604051808215151515815260200191505060405180910390f35b60015481565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080826000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205403101515156101cb57600080fd5b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555060019050929150505600a165627a7a7230582098f1551a391a3e65b3ce45cfa2b3fa5f91eea9a3e7181a81454e025ea0d7151c0029",archtype="evm", verbose=4) + ql = Qiling(code="0x6060604052341561000f57600080fd5b60405160208061031c833981016040528080519060200190919050508060018190556000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555050610299806100836000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806318160ddd1461005c57806370a0823114610085578063a9059cbb146100d2575b600080fd5b341561006757600080fd5b61006f61012c565b6040518082815260200191505060405180910390f35b341561009057600080fd5b6100bc600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610132565b6040518082815260200191505060405180910390f35b34156100dd57600080fd5b610112600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061017a565b604051808215151515815260200191505060405180910390f35b60015481565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080826000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205403101515156101cb57600080fd5b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555060019050929150505600a165627a7a7230582098f1551a391a3e65b3ce45cfa2b3fa5f91eea9a3e7181a81454e025ea0d7151c0029",archtype=QL_ARCH.EVM, verbose=QL_VERBOSE.DEBUG) testcheck = Checklist() argu = ql.arch.evm.abi.convert(['uint256'], [20]) code = ql.code + argu @@ -83,7 +84,7 @@ def check_balance(sender, destination): self.assertTrue(testcheck.visited_hookinsn) def test_underflow_filename(self): - ql = Qiling(["../examples/rootfs/evm/Hexagon.hex"], archtype="evm", verbose=4) + ql = Qiling(["../examples/rootfs/evm/Hexagon.hex"], archtype=QL_ARCH.EVM, verbose=QL_VERBOSE.DEBUG) user1 = ql.arch.evm.create_account(balance=100*10**18) user2 = ql.arch.evm.create_account(balance=100*10**18) @@ -118,7 +119,7 @@ def check_balance(sender, destination): self.assertEqual(int(result.output.hex()[2:], 16), 452312848583266388373324160190187140051835877600158453279131187530910662654) def test_abi_encoding(self): - ql = Qiling(code="0x608060405234801561001057600080fd5b506101a4806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063ead710c414610030575b600080fd5b6100e96004803603602081101561004657600080fd5b810190808035906020019064010000000081111561006357600080fd5b82018360208201111561007557600080fd5b8035906020019184600183028401116401000000008311171561009757600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610164565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561012957808201518184015260208101905061010e565b50505050905090810190601f1680156101565780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b606081905091905056fea2646970667358221220cf43353b75256fc42aaffd9632e06963c5c2aad72a91004bfd2f98cd56ae1a0c64736f6c63430006000033",archtype="evm", verbose=4) + ql = Qiling(code="0x608060405234801561001057600080fd5b506101a4806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063ead710c414610030575b600080fd5b6100e96004803603602081101561004657600080fd5b810190808035906020019064010000000081111561006357600080fd5b82018360208201111561007557600080fd5b8035906020019184600183028401116401000000008311171561009757600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610164565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561012957808201518184015260208101905061010e565b50505050905090810190601f1680156101565780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b606081905091905056fea2646970667358221220cf43353b75256fc42aaffd9632e06963c5c2aad72a91004bfd2f98cd56ae1a0c64736f6c63430006000033",archtype=QL_ARCH.EVM, verbose=QL_VERBOSE.DEBUG) user1 = ql.arch.evm.create_account(balance=100*10**18) c1 = ql.arch.evm.create_account() diff --git a/tests/test_mcu.py b/tests/test_mcu.py index 8fa7500e1..775700c21 100644 --- a/tests/test_mcu.py +++ b/tests/test_mcu.py @@ -8,7 +8,7 @@ sys.path.append("..") from qiling.core import Qiling -from qiling.const import QL_VERBOSE +from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE from qiling.extensions.mcu.stm32f4 import stm32f407, stm32f411, stm32f429 from qiling.extensions.mcu.stm32f1 import stm32f103 from qiling.extensions.mcu.atmel import sam3x8e @@ -16,8 +16,8 @@ class MCUTest(unittest.TestCase): def test_mcu_led_stm32f411(self): - ql = Qiling(["../examples/rootfs/mcu/stm32f411/rand_blink.hex"], - archtype="cortex_m", ostype="mcu", env=stm32f411, verbose=QL_VERBOSE.DISASM) + ql = Qiling(["../examples/rootfs/mcu/stm32f411/rand_blink.hex"], + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f411, verbose=QL_VERBOSE.DISASM) # Set verbose=QL_VERBOSE.DEFAULT to find warning ql.run(count=1000) @@ -26,9 +26,9 @@ def test_mcu_led_stm32f411(self): def test_mcu_snapshot_stm32f411(self): def create_qiling(): - ql = Qiling(["../examples/rootfs/mcu/stm32f411/hello_usart.hex"], - archtype="cortex_m", ostype="mcu", env=stm32f411) - + ql = Qiling(["../examples/rootfs/mcu/stm32f411/hello_usart.hex"], + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f411) + ql.hw.create('usart2') ql.hw.create('rcc') @@ -53,8 +53,8 @@ def create_qiling(): del ql1, ql2 def test_mcu_usart_input_stm32f411(self): - ql = Qiling(["../examples/rootfs/mcu/stm32f411/md5_server.hex"], - archtype="cortex_m", ostype="mcu", env=stm32f411, verbose=QL_VERBOSE.OFF) + ql = Qiling(["../examples/rootfs/mcu/stm32f411/md5_server.hex"], + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f411, verbose=QL_VERBOSE.OFF) ql.hw.create('usart2') ql.hw.create('rcc') @@ -74,8 +74,8 @@ def test_mcu_usart_input_stm32f411(self): del ql def test_mcu_patch_stm32f411(self): - ql = Qiling(["../examples/rootfs/mcu/stm32f411/patch_test.hex"], - archtype="cortex_m", ostype="mcu", env=stm32f411, verbose=QL_VERBOSE.DEFAULT) + ql = Qiling(["../examples/rootfs/mcu/stm32f411/patch_test.hex"], + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f411, verbose=QL_VERBOSE.DEFAULT) ql.hw.create('usart2') ql.hw.create('rcc') @@ -88,7 +88,7 @@ def test_mcu_patch_stm32f411(self): def test_mcu_freertos_stm32f411(self): ql = Qiling(["../examples/rootfs/mcu/stm32f411/os-demo.elf"], - archtype="cortex_m", ostype="mcu", env=stm32f411, verbose=QL_VERBOSE.DISABLED) + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f411, verbose=QL_VERBOSE.DISABLED) ql.hw.create('usart2') ql.hw.create('rcc') @@ -110,8 +110,8 @@ def counter(): del ql def test_mcu_dma_stm32f411(self): - ql = Qiling(["../examples/rootfs/mcu/stm32f411/dma-clock.elf"], - archtype="cortex_m", ostype="mcu", env=stm32f411, verbose=QL_VERBOSE.DEFAULT) + ql = Qiling(["../examples/rootfs/mcu/stm32f411/dma-clock.elf"], + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f411, verbose=QL_VERBOSE.DEFAULT) ql.hw.create('usart2') ql.hw.create('dma1') @@ -129,7 +129,7 @@ def test_mcu_dma_stm32f411(self): def test_mcu_i2c_stm32f411(self): ql = Qiling(["../examples/rootfs/mcu/stm32f411/i2c-lcd.bin", 0x8000000], - archtype="cortex_m", ostype="mcu", env=stm32f411, verbose=QL_VERBOSE.DEFAULT) + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f411, verbose=QL_VERBOSE.DEFAULT) ql.hw.create('i2c1') ql.hw.create('rcc') @@ -161,7 +161,7 @@ def step(self): def test_mcu_spi_stm32f411(self): ql = Qiling(["../examples/rootfs/mcu/stm32f411/spi-test.bin", 0x8000000], - archtype="cortex_m", ostype="mcu", env=stm32f411, verbose=QL_VERBOSE.DEFAULT) + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f411, verbose=QL_VERBOSE.DEFAULT) ql.hw.create('spi1') ql.hw.create('rcc') @@ -175,7 +175,7 @@ def test_mcu_spi_stm32f411(self): def test_mcu_led_rust_stm32f411(self): ql = Qiling(["../examples/rootfs/mcu/stm32f411/led-rust.hex"], - archtype="cortex_m", ostype="mcu", env=stm32f411, verbose=QL_VERBOSE.DEFAULT) + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f411, verbose=QL_VERBOSE.DEFAULT) count = 0 def counter(): @@ -192,9 +192,9 @@ def counter(): def test_mcu_hacklock_stm32f407(self): def crack(passwd): - ql = Qiling(["../examples/rootfs/mcu/stm32f407/backdoorlock.hex"], - archtype="cortex_m", ostype="mcu", env=stm32f407, verbose=QL_VERBOSE.OFF) - + ql = Qiling(["../examples/rootfs/mcu/stm32f407/backdoorlock.hex"], + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f407, verbose=QL_VERBOSE.OFF) + ql.hw.create('spi2') ql.hw.create('gpioe') ql.hw.create('gpiof') @@ -221,8 +221,8 @@ def crack(passwd): self.assertFalse(crack('123456')) def test_mcu_tim_speed_stm32f411(self): - ql = Qiling(['../examples/rootfs/mcu/stm32f411/basic-timer.elf'], - archtype="cortex_m", ostype="mcu", env=stm32f411, verbose=QL_VERBOSE.DEFAULT) + ql = Qiling(['../examples/rootfs/mcu/stm32f411/basic-timer.elf'], + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f411, verbose=QL_VERBOSE.DEFAULT) ql.hw.create('rcc') ql.hw.create('flash interface') @@ -260,8 +260,8 @@ def counter(): self.assertTrue(ql.hw.usart2.recv().startswith(b'hello\n')) def test_mcu_i2c_interrupt_stm32f411(self): - ql = Qiling(['../examples/rootfs/mcu/stm32f411/i2cit-lcd.elf'], - archtype="cortex_m", ostype="mcu", env=stm32f411, verbose=QL_VERBOSE.DEFAULT) + ql = Qiling(['../examples/rootfs/mcu/stm32f411/i2cit-lcd.elf'], + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f411, verbose=QL_VERBOSE.DEFAULT) ql.hw.create('i2c1') ql.hw.create('rcc').watch() @@ -295,8 +295,8 @@ def skip_delay(ql): def test_mcu_blink_gd32vf103(self): - ql = Qiling(['../examples/rootfs/mcu/gd32vf103/blink.hex'], archtype="riscv", - env=gd32vf103, ostype="mcu", verbose=QL_VERBOSE.DEFAULT) + ql = Qiling(['../examples/rootfs/mcu/gd32vf103/blink.hex'], + archtype=QL_ARCH.RISCV, ostype=QL_OS.MCU, env=gd32vf103, verbose=QL_VERBOSE.DEFAULT) ql.hw.create('rcu') ql.hw.create('gpioa') @@ -322,7 +322,7 @@ def counter(): def test_mcu_crc_stm32f407(self): ql = Qiling(["../examples/rootfs/mcu/stm32f407/ai-sine-test.elf"], - archtype="cortex_m", ostype="mcu", env=stm32f407, verbose=QL_VERBOSE.DEFAULT) + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f407, verbose=QL_VERBOSE.DEFAULT) ql.hw.create('rcc') ql.hw.create('pwr') @@ -350,7 +350,7 @@ def indicator(ql): def test_mcu_usart_stm32f103(self): ql = Qiling(["../examples/rootfs/mcu/stm32f103/sctf2020-password-lock-plus.hex"], - archtype="cortex_m", ostype="mcu", env=stm32f103, verbose=QL_VERBOSE.DEFAULT) + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f103, verbose=QL_VERBOSE.DEFAULT) ql.hw.create('rcc') ql.hw.create('flash interface') @@ -378,7 +378,7 @@ def gpio_set_cb(pin): def test_mcu_serial_sam3x8e(self): ql = Qiling(["../examples/rootfs/mcu/sam3x8e/serial.ino.hex"], - archtype="cortex_m", ostype="mcu", env=sam3x8e, verbose=QL_VERBOSE.DEFAULT) + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=sam3x8e, verbose=QL_VERBOSE.DEFAULT) ql.hw.create('wdt') ql.hw.create('efc0') @@ -400,8 +400,8 @@ def test_mcu_serial_sam3x8e(self): del ql def test_mcu_hackme_stm32f429(self): - ql = Qiling(["../examples/rootfs/mcu/stm32f429/bof.elf"], - archtype="cortex_m", env=stm32f429, ostype='mcu', verbose=QL_VERBOSE.DISABLED) + ql = Qiling(["../examples/rootfs/mcu/stm32f429/bof.elf"], + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f429, verbose=QL_VERBOSE.DISABLED) ql.hw.create('rcc') ql.hw.create('usart2') @@ -424,8 +424,8 @@ def test_mcu_hackme_stm32f429(self): self.assertEqual(ql.hw.usart3.recv(), b'Welcome to the world of Hacking!\naaaaaaaaaaaaaaaaaaaa\xa9\x05\n') def test_mcu_fastmode_stm32f429(self): - ql = Qiling(["../examples/rootfs/mcu/stm32f429/bof.elf"], - archtype="cortex_m", env=stm32f429, ostype='mcu', verbose=QL_VERBOSE.DEFAULT) + ql = Qiling(["../examples/rootfs/mcu/stm32f429/bof.elf"], + archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f429, verbose=QL_VERBOSE.DEFAULT) ql.hw.create('rcc') ql.hw.create('usart2') diff --git a/tests/test_peshellcode.py b/tests/test_peshellcode.py index 72ed657f4..bac350cdf 100644 --- a/tests/test_peshellcode.py +++ b/tests/test_peshellcode.py @@ -7,6 +7,7 @@ sys.path.append("..") from qiling import Qiling +from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE X86_WIN = bytes.fromhex(''' fce8820000006089e531c0648b50308b520c8b52148b72280fb74a2631ffac3c @@ -34,18 +35,18 @@ class PEShellcodeTest(unittest.TestCase): def test_windowssc_x86(self): - ql = Qiling(code=X86_WIN, archtype="x86", ostype="windows", rootfs="../examples/rootfs/x86_windows") + ql = Qiling(code=X86_WIN, rootfs=r'../examples/rootfs/x86_windows', archtype=QL_ARCH.X86, ostype=QL_OS.WINDOWS, verbose=QL_VERBOSE.OFF) ql.run() del ql def test_windowssc_x64(self): - ql = Qiling(code=X8664_WIN, archtype="x8664", ostype="windows", rootfs="../examples/rootfs/x8664_windows") + ql = Qiling(code=X8664_WIN, rootfs=r'../examples/rootfs/x8664_windows', archtype=QL_ARCH.X8664, ostype=QL_OS.WINDOWS, verbose=QL_VERBOSE.OFF) ql.run() del ql def test_read_ptr32(self): - ql = Qiling(code=POINTER_TEST, archtype="x86", ostype="windows", rootfs="../examples/rootfs/x86_windows") + ql = Qiling(code=POINTER_TEST, rootfs=r'../examples/rootfs/x86_windows', archtype=QL_ARCH.X86, ostype=QL_OS.WINDOWS, verbose=QL_VERBOSE.OFF) addr = ql.loader.entry_point self.assertEqual(0x11, ql.mem.read_ptr(addr, 1)) @@ -56,7 +57,7 @@ def test_read_ptr32(self): del ql def test_read_ptr64(self): - ql = Qiling(code=POINTER_TEST, archtype="x8664", ostype="windows", rootfs="../examples/rootfs/x8664_windows") + ql = Qiling(code=POINTER_TEST, rootfs=r'../examples/rootfs/x8664_windows', archtype=QL_ARCH.X8664, ostype=QL_OS.WINDOWS, verbose=QL_VERBOSE.OFF) addr = ql.loader.entry_point self.assertEqual(0x11, ql.mem.read_ptr(addr, 1)) diff --git a/tests/test_r2.py b/tests/test_r2.py index b25681089..06cdacbeb 100644 --- a/tests/test_r2.py +++ b/tests/test_r2.py @@ -4,7 +4,7 @@ sys.path.append("..") from qiling import Qiling -from qiling.const import QL_VERBOSE +from qiling.const import QL_ARCH, QL_VERBOSE try: from qiling.extensions.r2.r2 import R2 @@ -19,7 +19,7 @@ @unittest.skipUnless(test_r2, 'libr is missing') class R2Test(unittest.TestCase): def test_shellcode_disasm(self): - ql = Qiling(code=EVM_CODE, archtype="evm", verbose=QL_VERBOSE.DISABLED) + ql = Qiling(code=EVM_CODE, archtype=QL_ARCH.EVM, verbose=QL_VERBOSE.DISABLED) r2 = R2(ql) pd = r2._cmd("pd 32") self.assertTrue('callvalue' in pd) diff --git a/tests/test_shellcode.py b/tests/test_shellcode.py index 3aa0adb9c..4d3974153 100644 --- a/tests/test_shellcode.py +++ b/tests/test_shellcode.py @@ -8,7 +8,7 @@ sys.path.append("..") from qiling import Qiling -from qiling.const import QL_INTERCEPT, QL_VERBOSE +from qiling.const import QL_ARCH, QL_OS, QL_INTERCEPT, QL_VERBOSE # test = bytes.fromhex('cccc') @@ -89,17 +89,17 @@ def graceful_execve(ql: Qiling, pathname: int, argv: int, envp: int, retval: int class TestShellcode(unittest.TestCase): def test_linux_x86(self): print("Linux X86 32bit Shellcode") - ql = Qiling(code=X86_LIN, archtype="x86", ostype="linux", verbose=QL_VERBOSE.OFF) + ql = Qiling(code=X86_LIN, archtype=QL_ARCH.X86, ostype=QL_OS.LINUX, verbose=QL_VERBOSE.OFF) ql.run() def test_linux_x64(self): print("Linux X86 64bit Shellcode") - ql = Qiling(code=X8664_LIN, archtype="x8664", ostype="linux", verbose=QL_VERBOSE.OFF) + ql = Qiling(code=X8664_LIN, archtype=QL_ARCH.X8664, ostype=QL_OS.LINUX, verbose=QL_VERBOSE.OFF) ql.run() def test_linux_mips32(self): print("Linux MIPS 32bit EL Shellcode") - ql = Qiling(code=MIPS32EL_LIN, archtype="mips", ostype="linux", verbose=QL_VERBOSE.OFF) + ql = Qiling(code=MIPS32EL_LIN, archtype=QL_ARCH.MIPS, ostype=QL_OS.LINUX, verbose=QL_VERBOSE.OFF) ql.os.set_syscall('execve', graceful_execve, QL_INTERCEPT.EXIT) ql.run() @@ -107,17 +107,17 @@ def test_linux_mips32(self): # This shellcode needs to be changed to something non-blocking def test_linux_arm(self): print("Linux ARM 32bit Shellcode") - ql = Qiling(code=ARM_LIN, archtype="arm", ostype="linux", verbose=QL_VERBOSE.OFF) + ql = Qiling(code=ARM_LIN, archtype=QL_ARCH.ARM, ostype=QL_OS.LINUX, verbose=QL_VERBOSE.OFF) ql.run() def test_linux_arm_thumb(self): print("Linux ARM Thumb Shllcode") - ql = Qiling(code=ARM_THUMB, archtype="arm", ostype="linux", verbose=QL_VERBOSE.OFF, thumb=True) + ql = Qiling(code=ARM_THUMB, archtype=QL_ARCH.ARM, ostype=QL_OS.LINUX, verbose=QL_VERBOSE.OFF, thumb=True) ql.run() def test_linux_arm64(self): print("Linux ARM 64bit Shellcode") - ql = Qiling(code=ARM64_LIN, archtype="arm64", ostype="linux", verbose=QL_VERBOSE.OFF) + ql = Qiling(code=ARM64_LIN, archtype=QL_ARCH.ARM64, ostype=QL_OS.LINUX, verbose=QL_VERBOSE.OFF) ql.os.set_syscall('execve', graceful_execve, QL_INTERCEPT.EXIT) ql.run() @@ -125,37 +125,29 @@ def test_linux_arm64(self): # #This shellcode needs to be changed to something simpler not requiring rootfs # def test_windows_x86(self): # print("Windows X86 32bit Shellcode") - # ql = Qiling(code=X86_WIN, archtype="x86", ostype="windows", rootfs="../examples/rootfs/x86_reactos", verbose=QL_VERBOSE.OFF) + # ql = Qiling(code=X86_WIN, archtype=QL_ARCH.X86, ostype=QL_OS.WINDOWS, rootfs="../examples/rootfs/x86_reactos", verbose=QL_VERBOSE.OFF) # ql.run() # #This shellcode needs to be changed to something simpler not requiring rootfs # def test_windows_x64(self): # print("\nWindows X8664 64bit Shellcode") - # ql = Qiling(code=X8664_WIN, archtype="x8664", ostype="windows", rootfs="../examples/rootfs/x86_reactos", verbose=QL_VERBOSE.OFF) + # ql = Qiling(code=X8664_WIN, archtype=QL_ARCH.X8664, ostype=QL_OS.WINDOWS, rootfs="../examples/rootfs/x86_reactos", verbose=QL_VERBOSE.OFF) # ql.run() # #This shellcode needs to be changed to something simpler, listen is blocking # def test_freebsd_x64(self): # print("FreeBSD X86 64bit Shellcode") - # ql = Qiling(code=X8664_FBSD, archtype="x8664", ostype="freebsd", verbose=QL_VERBOSE.OFF) + # ql = Qiling(code=X8664_FBSD, archtype=QL_ARCH.X8664, ostype=QL_OS.FREEBSD, verbose=QL_VERBOSE.OFF) # ql.run() # def test_macos_x64(self): # print("macos X86 64bit Shellcode") - # ql = Qiling(code=X8664_macos, archtype="x8664", ostype="macos", verbose=QL_VERBOSE.OFF) + # ql = Qiling(code=X8664_macos, archtype=QL_ARCH.X8664, ostype=QL_OS.MACOS, verbose=QL_VERBOSE.OFF) # ql.run() - # def test_invalid_os(self): - # print("Testing Unknown OS") - # self.assertRaises(QlErrorOsType, Qiling, code=test, archtype="arm64", ostype="qilingos", verbose=QL_VERBOSE.DEFAULT ) - - # def test_invalid_arch(self): - # print("Testing Unknown Arch") - # self.assertRaises(QlErrorArch, Qiling, code=test, archtype="qilingarch", ostype="linux", verbose=QL_VERBOSE.DEFAULT ) - # def test_invalid_output(self): # print("Testing Invalid output") - # self.assertRaises(QlErrorOutput, Qiling, code=test, archtype="arm64", ostype="linux", verbose=QL_VERBOSE.DEFAULT ) + # self.assertRaises(QlErrorOutput, Qiling, code=test, archtype=QL_ARCH.ARM, ostype=QL_OS.LINUX, verbose=QL_VERBOSE.DEFAULT) if __name__ == "__main__": From 4860f958cbfb6dfe05ef56c02f416097188dae62 Mon Sep 17 00:00:00 2001 From: elicn Date: Sun, 18 Jun 2023 01:17:00 +0300 Subject: [PATCH 11/17] Opportunistic PEP8 fixes --- examples/adcache_x86_windows_debug.py | 3 +- examples/doogie_8086_crack.py | 28 +- examples/evm/evm_Hexagon_overflow.py | 14 +- examples/evm/evm_debugger.py | 4 +- examples/evm/evm_reentrancy.py | 12 +- examples/evm/evm_reentrancy_vol.py | 26 +- examples/evm/evm_simple_sc.py | 8 +- examples/evm/fuzzing/underflow_test.py | 8 +- examples/extensions/r2/hello_r2.py | 11 +- .../report/hello_x86_windows_json.py | 3 +- .../dlink_dir815/dir815_mips32el_linux.py | 14 +- examples/fuzzing/stm32f429/fuzz.py | 20 +- .../tenda_ac15/fuzz_tendaac15_httpd.py | 13 +- examples/hello_arm_uboot.py | 3 +- examples/mcu/gd32vf103_blink.py | 5 +- examples/mcu/stm32f407_hack_lock.py | 8 +- examples/mcu/stm32f411_dma_logger.py | 10 +- examples/mcu/stm32f411_freertos.py | 7 +- examples/mcu/stm32f411_gpio_hook.py | 5 +- examples/mcu/stm32f411_i2c_lcd.py | 24 +- examples/mcu/stm32f411_interact_usart.py | 12 +- examples/mcu/stm32f411_spi_oled12864.py | 2 +- qiling/core.py | 8 +- qiling/os/windows/windows.py | 11 +- qiling/utils.py | 2 +- qltool | 21 +- tests/test_blob.py | 5 +- tests/test_debugger.py | 15 +- tests/test_edl.py | 72 +++-- tests/test_elf.py | 305 ++++++++---------- tests/test_evm.py | 22 +- tests/test_macho.py | 11 +- tests/test_mcu.py | 35 +- tests/test_perf.py | 27 +- tests/test_peshellcode.py | 8 +- tests/test_posix.py | 9 +- tests/test_qltool.py | 23 +- tests/test_qnx.py | 15 +- tests/test_r2.py | 32 +- tests/test_shellcode.py | 3 +- tests/test_windows_stdio.py | 8 +- 41 files changed, 468 insertions(+), 404 deletions(-) diff --git a/examples/adcache_x86_windows_debug.py b/examples/adcache_x86_windows_debug.py index b2ddd9f25..fd9035315 100644 --- a/examples/adcache_x86_windows_debug.py +++ b/examples/adcache_x86_windows_debug.py @@ -3,10 +3,11 @@ # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # +from zipfile import ZipFile + import sys sys.path.append("..") -from zipfile import ZipFile from qiling import Qiling from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE diff --git a/examples/doogie_8086_crack.py b/examples/doogie_8086_crack.py index 35dc43b90..8553a9309 100644 --- a/examples/doogie_8086_crack.py +++ b/examples/doogie_8086_crack.py @@ -1,15 +1,22 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # -import sys, curses, math, struct, string, time +import curses +import math +import string +import time + +from struct import pack + +import sys sys.path.append("..") -from qiling import * + +from qiling import Qiling from qiling.const import * from qiling.os.disk import QlDisk from qiling.os.dos.utils import BIN2BCD -from struct import pack # https://stackoverflow.com/questions/9829578/fast-way-of-counting-non-zero-bits-in-positive-integer @@ -129,9 +136,7 @@ def show_once(ql: Qiling, key): # In this stage, we show every key. def third_stage(keys): # To setup terminal again, we have to restart the whole program. - ql = Qiling(["rootfs/8086/doogie/doogie.DOS_MBR"], - "rootfs/8086", - console=False) + ql = Qiling(["rootfs/8086/doogie/doogie.DOS_MBR"], "rootfs/8086", console=False) ql.add_fs_mapper(0x80, QlDisk("rootfs/8086/doogie/doogie.DOS_MBR", 0x80)) ql.os.set_api((0x1a, 4), set_required_datetime, QL_INTERCEPT.EXIT) hk = ql.hook_code(stop, begin=0x8018, end=0x8018) @@ -170,21 +175,22 @@ def read_until_zero(ql: Qiling, addr): addr += 1 return buf + def set_required_datetime(ql: Qiling): ql.log.info("Setting Feburary 06, 1990") ql.arch.regs.ch = BIN2BCD(19) - ql.arch.regs.cl = BIN2BCD(1990%100) + ql.arch.regs.cl = BIN2BCD(1990 % 100) ql.arch.regs.dh = BIN2BCD(2) ql.arch.regs.dl = BIN2BCD(6) + def stop(ql, addr, data): ql.emu_stop() + # In this stage, we get the encrypted data which xored with the specific date. def first_stage(): - ql = Qiling(["rootfs/8086/doogie/doogie.DOS_MBR"], - "rootfs/8086", - console=False) + ql = Qiling(["rootfs/8086/doogie/doogie.DOS_MBR"], "rootfs/8086", console=False) ql.add_fs_mapper(0x80, QlDisk("rootfs/8086/doogie/doogie.DOS_MBR", 0x80)) # Doogie suggests that the datetime should be 1990-02-06. ql.os.set_api((0x1a, 4), set_required_datetime, QL_INTERCEPT.EXIT) diff --git a/examples/evm/evm_Hexagon_overflow.py b/examples/evm/evm_Hexagon_overflow.py index 2dfdcc6e3..c26f0938d 100644 --- a/examples/evm/evm_Hexagon_overflow.py +++ b/examples/evm/evm_Hexagon_overflow.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # @@ -7,9 +7,9 @@ # https://www.anquanke.com/post/id/145520 import sys - sys.path.append("../..") -from qiling import * + +from qiling import Qiling from qiling.const import QL_ARCH @@ -37,9 +37,10 @@ def check_balance(sender, destination): # # SMART CONTRACT DEPENDENT: transform from user1 to user2 call_data = '0xa9059cbb'+ ql.arch.evm.abi.convert(['address'], [user2]) + \ ql.arch.evm.abi.convert(['uint256'], [0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe]) - msg1 = ql.arch.evm.create_message(user1, c1, data=call_data) + msg1 = ql.arch.evm.create_message(user1, c1, data=call_data) result = ql.run(code=msg1) - if int(result.output.hex()[2:], 16) == 1: + + if int(result.output.hex()[2:], 16) == 1: print('User1 transfered Token to User1') # # SMART CONTRACT DEPENDENT: User1 balance underflow, MAX - 1 @@ -49,5 +50,6 @@ def check_balance(sender, destination): result = check_balance(user2, c1) print('User2 final balance =', int(result.output.hex()[2:], 16)) + if __name__ == "__main__": - example_run_evm() \ No newline at end of file + example_run_evm() diff --git a/examples/evm/evm_debugger.py b/examples/evm/evm_debugger.py index 0d593cca7..9a6e53954 100644 --- a/examples/evm/evm_debugger.py +++ b/examples/evm/evm_debugger.py @@ -1,11 +1,11 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # import sys - sys.path.append("../..") + from qiling import Qiling from qiling.const import QL_ARCH diff --git a/examples/evm/evm_reentrancy.py b/examples/evm/evm_reentrancy.py index 73f63237a..09100f6cd 100644 --- a/examples/evm/evm_reentrancy.py +++ b/examples/evm/evm_reentrancy.py @@ -1,12 +1,12 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # import sys - sys.path.append("../..") -from qiling import * + +from qiling import Qiling from qiling.arch.evm.vm.utils import bytecode_to_bytes, runtime_code_detector from qiling.arch.evm.vm.vm import BaseVM from qiling.arch.evm.constants import CREATE_CONTRACT_ADDRESS @@ -29,9 +29,9 @@ ql.arch.evm.create_account(C2) ql.arch.evm.create_account(User1, 100*10**18) ql.arch.evm.create_account(User2, 100*10**18) - + EtherStore_contract = '0x6080604052670de0b6b3a764000060005534801561001c57600080fd5b506103b08061002c6000396000f30060806040526004361061006d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680631031ec3114610072578063155dd5ee146100c957806327e235e3146100f65780637ddfe78d1461014d578063e2c41dbc14610178575b600080fd5b34801561007e57600080fd5b506100b3600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610182565b6040518082815260200191505060405180910390f35b3480156100d557600080fd5b506100f46004803603810190808035906020019092919050505061019a565b005b34801561010257600080fd5b50610137600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610317565b6040518082815260200191505060405180910390f35b34801561015957600080fd5b5061016261032f565b6040518082815260200191505060405180910390f35b610180610335565b005b60016020528060005260406000206000915090505481565b80600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101515156101e857600080fd5b60005481111515156101f957600080fd5b62093a80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205401421015151561024c57600080fd5b3373ffffffffffffffffffffffffffffffffffffffff168160405160006040518083038185875af192505050151561028357600080fd5b80600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555042600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555050565b60026020528060005260406000206000915090505481565b60005481565b34600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505600a165627a7a72305820707bf0ae11ce52ff7b7846ede3497d41b6fadea29579773fc70e8e61c0f549f10029' - + print('Init Victim balance is', vm.state.get_balance(User1)/10**18) print('Init Attacker balance is', vm.state.get_balance(User2)/10**18) @@ -67,7 +67,7 @@ res_code = bytecode_to_bytes(res.output) runtime_code, aux_data, constructor_args = runtime_code_detector(res_code) rt_code1 = bytecode_to_bytes(runtime_code) - + print('\n------ Attacker deposit 1 ETH to DeFi contract, Start Reentrancy Attack') # 4. User2 pwnEtherStore with 1ETH call_data = '0xa75e4625' + ql.arch.evm.abi.convert(['bytes4'], [bytecode_to_bytes('0xe2c41dbc')]) + ql.arch.evm.abi.convert(['bytes4'], [bytecode_to_bytes('0x155dd5ee')]) diff --git a/examples/evm/evm_reentrancy_vol.py b/examples/evm/evm_reentrancy_vol.py index c7be400c2..965922a7c 100644 --- a/examples/evm/evm_reentrancy_vol.py +++ b/examples/evm/evm_reentrancy_vol.py @@ -1,12 +1,12 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # import sys - sys.path.append("../..") -from qiling import * + +from qiling import Qiling from qiling.arch.evm.vm.utils import bytecode_to_bytes, runtime_code_detector from qiling.arch.evm.vm.vm import BaseVM from qiling.arch.evm.constants import CREATE_CONTRACT_ADDRESS @@ -15,7 +15,7 @@ def template(vic_contract, deposit, withdraw): Attack_contract = '0x608060405234801561001057600080fd5b506040516020806104b883398101806040528101908080519060200190929190505050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050610435806100836000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063a75e462514610179578063ff11e1db146101e1575b670de0b6b3a76400006000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16311115610177576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600060149054906101000a90047c0100000000000000000000000000000000000000000000000000000000027c01000000000000000000000000000000000000000000000000000000009004670de0b6b3a76400006040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808267ffffffffffffffff1681526020019150506000604051808303816000875af192505050505b005b6101df60048036038101908080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690602001909291905050506101f8565b005b3480156101ed57600080fd5b506101f66103a9565b005b80600060146101000a81548163ffffffff02191690837c010000000000000000000000000000000000000000000000000000000090040217905550670de0b6b3a7640000341015151561024a57600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16670de0b6b3a7640000837c01000000000000000000000000000000000000000000000000000000009004906040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038185885af19350505050506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16817c01000000000000000000000000000000000000000000000000000000009004670de0b6b3a76400006040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808267ffffffffffffffff1681526020019150506000604051808303816000875af192505050505050565b3373ffffffffffffffffffffffffffffffffffffffff166108fc3073ffffffffffffffffffffffffffffffffffffffff16319081150290604051600060405180830381858888f19350505050158015610406573d6000803e3d6000fd5b505600a165627a7a723058205aacb19a5864d2c460aed6c844f2aca575d87de6477ac757a72511bb2975b3f80029' - + ql = Qiling(code=Attack_contract, archtype=QL_ARCH.EVM) vm:BaseVM = ql.arch.evm.vm @@ -31,7 +31,7 @@ def template(vic_contract, deposit, withdraw): EtherStore_contract = vic_contract - + print('Init Victim balance is', vm.state.get_balance(User1)/10**18) print('Init Attacker balance is', vm.state.get_balance(User2)/10**18) @@ -67,7 +67,7 @@ def template(vic_contract, deposit, withdraw): res_code = bytecode_to_bytes(res.output) runtime_code, aux_data, constructor_args = runtime_code_detector(res_code) rt_code1 = bytecode_to_bytes(runtime_code) - + print('\n------ Attacker deposit 1 ETH to DeFi contract, Start Reentrancy Attack') # 4. User2 pwnEtherStore with 1ETH call_data = '0xa75e4625' + ql.arch.evm.abi.convert(['bytes4'], [bytecode_to_bytes(deposit)]) + ql.arch.evm.abi.convert(['bytes4'], [bytecode_to_bytes(withdraw)]) @@ -89,7 +89,7 @@ def template(vic_contract, deposit, withdraw): contract_1 = '0x6080604052670de0b6b3a764000060005534801561001c57600080fd5b506103b08061002c6000396000f30060806040526004361061006d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680631031ec3114610072578063155dd5ee146100c957806327e235e3146100f65780637ddfe78d1461014d578063e2c41dbc14610178575b600080fd5b34801561007e57600080fd5b506100b3600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610182565b6040518082815260200191505060405180910390f35b3480156100d557600080fd5b506100f46004803603810190808035906020019092919050505061019a565b005b34801561010257600080fd5b50610137600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610317565b6040518082815260200191505060405180910390f35b34801561015957600080fd5b5061016261032f565b6040518082815260200191505060405180910390f35b610180610335565b005b60016020528060005260406000206000915090505481565b80600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101515156101e857600080fd5b60005481111515156101f957600080fd5b62093a80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205401421015151561024c57600080fd5b3373ffffffffffffffffffffffffffffffffffffffff168160405160006040518083038185875af192505050151561028357600080fd5b80600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555042600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555050565b60026020528060005260406000206000915090505481565b60005481565b34600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505600a165627a7a72305820707bf0ae11ce52ff7b7846ede3497d41b6fadea29579773fc70e8e61c0f549f10029' c1_deposit = '0xe2c41dbc' c1_withdraw = '0x155dd5ee' - + contract_2 = '0x608060405234801561001057600080fd5b5033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506106dc806100616000396000f300608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630c08bf881461014d578063590e1ae314610164578063a9059cbb1461017b578063e42c08f2146101c8575b600080339150349050600060648281151561007957fe5b0614151561008657600080fd5b60648181151561009257fe5b046000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055507fc848a0bc6fc10f63d456eae535b952f8768bfd21d409b4933f8032cce0432ea48183604051808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a15050005b34801561015957600080fd5b5061016261021f565b005b34801561017057600080fd5b50610179610312565b005b34801561018757600080fd5b506101c6600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610434565b005b3480156101d457600080fd5b50610209600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610658565b6040518082815260200191505060405180910390f35b600034111561022d57600080fd5b3373ffffffffffffffffffffffffffffffffffffffff16600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614151561028957600080fd5b7fedf2f7451a6c99c99b58baaddbe18df51bec156fe6ae8dd3ea730168326f94cd3073ffffffffffffffffffffffffffffffffffffffff16316040518082815260200191505060405180910390a1600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b600080600034111561032357600080fd5b3391506000808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050600081141561037557600080fd5b61038160648202610670565b60008060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055507f658eefd1c566207ffd3fb44f4d9b1e443698a39f8a6f7b134b3fef529e3f3f028183604051808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a15050565b60008034111561044357600080fd5b339050816000808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101561049157600080fd5b6000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054826000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205401101561051c57600080fd5b816000808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055507fa25be434081445744d5b297a785f7b7073142ae4bcd91a0e7aa802f802b4e0c7828285604051808481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001935050505060405180910390a1505050565b60006020528060005260406000206000915090505481565b60003373ffffffffffffffffffffffffffffffffffffffff168260405160006040518083038185875af19250505090508015156106ac57600080fd5b50505600a165627a7a72305820e5031f476480cd5d80ec7b267c7bf4c672137328294cb6b71bbb631ce9b99fc20029' c2_deposit = '' c2_withdraw = '0x590e1ae3' @@ -104,11 +104,11 @@ def template(vic_contract, deposit, withdraw): # contract Attack { # address victim; # bytes4 withdraw_id; - +# # constructor(address addr) { # victim = addr; # } - +# # function pwn(bytes4 deposit, bytes4 withdraw) public payable { # withdraw_id = withdraw; # // attack to the nearest ether @@ -116,19 +116,19 @@ def template(vic_contract, deposit, withdraw): # // send eth to the depositFunds() function # victim.call.value(1 ether)(deposit); # victim.call(withdraw, 1 ether); - +# # //etherStore.depositFunds.value(1 ether)(); # // start the magic # //etherStore.withdrawFunds(1 ether); # } - +# # function collectEther() public { # msg.sender.transfer(this.balance); # } - +# # function () payable { # if (victim.balance > 1 ether) { # victim.call(withdraw_id,1 ether); # } # } -# } \ No newline at end of file +# } diff --git a/examples/evm/evm_simple_sc.py b/examples/evm/evm_simple_sc.py index 54eed51b4..081e2be20 100644 --- a/examples/evm/evm_simple_sc.py +++ b/examples/evm/evm_simple_sc.py @@ -1,17 +1,17 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # import sys - sys.path.append("../..") + from qiling import Qiling from qiling.const import QL_ARCH def example_run_evm(): - + contract = '0x6060604052341561000f57600080fd5b60405160208061031c833981016040528080519060200190919050508060018190556000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555050610299806100836000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806318160ddd1461005c57806370a0823114610085578063a9059cbb146100d2575b600080fd5b341561006757600080fd5b61006f61012c565b6040518082815260200191505060405180910390f35b341561009057600080fd5b6100bc600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610132565b6040518082815260200191505060405180910390f35b34156100dd57600080fd5b610112600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061017a565b604051808215151515815260200191505060405180910390f35b60015481565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080826000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205403101515156101cb57600080fd5b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555060019050929150505600a165627a7a7230582098f1551a391a3e65b3ce45cfa2b3fa5f91eea9a3e7181a81454e025ea0d7151c0029' ql = Qiling(code=contract, archtype=QL_ARCH.EVM) @@ -40,7 +40,7 @@ def check_balance(sender, destination): call_data = '0xa9059cbb'+ ql.arch.evm.abi.convert(['address'], [user2]) + \ ql.arch.evm.abi.convert(['uint256'], [21]) # print('Transfer Calldata Code: ' + call_data) - msg1 = ql.arch.evm.create_message(user1, c1, call_data) + msg1 = ql.arch.evm.create_message(user1, c1, call_data) result = ql.run(code=msg1) if int(result.output.hex()[2:], 16) == 1: print('User1 transfered 21 Token to User2') diff --git a/examples/evm/fuzzing/underflow_test.py b/examples/evm/fuzzing/underflow_test.py index 10fa8ed7e..6fec1af2c 100644 --- a/examples/evm/fuzzing/underflow_test.py +++ b/examples/evm/fuzzing/underflow_test.py @@ -1,18 +1,18 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # import sys - sys.path.append("../../../..") -from qiling import * from qiling import Qiling from qiling.const import QL_ARCH + def underflow(fuzz_balance): code = '0x6060604052341561000f57600080fd5b60405160208061031c833981016040528080519060200190919050508060018190556000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555050610299806100836000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806318160ddd1461005c57806370a0823114610085578063a9059cbb146100d2575b600080fd5b341561006757600080fd5b61006f61012c565b6040518082815260200191505060405180910390f35b341561009057600080fd5b6100bc600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610132565b6040518082815260200191505060405180910390f35b34156100dd57600080fd5b610112600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061017a565b604051808215151515815260200191505060405180910390f35b60015481565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080826000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205403101515156101cb57600080fd5b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555060019050929150505600a165627a7a7230582098f1551a391a3e65b3ce45cfa2b3fa5f91eea9a3e7181a81454e025ea0d7151c0029' + ql = Qiling(code=code, archtype=QL_ARCH.EVM) # Add Balance Var to the contract @@ -38,7 +38,7 @@ def check_balance(sender, destination): # SMART CONTRACT DEPENDENT: transform 21 from user1 to user2 call_data = '0xa9059cbb'+ ql.arch.evm.abi.convert(['address'], [user2]) + \ ql.arch.evm.abi.convert(['uint256'], [fuzz_balance]) - msg1 = ql.arch.evm.create_message(user1, c1, call_data) + msg1 = ql.arch.evm.create_message(user1, c1, call_data) result = ql.run(code=msg1) # SMART CONTRACT DEPENDENT: User1 balance underflow, MAX - 1 diff --git a/examples/extensions/r2/hello_r2.py b/examples/extensions/r2/hello_r2.py index e2919da6f..88fbdc51f 100644 --- a/examples/extensions/r2/hello_r2.py +++ b/examples/extensions/r2/hello_r2.py @@ -15,6 +15,7 @@ def func(ql: Qiling, *args, **kwargs): ql.os.stdout.write(b"=====hooked main=====!\n") return + def my_sandbox(path, rootfs): ql = Qiling(path, rootfs, verbose=QL_VERBOSE.DISASM) # QL_VERBOSE.DISASM will be monkey-patched when r2 is available @@ -37,12 +38,20 @@ def my_sandbox(path, rootfs): # r2.enable_trace() ql.run() + if __name__ == "__main__": my_sandbox(["rootfs/x86_windows/bin/x86_hello.exe"], "rootfs/x86_windows") # test shellcode mode - ARM64_LIN = bytes.fromhex('420002ca210080d2400080d2c81880d2010000d4e60300aa01020010020280d2681980d2010000d4410080d2420002cae00306aa080380d2010000d4210400f165ffff54e0000010420002ca210001caa81b80d2010000d4020004d27f0000012f62696e2f736800') + ARM64_LIN = bytes.fromhex(''' + 420002ca210080d2400080d2c81880d2010000d4e60300aa01020010020280d2 + 681980d2010000d4410080d2420002cae00306aa080380d2010000d4210400f1 + 65ffff54e0000010420002ca210001caa81b80d2010000d4020004d27f000001 + 2f62696e2f736800 + ''') + print("\nLinux ARM 64bit Shellcode") + ql = Qiling(code=ARM64_LIN, archtype=QL_ARCH.ARM64, ostype=QL_OS.LINUX, verbose=QL_VERBOSE.DEBUG) r2 = R2(ql) # disassemble 32 instructions diff --git a/examples/extensions/report/hello_x86_windows_json.py b/examples/extensions/report/hello_x86_windows_json.py index 10335480b..1731e5df0 100644 --- a/examples/extensions/report/hello_x86_windows_json.py +++ b/examples/extensions/report/hello_x86_windows_json.py @@ -4,10 +4,9 @@ # import sys - sys.path.append("..") -from qiling import * +from qiling import Qiling from qiling.const import QL_VERBOSE from qiling.extensions.report import generate_report diff --git a/examples/fuzzing/dlink_dir815/dir815_mips32el_linux.py b/examples/fuzzing/dlink_dir815/dir815_mips32el_linux.py index 7e9416044..8505c8e03 100644 --- a/examples/fuzzing/dlink_dir815/dir815_mips32el_linux.py +++ b/examples/fuzzing/dlink_dir815/dir815_mips32el_linux.py @@ -1,19 +1,20 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # # Everything about the bug and firmware https://www.exploit-db.com/exploits/33863 import os,sys - sys.path.append("../../..") -from qiling import * + +from qiling import Qiling from qiling.const import QL_VERBOSE from qiling.extensions.afl import ql_afl_fuzz + def main(input_file, enable_trace=False): - + env_vars = { "REQUEST_METHOD": "POST", "REQUEST_URI": "/hedwig.cgi", @@ -24,9 +25,8 @@ def main(input_file, enable_trace=False): } ql = Qiling(["./rootfs/htdocs/web/hedwig.cgi"], "./rootfs", - verbose=QL_VERBOSE.DEBUG, env=env_vars, - console = True if enable_trace else False) - + verbose=QL_VERBOSE.DEBUG, env=env_vars, console=enable_trace) + def place_input_callback(ql: Qiling, input: bytes, _: int): env_var = ("HTTP_COOKIE=uid=1234&password=").encode() env_vars = env_var + input + b"\x00" + (ql.path).encode() + b"\x00" diff --git a/examples/fuzzing/stm32f429/fuzz.py b/examples/fuzzing/stm32f429/fuzz.py index a2be712e7..14ea27e1a 100644 --- a/examples/fuzzing/stm32f429/fuzz.py +++ b/examples/fuzzing/stm32f429/fuzz.py @@ -1,21 +1,23 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # import os -import sys -from typing import Any, Optional +from typing import Optional +import sys sys.path.append("../../..") + from qiling.core import Qiling from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE from qiling.extensions.afl import ql_afl_fuzz_custom from qiling.extensions.mcu.stm32f4 import stm32f429 -from unicorn import UC_ERR_OK, UcError +from unicorn import UC_ERR_OK + def main(input_file: str): ql = Qiling(["../../rootfs/mcu/stm32f429/bof.elf"], @@ -24,19 +26,19 @@ def main(input_file: str): ql.hw.create('rcc') ql.hw.create('usart2') ql.hw.create('usart3') - - ql.fast_mode = True + + ql.fast_mode = True def place_input_callback(ql: Qiling, input_bytes: bytes, persistent_round: int) -> Optional[bool]: """Called with every newly generated input.""" ql.hw.usart3.send(input_bytes) - + return True def fuzzing_callback(ql: Qiling): - ql.run(end=0x80006d9) - + ql.run(end=0x80006d9) + return UC_ERR_OK ql_afl_fuzz_custom(ql, input_file, place_input_callback, fuzzing_callback=fuzzing_callback, exits=[0x80006d9]) diff --git a/examples/fuzzing/tenda_ac15/fuzz_tendaac15_httpd.py b/examples/fuzzing/tenda_ac15/fuzz_tendaac15_httpd.py index d30d01de4..64738d622 100644 --- a/examples/fuzzing/tenda_ac15/fuzz_tendaac15_httpd.py +++ b/examples/fuzzing/tenda_ac15/fuzz_tendaac15_httpd.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # @@ -11,13 +11,16 @@ # 5. rm -rf webroot && mv webroot_ro webroot # 5. mv etc_ro etc -import os, pickle, socket, sys, threading +import os +import sys sys.path.append("../../../") -from qiling import * + +from qiling import Qiling from qiling.const import QL_VERBOSE from qiling.extensions.afl import ql_afl_fuzz + def patcher(ql): br0_addr = ql.mem.search("br0".encode() + b'\x00') for addr in br0_addr: @@ -44,14 +47,14 @@ def start_afl(_ql: Qiling): ql_afl_fuzz(_ql, input_file=input_file, place_input_callback=place_input_callback, exits=[ql.os.exit_point]) ql.hook_address(callback=start_afl, address=0x10930+8) - + try: ql.run(begin = 0x10930+4, end = 0x7a0cc+4) os._exit(0) except: if enable_trace: print("\nFuzzer Went Shit") - os._exit(0) + os._exit(0) if __name__ == "__main__": if len(sys.argv) == 1: diff --git a/examples/hello_arm_uboot.py b/examples/hello_arm_uboot.py index 0cdb70a2b..9544fe0ee 100644 --- a/examples/hello_arm_uboot.py +++ b/examples/hello_arm_uboot.py @@ -10,6 +10,7 @@ from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE from qiling.os.const import STRING + def get_kaimendaji_password(): def my_getenv(ql: Qiling): env = { @@ -56,7 +57,6 @@ def partial_run_init(ql: Qiling): ql.arch.regs.r2 = 2 ql.arch.regs.r3 = argv_ptr - with open("../examples/rootfs/blob/u-boot.bin.img", "rb") as f: uboot_code = f.read() @@ -70,5 +70,6 @@ def partial_run_init(ql: Qiling): ql.run(image_base_addr + 0x486B4, image_base_addr + 0x48718) + if __name__ == "__main__": get_kaimendaji_password() diff --git a/examples/mcu/gd32vf103_blink.py b/examples/mcu/gd32vf103_blink.py index 6ffbaab84..efcfd6286 100644 --- a/examples/mcu/gd32vf103_blink.py +++ b/examples/mcu/gd32vf103_blink.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # @@ -11,6 +11,7 @@ from qiling.extensions.mcu.gd32vf1 import gd32vf103 from qiling.const import QL_ARCH, QL_OS + ql = Qiling(['../rootfs/mcu/gd32vf103/blink.hex'], archtype=QL_ARCH.RISCV64, ostype=QL_OS.MCU, env=gd32vf103, verbose=QL_VERBOSE.DEBUG) @@ -21,9 +22,11 @@ delay_cycles_begin = 0x800015c delay_cycles_end = 0x800018c + def skip_delay(ql): ql.arch.regs.pc = delay_cycles_end + ql.hook_address(skip_delay, delay_cycles_begin) ql.hw.gpioc.hook_set(13, lambda : print('Set PC13')) diff --git a/examples/mcu/stm32f407_hack_lock.py b/examples/mcu/stm32f407_hack_lock.py index b6a2bbfc6..e6780ada7 100644 --- a/examples/mcu/stm32f407_hack_lock.py +++ b/examples/mcu/stm32f407_hack_lock.py @@ -1,13 +1,11 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # - -import sys from multiprocessing import Pool -from multiprocessing import Process +import sys sys.path.append("../..") from qiling.core import Qiling @@ -24,6 +22,7 @@ def dicts(): for x in range(1, 20): yield str((a*x*x + b*x + c) % M) + # Cracking the passwd of lock def crack(passwd): ql = Qiling(["../../examples/rootfs/mcu/stm32f407/backdoorlock.hex"], @@ -55,6 +54,7 @@ def crack(passwd): del ql + pool = Pool() for passwd in dicts(): pool.apply_async(crack, args=(passwd,)) diff --git a/examples/mcu/stm32f411_dma_logger.py b/examples/mcu/stm32f411_dma_logger.py index 0e129b58d..b3f110179 100644 --- a/examples/mcu/stm32f411_dma_logger.py +++ b/examples/mcu/stm32f411_dma_logger.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # @@ -10,6 +10,7 @@ from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE from qiling.extensions.mcu.stm32f4 import stm32f411 + def stm32f411_dma(): ql = Qiling(["../rootfs/mcu/stm32f411/dma-clock.hex"], archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f411, verbose=QL_VERBOSE.DEBUG) @@ -21,10 +22,11 @@ def stm32f411_dma(): ql.run(count=200000) buf = ql.hw.usart2.recv() - ## check timestamp + # check timestamp tick = [int(x) for x in buf.split()] for i in range(1, len(tick)): - assert(4 <= tick[i] - tick[i - 1] <= 6) + assert (4 <= tick[i] - tick[i - 1] <= 6) + if __name__ == "__main__": - stm32f411_dma() \ No newline at end of file + stm32f411_dma() diff --git a/examples/mcu/stm32f411_freertos.py b/examples/mcu/stm32f411_freertos.py index c5c2ed460..104056ed8 100644 --- a/examples/mcu/stm32f411_freertos.py +++ b/examples/mcu/stm32f411_freertos.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # @@ -17,10 +17,11 @@ def stm32f411_freertos(): ql.hw.create('usart2').watch() ql.hw.create('gpioa').watch() - ql.hw.create('rcc') + ql.hw.create('rcc') ql.hw.systick.set_ratio(100) ql.run(count=200000) + if __name__ == "__main__": - stm32f411_freertos() \ No newline at end of file + stm32f411_freertos() diff --git a/examples/mcu/stm32f411_gpio_hook.py b/examples/mcu/stm32f411_gpio_hook.py index 22f369003..a766312d9 100644 --- a/examples/mcu/stm32f411_gpio_hook.py +++ b/examples/mcu/stm32f411_gpio_hook.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # @@ -10,6 +10,7 @@ from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE from qiling.extensions.mcu.stm32f4 import stm32f411 + def test_mcu_gpio_stm32f411(): ql = Qiling(["../../examples/rootfs/mcu/stm32f411/hello_gpioA.hex"], archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f411, verbose=QL_VERBOSE.DEBUG) @@ -17,12 +18,12 @@ def test_mcu_gpio_stm32f411(): ql.hw.create('usart2').watch() ql.hw.create('rcc').watch() ql.hw.create('gpioa').watch() - ql.hw.gpioa.hook_set(5, lambda: print('LED light up')) ql.hw.gpioa.hook_reset(5, lambda: print('LED light off')) ql.run(count=10000) + if __name__ == "__main__": test_mcu_gpio_stm32f411() diff --git a/examples/mcu/stm32f411_i2c_lcd.py b/examples/mcu/stm32f411_i2c_lcd.py index 1e98722e3..4b80e8c6f 100644 --- a/examples/mcu/stm32f411_i2c_lcd.py +++ b/examples/mcu/stm32f411_i2c_lcd.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # @@ -12,33 +12,35 @@ from qiling.hw.external_device.lcd.lcd1602 import PyGameLCD1602 from qiling.extensions.mcu.stm32f4 import stm32f411 + def create(path, lcd): ql = Qiling([path], archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f411, verbose=QL_VERBOSE.DEBUG) ql.hw.create('i2c1') ql.hw.create('rcc') ql.hw.create('gpioa') - ql.hw.create('gpiob') + ql.hw.create('gpiob') ql.hw.i2c1.watch() - ql.hw.i2c1.connect(lcd) - + ql.hw.i2c1.connect(lcd) + ql.hw.systick.set_ratio(100) return ql + if __name__ == "__main__": - lcd = PyGameLCD1602() - - ## Example 1 + lcd = PyGameLCD1602() + + # Example 1 create("../rootfs/mcu/stm32f411/i2c-lcd.hex", lcd).run(count=50000) - ## Example 2 + # Example 2 create("../rootfs/mcu/stm32f411/lcd-plus.hex", lcd).run(count=100000) - - ## Example 3 + + # Example 3 ql = create("../rootfs/mcu/stm32f411/i2cit-lcd.hex", lcd) - + delay_start = 0x8002936 delay_end = 0x8002955 def skip_delay(ql): diff --git a/examples/mcu/stm32f411_interact_usart.py b/examples/mcu/stm32f411_interact_usart.py index ab1dcabad..c1086b2fb 100644 --- a/examples/mcu/stm32f411_interact_usart.py +++ b/examples/mcu/stm32f411_interact_usart.py @@ -1,15 +1,15 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # -import sys -sys.path.append("../..") - import time import threading +import sys +sys.path.append("../..") + from qiling.core import Qiling from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE from qiling.extensions.mcu.stm32f4 import stm32f411 @@ -25,8 +25,8 @@ while True: message = input('>> ').encode() - + ql.hw.usart2.send(message + b'\n') - + time.sleep(0.8) print(ql.hw.usart2.recv()) diff --git a/examples/mcu/stm32f411_spi_oled12864.py b/examples/mcu/stm32f411_spi_oled12864.py index c8877f209..0eb23079b 100644 --- a/examples/mcu/stm32f411_spi_oled12864.py +++ b/examples/mcu/stm32f411_spi_oled12864.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # diff --git a/qiling/core.py b/qiling/core.py index ab85be0f9..61ec85cbb 100644 --- a/qiling/core.py +++ b/qiling/core.py @@ -39,13 +39,13 @@ def __init__( ostype: Optional[QL_OS] = None, archtype: Optional[QL_ARCH] = None, verbose: QL_VERBOSE = QL_VERBOSE.DEFAULT, - profile: str = None, + profile: Optional[Union[str, Mapping]] = None, console: bool = True, - log_file=None, - log_override=None, + log_file: Optional[str] = None, + log_override: Optional['Logger'] = None, log_plain: bool = False, multithread: bool = False, - filter = None, + filter: Optional[str] = None, stop: QL_STOP = QL_STOP.NONE, *, endian: Optional[QL_ENDIAN] = None, diff --git a/qiling/os/windows/windows.py b/qiling/os/windows/windows.py index 6cb8bc5b0..921ba8dcc 100644 --- a/qiling/os/windows/windows.py +++ b/qiling/os/windows/windows.py @@ -29,6 +29,7 @@ import qiling.os.windows.dlls as api + class QlOsWindows(QlOs): type = QL_OS.WINDOWS @@ -94,7 +95,6 @@ def __make_fcall_selector(atype: QL_ARCH) -> Callable[[int], QlFunctionCall]: self.stdout = self._stdout self.stderr = self._stderr - @QlOs.stdin.setter def stdin(self, stream: TextIO) -> None: self._stdin = stream @@ -122,7 +122,6 @@ def stderr(self, stream: TextIO) -> None: handle.obj = stream - def load(self): self.setupGDT() self.__setup_components() @@ -135,8 +134,8 @@ def setupGDT(self): gdtm = GDTManager(self.ql) segm_class: Type[SegmentManager] = { - 32 : SegmentManager86, - 64 : SegmentManager64 + 32: SegmentManager86, + 64: SegmentManager64 }[self.ql.arch.bits] # setup gdt and segments selectors @@ -155,7 +154,6 @@ def setupGDT(self): self.ql.mem.map(GS_SEGMENT_ADDR, GS_SEGMENT_SIZE, info='[GS]') - def __setup_components(self): reghive = self.path.transform_to_real_path(ntpath.join(self.windir, 'registry')) @@ -201,12 +199,11 @@ def hook_winapi(self, ql: Qiling, address: int, size: int): if ql.debug_stop: raise QlErrorSyscallNotFound("Windows API implementation not found") - def run(self): if self.ql.exit_point is not None: self.exit_point = self.ql.exit_point - if self.ql.entry_point is not None: + if self.ql.entry_point is not None: self.ql.loader.entry_point = self.ql.entry_point entry_point = self.ql.loader.entry_point diff --git a/qiling/utils.py b/qiling/utils.py index 05cb0a43e..c907204e7 100644 --- a/qiling/utils.py +++ b/qiling/utils.py @@ -442,7 +442,7 @@ def profile_setup(ostype: QL_OS, user_config: Optional[Union[str, dict]]): elif user_config: config.read(user_config) - + return config diff --git a/qltool b/qltool index e949d985f..392ca5ace 100755 --- a/qltool +++ b/qltool @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # @@ -8,6 +8,7 @@ import os import sys import ast import pickle + from pprint import pprint from typing import TYPE_CHECKING, Mapping, Type @@ -33,8 +34,9 @@ def read_file(fname: str): return content + class __arg_env(argparse.Action): - def __call__(self, parser, namespace, values, option_string): + def __call__(self, parser, namespace, values: str, option_string): if os.path.exists(values): with open(values, 'rb') as f: env = pickle.load(f) @@ -70,12 +72,12 @@ def handle_code(options: argparse.Namespace): if options.format == 'hex': if options.input is not None: - print ("Load HEX from ARGV") + print("Load HEX from ARGV") code = str(options.input).strip("\\\\x").split("x") code = "".join(code).strip() - code = bytes.fromhex(code) + code = bytes.fromhex(code) elif options.filename is not None: - print ("Load HEX from FILE") + print("Load HEX from FILE") code = str(read_file(options.filename)).strip('b\'').strip('\\n') code = code.strip('x').split("\\\\x") code = "".join(code).strip() @@ -85,7 +87,7 @@ def handle_code(options: argparse.Namespace): exit(1) elif options.format == 'asm': - print ("Load ASM from FILE") + print("Load ASM from FILE") assembly = read_file(options.filename) assembler = arch_utils.assembler(options.arch, archendian, options.thumb) @@ -93,7 +95,7 @@ def handle_code(options: argparse.Namespace): code = bytes(code) elif options.format == 'bin': - print ("Load BIN from FILE") + print("Load BIN from FILE") if options.filename is not None: code = read_file(options.filename) else: @@ -115,6 +117,7 @@ def handle_code(options: argparse.Namespace): return ql + def handle_run(options: argparse.Namespace): effective_argv = [] @@ -150,6 +153,7 @@ def handle_run(options: argparse.Namespace): return ql + def handle_examples(parser: argparse.ArgumentParser): prog = os.path.basename(__file__) @@ -187,6 +191,7 @@ def handle_examples(parser: argparse.ArgumentParser): parser.exit(0, __ql_examples) + if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--version', action='version', version=f'qltool for Qiling {ql_ver}, using Unicorn {uc_ver}') @@ -236,7 +241,7 @@ if __name__ == '__main__': comm_parser.add_argument('--log-plain', action='store_true', help="do not use colors in log output") comm_parser.add_argument('--root', action='store_true', help='enable sudo required mode') comm_parser.add_argument('--debug-stop', action='store_true', help='stop running on error; requires verbose to be set to either "debug" or "dump"') - comm_parser.add_argument('-m', '--multithread',action='store_true', help='run in multithread mode') + comm_parser.add_argument('-m', '--multithread', action='store_true', help='run in multithread mode') comm_parser.add_argument('--timeout', type=int, default=0, help='set emulation timeout') comm_parser.add_argument('-c', '--coverage-file', default=None, help='code coverage file name') comm_parser.add_argument('--coverage-format', default='drcov', choices=cov_utils.factory.formats, help='code coverage file format') diff --git a/tests/test_blob.py b/tests/test_blob.py index 26bd5f7f2..bc191dc16 100644 --- a/tests/test_blob.py +++ b/tests/test_blob.py @@ -3,13 +3,16 @@ # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # -import sys, unittest +import unittest + +import sys sys.path.append("..") from qiling.core import Qiling from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE from qiling.os.const import STRING + class BlobTest(unittest.TestCase): def test_uboot_arm(self): def my_getenv(ql, *args, **kwargs): diff --git a/tests/test_debugger.py b/tests/test_debugger.py index ca7ca33e9..c8df8ae0c 100644 --- a/tests/test_debugger.py +++ b/tests/test_debugger.py @@ -1,14 +1,20 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # -import sys, threading, unittest, socket, time +import socket +import threading +import time +import unittest +import sys sys.path.append("..") + from qiling import Qiling from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE + class SimpleGdbClient: DELAY = 0.6 @@ -37,8 +43,8 @@ def send(self, msg: str): self.__file.write(f'${msg}#{SimpleGdbClient.checksum(msg):02x}') self.__file.flush() -class DebuggerTest(unittest.TestCase): +class DebuggerTest(unittest.TestCase): def test_gdbdebug_file_server(self): ql = Qiling(["../examples/rootfs/x8664_linux/bin/x8664_hello"], "../examples/rootfs/x8664_linux", verbose=QL_VERBOSE.DEBUG) ql.debugger = True @@ -192,5 +198,6 @@ def gdb_test_client(): ql.run() del ql + if __name__ == "__main__": - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/test_edl.py b/tests/test_edl.py index 7f86845a0..5f8c80b11 100644 --- a/tests/test_edl.py +++ b/tests/test_edl.py @@ -3,47 +3,50 @@ # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # -import os, sys, time, unittest +import time +import unittest + from struct import pack +from typing import Callable +import sys sys.path.append("..") -from qiling import * + +from qiling import Qiling from qiling.const import QL_VERBOSE from unicorn import * -from unicorn.arm64_const import * - -def replace_function(ql,addr,callback): - def runcode(ql): - ret=callback(ql) - ql.arch.regs.x0=ret - ql.arch.regs.pc=ql.arch.regs.x30 #lr - ql.hook_address(runcode,addr) - -def hook_mem_invalid(uc, access, address, size, value, user_data): - pc = uc.reg_read(UC_ARM64_REG_PC) - if access == UC_MEM_WRITE: - info=("invalid WRITE of 0x%x at 0x%X, data size = %u, data value = 0x%x" % (address, pc, size, value)) - if access == UC_MEM_READ: - info=("invalid READ of 0x%x at 0x%X, data size = %u" % (address, pc, size)) - if access == UC_MEM_FETCH: - info=("UC_MEM_FETCH of 0x%x at 0x%X, data size = %u" % (address, pc, size)) - if access == UC_MEM_READ_UNMAPPED: - info=("UC_MEM_READ_UNMAPPED of 0x%x at 0x%X, data size = %u" % (address, pc, size)) - if access == UC_MEM_WRITE_UNMAPPED: - info=("UC_MEM_WRITE_UNMAPPED of 0x%x at 0x%X, data size = %u" % (address, pc, size)) - if access == UC_MEM_FETCH_UNMAPPED: - info=("UC_MEM_FETCH_UNMAPPED of 0x%x at 0x%X, data size = %u" % (address, pc, size)) - if access == UC_MEM_WRITE_PROT: - info=("UC_MEM_WRITE_PROT of 0x%x at 0x%X, data size = %u" % (address, pc, size)) - if access == UC_MEM_FETCH_PROT: - info=("UC_MEM_FETCH_PROT of 0x%x at 0x%X, data size = %u" % (address, pc, size)) - if access == UC_MEM_FETCH_PROT: - info=("UC_MEM_FETCH_PROT of 0x%x at 0x%X, data size = %u" % (address, pc, size)) - if access == UC_MEM_READ_AFTER: - info=("UC_MEM_READ_AFTER of 0x%x at 0x%X, data size = %u" % (address, pc, size)) - print(info) + + +def replace_function(ql: Qiling, addr: int, callback: Callable): + def runcode(ql: Qiling): + ret = callback(ql) + ql.arch.regs.x0 = ret + ql.arch.regs.pc = ql.arch.regs.x30 # lr + + ql.hook_address(runcode, addr) + + +def hook_mem_invalid(ql: Qiling, access: int, address: int, size: int, value: int, user_data): + pc = ql.arch.regs.arch_pc + + info = { + UC_MEM_WRITE: f"invalid WRITE {address:#x} at {pc:#X}, data size = {size:d}, data value = {value:#x}", + UC_MEM_READ: f"invalid READ {address:#x} at {pc:#X}, data size = {size:d}", + UC_MEM_FETCH: f"UC_MEM_FETCH {address:#x} at {pc:#X}, data size = {size:d}", + UC_MEM_READ_UNMAPPED: f"UC_MEM_READ_UNMAPPED {address:#x} at {pc:#X}, data size = {size:d}", + UC_MEM_WRITE_UNMAPPED: f"UC_MEM_WRITE_UNMAPPED {address:#x} at {pc:#X}, data size = {size:d}", + UC_MEM_FETCH_UNMAPPED: f"UC_MEM_FETCH_UNMAPPED {address:#x} at {pc:#X}, data size = {size:d}", + UC_MEM_WRITE_PROT: f"UC_MEM_WRITE_PROT of {address:#x} at {pc:#X}, data size = {size:d}", + UC_MEM_FETCH_PROT: f"UC_MEM_FETCH_PROT of {address:#x} at {pc:#X}, data size = {size:d}", + UC_MEM_FETCH_PROT: f"UC_MEM_FETCH_PROT of {address:#x} at {pc:#X}, data size = {size:d}", + UC_MEM_READ_AFTER: f"UC_MEM_READ_AFTER of {address:#x} at {pc:#X}, data size = {size:d}" + } + + print(info[access]) + return False + class TestAndroid(unittest.TestCase): def test_edl_arm64(self): test_binary = "../examples/rootfs/arm64_edl/bin/arm64_edl" @@ -92,5 +95,6 @@ def devprg_tx_blocking(ql): del ql + if __name__ == "__main__": unittest.main() diff --git a/tests/test_elf.py b/tests/test_elf.py index dc9f187b2..b5f4545bb 100644 --- a/tests/test_elf.py +++ b/tests/test_elf.py @@ -1,11 +1,18 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # -import sys, unittest, string, random, os, io, re +import unittest +import string +import random +import os +import io +import re +import sys sys.path.append("..") + from qiling import Qiling from qiling.const import QL_ARCH, QL_OS, QL_INTERCEPT, QL_STOP, QL_VERBOSE from qiling.exception import * @@ -14,8 +21,8 @@ from qiling.os.posix import syscall from qiling.os.mapper import QlFsMappedObject -class ELFTest(unittest.TestCase): +class ELFTest(unittest.TestCase): def test_libpatch_elf_linux_x8664(self): ql = Qiling(["../examples/rootfs/x8664_linux/bin/patch_test.bin"], "../examples/rootfs/x8664_linux") @@ -23,13 +30,11 @@ def test_libpatch_elf_linux_x8664(self): ql.run() del ql - - def test_elf_freebsd_x8664(self): + def test_elf_freebsd_x8664(self): ql = Qiling(["../examples/rootfs/x8664_freebsd/bin/x8664_hello_asm"], "../examples/rootfs/x8664_freebsd", verbose=QL_VERBOSE.DUMP) ql.run() del ql - def test_elf_partial_linux_x8664(self): def dump(ql, *args, **kw): ql.save(reg=False, cpu_context=True, snapshot="/tmp/snapshot.bin") @@ -169,11 +174,10 @@ def my_puts_exit(ql): self.assertEqual(0x1, self.test_exit_rdi) self.assertEqual("CCCC", self.test_enter_str) - + del self.test_exit_rdi del self.test_enter_str - del ql - + del ql def test_elf_linux_x8664_flex_api(self): opened = [] @@ -201,13 +205,11 @@ def hook_main(ql: Qiling): # test whether we interpected opening urandom self.assertListEqual(opened, [r'/dev/urandom']) - def test_elf_linux_x8664_static(self): ql = Qiling(["../examples/rootfs/x8664_linux/bin/x8664_hello_static"], "../examples/rootfs/x8664_linux", verbose=QL_VERBOSE.DEBUG) ql.run() del ql - def test_elf_linux_x86(self): filename = 'test.qlog' @@ -217,18 +219,16 @@ def test_elf_linux_x86(self): os.remove(filename) del ql - def test_elf_linux_x86_static(self): ql = Qiling(["../examples/rootfs/x86_linux/bin/x86_hello_static"], "../examples/rootfs/x86_linux", verbose=QL_VERBOSE.DEBUG) ql.run() del ql - def test_elf_linux_x86_posix_syscall(self): def test_syscall_read(ql, read_fd, read_buf, read_count, *args): target = False pathname = ql.os.fd[read_fd].name.split('/')[-1] - + if pathname == "test_syscall_read.txt": print("test => read(%d, %s, %d)" % (read_fd, pathname, read_count)) target = True @@ -318,10 +318,10 @@ def test_syscall_truncate(ql, trunc_pathname, trunc_length, *args): def test_syscall_ftruncate(ql, ftrunc_fd, ftrunc_length, *args): target = False pathname = ql.os.fd[ftrunc_fd].name.split('/')[-1] - + reg = ql.arch.regs.read("eax") print("reg : 0x%x" % reg) - ql.arch.regs.eax = reg + ql.arch.regs.eax = reg if pathname == "test_syscall_ftruncate.txt": print("test => ftruncate(%d, 0x%x)" % (ftrunc_fd, ftrunc_length)) @@ -347,8 +347,7 @@ def test_syscall_ftruncate(ql, ftrunc_fd, ftrunc_length, *args): ql.run() del ql - - def test_elf_linux_arm(self): + def test_elf_linux_arm(self): def my_puts(ql): params = ql.os.resolve_fcall_params(ELFTest.PARAMS_PUTS) print(f'puts("{params["s"]}")') @@ -361,137 +360,131 @@ def my_puts(ql): ql.run() del ql - - def test_elf_linux_arm_static(self): + def test_elf_linux_arm_static(self): ql = Qiling(["../examples/rootfs/arm_linux/bin/arm_hello_static"], "../examples/rootfs/arm_linux", verbose=QL_VERBOSE.DEFAULT) all_mem = ql.mem.save() ql.mem.restore(all_mem) ql.run() del ql - # syscall testing for ARM, will be uncomment after ARM executable generated properly. # def test_elf_linux_arm_posix_syscall(self): - # def test_syscall_read(ql, read_fd, read_buf, read_count, *args): - # target = False - # pathname = ql.os.fd[read_fd].name.split('/')[-1] - - # if pathname == "test_syscall_read.txt": - # print("test => read(%d, %s, %d)" % (read_fd, pathname, read_count)) - # target = True - - # syscall.ql_syscall_read(ql, read_fd, read_buf, read_count, *args) - - # if target: - # real_path = ql.os.fd[read_fd].name - # with open(real_path) as fd: - # assert fd.read() == ql.mem.read(read_buf, read_count).decode() - # os.remove(real_path) - - # def test_syscall_write(ql, write_fd, write_buf, write_count, *args): - # target = False - # pathname = ql.os.fd[write_fd].name.split('/')[-1] - - # if pathname == "test_syscall_write.txt": - # print("test => write(%d, %s, %d)" % (write_fd, pathname, write_count)) - # target = True - - # syscall.ql_syscall_write(ql, write_fd, write_buf, write_count, *args) - - # if target: - # real_path = ql.os.fd[write_fd].name - # with open(real_path) as fd: - # assert fd.read() == 'Hello testing\x00' - # os.remove(real_path) - - # def test_syscall_open(ql, open_pathname, open_flags, open_mode, *args): - # target = False - # pathname = ql.os.utils.read_cstring(open_pathname) - - # if pathname == "test_syscall_open.txt": - # print("test => open(%s, 0x%x, 0%o)" % (pathname, open_flags, open_mode)) - # target = True - - # syscall.ql_syscall_open(ql, open_pathname, open_flags, open_mode, *args) - - # if target: - # real_path = ql.os.path.transform_to_real_path(pathname) - # assert os.path.isfile(real_path) == True - # os.remove(real_path) - - # def test_syscall_unlink(ql, unlink_pathname, *args): - # target = False - # pathname = ql.os.utils.read_cstring(unlink_pathname) - - # if pathname == "test_syscall_unlink.txt": - # print("test => unlink(%s)" % (pathname)) - # target = True - - # syscall.ql_syscall_unlink(ql, unlink_pathname, *args) - - # if target: - # real_path = ql.os.path.transform_to_real_path(pathname) - # assert os.path.isfile(real_path) == False - - # def test_syscall_truncate(ql, trunc_pathname, trunc_length, *args): - # target = False - # pathname = ql.os.utils.read_cstring(trunc_pathname) - - # if pathname == "test_syscall_truncate.txt": - # print("test => truncate(%s, 0x%x)" % (pathname, trunc_length)) - # target = True - - # syscall.ql_syscall_truncate(ql, trunc_pathname, trunc_length, *args) - - # if target: - # real_path = ql.os.path.transform_to_real_path(pathname) - # assert os.stat(real_path).st_size == 0 - # os.remove(real_path) - - # def test_syscall_ftruncate(ql, ftrunc_fd, ftrunc_length, *args): - # target = False - # pathname = ql.os.fd[ftrunc_fd].name.split('/')[-1] - - # if pathname == "test_syscall_ftruncate.txt": - # print("test => ftruncate(%d, 0x%x)" % (ftrunc_fd, ftrunc_length)) - # target = True - - # syscall.ql_syscall_ftruncate(ql, ftrunc_fd, ftrunc_length, *args) - - # if target: - # real_path = ql.os.path.transform_to_real_path(pathname) - # assert os.stat(real_path).st_size == 0x10 - # os.remove(real_path) - - # ql = Qiling(["../examples/rootfs/arm_linux/bin/arm_posix_syscall"], "../examples/rootfs/arm_linux", verbose=QL_VERBOSE.DEBUG) - # ql.os.set_syscall(0x3, test_syscall_read) - # ql.os.set_syscall(0x4, test_syscall_write) - # ql.os.set_syscall(0x5, test_syscall_open) - # ql.os.set_syscall(0xa, test_syscall_unlink) - # ql.os.set_syscall(0x5c, test_syscall_truncate) - # ql.os.set_syscall(0x5d, test_syscall_ftruncate) - # ql.run() - # del ql - + # def test_syscall_read(ql, read_fd, read_buf, read_count, *args): + # target = False + # pathname = ql.os.fd[read_fd].name.split('/')[-1] + # + # if pathname == "test_syscall_read.txt": + # print("test => read(%d, %s, %d)" % (read_fd, pathname, read_count)) + # target = True + # + # syscall.ql_syscall_read(ql, read_fd, read_buf, read_count, *args) + # + # if target: + # real_path = ql.os.fd[read_fd].name + # with open(real_path) as fd: + # assert fd.read() == ql.mem.read(read_buf, read_count).decode() + # os.remove(real_path) + # + # def test_syscall_write(ql, write_fd, write_buf, write_count, *args): + # target = False + # pathname = ql.os.fd[write_fd].name.split('/')[-1] + # + # if pathname == "test_syscall_write.txt": + # print("test => write(%d, %s, %d)" % (write_fd, pathname, write_count)) + # target = True + # + # syscall.ql_syscall_write(ql, write_fd, write_buf, write_count, *args) + # + # if target: + # real_path = ql.os.fd[write_fd].name + # with open(real_path) as fd: + # assert fd.read() == 'Hello testing\x00' + # os.remove(real_path) + # + # def test_syscall_open(ql, open_pathname, open_flags, open_mode, *args): + # target = False + # pathname = ql.os.utils.read_cstring(open_pathname) + # + # if pathname == "test_syscall_open.txt": + # print("test => open(%s, 0x%x, 0%o)" % (pathname, open_flags, open_mode)) + # target = True + # + # syscall.ql_syscall_open(ql, open_pathname, open_flags, open_mode, *args) + # + # if target: + # real_path = ql.os.path.transform_to_real_path(pathname) + # assert os.path.isfile(real_path) == True + # os.remove(real_path) + # + # def test_syscall_unlink(ql, unlink_pathname, *args): + # target = False + # pathname = ql.os.utils.read_cstring(unlink_pathname) + # + # if pathname == "test_syscall_unlink.txt": + # print("test => unlink(%s)" % (pathname)) + # target = True + # + # syscall.ql_syscall_unlink(ql, unlink_pathname, *args) + # + # if target: + # real_path = ql.os.path.transform_to_real_path(pathname) + # assert os.path.isfile(real_path) == False + # + # def test_syscall_truncate(ql, trunc_pathname, trunc_length, *args): + # target = False + # pathname = ql.os.utils.read_cstring(trunc_pathname) + # + # if pathname == "test_syscall_truncate.txt": + # print("test => truncate(%s, 0x%x)" % (pathname, trunc_length)) + # target = True + # + # syscall.ql_syscall_truncate(ql, trunc_pathname, trunc_length, *args) + # + # if target: + # real_path = ql.os.path.transform_to_real_path(pathname) + # assert os.stat(real_path).st_size == 0 + # os.remove(real_path) + # + # def test_syscall_ftruncate(ql, ftrunc_fd, ftrunc_length, *args): + # target = False + # pathname = ql.os.fd[ftrunc_fd].name.split('/')[-1] + # + # if pathname == "test_syscall_ftruncate.txt": + # print("test => ftruncate(%d, 0x%x)" % (ftrunc_fd, ftrunc_length)) + # target = True + # + # syscall.ql_syscall_ftruncate(ql, ftrunc_fd, ftrunc_length, *args) + # + # if target: + # real_path = ql.os.path.transform_to_real_path(pathname) + # assert os.stat(real_path).st_size == 0x10 + # os.remove(real_path) + # + # ql = Qiling(["../examples/rootfs/arm_linux/bin/arm_posix_syscall"], "../examples/rootfs/arm_linux", verbose=QL_VERBOSE.DEBUG) + # ql.os.set_syscall(0x3, test_syscall_read) + # ql.os.set_syscall(0x4, test_syscall_write) + # ql.os.set_syscall(0x5, test_syscall_open) + # ql.os.set_syscall(0xa, test_syscall_unlink) + # ql.os.set_syscall(0x5c, test_syscall_truncate) + # ql.os.set_syscall(0x5d, test_syscall_ftruncate) + # ql.run() + # del ql def test_elf_linux_arm64(self): ql = Qiling(["../examples/rootfs/arm64_linux/bin/arm64_hello"], "../examples/rootfs/arm64_linux", verbose=QL_VERBOSE.DEBUG) ql.run() del ql - - def test_elf_linux_arm64_static(self): + def test_elf_linux_arm64_static(self): ql = Qiling(["../examples/rootfs/arm64_linux/bin/arm64_hello_static"], "../examples/rootfs/arm64_linux", verbose=QL_VERBOSE.DEFAULT) ql.run() del ql - def test_elf_linux_mips32eb_static(self): ql = Qiling(["../examples/rootfs/mips32_linux/bin/mips32_hello_static"], "../examples/rootfs/mips32_linux") ql.run() del ql - def test_elf_linux_mips32eb(self): def random_generator(size=6, chars=string.ascii_uppercase + string.digits): return ''.join(random.choice(chars) for x in range(size)) @@ -501,7 +494,6 @@ def random_generator(size=6, chars=string.ascii_uppercase + string.digits): del ql - def test_mips32eb_fake_urandom(self): class Fake_urandom(QlFsMappedObject): @@ -510,7 +502,7 @@ def read(self, size): def fstat(self): return -1 - + def close(self): return 0 @@ -534,7 +526,6 @@ def check_exit_code(ql, exit_code, *args, **kw): self.assertEqual(0, ql.exit_group_code) del ql - def test_elf_onEnter_mips32el(self): def my_puts_onenter(ql: Qiling): params = ql.os.resolve_fcall_params(ELFTest.PARAMS_PUTS) @@ -553,7 +544,6 @@ def my_puts_onenter(ql: Qiling): del ql - def test_elf_linux_arm64_posix_syscall(self): def test_syscall_read(ql, read_fd, read_buf, read_count, *args): target = False @@ -561,7 +551,7 @@ def test_syscall_read(ql, read_fd, read_buf, read_count, *args): reg = ql.arch.regs.read("x0") print("reg : 0x%x" % reg) - ql.arch.regs.x0 = reg + ql.arch.regs.x0 = reg if pathname == "test_syscall_read.txt": print("test => read(%d, %s, %d)" % (read_fd, pathname, read_count)) @@ -682,7 +672,6 @@ def test_syscall_ftruncate(ql, ftrunc_fd, ftrunc_length, *args): ql.run() del ql - def test_elf_linux_mips32el(self): def random_generator(size=6, chars=string.ascii_uppercase + string.digits): return ''.join(random.choice(chars) for x in range(size)) @@ -691,25 +680,23 @@ def random_generator(size=6, chars=string.ascii_uppercase + string.digits): ql.run() del ql - def test_elf_linux_mips32el_static(self): def random_generator(size=6, chars=string.ascii_uppercase + string.digits): return ''.join(random.choice(chars) for x in range(size)) ql = Qiling(["../examples/rootfs/mips32el_linux/bin/mips32el_hello_static", random_generator(random.randint(1,99))], "../examples/rootfs/mips32el_linux") ql.run() - del ql - + del ql def test_elf_linux_mips32el_posix_syscall(self): def test_syscall_read(ql, read_fd, read_buf, read_count, *args): target = False pathname = ql.os.fd[read_fd].name.split('/')[-1] - + reg = ql.arch.regs.read("v0") print("reg : 0x%x" % reg) - ql.arch.regs.v0 = reg - + ql.arch.regs.v0 = reg + if pathname == "test_syscall_read.txt": print("test => read(%d, %s, %d)" % (read_fd, pathname, read_count)) target = True @@ -825,25 +812,23 @@ def test_syscall_ftruncate(ql, ftrunc_fd, ftrunc_length, *args): ql.run() del ql - def test_elf_linux_powerpc(self): ql = Qiling(["../examples/rootfs/powerpc_linux/bin/powerpc_hello"], "../examples/rootfs/powerpc_linux", verbose=QL_VERBOSE.DEBUG) ql.run() del ql - def test_elf_linux_arm_custom_syscall(self): def my_syscall_write(ql, write_fd, write_buf, write_count, *args, **kw): regreturn = 0 buf = None mapaddr = ql.mem.map_anywhere(0x100000) ql.log.info("0x%x" % mapaddr) - + reg = ql.arch.regs.read("r0") print("reg : 0x%x" % reg) ql.arch.regs.r0 = reg - - + + try: buf = ql.mem.read(write_buf, write_count) ql.log.info("\n+++++++++\nmy write(%d,%x,%i) = %d\n+++++++++" % (write_fd, write_buf, write_count, regreturn)) @@ -860,13 +845,12 @@ def my_syscall_write(ql, write_fd, write_buf, write_count, *args, **kw): ql = Qiling(["../examples/rootfs/arm_linux/bin/arm_hello"], "../examples/rootfs/arm_linux") ql.os.set_syscall(0x04, my_syscall_write) ql.run() - + self.assertEqual(1, self.set_syscall) - + del self.set_syscall del ql - def test_elf_linux_x86_crackme(self): def instruction_count(ql, address, size, user_data): user_data[0] += 1 @@ -925,10 +909,10 @@ def __init__(self): def read(self, size): return b'\x01' - + def fstat(self): return -1 - + def close(self): return 0 @@ -943,7 +927,7 @@ def check_exit_group_code(ql, exit_code, *args, **kw): ql.exit_group_code = exit_code def check_exit_code(ql, exit_code, *args, **kw): - ql.exit_code = exit_code + ql.exit_code = exit_code ql.os.set_syscall("exit_group", check_exit_group_code, QL_INTERCEPT.ENTER) ql.os.set_syscall("exit", check_exit_code, QL_INTERCEPT.ENTER) @@ -956,7 +940,6 @@ def check_exit_code(ql, exit_code, *args, **kw): self.assertEqual(last + 1, i) last = i del ql - def test_x86_fake_urandom(self): class Fake_urandom(QlFsMappedObject): @@ -966,7 +949,7 @@ def read(self, size): def fstat(self): return -1 - + def close(self): return 0 @@ -980,21 +963,20 @@ def check_exit_group_code(ql, exit_code, *args, **kw): ql.exit_group_code = exit_code def check_exit_code(ql, exit_code, *args, **kw): - ql.exit_code = exit_code + ql.exit_code = exit_code ql.os.set_syscall("exit_group", check_exit_group_code, QL_INTERCEPT.ENTER) ql.os.set_syscall("exit", check_exit_code, QL_INTERCEPT.ENTER) ql.run() self.assertEqual(0, ql.exit_code) - self.assertEqual(0, ql.exit_group_code) + self.assertEqual(0, ql.exit_group_code) del ql - def test_x8664_map_urandom(self): ql = Qiling(["../examples/rootfs/x8664_linux/bin/x8664_fetch_urandom"], "../examples/rootfs/x8664_linux", verbose=QL_VERBOSE.DEBUG) ql.add_fs_mapper("/dev/urandom","/dev/urandom") - + ql.exit_code = 0 ql.exit_group_code = 0 @@ -1002,7 +984,7 @@ def check_exit_group_code(ql, exit_code, *args, **kw): ql.exit_group_code = exit_code def check_exit_code(ql, exit_code, *args, **kw): - ql.exit_code = exit_code + ql.exit_code = exit_code ql.os.set_syscall("exit_group", check_exit_group_code, QL_INTERCEPT.ENTER) ql.os.set_syscall("exit", check_exit_code, QL_INTERCEPT.ENTER) @@ -1014,7 +996,6 @@ def check_exit_code(ql, exit_code, *args, **kw): del ql - def test_x8664_symlink(self): ql = Qiling(["../examples/rootfs/x8664_linux_symlink/bin/x8664_hello"], "../examples/rootfs/x8664_linux_symlink", verbose=QL_VERBOSE.DEBUG) ql.run() @@ -1076,12 +1057,12 @@ def test_elf_linux_x8664_getdents(self): del ql - def test_elf_linux_armeb(self): + def test_elf_linux_armeb(self): ql = Qiling(["../examples/rootfs/armeb_linux/bin/armeb_hello"], "../examples/rootfs/armeb_linux", verbose=QL_VERBOSE.DEBUG) ql.run() del ql - def test_elf_linux_armeb_static(self): + def test_elf_linux_armeb_static(self): ql = Qiling(["../examples/rootfs/armeb_linux/bin/armeb_hello_static"], "../examples/rootfs/armeb_linux", verbose=QL_VERBOSE.DEFAULT) ql.run() del ql @@ -1111,10 +1092,10 @@ def test_elf_linux_x86_getdents64(self): self.assertTrue("bin\n" in ql.os.stdout.read().decode("utf-8")) del ql - + def test_memory_search(self): ql = Qiling(code=b"\xCC", archtype=QL_ARCH.X8664, ostype=QL_OS.LINUX, verbose=QL_VERBOSE.DEBUG) - + ql.mem.map(0x1000, 0x1000) ql.mem.map(0x2000, 0x1000) ql.mem.map(0x3000, 0x1000) @@ -1149,7 +1130,7 @@ def test_memory_search(self): self.assertEqual([0x1000 + 11, 0x2000 + 11, 0x3000 + 43], ql.mem.search(re.compile(b"\x09\x53(\x0f|\x1a|\x04)[^\x0d]"))) del ql - + def test_elf_linux_x8664_path_traversion(self): ql = Qiling(["../examples/rootfs/x8664_linux/bin/path_traverse_static"], "../examples/rootfs/x8664_linux", verbose=QL_VERBOSE.DEBUG) diff --git a/tests/test_evm.py b/tests/test_evm.py index f7cc193bc..b17a7510a 100644 --- a/tests/test_evm.py +++ b/tests/test_evm.py @@ -1,8 +1,12 @@ #!/usr/bin/env python3 -import os, platform, sys, unittest +import os +import platform +import unittest +import sys sys.path.append("..") + from qiling import Qiling from qiling.const import QL_ARCH, QL_VERBOSE @@ -12,11 +16,11 @@ sys.exit(0) if platform.system() == "Darwin" and platform.machine() == "arm64": - sys.exit(0) + sys.exit(0) # python 3.10 has not been supported yet in the latest blake2b-py release -if sys.version_info >= (3,10): - sys.exit(0) +if sys.version_info >= (3, 10): + sys.exit(0) class Checklist: @@ -62,7 +66,7 @@ def check_balance(sender, destination): call_data = '0x70a08231'+ql.arch.evm.abi.convert(['address'], [sender]) msg2 = ql.arch.evm.create_message(sender, destination, call_data) return ql.run(code=msg2) - + result = check_balance(user1, c1) print('\n\nuser1 balance =', int(result.output.hex()[2:], 16)) ql.hook_del(h2) @@ -70,14 +74,14 @@ def check_balance(sender, destination): # SMART CONTRACT DEPENDENT - message3: transform 21 from user1 to user2 call_data = '0xa9059cbb'+ ql.arch.evm.abi.convert(['address'], [user2]) + \ ql.arch.evm.abi.convert(['uint256'], [21]) - msg1 = ql.arch.evm.create_message(user1, c1, call_data) + msg1 = ql.arch.evm.create_message(user1, c1, call_data) result = ql.run(code=msg1) print('\n\nis success =', int(result.output.hex()[2:], 16)) # message4: check balance of user1, should be MAX - 1 result = check_balance(user1, c1) print('\n\nuser1 balance =', hex(int(result.output.hex()[2:], 16))) - + self.assertEqual(hex(int(result.output.hex()[2:], 16)), '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff') self.assertTrue(testcheck.visited_hookaddr) self.assertTrue(testcheck.visited_hookcode) @@ -106,7 +110,7 @@ def check_balance(sender, destination): # # SMART CONTRACT DEPENDENT: transform from user1 to user2 call_data = '0xa9059cbb'+ ql.arch.evm.abi.convert(['address'], [user2]) + \ ql.arch.evm.abi.convert(['uint256'], [0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe]) - msg1 = ql.arch.evm.create_message(user1, c1, data=call_data) + msg1 = ql.arch.evm.create_message(user1, c1, data=call_data) result = ql.run(code=msg1) if int(result.output.hex()[2:], 16) == 1: print('User1 transfered Token to User1') @@ -148,7 +152,7 @@ def test_abi_encoding(self): msg1 = ql.arch.evm.create_message(user1, c1, data=call_data) result = ql.run(code=msg1) - + result_data = ql.arch.evm.abi.decode_params(['string'], result.output) self.assertEqual(call_param[0], result_data[0]) diff --git a/tests/test_macho.py b/tests/test_macho.py index ae766915c..d002209ee 100644 --- a/tests/test_macho.py +++ b/tests/test_macho.py @@ -1,15 +1,17 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # -import sys, unittest +import unittest + +import sys sys.path.append("..") -from qiling import * -from qiling.exception import * +from qiling import Qiling from qiling.const import QL_VERBOSE + class MACHOTest(unittest.TestCase): def test_macho_macos_x8664(self): ql = Qiling(["../examples/rootfs/x8664_macos/bin/x8664_hello"], "../examples/rootfs/x8664_macos", verbose=QL_VERBOSE.DEBUG) @@ -19,5 +21,6 @@ def test_usercorn_x8664(self): ql = Qiling(["../examples/rootfs/x8664_macos/bin/x8664_hello_usercorn"], "../examples/rootfs/x8664_macos", verbose=QL_VERBOSE.DEBUG) ql.run() + if __name__ == "__main__": unittest.main() diff --git a/tests/test_mcu.py b/tests/test_mcu.py index 775700c21..14bf9c235 100644 --- a/tests/test_mcu.py +++ b/tests/test_mcu.py @@ -1,19 +1,21 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # +import unittest -import sys, unittest +import sys sys.path.append("..") from qiling.core import Qiling from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE from qiling.extensions.mcu.stm32f4 import stm32f407, stm32f411, stm32f429 from qiling.extensions.mcu.stm32f1 import stm32f103 -from qiling.extensions.mcu.atmel import sam3x8e +from qiling.extensions.mcu.atmel import sam3x8e from qiling.extensions.mcu.gd32vf1 import gd32vf103 + class MCUTest(unittest.TestCase): def test_mcu_led_stm32f411(self): ql = Qiling(["../examples/rootfs/mcu/stm32f411/rand_blink.hex"], @@ -47,7 +49,7 @@ def create_qiling(): ql2.run(count=500) buf2 = ql2.hw.usart2.recv() print('[2] Received from usart: ', buf2) - + self.assertEqual(buf1 + buf2, b'Hello USART\n') del ql1, ql2 @@ -58,16 +60,16 @@ def test_mcu_usart_input_stm32f411(self): ql.hw.create('usart2') ql.hw.create('rcc') - + ql.run(count=1000) - + ql.hw.usart2.send(b'Hello\n') ql.run(count=30000) ql.hw.usart2.send(b'USART\n') ql.run(count=30000) ql.hw.usart2.send(b'Input\n') ql.run(count=30000) - + buf = ql.hw.usart2.recv() self.assertEqual(buf, b'8b1a9953c4611296a827abf8c47804d7\n2daeb613094400290a24fe5086c68f06\n324118a6721dd6b8a9b9f4e327df2bf5\n') @@ -180,10 +182,10 @@ def test_mcu_led_rust_stm32f411(self): count = 0 def counter(): nonlocal count - count += 1 + count += 1 ql.hw.create('gpioa').hook_set(5, counter) - ql.hw.create('rcc') + ql.hw.create('rcc') ql.run(count=1000) self.assertTrue(count >= 5) @@ -211,9 +213,9 @@ def crack(passwd): ql.hw.usart1.send(passwd.encode() + b'\r') ql.hw.systick.set_ratio(400) - + ql.run(count=400000, end=0x8003225) - + return ql.arch.effective_pc == 0x8003225 self.assertTrue(crack('618618')) @@ -266,7 +268,7 @@ def test_mcu_i2c_interrupt_stm32f411(self): ql.hw.create('i2c1') ql.hw.create('rcc').watch() ql.hw.create('gpioa') - ql.hw.create('gpiob') + ql.hw.create('gpiob') class LCD: address = 0x3f << 1 @@ -293,7 +295,6 @@ def skip_delay(ql): del ql - def test_mcu_blink_gd32vf103(self): ql = Qiling(['../examples/rootfs/mcu/gd32vf103/blink.hex'], archtype=QL_ARCH.RISCV, ostype=QL_OS.MCU, env=gd32vf103, verbose=QL_VERBOSE.DEFAULT) @@ -317,7 +318,7 @@ def counter(): ql.hw.gpioc.hook_set(13, counter) ql.run(count=20000) self.assertTrue(count > 350) - + del ql def test_mcu_crc_stm32f407(self): @@ -370,10 +371,10 @@ def gpio_set_cb(pin): ql.hw.gpioa.hook_set(4, gpio_set_cb, '4') ql.run(count=400000) - + self.assertTrue((''.join(data)).find('1442413') != -1) self.assertTrue(ql.hw.usart1.recv()[:23] == b'SCTF{that1s___r1ghtflag') - + del ql def test_mcu_serial_sam3x8e(self): @@ -422,7 +423,7 @@ def test_mcu_hackme_stm32f429(self): self.assertEqual(ql.hw.usart2.recv(), b'Nice Hack!\n') self.assertEqual(ql.hw.usart3.recv(), b'Welcome to the world of Hacking!\naaaaaaaaaaaaaaaaaaaa\xa9\x05\n') - + def test_mcu_fastmode_stm32f429(self): ql = Qiling(["../examples/rootfs/mcu/stm32f429/bof.elf"], archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f429, verbose=QL_VERBOSE.DEFAULT) diff --git a/tests/test_perf.py b/tests/test_perf.py index 4c2d8e7ab..f598be504 100644 --- a/tests/test_perf.py +++ b/tests/test_perf.py @@ -1,26 +1,19 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # import cProfile -import pstats -import sys import inspect import os as pyos -sys.path.append("..") -from qiling import * - from test_elf import * from test_macho import * from test_shellcode import * perf_res_dir = "./perf_results/" +test_mapping = [] -test_mapping = [ - -] def populate_tests(): global test_mapping @@ -28,12 +21,16 @@ def populate_tests(): unit_tests = [ELFTest(), MACHOTest(), TestShellcode()] for ut in unit_tests: - ut_functions = inspect.getmembers(ut, predicate = inspect.ismethod) + ut_functions = inspect.getmembers(ut, predicate=inspect.ismethod) + for test_name, test_fn in ut_functions: - if not test_name.startswith("test_"): continue + if not test_name.startswith("test_"): + continue + outfile = perf_res_dir + test_name + ".perf" - test_mapping.append( (test_fn, outfile) ) - + test_mapping.append((test_fn, outfile)) + + def ql_profile(run_fn, outfile): pr = cProfile.Profile() pr.enable() @@ -42,6 +39,7 @@ def ql_profile(run_fn, outfile): pr.dump_stats(outfile) pr.print_stats() + def profile_all_functions(): if not pyos.path.isdir("perf_results"): pyos.mkdir("perf_results") @@ -54,5 +52,6 @@ def profile_all_functions(): except: pass + if __name__ == "__main__": - profile_all_functions() \ No newline at end of file + profile_all_functions() diff --git a/tests/test_peshellcode.py b/tests/test_peshellcode.py index bac350cdf..3d0618a94 100644 --- a/tests/test_peshellcode.py +++ b/tests/test_peshellcode.py @@ -1,11 +1,13 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # -import sys, unittest +import unittest +import sys sys.path.append("..") + from qiling import Qiling from qiling.const import QL_ARCH, QL_OS, QL_VERBOSE @@ -33,13 +35,13 @@ POINTER_TEST = bytes.fromhex('1122334455667788') + class PEShellcodeTest(unittest.TestCase): def test_windowssc_x86(self): ql = Qiling(code=X86_WIN, rootfs=r'../examples/rootfs/x86_windows', archtype=QL_ARCH.X86, ostype=QL_OS.WINDOWS, verbose=QL_VERBOSE.OFF) ql.run() del ql - def test_windowssc_x64(self): ql = Qiling(code=X8664_WIN, rootfs=r'../examples/rootfs/x8664_windows', archtype=QL_ARCH.X8664, ostype=QL_OS.WINDOWS, verbose=QL_VERBOSE.OFF) ql.run() diff --git a/tests/test_posix.py b/tests/test_posix.py index 3f24483c6..067bff84c 100644 --- a/tests/test_posix.py +++ b/tests/test_posix.py @@ -1,16 +1,13 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # -import sys,unittest +import unittest -sys.path.append("..") -from qiling import * -from qiling.exception import * from test_elf import * from test_riscv import * from test_qltool import * if __name__ == "__main__": - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/test_qltool.py b/tests/test_qltool.py index 0640336b5..3168664b5 100644 --- a/tests/test_qltool.py +++ b/tests/test_qltool.py @@ -1,15 +1,13 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # -import sys, subprocess, unittest - -sys.path.append("..") -from qiling import * -from qiling.exception import * - import os +import subprocess +import sys +import unittest + class Qltool_Test(unittest.TestCase): def test_qltool_exec_args(self): @@ -17,24 +15,23 @@ def test_qltool_exec_args(self): p = subprocess.Popen(create, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) for line in iter(p.stdout.readline, b''): self.stdout = line - + self.assertEqual(b'arg 2 test3\n', self.stdout) - def test_qltool_shellcode(self): create = [sys.executable, '../qltool', 'code', '--os','linux','--arch', 'x86', '--format', 'asm', '-f', '../examples/shellcodes/lin32_execve.asm'] try: subprocess.check_output(create,stderr=subprocess.STDOUT) - except subprocess.CalledProcessError as e: - raise RuntimeError("command '{}' return with error (code {}): {}".format(e.cmd, e.returncode, e.output)) + except subprocess.CalledProcessError as e: + raise RuntimeError("command '{}' return with error (code {}): {}".format(e.cmd, e.returncode, e.output)) def test_qltool_coverage(self): os.makedirs("./log_test", exist_ok=True) create = [sys.executable, '../qltool', 'run', '-f','../examples/rootfs/x8664_efi/bin/TcgPlatformSetupPolicy','--rootfs', '../examples/rootfs/x8664_efi','--coverage-format', 'drcov', '--coverage-file', 'log_test/TcgPlatformSetupPolicy'] try: subprocess.check_output(create, stderr=subprocess.STDOUT) - except subprocess.CalledProcessError as e: - raise RuntimeError("command '{}' return with error (code {}): {}".format(e.cmd, e.returncode, e.output)) + except subprocess.CalledProcessError as e: + raise RuntimeError("command '{}' return with error (code {}): {}".format(e.cmd, e.returncode, e.output)) def test_qltool_json(self): create = [sys.executable, '../qltool', 'run', '-f','../examples/rootfs/x86_linux/bin/x86_hello','--rootfs', '../examples/rootfs/x86_linux','--verbose', 'off', '--json'] diff --git a/tests/test_qnx.py b/tests/test_qnx.py index 0303c7fb3..21fa8a2e3 100644 --- a/tests/test_qnx.py +++ b/tests/test_qnx.py @@ -1,19 +1,21 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # -import sys, unittest +import unittest +import sys sys.path.append("..") -from qiling import * + +from qiling import Qiling from qiling.exception import * -from qiling.const import QL_VERBOSE from qiling.const import QL_INTERCEPT, QL_CALL_BLOCK, QL_VERBOSE from qiling.os.const import STRING + class QNXTest(unittest.TestCase): - + def test_arm_qnx_static(self): env = { "FOO": "bar" @@ -21,11 +23,9 @@ def test_arm_qnx_static(self): ql = Qiling(["../examples/rootfs/arm_qnx/bin/hello_static", "foo", "bar"], "../examples/rootfs/arm_qnx", env=env, verbose=QL_VERBOSE.DEBUG) ql.run() - def test_arm_qnx_sqrt(self): ql = Qiling(["../examples/rootfs/arm_qnx/bin/hello_sqrt"], "../examples/rootfs/arm_qnx", verbose=QL_VERBOSE.DEBUG) ql.run() - def test_set_api_arm_qnx_sqrt(self): self.set_api_puts_onenter = False @@ -76,5 +76,6 @@ def my_printf_onexit(ql: Qiling): del self.set_api_printf_onenter del self.set_api_printf_onexit + if __name__ == "__main__": unittest.main() diff --git a/tests/test_r2.py b/tests/test_r2.py index 06cdacbeb..4009c2f59 100644 --- a/tests/test_r2.py +++ b/tests/test_r2.py @@ -1,8 +1,10 @@ #!/usr/bin/env python3 -import sys, unittest +import unittest +import sys sys.path.append("..") + from qiling import Qiling from qiling.const import QL_ARCH, QL_VERBOSE @@ -13,7 +15,33 @@ else: test_r2 = True -EVM_CODE = bytes.fromhex("6060604052341561000f57600080fd5b60405160208061031c833981016040528080519060200190919050508060018190556000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555050610299806100836000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806318160ddd1461005c57806370a0823114610085578063a9059cbb146100d2575b600080fd5b341561006757600080fd5b61006f61012c565b6040518082815260200191505060405180910390f35b341561009057600080fd5b6100bc600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610132565b6040518082815260200191505060405180910390f35b34156100dd57600080fd5b610112600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061017a565b604051808215151515815260200191505060405180910390f35b60015481565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080826000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205403101515156101cb57600080fd5b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555060019050929150505600a165627a7a7230582098f1551a391a3e65b3ce45cfa2b3fa5f91eea9a3e7181a81454e025ea0d7151c0029") +EVM_CODE = bytes.fromhex(""" + 6060604052341561000f57600080fd5b60405160208061031c83398101604052 + 8080519060200190919050508060018190556000803373ffffffffffffffffff + ffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffff + ff16815260200190815260200160002081905550506102998061008360003960 + 00f300606060405260043610610057576000357c010000000000000000000000 + 0000000000000000000000000000000000900463ffffffff16806318160ddd14 + 61005c57806370a0823114610085578063a9059cbb146100d2575b600080fd5b + 341561006757600080fd5b61006f61012c565b60405180828152602001915050 + 60405180910390f35b341561009057600080fd5b6100bc600480803573ffffff + ffffffffffffffffffffffffffffffffff16906020019091905050610132565b + 6040518082815260200191505060405180910390f35b34156100dd57600080fd + 5b610112600480803573ffffffffffffffffffffffffffffffffffffffff1690 + 602001909190803590602001909190505061017a565b60405180821515151581 + 5260200191505060405180910390f35b60015481565b60008060008373ffffff + ffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffff + ffffffffffffff168152602001908152602001600020549050919050565b6000 + 80826000803373ffffffffffffffffffffffffffffffffffffffff1673ffffff + ffffffffffffffffffffffffffffffffff168152602001908152602001600020 + 5403101515156101cb57600080fd5b816000803373ffffffffffffffffffffff + ffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16 + 8152602001908152602001600020600082825403925050819055508160008085 + 73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffff + ffffffffffffffffffffff168152602001908152602001600020600082825401 + 9250508190555060019050929150505600a165627a7a7230582098f1551a391a + 3e65b3ce45cfa2b3fa5f91eea9a3e7181a81454e025ea0d7151c0029 +""") @unittest.skipUnless(test_r2, 'libr is missing') diff --git a/tests/test_shellcode.py b/tests/test_shellcode.py index 4d3974153..9b2b4e054 100644 --- a/tests/test_shellcode.py +++ b/tests/test_shellcode.py @@ -3,10 +3,11 @@ # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # -import sys import unittest +import sys sys.path.append("..") + from qiling import Qiling from qiling.const import QL_ARCH, QL_OS, QL_INTERCEPT, QL_VERBOSE diff --git a/tests/test_windows_stdio.py b/tests/test_windows_stdio.py index 9050ec571..b3ae17b82 100644 --- a/tests/test_windows_stdio.py +++ b/tests/test_windows_stdio.py @@ -1,16 +1,18 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # -import sys from typing import Sequence +import sys sys.path.append("..") -from qiling import * + +from qiling import Qiling from qiling.const import QL_VERBOSE from qiling.extensions import pipe + def instruction_count(ql: Qiling, address: int, size: int, user_data): user_data[0] += 1 From a9233b5ad62e98ac0a354573e71099a4c1bdca7c Mon Sep 17 00:00:00 2001 From: elicn Date: Mon, 19 Jun 2023 11:40:31 +0300 Subject: [PATCH 12/17] Speed up ARM assembler and disassembler access --- qiling/arch/arm.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/qiling/arch/arm.py b/qiling/arch/arm.py index b290ee8e5..b065ebee7 100644 --- a/qiling/arch/arm.py +++ b/qiling/arch/arm.py @@ -3,7 +3,7 @@ # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # -from functools import cached_property +from functools import cached_property, lru_cache from unicorn import Uc, UC_ARCH_ARM, UC_MODE_ARM, UC_MODE_THUMB, UC_MODE_BIG_ENDIAN from capstone import Cs, CS_ARCH_ARM, CS_MODE_ARM, CS_MODE_THUMB, CS_MODE_BIG_ENDIAN @@ -70,11 +70,15 @@ def effective_pc(self) -> int: # append 1 to pc if in thumb mode, or 0 otherwise return self.regs.pc | int(self.is_thumb) + @lru_cache(maxsize=4) + def __cached_disasm(self, mode: int) -> Cs: + return Cs(CS_ARCH_ARM, mode) + @property def disassembler(self) -> Cs: - # note: we do not cache the disassembler instance; rather we refresh it - # each time to make sure current endianess and thumb mode are taken into - # account + # note: since endianess and thumb mode might change during execution, we cannot + # cache the disassembler instance directly; rather we pick the appropriate cached + # instance mode = CS_MODE_ARM @@ -84,13 +88,17 @@ def disassembler(self) -> Cs: if self.is_thumb: mode += CS_MODE_THUMB - return Cs(CS_ARCH_ARM, mode) + return self.__cached_disasm(mode) + + @lru_cache(maxsize=4) + def __cached_asm(self, mode: int) -> Ks: + return Ks(KS_ARCH_ARM, mode) @property def assembler(self) -> Ks: - # note: we do not cache the assembler instance; rather we refresh it - # each time to make sure current endianess and thumb mode are taken into - # account + # note: since endianess and thumb mode might change during execution, we cannot + # cache the assembler instance directly; rather we pick the appropriate cached + # instance mode = KS_MODE_ARM @@ -100,7 +108,7 @@ def assembler(self) -> Ks: if self.is_thumb: mode += KS_MODE_THUMB - return Ks(KS_ARCH_ARM, mode) + return self.__cached_asm(mode) def enable_vfp(self) -> None: # set full access to cp10 and cp11 From 526a1b0a29fe3ec07a78403655fdfcd33b97681b Mon Sep 17 00:00:00 2001 From: elicn Date: Wed, 28 Jun 2023 21:27:46 +0300 Subject: [PATCH 13/17] Rename and revamp netgear 6220 example --- examples/netgear_6220.py | 104 ++++++++++++++++++++++++ examples/netgear_6220_mips32el_linux.py | 75 ----------------- 2 files changed, 104 insertions(+), 75 deletions(-) create mode 100644 examples/netgear_6220.py delete mode 100644 examples/netgear_6220_mips32el_linux.py diff --git a/examples/netgear_6220.py b/examples/netgear_6220.py new file mode 100644 index 000000000..c2c65acc6 --- /dev/null +++ b/examples/netgear_6220.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + +# Setup: +# - Unpack firmware rootfs (assumed hereby: 'rootfs/netgear_r6220') +# - The firmware expects '/dev/mtdblock11' to exist and be functional, otherwise it crashes +# - cd rootfs/netgear_r6220/dev +# - dd if=/dev/zero of=mtdblock11 bs=1024 count=129030 +# - mkfs.ext4 mtdblock11 +# +# Run: +# $ PYTHONPATH=/path/to/qiling ROOTFS=/path/to/netgear_rootfs python3 netgear_6220.py +# +# Emulation: +# Soon after Qiling starts emulating the firmware it will look like it is done running, but actually it +# isn't. During the emulation a few OS child processes are spawned, waiting for connection. To see them +# run 'ps | grep python3'. +# +# Once a connection is established on 127.0.0.1:8080, more child processes will be spawned. Note that in +# case a child process dies with an exception, it turns into a zombie process. To kill the spawned child +# processes, run 'pkill python3' + +from typing import List +import logging +import os + +import sys +sys.path.append("..") + +from qiling import Qiling +from qiling.const import QL_INTERCEPT, QL_VERBOSE +from qiling.log import QlColoredFormatter, QlBaseFormatter + + +# user may set 'ROOTFS' environment variable to use as rootfs +ROOTFS = os.environ.get('ROOTFS', r'./rootfs/netgear_r6220') + + +def __onexit_fork(ql: Qiling, retval: int) -> None: + # as the emulated binary forks more and more child processes, it becomes hard to keep track + # of their log entries and tell them apart. here we intercept the 'fork' system call on-exit + # (i.e. after it was already simulated, but before resuming emulation) and modify the newly + # created qiling instance's logger to show the os child processes id as part of the logs. + # + # note: os process id should not be confused with the internal qiling thread id, which does + # not exist here since we do not use qiling multithreading feature. + + # fork returns 0 on the newly created child process + if retval == 0: + GREEN = '\033[92m' + DEFAULT = '\033[39m' + + def __add_color(s: str) -> str: + """Colorize text. + """ + + return f'{GREEN}{s}{DEFAULT}' + + def __do_nothing(s: str) -> str: + """Use text as-is. + """ + + return s + + # patch current logger instance handlers to show the process id + for h in ql.log.handlers: + formatter = h.formatter + + if isinstance(formatter, QlColoredFormatter): + fmt = __add_color + + elif isinstance(formatter, QlBaseFormatter): + fmt = __do_nothing + + else: + raise RuntimeError('unexpected formatter class') + + style = logging.PercentStyle(f'%(levelname)s {fmt(f"[{os.getpid():4d}]")} %(message)s') + + formatter._style = style + formatter._fmt = style._fmt + + +def my_sandbox(path: List[str], rootfs: str) -> None: + ql = Qiling(path, rootfs, profile='netgear_6220.ql', verbose=QL_VERBOSE.DEBUG) + + ql.add_fs_mapper(r'/proc', r'/proc') + ql.os.set_syscall('fork', __onexit_fork, QL_INTERCEPT.EXIT) + + ql.run() + + +if __name__ == '__main__': + argv = [ + f'{ROOTFS}/bin/mini_httpd', + '-d', '/www', + '-r', 'NETGEAR R6220', + '-c', '**.cgi', + '-t', '300' + ] + + my_sandbox(argv, ROOTFS) diff --git a/examples/netgear_6220_mips32el_linux.py b/examples/netgear_6220_mips32el_linux.py deleted file mode 100644 index 06299773c..000000000 --- a/examples/netgear_6220_mips32el_linux.py +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env python3 -# -# Cross Platform and Multi Architecture Advanced Binary Emulation Framework -# - -# After mapping /proc there will be a /dev/mtdblock11 missing and crash -# To fix this, -# - cd $yourfirmware_rootfs/dev -# - dd if=/dev/zero of=mtdblock11 bs=1024 count=129030 -# - mkfs.ext4 mtdblock11 -# -# This firmware will more or less alive now. - -from colorama import Back -import struct -import sys -sys.path.append("..") - -from qiling import Qiling -from qiling.const import QL_INTERCEPT, QL_VERBOSE -from qiling.os.posix import syscall -from qiling.os.const import UINT, POINTER - -def my_syscall_write(ql, write_fd, write_buf, write_count, *rest): - if write_fd == 2 and ql.os.fd[2].__class__.__name__ == 'ql_pipe': - return -1 - else: - return syscall.ql_syscall_write(ql, write_fd, write_buf, write_count, *rest) - - -def my_bind(ql: Qiling): - params = ql.os.resolve_fcall_params({ - 'fd': UINT, - 'addr': POINTER, - 'addrlen': UINT - }) - - bind_fd = params['fd'] - bind_addr = params['addr'] - bind_addrlen = params['addrlen'] - - print(Back.GREEN + f'Hijack bind({bind_fd}, {bind_addr:#x}, {bind_addrlen})' + Back.RESET) - # read from memory (start_address, len) - data = ql.mem.read(bind_addr, bind_addrlen) - # custom unpack (your own ql.unpack) of a C struct from memory - # https://linux.die.net/man/7/ip -> struct - sin_family = struct.unpack(" format_string -> https://docs.python.org/3/library/struct.html#format-strings - port, host = struct.unpack(">HI", data[2:8]) - # big-endian unsigned short, unsigned int -> format_string - print(Back.RED + f'[*] Socket Infos:' + Back.RESET) - print(f''' - Family: {sin_family} - Port: {port} (no root: +8000) - Host-interface?: {host} - ''') - return 0 # from syscall.ql_syscall_bind(ql, bind_fd, bind_addr, bind_addrlen) - -def my_netgear(path, rootfs): - ql = Qiling(path, rootfs, verbose=QL_VERBOSE.DEBUG, profile="netgear_6220.ql", multithread=False) - ql.os.root = False - - ql.add_fs_mapper('/proc', '/proc') - ql.os.set_syscall(4004, my_syscall_write) - ql.os.set_api('bind', my_bind, QL_INTERCEPT.ENTER) # intercepting the bind call on enter - - ql.run() - -if __name__ == "__main__": - my_netgear(["rootfs/netgear_r6220/bin/mini_httpd", - "-d", "/www", - "-r", "NETGEAR R6220", - "-c", "**.cgi", - "-t", "300"], - "rootfs/netgear_r6220") From d1accb972adcc3dc2efafa7dd1189ab1dcbd0c59 Mon Sep 17 00:00:00 2001 From: elicn Date: Wed, 28 Jun 2023 21:44:40 +0300 Subject: [PATCH 14/17] Revamp IDA custom script --- .../extensions/idaplugin/custom_script.py | 56 ++++++++++--------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/examples/extensions/idaplugin/custom_script.py b/examples/extensions/idaplugin/custom_script.py index 78ffca21d..6461f45d0 100644 --- a/examples/extensions/idaplugin/custom_script.py +++ b/examples/extensions/idaplugin/custom_script.py @@ -1,37 +1,41 @@ -from qiling import * +from future import __annotations__ -class QILING_IDA(): - def __init__(self): - pass +from typing import TYPE_CHECKING, List - def _show_context(self, ql:Qiling): - registers = [ k for k in ql.arch.regs.register_mapping.keys() if type(k) is str ] - for idx in range(0, len(registers), 3): - regs = registers[idx:idx+3] - s = "\t".join(map(lambda v: f"{v:4}: {ql.arch.regs.__getattr__(v):016x}", regs)) - ql.log.info(s) +if TYPE_CHECKING: + from qiling import Qiling + from qiling.core_hooks_types import HookRet - def custom_prepare(self, ql:Qiling): + +class QILING_IDA: + + def _show_context(self, ql: Qiling): + registers = tuple(ql.arch.regs.register_mapping.keys()) + grouping = 4 + + for idx in range(0, len(registers), grouping): + ql.log.info('\t'.join(f'{r:5s}: {ql.arch.regs.read(r):016x}' for r in registers[idx:idx + grouping])) + + def custom_prepare(self, ql: Qiling) -> None: ql.log.info('Context before starting emulation:') self._show_context(ql) - def custom_continue(self, ql:Qiling): - ql.log.info('custom_continue hook.') + def custom_continue(self, ql: Qiling) -> List[HookRet]: + ql.log.info('custom_continue hook') self._show_context(ql) - hook = [] - return hook - def custom_step(self, ql:Qiling): - def step_hook(ql, addr, size): - ql.log.info(f"Executing: {hex(addr)}") + return [] + + def custom_step(self, ql: Qiling) -> List[HookRet]: + def step_hook(ql: Qiling, addr: int, size: int): + ql.log.info(f'Executing: {addr:#x}') self._show_context(ql) ql.log.info('custom_step hook') - hook = [] - hook.append(ql.hook_code(step_hook)) - return hook - - def custom_execute_selection(self, ql:Qiling): - ql.log.info('custom execute selection hook') - hook = [] - return hook \ No newline at end of file + + return [ql.hook_code(step_hook)] + + def custom_execute_selection(self, ql: Qiling) -> List[HookRet]: + ql.log.info('custom_execute_selection hook') + + return [] From c85f5926d87f11eb586c6caadce3064d54130014 Mon Sep 17 00:00:00 2001 From: elicn Date: Wed, 28 Jun 2023 21:53:13 +0300 Subject: [PATCH 15/17] Revamp Tenda AC15 example --- examples/tendaac1518_httpd.py | 92 ++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/examples/tendaac1518_httpd.py b/examples/tendaac1518_httpd.py index 819e8310c..26e98be7f 100644 --- a/examples/tendaac1518_httpd.py +++ b/examples/tendaac1518_httpd.py @@ -1,19 +1,25 @@ #!/usr/bin/env python3 -# +# # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # - -# 1. Download AC15 Firmware from https://down.tenda.com.cn/uploadfile/AC15/US_AC15V1.0BR_V15.03.05.19_multi_TD01.zip -# 2. unzip -# 3. binwalk -e US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin -# 4. locate squashfs-root -# 5. rm -rf webroot && mv webroot_ro webroot +# Setup: +# - Unpack firmware rootfs (assumed hereby: 'rootfs/tendaac15') +# - AC15 firmware may be acquired from https://down.tenda.com.cn/uploadfile/AC15/US_AC15V1.0BR_V15.03.05.19_multi_TD01.zip +# - Refresh webroot directory: +# - Enter the 'squashfs-root' directory +# - rm -rf webroot +# - mv webroot_ro webroot +# - Set network device +# - Open "qiling/profiles/linux.ql" +# - Set 'ifrname_override' to your hosting system network device name (e.g. eth0, lo, etc.) # -# notes: we are using rootfs in this example, so rootfs = squashfs-root -# +# Run: +# $ PYTHONPATH=/path/to/qiling ROOTFS=/path/to/tenda_rootfs python3 tendaac1518_httpd.py -import os, socket, threading +import os +import socket +import threading import sys sys.path.append("..") @@ -21,66 +27,62 @@ from qiling import Qiling from qiling.const import QL_VERBOSE -def patcher(ql: Qiling): - br0_addr = ql.mem.search("br0".encode() + b'\x00') - for addr in br0_addr: - ql.mem.write(addr, b'lo\x00') +# user may set 'ROOTFS' environment variable to use as rootfs +ROOTFS = os.environ.get('ROOTFS', r'./rootfs/tendaac15') + def nvram_listener(): - server_address = 'rootfs/var/cfm_socket' - data = "" + server_address = fr'{ROOTFS}/var/cfm_socket' - try: + if os.path.exists(server_address): os.unlink(server_address) - except OSError: - if os.path.exists(server_address): - raise # Create UDS socket - sock = socket.socket(socket.AF_UNIX,socket.SOCK_STREAM) + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.bind(server_address) sock.listen(1) - while True: - connection, _ = sock.accept() + data = bytearray() - try: - while True: - data += str(connection.recv(1024)) + with open('cfm_socket.log', 'wb') as ofile: + while True: + connection, _ = sock.accept() - if "lan.webiplansslen" in data: - connection.send('192.168.170.169'.encode()) - else: - break + try: + while True: + data += connection.recv(1024) - data = "" - finally: - connection.close() + if b'lan.webiplansslen' not in data: + break -def myvfork(ql: Qiling): - regreturn = 0 - ql.log.info("vfork() = %d" % regreturn) + connection.send(b'192.168.170.169') + + ofile.write(data) + data.clear() + finally: + connection.close() - return regreturn def my_sandbox(path, rootfs): ql = Qiling(path, rootfs, verbose=QL_VERBOSE.DEBUG) - ql.add_fs_mapper("/dev/urandom","/dev/urandom") - ql.hook_address(patcher, ql.loader.elf_entry) + ql.add_fs_mapper(r'/dev/urandom', r'/dev/urandom') - # $ gdb-multiarch -q rootfs/bin/httpd + # $ gdb-multiarch -q rootfs/tendaac15/bin/httpd # gdb> set remotetimeout 100 # gdb> target remote localhost:9999 - ql.debugger = False - if ql.debugger == True: - ql.os.set_syscall("vfork", myvfork) + if ql.debugger: + def __vfork(ql: Qiling): + return 0 + + ql.os.set_syscall('vfork', __vfork) ql.run() -if __name__ == "__main__": + +if __name__ == '__main__': nvram_listener_therad = threading.Thread(target=nvram_listener, daemon=True) nvram_listener_therad.start() - my_sandbox(["rootfs/bin/httpd"], "rootfs") + my_sandbox([fr'{ROOTFS}/bin/httpd'], ROOTFS) From 88c69ebb2cb390ac9f996acc9554598770338e79 Mon Sep 17 00:00:00 2001 From: elicn Date: Mon, 3 Jul 2023 14:21:53 +0300 Subject: [PATCH 16/17] Make progress animation more robust --- qiling/loader/pe.py | 120 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 100 insertions(+), 20 deletions(-) diff --git a/qiling/loader/pe.py b/qiling/loader/pe.py index 9234bbafa..85e9fb7cf 100644 --- a/qiling/loader/pe.py +++ b/qiling/loader/pe.py @@ -26,6 +26,7 @@ from .loader import QlLoader, Image if TYPE_CHECKING: + from logging import Logger from qiling import Qiling @@ -191,7 +192,7 @@ def load_dll(self, name: str, is_driver: bool = False) -> int: relocate = True if relocate: - with ShowProgress(0.1337): + with ShowProgress(self.ql.log, 0.1337): dll.relocate_image(image_base) data = bytearray(dll.get_memory_mapped_image()) @@ -870,44 +871,123 @@ def load(self, pe: Optional[pefile.PE]): class ShowProgress: - """Display a progress animation while performing a time - consuming task. + """Display a progress animation while performing a time consuming task. Example: - >>> with ShowProgress(0.1): + >>> with ShowProgress(logger, 0.15): ... do_some_time_consuming_task() """ - def __init__(self, interval: float) -> None: - import sys - from threading import Thread, Event + # animation frames: a sequence of chars or strings to display. any sequence of string elements + # may be used as long as they are of the same length. + # + # for example: ['> ', '>> ', ' >> ', ' >>', ' >', ' '] + _frames_ = r'/-\|' + + # animation marker: this is used to tell animation log records from the rest. + _marker_ = r'$__ql_anim__' - # animation frames; any sequence of chars or strings may be used, as long - # as they are of the same length. e.g. ['> ', '>> ', ' >> ', ' >>', ' >', ' '] - frames = r'/-\|' - stream = sys.stderr + def __init__(self, logger: Logger, interval: float) -> None: + from typing import List, Callable + from threading import Thread, Event def show_animation(): i = 0 while not self.stopped.wait(interval): - # TODO: find a proper way to use the logger for that - print(f'[{frames[i % len(frames)]}]', end='\r', flush=True, file=stream) + frame = self._frames_[i % len(self._frames_)] + logger.info(f'{self._marker_}{frame}') + i += 1 - def show_nothing(): - pass + self.stopped = Event() + self.thread = Thread(target=show_animation) - # avoid flooding log files with animation frames - action = show_animation if stream.isatty() else show_nothing + self.logger = logger + self.handlers_restorers: List[Callable[[], None]] = [] - self.stopped = Event() - self.thread = Thread(target=action) + def __setup_handlers(self): + from logging import Filter, Formatter, LogRecord, StreamHandler + + # while progress animation is useful on tty streams, it is not very useful on log files + # and most probably just flood the log files with animation frames. + # + # to avoid such flooding an animation filter is added to the non-tty stream handlers to + # filter out the animation records. in addition, tty stream handlers are assigned with + # an animation formatter to display the animation frames nicely. + # + # when the animation context exits, all the changes made to the handlers are reverted. + + def has_anim_marker(rec: LogRecord) -> bool: + """Tell whether a log record is an animation record or not. + """ + + return rec.getMessage().startswith(ShowProgress._marker_) + + def strip_anim_marker(rec: LogRecord) -> None: + """Remove animation marker from log record. + """ + + rec.message = rec.message[len(ShowProgress._marker_):] + + class AnimFormatter(Formatter): + """A log record formatter that removes animation markers. + """ + + def formatMessage(self, record: LogRecord) -> str: + if has_anim_marker(record): + strip_anim_marker(record) + + return super().formatMessage(record) + + class AnimFilter(Filter): + """A log record filter that thwarts animation records. + """ + + def filter(self, record: LogRecord) -> bool: + return not has_anim_marker(record) + + # the animation frames will be displayed within brackets + anim_formatter = AnimFormatter('[%(message)s]') + anim_filter = AnimFilter() + + for h in self.logger.handlers: + # if this is a tty stream handler, modify some of its attributes to + # let the animation display correctly + if isinstance(h, StreamHandler) and h.stream.isatty(): + orig_terminator = h.terminator + orig_formatter = h.formatter + + h.terminator = '\r' + h.setFormatter(anim_formatter) + + def __restore_modified() -> None: + h.terminator = orig_terminator + h.setFormatter(orig_formatter) + + restorer = __restore_modified + + # otherwise, apply a filter that will ignore animation records + else: + h.addFilter(anim_filter) + + def __restore_silenced() -> None: + h.removeFilter(anim_filter) + + restorer = __restore_silenced + + self.handlers_restorers.append(restorer) + + def __restore_handlers(self) -> None: + for restorer in self.handlers_restorers: + restorer() def __enter__(self): + self.__setup_handlers() self.thread.start() return self - def __exit__(self, *args) -> None: + def __exit__(self, extype, value, traceback): self.stopped.set() + self.__restore_handlers() From 260aad777f4719d14771f8f6c28460f1566b2180 Mon Sep 17 00:00:00 2001 From: elicn Date: Mon, 3 Jul 2023 14:27:26 +0300 Subject: [PATCH 17/17] Some more opportunistic PEP8 fixes --- qiling/const.py | 2 +- qiling/extensions/pipe.py | 31 +++++++++++++++++++------------ qiling/os/windows/windows.py | 5 ++--- qiling/utils.py | 2 +- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/qiling/const.py b/qiling/const.py index 8aabafe4e..fca6dde01 100644 --- a/qiling/const.py +++ b/qiling/const.py @@ -88,7 +88,7 @@ def __casefold_enum(e: Type[T]) -> Mapping[str, T]: '''Create a casefolded mapping of an enum to allow case-insensitive lookup. ''' - return dict((k.casefold(), v) for k, v in e._member_map_.items()) + return dict((k.casefold(), v) for k, v in e.__members__.items()) debugger_map = __casefold_enum(QL_DEBUGGER) diff --git a/qiling/extensions/pipe.py b/qiling/extensions/pipe.py index f69d1e326..f92fe9c88 100644 --- a/qiling/extensions/pipe.py +++ b/qiling/extensions/pipe.py @@ -2,19 +2,20 @@ # # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # + import io import os -from typing import TextIO from qiling.os.posix import stat + class SimpleStringBuffer(io.BytesIO): """Simple FIFO pipe. """ def __init__(self): super().__init__() - + # Compatible with old implementation def seek(self, offset: int, origin: int = 0) -> int: # Imitate os.lseek @@ -30,22 +31,23 @@ def write(self, buf: bytes) -> int: ret = super().write(buf) super().seek(pos) return ret - + # Compatible with previous TextIO @property def name(self): return None + class SimpleStreamBase: def __init__(self, fd: int): super().__init__() self.__fd = fd self.__closed = False - + def close(self) -> None: self.__closed = True - + @property def closed(self) -> bool: return self.__closed @@ -56,18 +58,21 @@ def fileno(self) -> int: def fstat(self): return stat.Fstat(self.fileno()) + class SimpleInStream(SimpleStreamBase, SimpleStringBuffer): """Simple input stream. May be used to mock stdin. """ pass + class SimpleOutStream(SimpleStreamBase, SimpleStringBuffer): """Simple output stream. May be used to mock stdout or stderr. """ pass + class NullOutStream(SimpleStreamBase): """Null out-stream, may be used to disregard process output. """ @@ -81,31 +86,33 @@ def flush(self) -> None: def writable(self) -> bool: return True + class SimpleBufferedStream(io.BytesIO): """Simple buffered IO. """ def __init__(self): - super.__init__() + super().__init__() + class InteractiveInStream(io.BytesIO): def read(self, size: int) -> bytes: ''' Read from the BytesIO buffer. If theres no data left in the buffer, get additional user input - + Args: size (int): The amount of bytes to read from the buffer Returns: bytes: The data read from the buffer ''' - - #get the amount of bytes left in the buffer + + # get the amount of bytes left in the buffer bytes_left = self.getbuffer().nbytes - self.tell() - #if theres no bytes left in the buffer, get user input + # if theres no bytes left in the buffer, get user input if bytes_left == 0: - user_data = input().encode()+ b'\x0a' + user_data = input().encode() + b'\x0a' self.write(user_data) self.seek(-len(user_data), io.SEEK_CUR) - return super().read(size) \ No newline at end of file + return super().read(size) diff --git a/qiling/os/windows/windows.py b/qiling/os/windows/windows.py index 921ba8dcc..bbf674f4c 100644 --- a/qiling/os/windows/windows.py +++ b/qiling/os/windows/windows.py @@ -123,14 +123,13 @@ def stderr(self, stream: TextIO) -> None: handle.obj = stream def load(self): - self.setupGDT() + self.__setup_gdt() self.__setup_components() # hook win api self.ql.hook_code(self.hook_winapi) - - def setupGDT(self): + def __setup_gdt(self): gdtm = GDTManager(self.ql) segm_class: Type[SegmentManager] = { diff --git a/qiling/utils.py b/qiling/utils.py index c907204e7..925d3b791 100644 --- a/qiling/utils.py +++ b/qiling/utils.py @@ -293,7 +293,7 @@ def ql_guess_emu_env(path: str) -> Tuple[Optional[QL_ARCH], Optional[QL_OS], Opt def select_loader(ostype: QL_OS, libcache: bool) -> QlClassInit['QlLoader']: - if ostype == QL_OS.WINDOWS: + if ostype is QL_OS.WINDOWS: kwargs = {'libcache': libcache} else: