diff --git a/manticore/core/cpu/abstractcpu.py b/manticore/core/cpu/abstractcpu.py index 21c9b73..98becd1 100644 --- a/manticore/core/cpu/abstractcpu.py +++ b/manticore/core/cpu/abstractcpu.py @@ -1,27 +1,18 @@ from capstone import * from capstone.arm import * from capstone.x86 import * -from unicorn import * -from unicorn.x86_const import * -from unicorn.arm_const import * from abc import ABCMeta, abstractmethod from ..smtlib import Expression, Bool, BitVec, Array, Operators, Constant -from ..memory import MemoryException +from ..memory import MemoryException, FileMap, AnonMap from ...utils.helpers import issymbolic +from ...utils.emulate import UnicornEmulator import sys from functools import wraps import types import logging logger = logging.getLogger("CPU") -###################################################################### -# Abstract classes for capstone/unicorn based cpus -# no emulator by default -MU = { - (CS_ARCH_ARM, CS_MODE_ARM): Uc(UC_ARCH_ARM, UC_MODE_ARM), - (CS_ARCH_X86, CS_MODE_32): Uc(UC_ARCH_X86, UC_MODE_32), - (CS_ARCH_X86, CS_MODE_64): Uc(UC_ARCH_X86, UC_MODE_64) - } + SANE_SIZES = {8, 16, 32, 64, 80, 128, 256} # This encapsulates how to acccess operands (regs/mem/immediates) for differents cpus @@ -154,7 +145,6 @@ class Cpu(object): self._md.detail = True self._md.syntax = 0 self.instruction = None - #FIXME self.transactions = [] def __getstate__(self): state = {} @@ -189,6 +179,13 @@ class Cpu(object): :rtype: tuple[str] ''' return self._regfile.all_registers + @property + def canonical_registers(self): + ''' Returns the list of all register names for this CPU. + @rtype: tuple + @return: the list of register names for this CPU. + ''' + return self._regfile.canonical_registers def write_register(self, register, value): '''Dynamic interface for writing cpu registers @@ -357,16 +354,20 @@ class Cpu(object): name = self.canonicalize_instruction_name(instruction) - try: - implementation = getattr(self, name) - except AttributeError as ae: - logger.debug("UNIMPLEMENTED INSTRUCTION: 0x%016x:\t%s\t%s\t%s", instruction.address, ' '.join(map(lambda x: '%02x'%x, instruction.bytes)), instruction.mnemonic, instruction.op_str) - implementation = lambda *ops: self.emulate(instruction) + def fallback_to_emulate(*operands): + text_bytes = ' '.join('%02x'%x for x in instruction.bytes) + logger.info("UNIMPLEMENTED INSTRUCTION: 0x%016x:\t%s\t%s\t%s", + instruction.address, text_bytes, instruction.mnemonic, + instruction.op_str) + self.emulate(instruction) + + implementation = getattr(self, name, fallback_to_emulate) #log if logger.level == logging.DEBUG : for l in str(self).split('\n'): logger.debug(l) + implementation(*instruction.operands) self._icount+=1 @@ -374,111 +375,16 @@ class Cpu(object): def get_syscall_description(self): pass - ############################################################# - # Emulation - def _concretize_registers(self, instruction): - pass - - def _unicorn(self): - return MU[(self.arch, self.mode)] - def emulate(self, instruction): - #Fix Taint propagation - needed_pages = set() - needed_bytes = set() - mapped = set() - accessed = set() - byte_values = {} + ''' + If we could not handle emulating an instruction, use Unicorn to emulate + it. - reg_values = self._concretize_registers(instruction) - # Request any memory nearby the memory directly needed by the memory - # operands of the instruction. - for op in instruction.operands: - if op.type != {CS_ARCH_ARM: ARM_OP_MEM, CS_ARCH_X86: X86_OP_MEM}[self.arch]: - continue - self.PC += instruction.size - addr = op.address() #FIXME maybe add a kwarg parameter to operand.address() with the current pc? - self.PC -= instruction.size - assert not issymbolic(addr) - num_bytes = op.size/8 - needed_bytes.update(range(addr, addr + num_bytes)) - # Request the bytes of the instruction. - needed_bytes.update(range(self.PC, self.PC + instruction.size)) - - # Concretizes the bytes of memory potentially needed by the instruction. - for addr in needed_bytes: - needed_pages.add(addr & (~0xFFF)) - val = self.read_int(addr, 8) - if issymbolic(val): - logger.debug("Concretizing bytes before passing it to unicorn") - raise ConcretizeMemory(addr, 8, "Passing control to emulator", 'SAMPLED') - byte_values[addr] = val - - mu = self._unicorn() - - touched = set() - def hook_mem_access(uc, access, address, size, value, user_data): - if access & UC_MEM_WRITE: - for i in range(address, address+size): - user_data.add(i) - if access & UC_MEM_READ: - for i in range(address, address+size): - if i not in needed_bytes: - logger.error("Not initalized memory used by emulator at %x", address) - try: - # Copy in the concrete values of all needed registers. - for reg, value in reg_values.items(): - #stem = {CS_ARCH_ARM: 'UC_ARM_REG_', CS_ARCH_X86: 'UC_X86_REG_'}[self.arch] - stem = 'UC_X86_REG_' - mu.reg_write(globals()[stem+reg], value) - - #Map needed pages - for page in needed_pages: - mapped.add(page) - mu.mem_map(page, 0x1000, UC_PROT_ALL) - # Copy in memory bytes needed by instruction. - for addr, value in byte_values.items(): - mu.mem_write(addr, Operators.CHR(value)) - - # Run the instruction. - hook_id = mu.hook_add(UC_HOOK_MEM_WRITE | UC_HOOK_MEM_READ, hook_mem_access, touched) - mu.emu_start(self.PC, self.PC+instruction.size) - mu.hook_del(hook_id) - mu.emu_stop() - - # Copy back the memory modified by the unicorn emulation. - for addr in touched: - if not addr in needed_bytes: - logger.error("Some address was touched in the emulation but not provided %x", addr) - assert addr in needed_bytes - try: - cpu.write_int(addr, ord(mu.mem_read(addr, 1)), 8) - except: - pass - - # Copy back the new values of all registers. - if hasattr(instruction, 'regs_access') and instruction.regs_access is not None: - (regs_read, regs_write) = instruction.regs_access() - regs = [ instruction.reg_name(r).upper() for r in regs_write ] - if self.arch == CS_ARCH_X86: - regs += ['FPSW', 'FPCW', 'FPTAG', 'FP0', 'FP1', 'FP2', 'FP3', 'FP4', 'FP5', 'FP6', 'FP7'] - else: - regs = reg_values.keys() - logger.debug("Emulator wrote to this regs %r", regs) - for reg in regs: - #stem = {CS_ARCH_ARM: 'UC_ARM_REG_', CS_ARCH_X86: 'UC_X86_REG_'}[self.arch] - stem = 'UC_X86_REG_' - new_value = mu.reg_read(globals()[stem+reg]) - self.write_register(reg, new_value) - - self.PC = self.PC+instruction.size - return - except Exception as e: - logger.error('Exception in emulatin code:') - logger.error(e, exc_info=True) - finally: - for i in mapped: - mu.mem_unmap(i,0x1000) + @param instruction The instruction object to emulate + ''' + emu = UnicornEmulator(self) + emu.emulate(instruction) + del emu #Generic string representation def __str__(self): @@ -552,36 +458,53 @@ class Syscall(CpuInterrupt): def __init__(self): super(Syscall, self).__init__("CPU Syscall") -class ConcretizeRegister(Exception): - ''' ''' - def __init__(self, reg_name, message, policy='MINMAX'): - assert policy in ['MINMAX', 'ALL', 'SAMPLED'] - super(ConcretizeRegister, self).__init__("Concretizing %s (%s). %s"%(reg_name, policy, message)) - self.reg_name = reg_name - self.policy = policy +# TODO(yan): Move this into State or a more appropriate location -class ConcretizeMemory(Exception): - ''' ''' +class ConcretizeException(Exception): + ''' + Base class for all exceptions that trigger the concretization of a symbolic + value. + ''' + _ValidPolicies = ['MINMAX', 'ALL', 'SAMPLED', 'ONE'] + def __init__(self, message, policy): + assert policy in self._ValidPolicies, "Policy must be one of: %s"%(', '.join(self._ValidPolicies),) + self.policy = policy + super(ConcretizeException, self).__init__("%s (Policy: %s)"%(message, policy)) + +class ConcretizeRegister(ConcretizeException): + ''' + Raised when a symbolic register needs to be concretized. + ''' + def __init__(self, reg_name, message, policy='MINMAX'): + message = "Concretizing %s. %s"%(reg_name, message) + super(ConcretizeRegister, self).__init__(message, policy) + self.reg_name = reg_name + +class ConcretizeMemory(ConcretizeException): + ''' + Raised when a symbolic memory location needs to be concretized. + ''' def __init__(self, address, size, message, policy='MINMAX'): - assert policy in ['MINMAX', 'ALL', 'SAMPLED'] - super(ConcretizeMemory, self).__init__("Concretizing byte at %x (%s). %s"%(address, policy, message)) + message = "Concretizing byte at %x. %s"%(address, message) + super(ConcretizeMemory, self).__init__(message, policy) self.address = address self.size = size - self.policy = policy -class ConcretizeArgument(Exception): - ''' ''' +class ConcretizeArgument(ConcretizeException): + ''' + Raised when a symbolic argument needs to be concretized. + ''' def __init__(self, argnum, policy='MINMAX'): - assert policy in ['MINMAX', 'ALL', 'SAMPLED'] - super(ConcretizeArgument, self).__init__("Concretizing argument #%d (%s): "%(argnum, policy)) + message = "Concretizing argument #%d."%(argnum,) + super(ConcretizeArgument, self).__init__(message, policy) self.argnum = argnum - self.policy = policy - class SymbolicPCException(ConcretizeRegister): - ''' ''' + ''' + Raised when we attempt to execute from a symbolic location. + ''' def __init__(self): - super(SymbolicPCException, self).__init__("PC", "Symbolic PC", "ALL") + super(SymbolicPCException, self).__init__("PC", "Can't execute from a symbolic address.", "ALL") class IgnoreAPI(Exception): def __init__(self, name): diff --git a/manticore/core/cpu/arm.py b/manticore/core/cpu/arm.py index 98899c2..bc23d30 100644 --- a/manticore/core/cpu/arm.py +++ b/manticore/core/cpu/arm.py @@ -277,26 +277,6 @@ class Armv7Cpu(Cpu): self._last_flags = state['_last_flags'] self._force_next = state['_force_next'] - def _concretize_registers(cpu, instruction): - reg_values = {} - if hasattr(instruction, 'regs_access'): - (regs_read, regs_write) = instruction.regs_access() - regs = [ instruction.reg_name(r).upper() for r in regs_read ] - regs.append('R15') - else: - regs = self.canonical_registers - - logger.debug("Emulator wants this regs %r", regs) - for reg in regs: - value = cpu.read_register(reg) - if issymbolic(value): - raise ConcretizeRegister(reg, "Passing control to emulator") #FIXME improve exception to handle multiple registers at a time - reg_values[reg] = value - - logger.info("Emulator wants this regs %r", reg_values) - return reg_values - - # Flags that are the result of arithmetic instructions. Unconditionally # set, but conditionally committed. # @@ -855,3 +835,11 @@ class Armv7Cpu(Cpu): def STCL(cpu, *operands): pass + @instruction + def DMB(cpu, *operands): + ''' + Used by the the __kuser_dmb ARM Linux user-space handler. This is a nop + under Manticore's memory and execution model. + ''' + pass + diff --git a/manticore/core/cpu/x86.py b/manticore/core/cpu/x86.py index 7972aec..72dc502 100644 --- a/manticore/core/cpu/x86.py +++ b/manticore/core/cpu/x86.py @@ -771,32 +771,6 @@ class X86Cpu(Cpu): name = OP_NAME_MAP.get(name, name) return name - - def _concretize_registers(cpu, instruction): - reg_values = {} - if hasattr(instruction, 'regs_access'): - (regs_read, regs_write) = instruction.regs_access() - regs = [ str(instruction.reg_name(r).upper()) for r in regs_read ] - - else: - # TODO: only concretize registers the instruction touches - if cpu.mode == CS_MODE_64: - regs = ('RAX', 'RCX', 'RDX', 'RBX', 'RSP', 'RBP', 'RSI', 'RDI', 'R8', 'R9', 'R10', 'R11', 'R12', 'R13', 'R14', 'R15', 'RIP', 'YMM0', 'YMM1', 'YMM2', 'YMM3', 'YMM4', 'YMM5', 'YMM6', 'YMM7', 'YMM8', 'YMM9', 'YMM10', 'YMM11', 'YMM12', 'YMM13', 'YMM14', 'YMM15') - else: - regs = ('EAX', 'ECX', 'EDX', 'EBX', 'ESP', 'EBP', 'ESI', 'EDI', 'EIP', 'XMM0', 'XMM1', 'XMM2', 'XMM3', 'XMM4', 'XMM5', 'XMM6', 'XMM7') - - regs += ('FPSW', 'FPCW', 'FPTAG', 'FP0', 'FP1', 'FP2', 'FP3', 'FP4', 'FP5', 'FP6', 'FP7') - - for reg in regs: - value = cpu.read_register(reg) - if issymbolic(value): - raise ConcretizeRegister(reg, "Passing control to emulator") - reg_values[reg] = value - - logger.info("Emulator wants this regs %r", reg_values) - return reg_values - - ################################################3 # instruction implementation diff --git a/manticore/core/executor.py b/manticore/core/executor.py index 2ffe310..6c301e3 100644 --- a/manticore/core/executor.py +++ b/manticore/core/executor.py @@ -440,7 +440,7 @@ class Executor(object): output.write("CPU:\n{}".format(cpu)) - if hasattr(cpu, "instruction"): + if hasattr(cpu, "instruction") and cpu.instruction is not None: i = cpu.instruction output.write(" Instruction: 0x%x\t(%s %s)\n" %(i.address, i.mnemonic, i.op_str)) else: @@ -716,7 +716,7 @@ class Executor(object): del vals, symbolic, setstate except SymbolicMemoryException as e: - logger.error('SymbolicMemoryException at PC: 0x%16x. Cause: %s', current_state.current.PC, e.cause) + logger.error('SymbolicMemoryException at PC: 0x%16x. Cause: %s', current_state.PC, e.cause) logger.info('Constraint for crashing! %s', e.constraint) children = [] diff --git a/manticore/core/memory.py b/manticore/core/memory.py index 7ce437c..7cf216a 100644 --- a/manticore/core/memory.py +++ b/manticore/core/memory.py @@ -583,7 +583,7 @@ class Memory(object): #remove m from the maps set self._maps.remove(m) - def _get(self, address): + def map_containing(self, address): ''' Returns the L{MMap} object containing the address. @rtype: L{MMap} @@ -592,7 +592,10 @@ class Memory(object): @todo: symbolic address ''' - return self._page2map[self._page(address)] + page_offset = self._page(address) + if page_offset not in self._page2map: + raise MemoryException("Page not mapped", address) + return self._page2map[page_offset] def mappings(self): @@ -699,7 +702,7 @@ class Memory(object): # get the more restrictive set of perms for the range raise NotImplementedError('No perms for slices') else: - return self._get(index).perms + return self.map_containing(index).perms def access_ok(self, index, access): if isinstance(index, slice): @@ -709,7 +712,7 @@ class Memory(object): while addr < index.stop: if addr not in self: return False - m = self._get(addr) + m = self.map_containing(addr) size = min(m.end-addr, index.stop-addr) if not m.access_ok(access): @@ -720,7 +723,7 @@ class Memory(object): else: if index not in self: return False - m = self._get(index) + m = self.map_containing(index) return m.access_ok(access) #write and read potentially symbolic bytes at symbolic indexes @@ -734,7 +737,7 @@ class Memory(object): stop = addr+size p = addr while p < stop: - m = self._get(p) + m = self.map_containing(p) _size = min(m.end-p, stop-p) result += m[p:p+_size] @@ -754,7 +757,7 @@ class Memory(object): stop = addr + size start = addr while addr < stop: - m = self._get(addr) + m = self.map_containing(addr) size = min(m.end-addr, stop-addr) m[addr:addr+size] = buf[addr-start:addr-start+size] addr+=size @@ -886,7 +889,7 @@ class SMemory(Memory): #Given ALL solutions for the symbolic address for base in solutions: addr_value = base + offset - byte = Operators.ORD(self._get(addr_value)[addr_value]) + byte = Operators.ORD(self.map_containing(addr_value)[addr_value]) if addr_value in self._symbols: for condition, value in self._symbols[addr_value]: byte = Operators.ITEBV(8, condition, Operators.ORD(value), byte) diff --git a/manticore/core/smtlib/expression.py b/manticore/core/smtlib/expression.py index dd03fd6..76836fe 100644 --- a/manticore/core/smtlib/expression.py +++ b/manticore/core/smtlib/expression.py @@ -356,6 +356,7 @@ class BitVec(Expression): def Bool(self): return self != 0 + class BitVecVariable(BitVec, Variable): def __init__(self, *args, **kwargs): super(BitVecVariable, self).__init__(*args, **kwargs) @@ -605,20 +606,9 @@ class ArrayStore(ArrayOperation): class ArrayProxy(ArrayVariable): def __init__(self, array): assert isinstance(array, ArrayVariable) + super(ArrayProxy, self).__init__(array.index_bits, array.index_max, array.name) self._array = array - @property - def name(self): - return self._array.name - - @property - def index_bits(self): - return self._array._index_bits - - @property - def index_max(self): - return self._array._index_max - @property def operands(self): return self._array.operands diff --git a/manticore/core/state.py b/manticore/core/state.py index 3e684f0..1ef20ea 100644 --- a/manticore/core/state.py +++ b/manticore/core/state.py @@ -176,16 +176,18 @@ class State(object): def concretize(self, symbolic, policy, maxcount=100): vals = [] if policy == 'MINMAX': - vals = solver.minmax(self.constraints, symbolic) + vals = self._solver.minmax(self.constraints, symbolic) elif policy == 'SAMPLED': - m, M = solver.minmax(self.constraints, symbolic) + m, M = self._solver.minmax(self.constraints, symbolic) vals += [m, M] if M - m > 3: - if solver.can_be_true(self.constraints, symbolic == (m + M) / 2): + if self._solver.can_be_true(self.constraints, symbolic == (m + M) / 2): vals.append((m + M) / 2) if M - m > 100: - vals += solver.get_all_values(self.constraints, symbolic, maxcnt=maxcount, - silent=True) + vals += self._solver.get_all_values(self.constraints, symbolic, + maxcnt=maxcount, silent=True) + elif policy == 'ONE': + vals = [self._solver.get_value(self.constraints, symbolic)] else: assert policy == 'ALL' vals = solver.get_all_values(self.constraints, symbolic, maxcnt=maxcount, diff --git a/manticore/models/linux.py b/manticore/models/linux.py index 166bb1f..0e234ab 100644 --- a/manticore/models/linux.py +++ b/manticore/models/linux.py @@ -361,8 +361,8 @@ class Linux(object): state['auxv'] = self.auxv state['program'] = self.program state['syscall_arg_regs'] = self.syscall_arg_regs - if hasattr(self, 'tls_value'): - state['tls_value'] = self.tls_value + if hasattr(self, '_arm_tls_memory'): + state['_arm_tls_memory'] = self._arm_tls_memory return state def __setstate__(self, state): @@ -409,8 +409,8 @@ class Linux(object): self.auxv = state['auxv'] self.program = state['program'] self.syscall_arg_regs = state['syscall_arg_regs'] - if 'tls_value' in state: - self.tls_value = state['tls_value'] + if '_arm_tls_memory' in state: + self._arm_tls_memory = state['_arm_tls_memory'] def _read_string(self, cpu, buf): """ @@ -425,6 +425,85 @@ class Linux(object): filename += c return filename + def _init_arm_kernel_helpers(self, cpu): + ''' + ARM kernel helpers + + https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt + ''' + + page_data = bytearray('\xf1\xde\xfd\xe7' * 1024) + + # Extracted from a RPi2 + preamble = ( + 'ff0300ea' + + '650400ea' + + 'f0ff9fe5' + + '430400ea' + + '220400ea' + + '810400ea' + + '000400ea' + + '870400ea' + ).decode('hex') + + # XXX(yan): The following implementations of cmpxchg and cmpxchg64 were + # handwritten to not use any exclusive instructions (e.g. ldrexd) or + # locking. For actual implementations, refer to + # arch/arm64/kernel/kuser32.S in the Linux source code. + __kuser_cmpxchg64 = ( + '30002de9' + # push {r4, r5} + '08c09de5' + # ldr ip, [sp, #8] + '30009ce8' + # ldm ip, {r4, r5} + '010055e1' + # cmp r5, r1 + '00005401' + # cmpeq r4, r0 + '0100a013' + # movne r0, #1 + '0000a003' + # moveq r0, #0 + '0c008c08' + # stmeq ip, {r2, r3} + '3000bde8' + # pop {r4, r5} + '1eff2fe1' # bx lr + ).decode('hex') + + __kuser_dmb = ( + '5bf07ff5' + # dmb ish + '1eff2fe1' # bx lr + ).decode('hex') + + __kuser_cmpxchg = ( + '003092e5' + # ldr r3, [r2] + '000053e1' + # cmp r3, r0 + '0000a003' + # moveq r0, #0 + '00108205' + # streq r1, [r2] + '0100a013' + # movne r0, #1 + '1eff2fe1' # bx lr + ).decode('hex') + + # Map a TLS segment + self._arm_tls_memory = cpu.memory.mmap(None, 4, 'rw ') + + __kuser_get_tls = ( + '04009FE5' + # ldr r0, [pc, #4] + '010090e8' + # ldm r0, {r0} + '1eff2fe1' # bx lr + ).decode('hex') + struct.pack('"*10) + + # Bring back Unicorn registers to Manticore + for reg in registers: + val = self._emu.reg_read(self._to_unicorn_id(reg)) + self._cpu.write_register(reg, val) + + #Unicorn hack. On single step unicorn wont advance the PC register + mu_pc = self.get_unicorn_pc() + if saved_PC == mu_pc: + self._cpu.PC = saved_PC + instruction.size + + # Raise the exception from a hook that Unicorn would have eaten + if self._to_raise: + raise self._to_raise + + return + diff --git a/tests/test_armv7cpu.py b/tests/test_armv7cpu.py index 7250ead..233117c 100644 --- a/tests/test_armv7cpu.py +++ b/tests/test_armv7cpu.py @@ -2,8 +2,7 @@ import unittest import struct from functools import wraps -from manticore.core.cpu.arm import Armv7Cpu as cpu -from manticore.core.cpu.arm import * +from manticore.core.cpu.arm import Armv7Cpu as Cpu, Mask, Interruption from manticore.core.memory import Memory32 from capstone.arm import * @@ -25,7 +24,7 @@ def assemble(asm): class Armv7CpuTest(unittest.TestCase): def setUp(self): - self.c = cpu(Memory32(), 'armv7') + self.c = Cpu(Memory32(), 'armv7') self.rf = self.c.regfile self._setupStack() @@ -121,7 +120,6 @@ def itest_setregs(*preds): return instr_dec - def itest_custom(asm): def instr_dec(custom_func): @wraps(custom_func) @@ -136,7 +134,7 @@ def itest_custom(asm): class Armv7CpuInstructions(unittest.TestCase): def setUp(self): - self.cpu = cpu(Memory32(), 'armv7') + self.cpu = Cpu(Memory32(), 'armv7') self.mem = self.cpu.memory self.rf = self.cpu.regfile @@ -144,8 +142,9 @@ class Armv7CpuInstructions(unittest.TestCase): self.code = self.mem.mmap(0x1000, 0x1000, 'rwx') self.data = self.mem.mmap(0xd000, 0x1000, 'rw') self.stack = self.mem.mmap(0xf000, 0x1000, 'rw') - self.mem.write(self.code, assemble(asm)) - self.rf.write('PC', self.code) + start = self.code + 4 + self.mem.write(start, assemble(asm)) + self.rf.write('PC', start) self.rf.write('SP', self.stack + 0x1000) def _checkFlagsNZCV(self, n, z, c, v): @@ -1009,16 +1008,17 @@ class Armv7CpuInstructions(unittest.TestCase): self.assertEqual(self.cpu.read_int(addr + 8, self.cpu.address_bit_size), 4) @itest_custom("bx r1") - @itest_setregs("R1=0x1004") + @itest_setregs("R1=0x1008") def test_bx_basic(self): self.cpu.execute() - self.assertEqual(self.rf.read('PC'), 0x1004) + self.assertEqual(self.rf.read('PC'), 0x1008) @itest_custom("bx r1") - @itest_setregs("R1=0x1005") + @itest_setregs("R1=0x1009") def test_bx_thumb(self): + pre_pc = self.rf.read('PC') self.cpu.execute() - self.assertEqual(self.rf.read('PC'), 0x1004) + self.assertEqual(self.rf.read('PC'), pre_pc + 4) # ORR @@ -1300,13 +1300,13 @@ class Armv7CpuInstructions(unittest.TestCase): @itest("BLX R1") def test_blx_reg(self): self.assertEqual(self.rf.read('PC'), 0x1008) - self.assertEqual(self.rf.read('LR'), 0x1004) + self.assertEqual(self.rf.read('LR'), 0x1008) @itest_setregs("R1=0x1009") @itest("BLX R1") def test_blx_reg_thumb(self): self.assertEqual(self.rf.read('PC'), 0x1008) - self.assertEqual(self.rf.read('LR'), 0x1004) + self.assertEqual(self.rf.read('LR'), 0x1008) @itest_setregs("R1=0xffffffff", "R2=2") @itest("UMULLS R1, R2, R1, R2") @@ -1348,3 +1348,7 @@ class Armv7CpuInstructions(unittest.TestCase): self.assertEqual(self.rf.read('R2'), (mul >> 32) & Mask(32)) self._checkFlagsNZCV(0, 1, pre_c, pre_v) + @itest("dmb ish") + def test_dmb(self): + # This is a nop, ensure that the instruction exists + self.assertTrue(True) diff --git a/tests/test_cpu_manual.py b/tests/test_cpu_manual.py index da77b85..b5ea307 100644 --- a/tests/test_cpu_manual.py +++ b/tests/test_cpu_manual.py @@ -273,8 +273,8 @@ class SymCPUTest(unittest.TestCase): self.cpu.CF = 1 self.cpu.AF = 1 - a = BitVecConstant(32, 1).Bool() - b = BitVecConstant(32, 0).Bool() + a = BitVecConstant(32, 1) != 0 + b = BitVecConstant(32, 0) != 0 self.cpu.ZF = a self.cpu.SF = b diff --git a/tests/test_state.py b/tests/test_state.py index 87eff6b..b00a6b2 100644 --- a/tests/test_state.py +++ b/tests/test_state.py @@ -60,6 +60,14 @@ class StateTest(unittest.TestCase): self.state.add(expr < 100) solved = self.state.solve_n(expr, 5) self.assertEqual(len(solved), 5) + + def test_policy_one(self): + expr = BitVecVariable(32, 'tmp') + self.state.add(expr > 0) + self.state.add(expr < 100) + solved = self.state.concretize(expr, 'ONE') + self.assertEqual(len(solved), 1) + self.assertIn(solved[0], xrange(100)) def test_state(self): constraints = ConstraintSet() diff --git a/tests/test_unicorn.py b/tests/test_unicorn.py new file mode 100644 index 0000000..2978f49 --- /dev/null +++ b/tests/test_unicorn.py @@ -0,0 +1,1399 @@ +import unittest +import struct +from functools import wraps + +from manticore.core.cpu.arm import Armv7Cpu as Cpu, Mask, Interruption +from manticore.core.cpu.abstractcpu import ConcretizeMemory, ConcretizeRegister +from manticore.core.memory import Memory32, SMemory32 +from manticore.core.executor import State +from manticore.core.smtlib import BitVecVariable, ConstraintSet +from manticore.models import linux +from manticore.utils.emulate import UnicornEmulator + +from capstone.arm import * +from keystone import Ks, KS_ARCH_ARM, KS_MODE_ARM + +ks = Ks(KS_ARCH_ARM, KS_MODE_ARM) + +import logging + +logger = logging.getLogger("ARM_TESTS") + +__doc__ = ''' +Test the Unicorn emulation stub. Armv7UnicornInstructions includes all +semantics from ARM tests to ensure that they match. UnicornConcretization tests +to make sure symbolic values get properly concretized. +''' + +def assemble(asm): + ords = ks.asm(asm)[0] + if not ords: + raise Exception('bad assembly: {}'.format(asm)) + return ''.join(map(chr, ords)) + + +def emulate_next(cpu): + 'Read the next instruction and emulate it with Unicorn ' + instruction = cpu.decode_instruction(cpu.PC) + emu = UnicornEmulator(cpu) + emu.emulate(instruction) + + +def itest(asm): + def instr_dec(assertions_func): + @wraps(assertions_func) + def wrapper(self): + self._setupCpu(asm) + emulate_next(self.cpu) + assertions_func(self) + + return wrapper + + return instr_dec + + +def itest_setregs(*preds): + def instr_dec(custom_func): + @wraps(custom_func) + def wrapper(self): + for p in preds: + dest, src = p.split('=') + + try: + src = int(src, 0) + except: + pass + + self.rf.write(dest.upper(), src) + custom_func(self) + + return wrapper + + return instr_dec + + +def itest_custom(asm): + def instr_dec(custom_func): + @wraps(custom_func) + def wrapper(self): + self._setupCpu(asm) + custom_func(self) + + return wrapper + + return instr_dec + + +class Armv7UnicornInstructions(unittest.TestCase): + ''' + Import all of the tests from ARM, but execute with Unicorn to verify that + all semantics match. + ''' + def setUp(self): + self.cpu = Cpu(Memory32(), 'armv7') + self.mem = self.cpu.memory + self.rf = self.cpu.regfile + + def _setupCpu(self, asm): + self.code = self.mem.mmap(0x1000, 0x1000, 'rwx') + self.data = self.mem.mmap(0xd000, 0x1000, 'rw') + self.stack = self.mem.mmap(0xf000, 0x1000, 'rw') + start = self.code + 4 + self.mem.write(start, assemble(asm)) + self.rf.write('PC', start) + self.rf.write('SP', self.stack + 0x1000) + + def _checkFlagsNZCV(self, n, z, c, v): + self.assertEqual(self.rf.read('APSR_N'), n) + self.assertEqual(self.rf.read('APSR_Z'), z) + self.assertEqual(self.rf.read('APSR_C'), c) + self.assertEqual(self.rf.read('APSR_V'), v) + + # MOV + + @itest("mov r0, 0x0") + def test_mov_imm_min(self): + self.assertEqual(self.rf.read('R0'), 0x0) + + @itest("mov r0, 42") + def test_mov_imm_norm(self): + self.assertEqual(self.rf.read('R0'), 42) + + @itest("mov r0, 0x100") + def test_mov_imm_modified_imm_min(self): + self.assertEqual(self.rf.read('R0'), 0x100) + + @itest("mov r0, 0xff000000") + def test_mov_imm_modified_imm_max(self): + self.assertEqual(self.rf.read('R0'), 0xff000000) + + @itest_custom("mov r0, r1") + def test_mov_immreg(self): + self.rf.write('R1', 0) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R0'), 0) + + @itest_custom("mov r0, r1") + def test_mov_immreg1(self): + self.rf.write('R1', 2 ** 32) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R0'), 0) + + @itest_custom("mov r0, r1") + def test_mov_immreg2(self): + self.rf.write('R1', 0xffffffff) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R0'), 0xffffffff) + + @itest_custom("mov r0, r1") + def test_mov_immreg3(self): + self.rf.write('R1', 42) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R0'), 42) + + # MOVW + + @itest("movw r0, 0") + def test_movw_imm_min(self): + self.assertEqual(self.rf.read('R0'), 0x0) + + @itest("movw r0, 0xffff") + def test_movw_imm_max(self): + self.assertEqual(self.rf.read('R0'), 0xffff) + + # MOVS + + @itest_custom("movs r0, 0") + def test_movs_imm_min(self): + pre_c = self.rf.read('APSR_C') + pre_v = self.rf.read('APSR_V') + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R0'), 0) + self._checkFlagsNZCV(0, 1, pre_c, pre_v) + + @itest_custom("movs r0, 42") + def test_movs_imm_norm(self): + pre_c = self.rf.read('APSR_C') + pre_v = self.rf.read('APSR_V') + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R0'), 42) + self._checkFlagsNZCV(0, 0, pre_c, pre_v) + + @itest_custom("movs r0, 0x100") + def test_movs_imm_modified_imm_min(self): + pre_c = self.rf.read('APSR_C') + pre_v = self.rf.read('APSR_V') + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R0'), 0x100) + self._checkFlagsNZCV(0, 0, pre_c, pre_v) + + @itest_custom("movs r0, 0xff000000") + def test_movs_imm_modified_imm_max(self): + pre_v = self.rf.read('APSR_V') + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R0'), 0xff000000) + self._checkFlagsNZCV(1, 0, 1, pre_v) + + @itest_custom("movs r0, 0x0e000000") + def test_movs_imm_modified_imm_sans_carry(self): + pre_v = self.rf.read('APSR_V') + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R0'), 0x0e000000) + self._checkFlagsNZCV(0, 0, 0, pre_v) + + @itest_custom("movs r0, r1") + def test_movs_reg(self): + self.rf.write('R1', 0) + pre_c = self.rf.read('APSR_C') + pre_v = self.rf.read('APSR_V') + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R0'), 0) + self._checkFlagsNZCV(0, 1, pre_c, pre_v) + + @itest_custom("movs r0, r1") + def test_movs_reg1(self): + self.rf.write('R1', 2 ** 32) + pre_c = self.rf.read('APSR_C') + pre_v = self.rf.read('APSR_V') + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R0'), 0) + self._checkFlagsNZCV(0, 1, pre_c, pre_v) + + @itest_custom("movs r0, r1") + def test_movs_reg2(self): + self.rf.write('R1', 2 ** 32 - 1) + pre_c = self.rf.read('APSR_C') + pre_v = self.rf.read('APSR_V') + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R0'), 2 ** 32 - 1) + self._checkFlagsNZCV(1, 0, pre_c, pre_v) + + @itest_custom("movs r0, r1") + def test_movs_reg3(self): + self.rf.write('R1', 42) + pre_c = self.rf.read('APSR_C') + pre_v = self.rf.read('APSR_V') + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R0'), 42) + self._checkFlagsNZCV(0, 0, pre_c, pre_v) + + # ADD + + @itest_custom("add r3, r1, 55") + def test_add_imm_norm(self): + self.rf.write('R1', 44) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 99) + + @itest_custom("add r3, r1, 0x100") + def test_add_imm_mod_imm_min(self): + self.rf.write('R1', 44) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 44 + 0x100) + + @itest_custom("add r3, r1, 0xff000000") + def test_add_imm_mod_imm_max(self): + self.rf.write('R1', 44) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 44 + 0xff000000) + + @itest_custom("add r3, r1, 0x1000000") + def test_add_imm_carry(self): + self.rf.write('R1', 0xff000001) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 1) + + @itest_custom("add r3, r1, 0x1") + def test_add_imm_overflow(self): + self.rf.write('R1', (2 ** 31 - 1)) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 0x80000000) + + @itest_custom("add r3, r1, r2") + def test_add_reg_norm(self): + self.rf.write('R1', 44) + self.rf.write('R2', 55) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 99) + + @itest_custom("add r3, r1, r2") + def test_add_reg_mod_imm_min(self): + self.rf.write('R1', 44) + self.rf.write('R2', 0x100) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 44 + 0x100) + + @itest_custom("add r3, r1, r2") + def test_add_reg_mod_imm_max(self): + self.rf.write('R1', 44) + self.rf.write('R2', 0xff000000) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 44 + 0xff000000) + + @itest_custom("add r3, r1, r2") + def test_add_reg_carry(self): + self.rf.write('R1', 0x1000000) + self.rf.write('R2', 0xff000001) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 1) + + @itest_custom("add r3, r1, r2") + def test_add_reg_overflow(self): + self.rf.write('R1', (2 ** 31 - 1)) + self.rf.write('R2', 1) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), (1 << 31)) + + @itest_custom("add r3, r1, r2, lsl #3") + def test_add_reg_sft_lsl(self): + self.rf.write('R1', 0x0) + self.rf.write('R2', 0x1) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), (1 << 3)) + + @itest_custom("add r3, r1, r2, lsr #3") + def test_add_reg_sft_lsr(self): + self.rf.write('R1', 0x0) + self.rf.write('R2', 0x8) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), (0x8 >> 3)) + + @itest_custom("add r3, r1, r2, asr #3") + def test_add_reg_sft_asr(self): + self.rf.write('R1', 0x0) + self.rf.write('R2', 0x80000000) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 0xf0000000) + + @itest_custom("add r3, r1, r2, asr #3") + def test_add_reg_sft_asr2(self): + self.rf.write('R1', 0x0) + self.rf.write('R2', 0x40000000) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), (0x40000000 >> 3)) + + @itest_custom("add r3, r1, r2, ror #3") + def test_add_reg_sft_ror_norm(self): + self.rf.write('R1', 0x0) + self.rf.write('R2', 0x8) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 0x1) + + @itest_custom("add r3, r1, r2, ror #3") + def test_add_reg_sft_ror(self): + self.rf.write('R1', 0x0) + self.rf.write('R2', 0x3) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 0x60000000) + + @itest_custom("adc r3, r1, r2") + @itest_setregs("R1=1", "R2=2", "APSR_C=1") + def test_adc_basic(self): + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 4) + + @itest_custom("adc r3, r1, r2, ror #3") + @itest_setregs("R1=1", "R2=2", "APSR_C=1") + def test_adc_reg_sft_ror(self): + self.rf.write('R1', 0x0) + self.rf.write('R2', 0x3) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 0x60000001) + + # TODO what is shifter_carry_out in the manual, A8-291? it gets set to + # Bit[0] presumably, but i have no clue what it is. Not mentioned again in + # manual. + @itest_custom("add r3, r1, r2, rrx") + def test_add_reg_sft_rrx(self): + self.rf.write('APSR_C', 0x0) + self.rf.write('R1', 0x0) + self.rf.write('R2', 2 ** 32 - 1) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 2 ** 31 - 1) + + @itest_custom("add r3, r1, r2, rrx") + def test_add_reg_sft_rrx2(self): + self.rf.write('APSR_C', 0x1) + self.rf.write('R1', 0x0) + self.rf.write('R2', 2 ** 32 - 1) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 2 ** 32 - 1) + + @itest_custom("add r3, r1, r2, lsl r4") + def test_add_reg_sft_lsl_reg(self): + self.rf.write('R1', 0x0) + self.rf.write('R4', 0x3) + self.rf.write('R2', 0x1) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), (1 << 3)) + + @itest_custom("add r3, r1, r2, lsr r4") + def test_add_reg_sft_lsr_reg(self): + self.rf.write('R1', 0x0) + self.rf.write('R4', 0x3) + self.rf.write('R2', 0x8) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), (0x8 >> 3)) + + @itest_custom("add r3, r1, r2, asr r4") + def test_add_reg_sft_asr_reg(self): + self.rf.write('R1', 0x0) + self.rf.write('R4', 0x3) + self.rf.write('R2', 0x80000000) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 0xf0000000) + + @itest_custom("add r3, r1, r2, asr r4") + def test_add_reg_sft_asr2_reg(self): + self.rf.write('R1', 0x0) + self.rf.write('R4', 0x3) + self.rf.write('R2', 0x40000000) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), (0x40000000 >> 3)) + + @itest_custom("add r3, r1, r2, ror r4") + def test_add_reg_sft_ror_norm_reg(self): + self.rf.write('R1', 0x0) + self.rf.write('R4', 0x3) + self.rf.write('R2', 0x8) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 1) + + @itest_custom("add r3, r1, r2, ror r4") + def test_add_reg_sft_ror_reg(self): + self.rf.write('R1', 0x0) + self.rf.write('R4', 0x3) + self.rf.write('R2', 0x3) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 0x60000000) + + @itest_custom("add r3, r1, r2, rrx") + def test_add_reg_sft_rrx_reg(self): + self.rf.write('R1', 0x0) + self.rf.write('APSR_C', 0x0) + self.rf.write('R2', 2 ** 32 - 1) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 2 ** 31 - 1) + + @itest_custom("add r3, r1, r2, rrx") + def test_add_reg_sft_rrx2_reg(self): + self.rf.write('R1', 0x0) + self.rf.write('APSR_C', 0x1) + self.rf.write('R2', 2 ** 32 - 1) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 2 ** 32 - 1) + + # ADDS + + @itest_custom("adds r3, r1, 55") + def test_adds_imm_norm(self): + self.rf.write('R1', 44) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 99) + self._checkFlagsNZCV(0, 0, 0, 0) + + @itest_custom("adds r3, r1, 0x100") + def test_adds_imm_mod_imm_min(self): + self.rf.write('R1', 44) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 44 + 0x100) + self._checkFlagsNZCV(0, 0, 0, 0) + + @itest_custom("adds r3, r1, 0xff000000") + def test_adds_imm_mod_imm_max(self): + self.rf.write('R1', 44) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 44 + 0xff000000) + self._checkFlagsNZCV(1, 0, 0, 0) + + @itest_custom("adds r3, r1, 0x1000000") + def test_adds_imm_carry(self): + self.rf.write('R1', 0xff000001) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 1) + self._checkFlagsNZCV(0, 0, 1, 0) + + @itest_custom("adds r3, r1, 0x80000000") + def test_adds_imm_carry_overflow(self): + self.rf.write('R1', 0x80000001) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 1) + self._checkFlagsNZCV(0, 0, 1, 1) + + @itest_custom("adds r3, r1, 0x1") + def test_adds_imm_overflow(self): + self.rf.write('R1', (2 ** 31 - 1)) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 0x80000000) + self._checkFlagsNZCV(1, 0, 0, 1) + + @itest_custom("adds r3, r3, 0x0") + def test_adds_imm_zf(self): + self.rf.write('R3', 0) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 0) + self._checkFlagsNZCV(0, 1, 0, 0) + + @itest_custom("adds r3, r1, r2") + def test_adds_reg_norm(self): + self.rf.write('R1', 44) + self.rf.write('R2', 55) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 99) + self._checkFlagsNZCV(0, 0, 0, 0) + + @itest_custom("adds r3, r1, r2") + def test_adds_reg_mod_imm_min(self): + self.rf.write('R1', 44) + self.rf.write('R2', 0x100) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 44 + 0x100) + self._checkFlagsNZCV(0, 0, 0, 0) + + @itest_custom("adds r3, r1, r2") + def test_adds_reg_mod_imm_max(self): + self.rf.write('R1', 44) + self.rf.write('R2', 0xff000000) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 44 + 0xff000000) + self._checkFlagsNZCV(1, 0, 0, 0) + + @itest_custom("adds r3, r1, r2") + def test_adds_reg_carry(self): + self.rf.write('R1', 0x1000000) + self.rf.write('R2', 0xff000001) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 1) + self._checkFlagsNZCV(0, 0, 1, 0) + + @itest_custom("adds r3, r1, r2") + def test_adds_reg_overflow(self): + self.rf.write('R1', (2 ** 31 - 1)) + self.rf.write('R2', 1) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), (1 << 31)) + self._checkFlagsNZCV(1, 0, 0, 1) + + @itest_custom("adds r3, r1, r2") + def test_adds_reg_carry_overflow(self): + self.rf.write('R1', 0x80000001) + self.rf.write('R2', 0x80000000) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 1) + self._checkFlagsNZCV(0, 0, 1, 1) + + @itest_custom("adds r3, r1, r2") + def test_adds_reg_zf(self): + self.rf.write('R1', 0x0) + self.rf.write('R2', 0x0) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 0) + self._checkFlagsNZCV(0, 1, 0, 0) + + @itest_custom("adds r3, r1, r2, asr #3") + def test_adds_reg_sft_asr(self): + self.rf.write('R1', 0x0) + self.rf.write('R2', 0x80000000) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 0xf0000000) + self._checkFlagsNZCV(1, 0, 0, 0) + + @itest_custom("adds r3, r1, r2, asr #3") + def test_adds_reg_sft_asr2(self): + self.rf.write('R1', 0x0) + self.rf.write('R2', 0x40000000) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), (0x40000000 >> 3)) + self._checkFlagsNZCV(0, 0, 0, 0) + + @itest_custom("adds r3, r1, r2, rrx") + def test_adds_reg_sft_rrx(self): + self.rf.write('APSR_C', 0x0) + self.rf.write('R1', 0x0) + self.rf.write('R2', 2 ** 32 - 1) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 2 ** 31 - 1) + self._checkFlagsNZCV(0, 0, 0, 0) + + @itest_custom("adds r3, r1, r2, rrx") + def test_adds_reg_sft_rrx2(self): + self.rf.write('APSR_C', 0x1) + self.rf.write('R1', 0x0) + self.rf.write('R2', 2 ** 32 - 1) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R3'), 2 ** 32 - 1) + self._checkFlagsNZCV(1, 0, 0, 0) + + # LDR imm + + @itest_custom("ldr r1, [sp]") + def test_ldr_imm_off_none(self): + self.cpu.stack_push(42) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R1'), 42) + + @itest_custom("ldr r1, [sp, #4]") + def test_ldr_imm_off_pos(self): + self.cpu.stack_push(42) + self.cpu.stack_push(41) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R1'), 42) + + @itest_custom("ldr r1, [sp, #-4]") + def test_ldr_imm_off_neg(self): + self.cpu.stack_push(42) + self.cpu.stack_push(41) + self.cpu.STACK += 4 + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R1'), 41) + + @itest_custom("ldr r1, [sp, #4]!") + def test_ldr_imm_preind_pos(self): + self.cpu.stack_push(42) + self.cpu.stack_push(41) + pre_stack = self.cpu.STACK + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R1'), 42) + self.assertEqual(self.rf.read('SP'), pre_stack + 4) + + @itest_custom("ldr r1, [sp, #-4]!") + def test_ldr_imm_preind_neg(self): + self.cpu.stack_push(42) + self.cpu.stack_push(41) + self.cpu.STACK += 4 + pre_stack = self.cpu.STACK + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R1'), 41) + self.assertEqual(self.rf.read('SP'), pre_stack - 4) + + @itest_custom("ldr r1, [sp], #5") + def test_ldr_imm_postind_pos(self): + self.cpu.stack_push(42) + pre_stack = self.cpu.STACK + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R1'), 42) + self.assertEqual(self.rf.read('SP'), pre_stack + 5) + + @itest_custom("ldr r1, [sp], #-5") + def test_ldr_imm_postind_neg(self): + self.cpu.stack_push(42) + pre_stack = self.cpu.STACK + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R1'), 42) + self.assertEqual(self.rf.read('SP'), pre_stack - 5) + + # LDR reg + + @itest_custom("ldr r1, [sp, r2]") + def test_ldr_reg_off(self): + self.cpu.regfile.write('R2', 4) + self.cpu.stack_push(42) + self.cpu.stack_push(48) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R1'), 42) + + @itest_custom("ldr r1, [sp, -r2]") + def test_ldr_reg_off_neg(self): + self.cpu.regfile.write('R2', 4) + self.cpu.stack_push(42) + self.cpu.stack_push(48) + self.cpu.STACK += 4 + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R1'), 48) + + @itest_custom("ldr r1, [sp, r2, lsl #3]") + def test_ldr_reg_off_shift(self): + self.cpu.regfile.write('R2', 1) + self.cpu.stack_push(42) + self.cpu.stack_push(48) + self.cpu.stack_push(40) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R1'), 42) + + @itest_custom("ldr r1, [sp, -r2, lsl #3]") + def test_ldr_reg_off_neg_shift(self): + self.cpu.regfile.write('R2', 1) + self.cpu.stack_push(42) + self.cpu.stack_push(48) + self.cpu.STACK += 8 + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R1'), 48) + + @itest_custom("ldr r1, [sp, r2]!") + def test_ldr_reg_preind(self): + self.cpu.regfile.write('R2', 4) + self.cpu.stack_push(42) + self.cpu.stack_push(48) + pre_stack = self.cpu.STACK + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R1'), 42) + self.assertEqual(self.rf.read('SP'), pre_stack + 4) + + @itest_custom("ldr r1, [sp, -r2, lsl #3]!") + def test_ldr_reg_preind_shift(self): + self.cpu.regfile.write('R2', 1) + self.cpu.stack_push(42) + self.cpu.stack_push(48) + self.cpu.STACK += 8 + pre_stack = self.cpu.STACK + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R1'), 48) + self.assertEqual(self.rf.read('SP'), pre_stack - 8) + + @itest_custom("ldr r1, [sp], r2") + def test_ldr_reg_postind(self): + self.cpu.regfile.write('R2', 4) + self.cpu.stack_push(42) + pre_stack = self.cpu.STACK + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R1'), 42) + self.assertEqual(self.rf.read('SP'), pre_stack + 4) + + @itest_custom("ldr r1, [sp], -r2, lsl #3") + def test_ldr_reg_postind_neg_shift(self): + self.cpu.regfile.write('R2', 1) + self.cpu.stack_push(42) + pre_stack = self.cpu.STACK + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R1'), 42) + self.assertEqual(self.rf.read('SP'), pre_stack - 8) + + @itest_custom("pop {r1}") + def test_pop_one_reg(self): + self.cpu.stack_push(0x55) + pre_stack = self.cpu.STACK + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R1'), 0x55) + self.assertEqual(self.rf.read('SP'), pre_stack + 4) + + @itest_custom("pop {r1, r2, r3}") + def test_pop_multops(self): + vals = [0x01, 0x55, 0xAA] + for v in vals: + self.cpu.stack_push(v) + emulate_next(self.cpu) + self.assertEqual(self.rf.read('R1'), 0xAA) + self.assertEqual(self.rf.read('R2'), 0x55) + self.assertEqual(self.rf.read('R3'), 0x01) + + @itest_custom("push {r1}") + @itest_setregs("R1=3") + def test_push_one_reg(self): + emulate_next(self.cpu) + self.assertItemsEqual(self.cpu.stack_peek(), struct.pack('> 3) + + @itest_setregs("R0=0x1000") + @itest("lsr r0, r0, #3") + def test_lsr_reg_imm(self): + self.assertEqual(self.rf.read('R0'), 0x1000 >> 3) + + @itest_setregs("R2=29") + @itest("RSB r2, r2, #31") + def test_rsb_imm(self): + # Diverging instruction from trace + self.assertEqual(self.rf.read('R2'), 2) + + def test_flag_state_continuity(self): + '''If an instruction only partially updates flags, cpu.setFlags should + ensure unupdated flags are preserved. + + For example: + r1 = 2**31 - 1 + add r2, r1, 0x1 // overflow = 1 + mov r1, 1 + mov r3, 0 + tst r3, r1 // does not change overflow flag + // ovf should still be 1 + ''' + + self.rf.write('R1', (2 ** 31 - 1)) + self._setupCpu("adds r2, r1, #0x1") + emulate_next(self.cpu) + self.rf.write('R1', 1) + self.rf.write('R3', 0) + self.mem.write(self.cpu.PC, assemble("tst r3, r1")) + emulate_next(self.cpu) + self._checkFlagsNZCV(0, 1, 0, 1) + + @itest_setregs("R1=30", "R2=10") + @itest("MUL R1, R2") + def test_mul_reg(self): + self.assertEqual(self.rf.read('R1'), 300) + + @itest_setregs("R1=30", "R2=10") + @itest("MUL R3, R1, R2") + def test_mul_reg_w_dest(self): + self.assertEqual(self.rf.read('R3'), 300) + + @itest_setregs("R2=10", "R3=15", "R4=7") + @itest("MLA R1, R2, R3, R4") + def test_mla_reg(self): + self.assertEqual(self.rf.read('R1'), 157) + + @itest_setregs("R1=0xFF") + @itest("BIC R2, R1, #0x10") + def test_bic_reg_imm(self): + self.assertEqual(self.rf.read('R2'), 0xEF) + + @itest_setregs("R1=0x1008") + @itest("BLX R1") + def test_blx_reg(self): + self.assertEqual(self.rf.read('PC'), 0x1008) + self.assertEqual(self.rf.read('LR'), 0x1008) + + @itest_setregs("R1=0x1009") + @itest("BLX R1") + def test_blx_reg_thumb(self): + self.assertEqual(self.rf.read('PC'), 0x1008) + self.assertEqual(self.rf.read('LR'), 0x1008) + + @itest_setregs("R1=0xffffffff", "R2=2") + @itest("UMULLS R1, R2, R1, R2") + def test_umull(self): + mul = 0xffffffff * 2 + pre_c = self.rf.read('APSR_C') + pre_v = self.rf.read('APSR_V') + self.assertEqual(self.rf.read('R1'), mul & Mask(32)) + self.assertEqual(self.rf.read('R2'), mul >> 32) + self._checkFlagsNZCV(0, 0, pre_c, pre_v) + + @itest_setregs("R1=2", "R2=2") + @itest("UMULLS R1, R2, R1, R2") + def test_umull_still32(self): + mul = 2 * 2 + pre_c = self.rf.read('APSR_C') + pre_v = self.rf.read('APSR_V') + self.assertEqual(self.rf.read('R1'), mul & Mask(32)) + self.assertEqual(self.rf.read('R2'), mul >> 32) + self._checkFlagsNZCV(0, 0, pre_c, pre_v) + + @itest_setregs("R1=0xfffffffe", "R2=0xfffffffe") + @itest("UMULLS R1, R2, R1, R2") + def test_umull_max(self): + mul = 0xfffffffe ** 2 + pre_c = self.rf.read('APSR_C') + pre_v = self.rf.read('APSR_V') + self.assertEqual(self.rf.read('R1'), mul & Mask(32)) + self.assertEqual(self.rf.read('R2'), mul >> 32) + self._checkFlagsNZCV(1, 0, pre_c, pre_v) + + @itest_setregs("R1=3", "R2=0") + @itest("UMULLS R1, R2, R1, R2") + def test_umull_z(self): + mul = 3 * 0 + pre_c = self.rf.read('APSR_C') + pre_v = self.rf.read('APSR_V') + self.assertEqual(self.rf.read('R1'), mul & Mask(32)) + self.assertEqual(self.rf.read('R2'), (mul >> 32) & Mask(32)) + self._checkFlagsNZCV(0, 1, pre_c, pre_v) + + +class UnicornConcretization(unittest.TestCase): + ''' + Test the ability of Unicorn-based emulation to correctly request certain + locations to be concretized. + ''' + cpu = None + state = None + + @classmethod + def get_state(cls): + if cls.cpu is None: + constraints = ConstraintSet() + model = linux.SLinux(constraints, '/bin/ls', argv=[], envp=[]) + cls.state = State(constraints, model) + cls.cpu = model._mk_proc('armv7') + return (cls.cpu, cls.state) + + + def setUp(self): + self.cpu, self.state = self.__class__.get_state() + self.mem = self.cpu.memory + self.rf = self.cpu.regfile + + def _setupCpu(self, asm): + self.code = self.mem.mmap(0x1000, 0x1000, 'rwx') + self.data = self.mem.mmap(0xd000, 0x1000, 'rw') + self.stack = self.mem.mmap(0xf000, 0x1000, 'rw') + start = self.code + 4 + self.mem.write(start, assemble(asm)) + self.rf.write('PC', start) + self.rf.write('SP', self.stack + 0xffc) + + @itest_custom("ldr r1, [sp]") + def test_load_symbolic(self): + self.cpu.STACK -= 4 + + val = self.state.symbolicate_buffer('++++', wildcard='+') + self.cpu.write_bytes(self.rf.read('SP'), val) + with self.assertRaises(ConcretizeMemory) as e: + emulate_next(self.cpu) + + @itest_custom("ldr r1, [sp]") + def test_load_symbolic_correct_address(self): + self.cpu.STACK -= 4 + + val = self.state.symbolicate_buffer('++++', wildcard='+') + sp = self.rf.read('SP') + self.cpu.write_bytes(sp, val) + try: + emulate_next(self.cpu) + # Make sure we raise + self.assertFalse(True) + except ConcretizeMemory as e: + sp = self.rf.read('SP') + self.assertTrue(e.address in range(sp, sp+len(val))) + + @itest_custom("mov r1, r2") + def test_load_symbolic_from_register(self): + val = self.state.new_symbolic_value(32) + self.rf.write('R2', val) + + try: + emulate_next(self.cpu) + # Make sure we raise + self.assertFalse(True) + except ConcretizeRegister as e: + self.assertEqual(e.reg_name, 'R2') + + def test_arm_constant(self): + self.code = self.mem.mmap(0x1000, 0x1000, 'rwx') + self.data = self.mem.mmap(0xd000, 0x1000, 'rw') + self.stack = self.mem.mmap(0xf000, 0x1000, 'rw') + start = self.code + 4 + constant = 0x42424242 + + asm = ''' + ldr r0, [pc, #-4] + ''' + + code = assemble(asm) + code += '\x78\x56\x34\x12' + + self.mem.write(start, code) + + self.rf.write('PC', start) + self.rf.write('SP', self.stack + 0x1000) + + emulate_next(self.cpu) + + self.assertEqual(self.rf.read('PC'), self.code+8) + self.assertEqual(self.rf.read('R0'), 0x12345678) +