diff --git a/qiling/arch/arm.py b/qiling/arch/arm.py index 1a5b7f8c2..8c42a7324 100644 --- a/qiling/arch/arm.py +++ b/qiling/arch/arm.py @@ -13,6 +13,7 @@ from qiling import Qiling from qiling.arch.arch import QlArch from qiling.arch import arm_const +from qiling.arch.cpr import QlCprManager from qiling.arch.models import ARM_CPU_MODEL from qiling.arch.register import QlRegisterManager from qiling.const import QL_ARCH, QL_ENDIAN @@ -69,6 +70,13 @@ def is_thumb(self) -> bool: def endian(self) -> QL_ENDIAN: return QL_ENDIAN.EB if self.regs.cpsr & (1 << 9) else QL_ENDIAN.EL + @cached_property + def cpr(self) -> QlCprManager: + """Coprocessor Registers. + """ + + return QlCprManager(self.uc) + @property def effective_pc(self) -> int: """Get effective PC value, taking Thumb mode into account. @@ -119,6 +127,7 @@ def assembler(self) -> Ks: def enable_vfp(self) -> None: # set full access to cp10 and cp11 - self.regs.c1_c0_2 = self.regs.c1_c0_2 | (0b11 << 20) | (0b11 << 22) + cpacr = self.cpr.read(*arm_const.CPACR) + self.cpr.write(*arm_const.CPACR, cpacr | (0b11 << 20) | (0b11 << 22)) - self.regs.fpexc = (1 << 30) + self.regs.fpexc = (0b1 << 30) diff --git a/qiling/arch/arm64.py b/qiling/arch/arm64.py index ba9f69b35..3029f7793 100644 --- a/qiling/arch/arm64.py +++ b/qiling/arch/arm64.py @@ -13,6 +13,7 @@ from qiling import Qiling from qiling.arch.arch import QlArch from qiling.arch import arm64_const +from qiling.arch.cpr64 import QlCpr64Manager from qiling.arch.models import ARM64_CPU_MODEL from qiling.arch.register import QlRegisterManager from qiling.const import QL_ARCH, QL_ENDIAN @@ -56,6 +57,13 @@ def regs(self) -> QlRegisterManager: def endian(self) -> QL_ENDIAN: return QL_ENDIAN.EL + @cached_property + def cpr(self) -> QlCpr64Manager: + """Coprocessor Registers. + """ + + return QlCpr64Manager(self.uc) + @cached_property def disassembler(self) -> Cs: return Cs(CS_ARCH_ARM64, CS_MODE_ARM) diff --git a/qiling/arch/arm64_const.py b/qiling/arch/arm64_const.py index 0845df59a..0864da3de 100644 --- a/qiling/arch/arm64_const.py +++ b/qiling/arch/arm64_const.py @@ -5,6 +5,31 @@ from unicorn.arm64_const import * + +# coprocessor registers +TPIDR_EL0 = (3, 3, 13, 0, 2) +TPIDRRO_EL0 = (3, 3, 13, 0, 3) +TPIDR_EL1 = (3, 0, 13, 0, 4) +ELR_EL1 = (3, 0, 4, 0, 1) +ELR_EL2 = (3, 4, 4, 0, 1) +ELR_EL3 = (3, 6, 4, 0, 1) +SP_EL0 = (3, 0, 4, 1, 0) +SP_EL1 = (3, 4, 4, 1, 0) +SP_EL2 = (3, 6, 4, 1, 0) +TTBR0_EL1 = (3, 0, 2, 0, 0) +TTBR1_EL1 = (3, 0, 2, 0, 1) +ESR_EL1 = (3, 0, 5, 2, 0) +ESR_EL2 = (3, 4, 5, 2, 0) +ESR_EL3 = (3, 6, 5, 2, 0) +FAR_EL1 = (3, 0, 6, 0, 0) +FAR_EL2 = (3, 4, 6, 0, 0) +FAR_EL3 = (3, 6, 6, 0, 0) +PAR_EL1 = (3, 0, 7, 4, 0) +MAIR_EL1 = (3, 0, 10, 2, 0) +VBAR_EL1 = (3, 0, 12, 0, 0) +VBAR_EL2 = (3, 4, 12, 0, 0) +VBAR_EL3 = (3, 6, 12, 0, 0) + reg_map = { "x0": UC_ARM64_REG_X0, "x1": UC_ARM64_REG_X1, @@ -41,8 +66,6 @@ "pc": UC_ARM64_REG_PC, "lr": UC_ARM64_REG_LR, "cpacr_el1": UC_ARM64_REG_CPACR_EL1, - "tpidr_el0": UC_ARM64_REG_TPIDR_EL0, - "pstate": UC_ARM64_REG_PSTATE } reg_map_b = { diff --git a/qiling/arch/arm_const.py b/qiling/arch/arm_const.py index 1726a8071..f2f3fd736 100644 --- a/qiling/arch/arm_const.py +++ b/qiling/arch/arm_const.py @@ -5,27 +5,31 @@ from unicorn.arm_const import * + +CPACR = (15, 0, 1, 0, 2, 0, False) +TPIDRURO = (15, 0, 13, 0, 3, 0, False) + reg_map = { - "r0": UC_ARM_REG_R0, - "r1": UC_ARM_REG_R1, - "r2": UC_ARM_REG_R2, - "r3": UC_ARM_REG_R3, - "r4": UC_ARM_REG_R4, - "r5": UC_ARM_REG_R5, - "r6": UC_ARM_REG_R6, - "r7": UC_ARM_REG_R7, - "r8": UC_ARM_REG_R8, - "r9": UC_ARM_REG_R9, + "r0": UC_ARM_REG_R0, + "r1": UC_ARM_REG_R1, + "r2": UC_ARM_REG_R2, + "r3": UC_ARM_REG_R3, + "r4": UC_ARM_REG_R4, + "r5": UC_ARM_REG_R5, + "r6": UC_ARM_REG_R6, + "r7": UC_ARM_REG_R7, + "r8": UC_ARM_REG_R8, + "r9": UC_ARM_REG_R9, "r10": UC_ARM_REG_R10, "r11": UC_ARM_REG_R11, "r12": UC_ARM_REG_R12, - "sp": UC_ARM_REG_SP, - "lr": UC_ARM_REG_LR, - "pc": UC_ARM_REG_PC, + "sp": UC_ARM_REG_SP, + "lr": UC_ARM_REG_LR, + "pc": UC_ARM_REG_PC, - "cpsr": UC_ARM_REG_CPSR, - "c1_c0_2": UC_ARM_REG_C1_C0_2, - "c13_c0_3": UC_ARM_REG_C13_C0_3, + "apsr": UC_ARM_REG_APSR, + "cpsr": UC_ARM_REG_CPSR, + "spsr": UC_ARM_REG_SPSR, "fpexc": UC_ARM_REG_FPEXC } diff --git a/qiling/arch/arm_utils.py b/qiling/arch/arm_utils.py index 14181a699..8a8830b80 100644 --- a/qiling/arch/arm_utils.py +++ b/qiling/arch/arm_utils.py @@ -3,11 +3,13 @@ # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # +from typing import Mapping + from qiling import Qiling from qiling.const import QL_ENDIAN -def init_linux_traps(ql: Qiling, address_map) -> None: +def init_linux_traps(ql: Qiling, address_map: Mapping[str, int]) -> None: # If the compiler for the target does not provides some primitives for some # reasons (e.g. target limitations), the kernel is responsible to assist # with these operations. @@ -16,51 +18,32 @@ def init_linux_traps(ql: Qiling, address_map) -> None: # https://elixir.bootlin.com/linux/latest/source/arch/arm/kernel/entry-armv.S#L899 trap_map = { - 'memory_barrier': # @ 0xffff0fa0 - # mcr p15, 0, r0, c7, c10, 5 - # nop - # mov pc, lr - ''' - ba 0f 07 ee - 00 f0 20 e3 - 0e f0 a0 e1 - ''', + 'memory_barrier': bytes.fromhex( + 'ba 0f 07 ee' # mcr p15, 0, r0, c7, c10, 5 + '00 f0 20 e3' # nop + '0e f0 a0 e1' # mov pc, lr + ), - 'cmpxchg': # @ 0xffff0fc0 - # ldr r3, [r2] - # subs r3, r3, r0 - # streq r1, [r2] - # rsbs r0, r3, #0 - # mov pc, lr - ''' - 00 30 92 e5 - 00 30 53 e0 - 00 10 82 05 - 00 00 73 e2 - 0e f0 a0 e1 - ''', + 'cmpxchg': bytes.fromhex( + '00 30 92 e5' # ldr r3, [r2] + '00 30 53 e0' # subs r3, r3, r0 + '00 10 82 05' # streq r1, [r2] + '00 00 73 e2' # rsbs r0, r3, #0 + '0e f0 a0 e1' # mov pc, lr + ), - 'get_tls': # @ 0xffff0fe0 - # ldr r0, [pc, #(16 - 8)] - # mov pc, lr - # mrc p15, 0, r0, c13, c0, 3 - # padding (e7 fd de f1) - # data: - # "\x00\x00\x00\x00" - # "\x00\x00\x00\x00" - # "\x00\x00\x00\x00" - ''' - 08 00 9f e5 - 0e f0 a0 e1 - 70 0f 1d ee - e7 fd de f1 - 00 00 00 00 - 00 00 00 00 - 00 00 00 00 - ''' + 'get_tls': bytes.fromhex( + '08 00 9f e5' # ldr r0, [pc, #(16 - 8)] + '0e f0 a0 e1' # mov pc, lr + '70 0f 1d ee' # mrc p15, 0, r0, c13, c0, 3 + 'e7 fd de f1' # padding (e7 fd de f1) + '00 00 00 00' # data + '00 00 00 00' # data + '00 00 00 00' # data + ) } if address_map: @@ -74,16 +57,14 @@ def init_linux_traps(ql: Qiling, address_map) -> None: ql.mem.map(base, size, info="[arm_traps]") - for trap_name, trap_hex in trap_map.items(): - trap_code = bytes.fromhex(trap_hex) - - if ql.arch.endian == QL_ENDIAN.EB: + for trap_name, trap_code in trap_map.items(): + if ql.arch.endian is QL_ENDIAN.EB: trap_code = swap_endianness(trap_code) if trap_name in address_map: ql.mem.write(address_map[trap_name], trap_code) - ql.log.debug(f'Set kernel trap: {trap_name} at {address_map[trap_name]:#x}') + ql.log.debug(f'Setting kernel trap {trap_name} at {address_map[trap_name]:#x}') def swap_endianness(s: bytes, blksize: int = 4) -> bytes: diff --git a/qiling/arch/cpr.py b/qiling/arch/cpr.py new file mode 100644 index 000000000..333472de8 --- /dev/null +++ b/qiling/arch/cpr.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + +from __future__ import annotations + +import weakref + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from unicorn import Uc + + +class QlCprManager: + """Enables access to ARM coprocessor registers. + """ + + # for more information about various aarch32 coprocessor register, pelase refer to: + # https://developer.arm.com/documentation/ddi0601/latest/AArch32-Registers + + def __init__(self, uc: Uc) -> None: + self.uc: Uc = weakref.proxy(uc) + + def read(self, coproc: int, opc1: int, crn: int, crm: int, opc2: int, el: int, is_64: bool) -> int: + """Read a coprocessor register value. + + Args: + coproc : coprocessor to access, value varies between 0 and 15 + opc1 : opcode 1, value varies between 0 and 7 + crn : coprocessor register to access (CRn), value varies between 0 and 15 + crm : additional coprocessor register to access (CRm), value varies between 0 and 15 + opc2 : opcode 2, value varies between 0 and 7 + el : the exception level the coprocessor register belongs to, value varies between 0 and 3 + is_64 : indicates whether this is a 64-bit register + + Returns: value of coprocessor register + """ + + return self.uc.cpr_read(coproc, opc1, crn, crm, opc2, el, is_64) + + def write(self, coproc: int, opc1: int, crn: int, crm: int, opc2: int, el: int, is_64: bool, value: int) -> None: + """Write a coprocessor register value. + + Args: + coproc : coprocessor to access, value varies between 0 and 15 + opc1 : opcode 1, value varies between 0 and 7 + crn : coprocessor register to access (CRn), value varies between 0 and 15 + crm : additional coprocessor register to access (CRm), value varies between 0 and 15 + opc2 : opcode 2, value varies between 0 and 7 + el : the exception level the coprocessor register belongs to, value varies between 0 and 3 + is_64 : indicates whether this is a 64-bit register + value : value to write + """ + + self.uc.cpr_write(coproc, opc1, crn, crm, opc2, el, is_64, value) + + +__all__ = ['QlCprManager'] diff --git a/qiling/arch/cpr64.py b/qiling/arch/cpr64.py new file mode 100644 index 000000000..419d045a3 --- /dev/null +++ b/qiling/arch/cpr64.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + +from __future__ import annotations + +import weakref + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from unicorn import Uc + + +class QlCpr64Manager: + """Enables access to ARM64 coprocessor registers. + """ + + # for more information about various aarch32 coprocessor register, pelase refer to: + # https://developer.arm.com/documentation/ddi0601/latest/AArch64-Registers + + def __init__(self, uc: Uc) -> None: + self.uc: Uc = weakref.proxy(uc) + + def read(self, op0: int, op1: int, crn: int, crm: int, op2: int) -> int: + """Read a coprocessor register value. + + Args: + op0 : opcode 0, value varies between 0 and 3 + op1 : opcode 1, value varies between 0 and 7 + crn : coprocessor register to access (CRn), value varies between 0 and 15 + crm : additional coprocessor register to access (CRm), value varies between 0 and 15 + op2 : opcode 2, value varies between 0 and 7 + + Returns: value of coprocessor register + """ + + return self.uc.cpr_read(op0, op1, crn, crm, op2) + + def write(self, op0: int, op1: int, crn: int, crm: int, op2: int, value: int) -> None: + """Write a coprocessor register value. + + Args: + op0 : opcode 0, value varies between 0 and 3 + op1 : opcode 1, value varies between 0 and 7 + crn : coprocessor register to access (CRn), value varies between 0 and 15 + crm : additional coprocessor register to access (CRm), value varies between 0 and 15 + op2 : opcode 2, value varies between 0 and 7 + value : value to write + """ + + self.uc.cpr_write(op0, op1, crn, crm, op2, value) + + +__all__ = ['QlCpr64Manager'] diff --git a/qiling/arch/mips.py b/qiling/arch/mips.py index 87a703b37..4785d5b9e 100644 --- a/qiling/arch/mips.py +++ b/qiling/arch/mips.py @@ -39,9 +39,7 @@ def uc(self) -> Uc: @cached_property def regs(self) -> QlRegisterManager: regs_map = dict( - **mips_const.reg_map, - **mips_const.reg_map_afpr128, - **mips_const.reg_map_fpu + **mips_const.reg_map ) pc_reg = 'pc' diff --git a/qiling/arch/mips_const.py b/qiling/arch/mips_const.py index 1c4467ebb..c7f1a5722 100644 --- a/qiling/arch/mips_const.py +++ b/qiling/arch/mips_const.py @@ -39,6 +39,8 @@ "r30": UC_MIPS_REG_30, "r31": UC_MIPS_REG_31, + # aliases + "pc": UC_MIPS_REG_PC, "zero": UC_MIPS_REG_ZERO, "at": UC_MIPS_REG_AT, "v0": UC_MIPS_REG_V0, @@ -69,52 +71,14 @@ "k1": UC_MIPS_REG_K1, "gp": UC_MIPS_REG_GP, "sp": UC_MIPS_REG_SP, + "fp": UC_MIPS_REG_FP, "s8": UC_MIPS_REG_S8, "ra": UC_MIPS_REG_RA, - "status": UC_MIPS_REG_INVALID, - "lo": UC_MIPS_REG_LO, + "hi": UC_MIPS_REG_HI, - "badvaddr": UC_MIPS_REG_INVALID, - "cause": UC_MIPS_REG_INVALID, - "pc": UC_MIPS_REG_PC -} + "lo": UC_MIPS_REG_LO, -reg_map_afpr128 = { "cp0_config3": UC_MIPS_REG_CP0_CONFIG3, - "cp0_userlocal": UC_MIPS_REG_CP0_USERLOCAL -} - -reg_map_fpu = { - "f0": UC_MIPS_REG_F0, - "f1": UC_MIPS_REG_F1, - "f2": UC_MIPS_REG_F2, - "f3": UC_MIPS_REG_F3, - "f4": UC_MIPS_REG_F4, - "f5": UC_MIPS_REG_F5, - "f6": UC_MIPS_REG_F6, - "f7": UC_MIPS_REG_F7, - "f8": UC_MIPS_REG_F8, - "f9": UC_MIPS_REG_F9, - "f10": UC_MIPS_REG_F10, - "f11": UC_MIPS_REG_F11, - "f12": UC_MIPS_REG_F12, - "f13": UC_MIPS_REG_F13, - "f14": UC_MIPS_REG_F14, - "f15": UC_MIPS_REG_F15, - "f16": UC_MIPS_REG_F16, - "f17": UC_MIPS_REG_F17, - "f18": UC_MIPS_REG_F18, - "f19": UC_MIPS_REG_F19, - "f20": UC_MIPS_REG_F20, - "f21": UC_MIPS_REG_F21, - "f22": UC_MIPS_REG_F22, - "f23": UC_MIPS_REG_F23, - "f24": UC_MIPS_REG_F24, - "f25": UC_MIPS_REG_F25, - "f26": UC_MIPS_REG_F26, - "f27": UC_MIPS_REG_F27, - "f28": UC_MIPS_REG_F28, - "f29": UC_MIPS_REG_F29, - "f30": UC_MIPS_REG_F30, - "f31": UC_MIPS_REG_F31 + "cp0_userlocal": UC_MIPS_REG_CP0_USERLOCAL, + "cp0_status": UC_MIPS_REG_CP0_STATUS } diff --git a/qiling/arch/msr.py b/qiling/arch/msr.py index 8ea13cafc..84cac5819 100644 --- a/qiling/arch/msr.py +++ b/qiling/arch/msr.py @@ -3,7 +3,14 @@ # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # -from unicorn import Uc +from __future__ import annotations + +import weakref + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from unicorn import Uc class QlMsrManager: @@ -11,7 +18,7 @@ class QlMsrManager: """ def __init__(self, uc: Uc) -> None: - self.uc = uc + self.uc: Uc = weakref.proxy(uc) def read(self, msr: int) -> int: """Read a model-specific register value. diff --git a/qiling/arch/x86.py b/qiling/arch/x86.py index e4b64a62f..b5b34642b 100644 --- a/qiling/arch/x86.py +++ b/qiling/arch/x86.py @@ -89,7 +89,9 @@ def regs(self) -> QlRegisterManager: **x86_const.reg_map_cr, **x86_const.reg_map_dr, **x86_const.reg_map_st, - **x86_const.reg_map_misc + **x86_const.reg_map_misc, + **x86_const.reg_map_xmm, + **x86_const.reg_map_ymm ) pc_reg = 'eip' @@ -118,6 +120,7 @@ def regs(self) -> QlRegisterManager: **x86_const.reg_map_32, **x86_const.reg_map_64, **x86_const.reg_map_cr, + **x86_const.reg_map_cr_64, **x86_const.reg_map_dr, **x86_const.reg_map_st, **x86_const.reg_map_misc, @@ -126,7 +129,9 @@ def regs(self) -> QlRegisterManager: **x86_const.reg_map_64_d, **x86_const.reg_map_seg_base, **x86_const.reg_map_xmm, + **x86_const.reg_map_xmm_64, **x86_const.reg_map_ymm, + **x86_const.reg_map_ymm_64, **x86_const.reg_map_zmm ) diff --git a/qiling/arch/x86_const.py b/qiling/arch/x86_const.py index 0bdfe5b46..f4a6035e4 100644 --- a/qiling/arch/x86_const.py +++ b/qiling/arch/x86_const.py @@ -152,7 +152,10 @@ "cr1": UC_X86_REG_CR1, "cr2": UC_X86_REG_CR2, "cr3": UC_X86_REG_CR3, - "cr4": UC_X86_REG_CR4, + "cr4": UC_X86_REG_CR4 +} + +reg_map_cr_64 = { "cr8": UC_X86_REG_CR8 } @@ -207,7 +210,10 @@ "xmm4": UC_X86_REG_XMM4, "xmm5": UC_X86_REG_XMM5, "xmm6": UC_X86_REG_XMM6, - "xmm7": UC_X86_REG_XMM7, + "xmm7": UC_X86_REG_XMM7 +} + +reg_map_xmm_64 = { "xmm8": UC_X86_REG_XMM8, "xmm9": UC_X86_REG_XMM9, "xmm10": UC_X86_REG_XMM10, @@ -243,6 +249,9 @@ "ymm5": UC_X86_REG_YMM5, "ymm6": UC_X86_REG_YMM6, "ymm7": UC_X86_REG_YMM7, +} + +reg_map_ymm_64 = { "ymm8": UC_X86_REG_YMM8, "ymm9": UC_X86_REG_YMM9, "ymm10": UC_X86_REG_YMM10, diff --git a/qiling/cc/arm.py b/qiling/cc/arm.py index 7974503e0..7c4e3ff26 100644 --- a/qiling/cc/arm.py +++ b/qiling/cc/arm.py @@ -24,7 +24,7 @@ def setReturnAddress(self, addr: int) -> None: def unwind(self, nslots: int) -> int: # TODO: cleanup? - return self.arch.stack_pop() + return self.arch.regs.lr class aarch64(QlArmBaseCC): _retaddr_on_stack = False diff --git a/qiling/debugger/gdb/xmlregs.py b/qiling/debugger/gdb/xmlregs.py index 89b68964b..c7961eea9 100644 --- a/qiling/debugger/gdb/xmlregs.py +++ b/qiling/debugger/gdb/xmlregs.py @@ -18,8 +18,7 @@ reg_map_v as arm64_regs_v ) from qiling.arch.mips_const import ( - reg_map as mips_regs_gpr, - reg_map_fpu as mips_regs_fpu + reg_map as mips_regs_gpr ) from qiling.arch.x86_const import ( reg_map_32 as x86_regs_32, diff --git a/qiling/extensions/multitask.py b/qiling/extensions/multitask.py index 87915f1b8..dd8cbe0ee 100644 --- a/qiling/extensions/multitask.py +++ b/qiling/extensions/multitask.py @@ -148,6 +148,7 @@ def __exit__(self, *args, **kwargs): # # Bear in mind that only one task can be picked to emulate at # the same time. +@ucsubclass class MultiTaskUnicorn(Uc): def __init__(self, arch: int, mode: int, cpu: Optional[int], interval: Optional[int] = 100): diff --git a/qiling/loader/elf.py b/qiling/loader/elf.py index 1a00bcec7..f4c17fd3c 100644 --- a/qiling/loader/elf.py +++ b/qiling/loader/elf.py @@ -325,39 +325,44 @@ def __push_str(top: int, s: str) -> int: elf_phent = elffile['e_phentsize'] elf_phnum = elffile['e_phnum'] - if self.ql.arch.bits == 64: - elf_hwcap = 0x078bfbfd - elif self.ql.arch.bits == 32: - elf_hwcap = 0x1fb8d7 + # for more details on the following values see: + # https://github.com/google/cpu_features/blob/main/include/internal/hwcaps.h + hwcap_values = { + (QL_ARCH.ARM, QL_ENDIAN.EL, 32): 0x001fb8d7, + (QL_ARCH.ARM, QL_ENDIAN.EB, 32): 0x001fb8d7, + (QL_ARCH.ARM64, QL_ENDIAN.EL, 64): 0x078bfbfd + } - if self.ql.arch.endian == QL_ENDIAN.EB: - # FIXME: considering this is a 32 bits value, it is not a big-endian version of the - # value above like it is meant to be, since the one above has an implied leading zero - # byte (i.e. 0x001fb8d7) which the EB value didn't take into account - elf_hwcap = 0xd7b81f + # determine hwcap value by arch properties; if not found default to 0 + hwcap = hwcap_values.get((self.ql.arch.type, self.ql.arch.endian, self.ql.arch.bits), 0) # setup aux vector - auxv_entries = ( - (AUXV.AT_HWCAP, elf_hwcap), - (AUXV.AT_PAGESZ, self.ql.mem.pagesize), - (AUXV.AT_CLKTCK, 100), + auxv_entries = [ (AUXV.AT_PHDR, elf_phdr), (AUXV.AT_PHENT, elf_phent), (AUXV.AT_PHNUM, elf_phnum), - (AUXV.AT_BASE, interp_address), + (AUXV.AT_PAGESZ, self.ql.mem.pagesize), + ] + + if interp_path: + auxv_entries.append((AUXV.AT_BASE, interp_address)) + + auxv_entries.extend([ (AUXV.AT_FLAGS, 0), (AUXV.AT_ENTRY, self.elf_entry), (AUXV.AT_UID, self.ql.os.uid), (AUXV.AT_EUID, self.ql.os.euid), (AUXV.AT_GID, self.ql.os.gid), (AUXV.AT_EGID, self.ql.os.egid), + (AUXV.AT_CLKTCK, 100), + (AUXV.AT_PLATFORM, cpustraddr), + (AUXV.AT_HWCAP, hwcap), (AUXV.AT_SECURE, 0), (AUXV.AT_RANDOM, randstraddr), (AUXV.AT_HWCAP2, 0), (AUXV.AT_EXECFN, execfn), - (AUXV.AT_PLATFORM, cpustraddr), (AUXV.AT_NULL, 0) - ) + ]) bytes_before_auxv = len(elf_table) @@ -366,7 +371,7 @@ def __push_str(top: int, s: str) -> int: elf_table.extend(self.ql.pack(key)) elf_table.extend(self.ql.pack(val)) - new_stack = self.ql.mem.align(new_stack - len(elf_table), 0x10) + new_stack = self.ql.mem.align(new_stack - len(elf_table), self.ql.arch.pointersize) self.ql.mem.write(new_stack, bytes(elf_table)) self.auxv = new_stack + bytes_before_auxv diff --git a/qiling/log.py b/qiling/log.py index d83854999..2735b8b77 100644 --- a/qiling/log.py +++ b/qiling/log.py @@ -9,7 +9,6 @@ import logging import os import re -import sys import weakref from typing import TYPE_CHECKING, Collection, IO, Optional, Protocol, Union, runtime_checkable diff --git a/qiling/os/linux/syscall.py b/qiling/os/linux/syscall.py index 6092eefd0..713bbbc65 100644 --- a/qiling/os/linux/syscall.py +++ b/qiling/os/linux/syscall.py @@ -5,6 +5,7 @@ from qiling import Qiling from qiling.arch.x86_const import * +from qiling.arch.arm_const import TPIDRURO from qiling.const import QL_ARCH from datetime import datetime @@ -68,11 +69,12 @@ def ql_syscall_set_thread_area(ql: Qiling, u_info_addr: int): def ql_syscall_set_tls(ql: Qiling, address: int): - if ql.arch.type == QL_ARCH.ARM: - ql.arch.regs.c13_c0_3 = address + if ql.arch.type is QL_ARCH.ARM: + ql.arch.cpr.write(*TPIDRURO, address) ql.mem.write_ptr(ql.arch.arm_get_tls_addr + 16, address, 4) ql.arch.regs.r0 = address - ql.log.debug("settls(0x%x)" % address) + + ql.log.debug("settls(%#x)", address) def ql_syscall_clock_gettime(ql: Qiling, clock_id: int, tp: int): ts_obj = __get_timespec_obj(ql.arch.bits) diff --git a/qiling/os/linux/thread.py b/qiling/os/linux/thread.py index a4162743d..bb4a9150b 100644 --- a/qiling/os/linux/thread.py +++ b/qiling/os/linux/thread.py @@ -3,7 +3,8 @@ # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # -import gevent, os +import os +import gevent from typing import Callable, Sequence from abc import abstractmethod @@ -14,6 +15,8 @@ from qiling.const import QL_ARCH from qiling.os.thread import * from qiling.arch.x86_const import * +from qiling.arch.arm_const import TPIDRURO +from qiling.arch.arm64_const import TPIDR_EL0 from qiling.exception import QlErrorExecutionStop LINUX_THREAD_ID = 2000 @@ -473,56 +476,64 @@ def clone(self): class QlLinuxARMThread(QlLinuxThread): """docstring for QlLinuxARMThread""" def __init__(self, ql, start_address, exit_point, context = None, set_child_tid_addr = None, thread_id = None): - super(QlLinuxARMThread, self).__init__(ql, start_address, exit_point, context, set_child_tid_addr, thread_id) - self.tls = 0 + super().__init__(ql, start_address, exit_point, context, set_child_tid_addr, thread_id) + self.tls = 0 - def set_thread_tls(self, tls_addr): + def set_thread_tls(self, tls_addr: int) -> None: self.tls = tls_addr - self.ql.arch.regs.c13_c0_3 = self.tls - self.ql.log.debug(f"Set c13_c0_3 to {hex(self.ql.arch.regs.c13_c0_3)}") + self.ql.arch.cpr.write(*TPIDRURO, self.tls) - def save(self): + self.ql.log.debug(f"Setting TPIDRURO to {self.tls:#010x}") + + def save(self) -> None: self.save_context() - self.tls = self.ql.arch.regs.c13_c0_3 - self.ql.log.debug(f"Saved context. c13_c0_3={hex(self.ql.arch.regs.c13_c0_3)}") + self.tls = self.ql.arch.cpr.read(*TPIDRURO) + self.ql.log.debug(f"Context saved. TPIDRURO = {self.tls:#010x}") - def restore(self): + def restore(self) -> None: self.restore_context() self.set_thread_tls(self.tls) - self.ql.log.debug(f"Restored context. c13_c0_3={hex(self.ql.arch.regs.c13_c0_3)}") + + self.ql.log.debug(f"Context restored. TPIDRURO = {self.ql.arch.cpr.read(*TPIDRURO):#010x}") def clone(self): - new_thread = super(QlLinuxARMThread, self).clone() + new_thread = super().clone() new_thread.tls = self.tls + return new_thread class QlLinuxARM64Thread(QlLinuxThread): """docstring for QlLinuxARM64Thread""" def __init__(self, ql, start_address, exit_point, context = None, set_child_tid_addr = None, thread_id = None): - super(QlLinuxARM64Thread, self).__init__(ql, start_address, exit_point, context, set_child_tid_addr, thread_id) + super().__init__(ql, start_address, exit_point, context, set_child_tid_addr, thread_id) + self.tls = 0 - def set_thread_tls(self, tls_addr): + def set_thread_tls(self, tls_addr: int) -> None: self.tls = tls_addr - self.ql.arch.regs.tpidr_el0 = self.tls - self.ql.log.debug(f"Set tpidr_el0 to {hex(self.ql.arch.regs.tpidr_el0)}") + self.ql.arch.cpr.write(*TPIDR_EL0, self.tls) - def save(self): + self.ql.log.debug(f"Setting TPIDR_EL0 to {self.tls:#010x}") + + def save(self) -> None: self.save_context() - self.tls = self.ql.arch.regs.tpidr_el0 - self.ql.log.debug(f"Saved context. tpidr_el0={hex(self.ql.arch.regs.tpidr_el0)}") + self.tls = self.ql.arch.cpr.read(*TPIDR_EL0) - def restore(self): + self.ql.log.debug(f"Context saved. TPIDR_EL0 = {self.tls:#010x}") + + def restore(self) -> None: self.restore_context() self.set_thread_tls(self.tls) - self.ql.log.debug(f"Restored context. tpidr_el0={hex(self.ql.arch.regs.tpidr_el0)}") + + self.ql.log.debug(f"Context restored. TPIDR_EL0 = {self.ql.arch.cpr.read(*TPIDR_EL0):#010x}") def clone(self): - new_thread = super(QlLinuxARM64Thread, self).clone() + new_thread = super().clone() new_thread.tls = self.tls + return new_thread class QlLinuxThreadManagement: diff --git a/qiling/os/os.py b/qiling/os/os.py index 9108f7024..636e089c4 100644 --- a/qiling/os/os.py +++ b/qiling/os/os.py @@ -246,10 +246,15 @@ def stop(self): self.ql.emu_stop() def emu_error(self): - self.ql.log.error(f'CPU Context:') + self.ql.log.error('CPU Context:') + for reg in self.ql.arch.regs.register_mapping: - if isinstance(reg, str): - self.ql.log.error(f'{reg}\t: {self.ql.arch.regs.read(reg):#x}') + try: + value = f'{self.ql.arch.regs.read(reg):#x}' + except UcError: + value = 'n/a' + + self.ql.log.error(f'{reg}\t: {value}') pc = self.ql.arch.regs.arch_pc diff --git a/qiling/profiles/linux.ql b/qiling/profiles/linux.ql index 3aae7fcd9..eac82348b 100644 --- a/qiling/profiles/linux.ql +++ b/qiling/profiles/linux.ql @@ -18,7 +18,8 @@ stack_address = 0x7ff0d000 stack_size = 0x30000 load_address = 0x56555000 interp_address = 0x047ba000 -mmap_address = 0x90000000 +# used to be 0x90000000, but changed to comply with MIPS reserved areas +mmap_address = 0x01000000 [KERNEL] diff --git a/tests/test_elf_multithread.py b/tests/test_elf_multithread.py index be48773f1..e21a6610a 100644 --- a/tests/test_elf_multithread.py +++ b/tests/test_elf_multithread.py @@ -14,6 +14,8 @@ from typing import List +from qiling.arch.models import X86_CPU_MODEL + sys.path.append("..") from qiling import Qiling from qiling.const import * @@ -76,7 +78,7 @@ def check_write(ql: Qiling, fd: int, write_buf, count: int): logged.extend(content.decode().splitlines()) - ql = Qiling([fr'{X86_LINUX_ROOTFS}/bin/x86_multithreading'], X86_LINUX_ROOTFS, multithread=True, verbose=QL_VERBOSE.DEBUG) + ql = Qiling([fr'{X86_LINUX_ROOTFS}/bin/x86_multithreading'], X86_LINUX_ROOTFS, cputype=X86_CPU_MODEL.INTEL_HASWELL, multithread=True, verbose=QL_VERBOSE.DEBUG) ql.os.stats = QlOsNullStats() ql.os.set_syscall("write", check_write, QL_INTERCEPT.ENTER) @@ -114,7 +116,7 @@ def check_write(ql: Qiling, fd: int, write_buf, count: int): logged.extend(content.decode().splitlines()) - ql = Qiling([fr'{X64_LINUX_ROOTFS}/bin/x8664_multithreading'], X64_LINUX_ROOTFS, multithread=True, verbose=QL_VERBOSE.DEBUG) + ql = Qiling([fr'{X64_LINUX_ROOTFS}/bin/x8664_multithreading'], X64_LINUX_ROOTFS, cputype=X86_CPU_MODEL.INTEL_HASWELL, multithread=True, verbose=QL_VERBOSE.DEBUG) ql.os.stats = QlOsNullStats() ql.os.set_syscall("write", check_write, QL_INTERCEPT.ENTER) diff --git a/tests/test_macho.py b/tests/test_macho.py index d002209ee..191e01d1c 100644 --- a/tests/test_macho.py +++ b/tests/test_macho.py @@ -6,19 +6,24 @@ import unittest import sys + sys.path.append("..") from qiling import Qiling +from qiling.arch.models import X86_CPU_MODEL from qiling.const import QL_VERBOSE +ROOTFS = r'../examples/rootfs/x8664_macos' + + 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) + ql = Qiling([fr'{ROOTFS}/bin/x8664_hello'], ROOTFS, cputype=X86_CPU_MODEL.INTEL_HASWELL, verbose=QL_VERBOSE.DEBUG) ql.run() def test_usercorn_x8664(self): - ql = Qiling(["../examples/rootfs/x8664_macos/bin/x8664_hello_usercorn"], "../examples/rootfs/x8664_macos", verbose=QL_VERBOSE.DEBUG) + ql = Qiling([fr'{ROOTFS}/bin/x8664_hello_usercorn'], ROOTFS, cputype=X86_CPU_MODEL.INTEL_HASWELL, verbose=QL_VERBOSE.DEBUG) ql.run()