mirror of
https://github.com/trailofbits/manticore.git
synced 2026-10-11 22:29:55 +00:00
Capstone 5 updates (5.0.0rc2) (#1701)
* Use latest tagged capstone==5.0.0rc2 * Fix aarch64 * Fix ARM * New x86 instrution test Co-authored-by: feliam <[email protected]> Co-authored-by: Boyan MILANOV <[email protected]>
This commit is contained in:
co-authored by
feliam
Boyan MILANOV
parent
f8ad2dfdb1
commit
9e11bc9ae9
@@ -17,3 +17,5 @@ exclude_lines =
|
||||
|
||||
# We don't bother testing code that's explicitly unimplemented
|
||||
raise NotImplementedError
|
||||
raise AssertionError
|
||||
raise Aarch64InvalidInstruction
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import warnings
|
||||
from typing import NamedTuple
|
||||
from copy import copy
|
||||
|
||||
import capstone as cs
|
||||
@@ -17,7 +17,6 @@ from .abstractcpu import (
|
||||
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 @@ class Aarch64Cpu(Cpu):
|
||||
# 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 @@ class Aarch64Cpu(Cpu):
|
||||
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 @@ class Aarch64Cpu(Cpu):
|
||||
|
||||
# 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 @@ class Aarch64Cpu(Cpu):
|
||||
: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 @@ class Aarch64Cpu(Cpu):
|
||||
: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 @@ class Aarch64Cpu(Cpu):
|
||||
|
||||
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 @@ class Aarch64Operand(Operand):
|
||||
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 @@ class Aarch64Operand(Operand):
|
||||
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 @@ class Aarch64Operand(Operand):
|
||||
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}'")
|
||||
|
||||
@@ -764,14 +764,11 @@ class Armv7Cpu(Cpu):
|
||||
@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):
|
||||
|
||||
@@ -1167,11 +1167,7 @@ class X86Cpu(Cpu):
|
||||
: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 @@ class X86Cpu(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 @@ class X86Cpu(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 @@ class X86Cpu(Cpu):
|
||||
|
||||
: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 @@ class X86Cpu(Cpu):
|
||||
|
||||
: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 @@ class X86Cpu(Cpu):
|
||||
:param cpu: current CPU.
|
||||
:param arg0: this argument is ignored.
|
||||
"""
|
||||
pass
|
||||
|
||||
@instruction
|
||||
def ENDBR32(cpu):
|
||||
|
||||
@@ -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"]
|
||||
|
||||
|
||||
@@ -1300,6 +1300,170 @@ class SymCPUTest(unittest.TestCase):
|
||||
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user