diff --git a/.coveragerc b/.coveragerc index f1bd1a4e9..032b95d78 100644 --- a/.coveragerc +++ b/.coveragerc @@ -17,3 +17,5 @@ exclude_lines = # We don't bother testing code that's explicitly unimplemented raise NotImplementedError + raise AssertionError + raise Aarch64InvalidInstruction diff --git a/manticore/native/cpu/aarch64.py b/manticore/native/cpu/aarch64.py index cc406bfc8..9ac2f624c 100644 --- a/manticore/native/cpu/aarch64.py +++ b/manticore/native/cpu/aarch64.py @@ -1,4 +1,4 @@ -import warnings +from typing import NamedTuple from copy import copy import capstone as cs @@ -17,7 +17,6 @@ Operand, instruction, ) -from .arm import HighBit, Armv7Operand from .bitwise import SInt, UInt, ASR, LSL, LSR, ROR, Mask, GetNBits from .register import Register from ...core.smtlib import Operators @@ -306,45 +305,7 @@ def canonicalize_instruction_name(insn): # work for B.cond. Instead of being set to something like 'b.eq', # it just returns 'b'. name = insn.mnemonic.upper() - name = OP_NAME_MAP.get(name, name) - ops = insn.operands - name_list = name.split(".") - - # Make sure MOV (bitmask immediate) and MOV (register) go through 'MOV'. - if ( - name == "ORR" - and len(ops) == 3 - and ops[1].type == cs.arm64.ARM64_OP_REG - and ops[1].reg in ["WZR", "XZR"] - and not ops[2].is_shifted() - ): - name = "MOV" - insn._raw.mnemonic = name.lower().encode("ascii") - del ops[1] - - # Map all B.cond variants to a single implementation. - elif len(name_list) == 2 and name_list[0] == "B" and insn.cc != cs.arm64.ARM64_CC_INVALID: - name = "B_cond" - - # XXX: BFI is only valid when Rn != 11111: - # https://github.com/aquynh/capstone/issues/1441 - elif ( - name == "BFI" - and len(ops) == 4 - and ops[1].type == cs.arm64.ARM64_OP_REG - and ops[1].reg in ["WZR", "XZR"] - ): - name = "BFC" - insn._raw.mnemonic = name.lower().encode("ascii") - del ops[1] - - # XXX: CMEQ incorrectly sets the type to 'ARM64_OP_FP' for - # 'cmeq v0.16b, v1.16b, #0': - # https://github.com/aquynh/capstone/issues/1443 - elif name == "CMEQ" and len(ops) == 3 and ops[2].type == cs.arm64.ARM64_OP_FP: - ops[2]._type = cs.arm64.ARM64_OP_IMM - - return name + return OP_NAME_MAP.get(name, name) @property def insn_bit_str(self): @@ -2373,13 +2334,15 @@ def _CMEQ_zero(cpu, res_op, reg_op, imm_op): cpu._cmeq(res_op, reg_op, imm_op, register=False) @instruction - def CMEQ(cpu, res_op, reg_op, reg_imm_op): + def CMEQ(cpu, res_op, reg_op, reg_imm_op, _bug=0): """ Combines CMEQ (register) and CMEQ (zero). :param res_op: destination register. :param reg_op: source register. :param reg_imm_op: source register or immediate (zero). + + :param bug: Buggy extra operand https://github.com/aquynh/capstone/issues/1629 """ assert res_op.type is cs.arm64.ARM64_OP_REG assert reg_op.type is cs.arm64.ARM64_OP_REG @@ -3655,17 +3618,6 @@ def _MOV_to_general(cpu, res_op, reg_op): # XXX: Check if trapped. - # XXX: Capstone doesn't set 'vess' for this alias: - # https://github.com/aquynh/capstone/issues/1452 - if res_op.size == 32: - reg_op.op.vess = cs.arm64.ARM64_VESS_S - - elif res_op.size == 64: - reg_op.op.vess = cs.arm64.ARM64_VESS_D - - else: - raise Aarch64InvalidInstruction - # The 'instruction' decorator advances PC, so call the original # method. cpu.UMOV.__wrapped__(cpu, res_op, reg_op) @@ -3858,7 +3810,7 @@ def MRS(cpu, res_op, reg_op): :param reg_op: source system register. """ assert res_op.type is cs.arm64.ARM64_OP_REG - assert reg_op.type is cs.arm64.ARM64_OP_REG_MRS + assert reg_op.type is cs.arm64.ARM64_OP_SYS insn_rx = "1101010100" insn_rx += "1" # L @@ -3884,7 +3836,7 @@ def MSR(cpu, res_op, reg_op): :param res_op: destination system register. :param reg_op: source register. """ - assert res_op.type is cs.arm64.ARM64_OP_REG_MSR + assert res_op.type is cs.arm64.ARM64_OP_SYS assert reg_op.type is cs.arm64.ARM64_OP_REG insn_rx = "1101010100" @@ -5175,18 +5127,18 @@ def UMOV(cpu, res_op, reg_op): reg = reg_op.read() index = reg_op.op.vector_index - vess = reg_op.op.vess + vas = reg_op.op.vas - if vess == cs.arm64.ARM64_VESS_B: + if vas == cs.arm64.ARM64_VAS_1B: elem_size = 8 - elif vess == cs.arm64.ARM64_VESS_H: + elif vas == cs.arm64.ARM64_VAS_1H: elem_size = 16 - elif vess == cs.arm64.ARM64_VESS_S: + elif vas == cs.arm64.ARM64_VAS_1S: elem_size = 32 - elif vess == cs.arm64.ARM64_VESS_D: + elif vas == cs.arm64.ARM64_VAS_1D: elem_size = 64 else: @@ -5352,6 +5304,7 @@ def __init__(self, cpu, op, **kwargs): cs.arm64.ARM64_OP_MEM, cs.arm64.ARM64_OP_IMM, cs.arm64.ARM64_OP_FP, + cs.arm64.ARM64_OP_SYS, cs.arm64.ARM64_OP_BARRIER, ): raise NotImplementedError(f"Unsupported operand type: '{self.op.type}'") @@ -5399,7 +5352,7 @@ def is_extended(self): def read(self): if self.type == cs.arm64.ARM64_OP_REG: return self.cpu.regfile.read(self.reg) - elif self.type == cs.arm64.ARM64_OP_REG_MRS: + elif self.type == cs.arm64.ARM64_OP_REG_MRS or self.type == cs.arm64.ARM64_OP_SYS: name = SYS_REG_MAP.get(self.op.sys) if not name: raise NotImplementedError(f"Unsupported system register: '0x{self.op.sys:x}'") @@ -5412,7 +5365,7 @@ def read(self): def write(self, value): if self.type == cs.arm64.ARM64_OP_REG: self.cpu.regfile.write(self.reg, value) - elif self.type == cs.arm64.ARM64_OP_REG_MSR: + elif self.type == cs.arm64.ARM64_OP_REG_MSR or cs.arm64.ARM64_OP_SYS: name = SYS_REG_MAP.get(self.op.sys) if not name: raise NotImplementedError(f"Unsupported system register: '0x{self.op.sys:x}'") diff --git a/manticore/native/cpu/arm.py b/manticore/native/cpu/arm.py index efe6a0e67..e82ccb702 100644 --- a/manticore/native/cpu/arm.py +++ b/manticore/native/cpu/arm.py @@ -764,14 +764,11 @@ def set_arm_tls(self, data): @staticmethod def canonicalize_instruction_name(instr): name = instr.insn_name().upper() - # XXX bypass a capstone bug that incorrectly labels some insns as mov - if name == "MOV": - if instr.mnemonic.startswith("lsr"): - return "LSR" - elif instr.mnemonic.startswith("lsl"): - return "LSL" - elif instr.mnemonic.startswith("asr"): - return "ASR" + # FIXME: Workaround https://github.com/aquynh/capstone/issues/1630 + if instr.mnemonic == "addw": + return "ADDW" + elif instr.mnemonic == "subw": + return "SUBW" return OP_NAME_MAP.get(name, name) def _wrap_operands(self, operands): diff --git a/manticore/native/cpu/x86.py b/manticore/native/cpu/x86.py index 7b5bd5852..49ca8b6eb 100644 --- a/manticore/native/cpu/x86.py +++ b/manticore/native/cpu/x86.py @@ -1167,11 +1167,7 @@ def XOR(cpu, dest, src): :param dest: destination operand. :param src: source operand. """ - if dest == src: - # if the operands are the same write zero - res = dest.write(0) - else: - res = dest.write(dest.read() ^ src.read()) + res = dest.write(dest.read() ^ src.read()) # Defined Flags: szp cpu._calculate_logic_flags(dest.size, res) @@ -1226,7 +1222,7 @@ def AAA(cpu): This instruction executes as described in compatibility mode and legacy mode. It is not valid in 64-bit mode. :: - IF ((AL AND 0FH) > 9) Operators.OR(AF = 1) + IF ((AL AND 0FH) > 9) OR (AF = 1) THEN AL = (AL + 6); AH = AH + 1; @@ -1243,20 +1239,10 @@ def AAA(cpu): cpu.CF = cpu.AF cpu.AH = Operators.ITEBV(8, cpu.AF, cpu.AH + 1, cpu.AH) cpu.AL = Operators.ITEBV(8, cpu.AF, cpu.AL + 6, cpu.AL) - """ - if (cpu.AL & 0x0F > 9) or cpu.AF == 1: - cpu.AL = cpu.AL + 6 - cpu.AH = cpu.AH + 1 - cpu.AF = True - cpu.CF = True - else: - cpu.AF = False - cpu.CF = False - """ cpu.AL = cpu.AL & 0x0F @instruction - def AAD(cpu, imm=None): + def AAD(cpu, imm): """ ASCII adjust AX before division. @@ -1282,12 +1268,7 @@ def AAD(cpu, imm=None): :param cpu: current CPU. """ - if imm is None: - imm = 10 - else: - imm = imm.read() - - cpu.AL += cpu.AH * imm + cpu.AL += cpu.AH * imm.read() cpu.AH = 0 # Defined flags: ...sz.p. @@ -1317,11 +1298,7 @@ def AAM(cpu, imm=None): :param cpu: current CPU. """ - if imm is None: - imm = 10 - else: - imm = imm.read() - + imm = imm.read() cpu.AH = Operators.UDIV(cpu.AL, imm) cpu.AL = Operators.UREM(cpu.AL, imm) @@ -5570,6 +5547,7 @@ def NOP(cpu, arg0=None): :param cpu: current CPU. :param arg0: this argument is ignored. """ + pass @instruction def ENDBR32(cpu): diff --git a/setup.py b/setup.py index 3e603428f..5cbeec53d 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,11 @@ def rtd_dependent_deps(): # If you update native_deps please update the `REQUIREMENTS_TO_IMPORTS` dict in `utils/install_helper.py` # (we need to know how to import a given native dependency so we can check if native dependencies are installed) -native_deps = ["capstone==4.0.2", "pyelftools", "unicorn==1.0.2"] +native_deps = [ + "capstone==5.0.0rc2", + "pyelftools", + "unicorn==1.0.2", +] lint_deps = ["black~=22.0", "mypy==0.790"] diff --git a/tests/native/test_cpu_manual.py b/tests/native/test_cpu_manual.py index 2e8296af8..24564ca8f 100644 --- a/tests/native/test_cpu_manual.py +++ b/tests/native/test_cpu_manual.py @@ -1300,6 +1300,170 @@ def test_symbolic_instruction(self): self.assertEqual(cpu.EIP, code + 1) + def test_AAA_0(self): + """ASCII Adjust AL after subtraction.""" + + cs = ConstraintSet() + mem = SMemory32(cs) + cpu = I386Cpu(mem) + + # alloc/map a little mem + code = mem.mmap(0x1000, 0x1000, "rwx") + stack = mem.mmap(0xF000, 0x1000, "rw") + + # 37 AAA + mem[code] = BitVecConstant(size=8, value=0x37) + cpu.EIP = code + AL = 10 + AH = 0x41 + AF = False + cpu.AL = AL + cpu.AH = AH + cpu.AF = False + cpu.execute() + + self.assertEqual(cpu.AL, 0) + self.assertEqual(cpu.AH, AH + 1) + self.assertEqual(cpu.AF, True) + self.assertEqual(cpu.CF, True) + + def test_AAA_1(self): + """ASCII Adjust AL after subtraction.""" + cs = ConstraintSet() + mem = SMemory32(cs) + cpu = I386Cpu(mem) + # alloc/map a little mem + code = mem.mmap(0x1000, 0x1000, "rwx") + stack = mem.mmap(0xF000, 0x1000, "rw") + + # 37 AAA + mem[code] = BitVecConstant(size=8, value=0x37) + cpu.EIP = code + AL = 18 + AH = 0x41 + AF = False + cpu.AL = AL + cpu.AH = AH + cpu.AF = False + cpu.execute() + + self.assertEqual(cpu.AL, AL & 0xF) + self.assertEqual(cpu.AF, False) + self.assertEqual(cpu.CF, False) + + def test_AAS_0(self): + """ASCII Adjust AL after subtraction.""" + + cs = ConstraintSet() + mem = SMemory32(cs) + cpu = I386Cpu(mem) + + # alloc/map a little mem + code = mem.mmap(0x1000, 0x1000, "rwx") + stack = mem.mmap(0xF000, 0x1000, "rw") + + # 3F AAS + mem[code] = BitVecConstant(size=8, value=0x3F) + cpu.EIP = code + AL = 10 + AH = 0x41 + AF = False + cpu.AL = AL + cpu.AH = AH + cpu.AF = False + cpu.execute() + + self.assertEqual(cpu.AL, (AL - 6) & 0xF) + self.assertEqual(cpu.AH, AH - 1) + self.assertEqual(cpu.AF, True) + self.assertEqual(cpu.CF, True) + + def test_AAS_1(self): + """ASCII Adjust AL after subtraction.""" + cs = ConstraintSet() + mem = SMemory32(cs) + cpu = I386Cpu(mem) + # alloc/map a little mem + code = mem.mmap(0x1000, 0x1000, "rwx") + stack = mem.mmap(0xF000, 0x1000, "rw") + + # 3F AAS + mem[code] = BitVecConstant(size=8, value=0x3F) + cpu.EIP = code + AL = 18 + AH = 0x41 + AF = False + cpu.AL = AL + cpu.AH = AH + cpu.AF = False + cpu.execute() + + self.assertEqual(cpu.AL, AL & 0xF) + self.assertEqual(cpu.AF, False) + self.assertEqual(cpu.CF, False) + + def test_DAA_0(self): + """Decimal Adjust AL after Addition.""" + + cs = ConstraintSet() + mem = SMemory32(cs) + cpu = I386Cpu(mem) + # alloc/map a little mem + code = mem.mmap(0x1000, 0x1000, "rwx") + stack = mem.mmap(0xF000, 0x1000, "rw") + + # 27 DAA + mem[code] = BitVecConstant(size=8, value=0x27) + cpu.EIP = code + + cpu.AL = 0xAE + cpu.BL = 0x35 + cpu.OF = True + cpu.SF = True + cpu.ZF = False + cpu.AF = False + cpu.PF = False + cpu.CF = False + + cpu.execute() + self.assertEqual(cpu.AL, 0x14) + self.assertEqual(cpu.BL, 0x35) + self.assertEqual(cpu.SF, False) + self.assertEqual(cpu.ZF, False) + self.assertEqual(cpu.AF, True) + self.assertEqual(cpu.PF, True) + self.assertEqual(cpu.CF, True) + + def test_DAS_0(self): + """Decimal Adjust AL after Subtraction.""" + + cs = ConstraintSet() + mem = SMemory32(cs) + cpu = I386Cpu(mem) + # alloc/map a little mem + code = mem.mmap(0x1000, 0x1000, "rwx") + stack = mem.mmap(0xF000, 0x1000, "rw") + + # 2F DAS + mem[code] = BitVecConstant(size=8, value=0x2F) + cpu.EIP = code + + cpu.AL = 0xAE + cpu.OF = True + cpu.SF = True + cpu.ZF = False + cpu.AF = False + cpu.PF = False + cpu.CF = False + + cpu.execute() + self.assertEqual(cpu.AL, 0x48) + self.assertEqual(cpu.SF, False) + self.assertEqual(cpu.ZF, False) + self.assertEqual(cpu.AF, True) + self.assertEqual(cpu.PF, True) + self.assertEqual(cpu.CF, True) + if __name__ == "__main__": unittest.main()