From 0b850dba406a49dbb388394dd04a36540b33ed8b Mon Sep 17 00:00:00 2001 From: Yan Date: Mon, 3 Apr 2017 16:00:49 -0400 Subject: [PATCH] Make unicorn be pull-based (#97) * Enable simple ARM register concretization for Unicorn * Add canonical_registers property to abstractcpu * cpu to self * Check for regs_access better * Emulate a single instruction * Bypass capstone 3.0.4 arm bug * Dealing with capstone * Temporary disable ASR and remobe BitVec.Bool from test * WIP WIP debug prints WIP WIP * Unicorn fallback working (using unicorn master) * HAck to support unicorn 1.0.0 * WIP * Unicorn hack to handle PC updates * [WIP] do not do anything with this commit; for debugging only * Adding before clean up * emulation more or less works; need to work out more unicorn bugs * clean up emulate() caller code * move hooks to methods; cleanup * Concretize memory when emulating * Re-add Bool() * Update tests to start at offset 4 When an instruction branches to the previous instruction, Unicorn attempts to dereference that memory. We'd like to use unit tests to also make sure Unicorn emulation is in line with our own semantics. If we start all tests at offset 4, we can jump to a previous instruction and not fault when Unicorn dereferences it. * Fix concretization * Clean up test imports; upper-case Cpu * Unicorn tests * Add tests for all the ARM semantics, but make sure they're equivalent on unicorn. * Add a few tests to make sure unicorn correctly concretizes the memory it references * Fix broken import * Add symbolic register tests * Re-introduce the unicorn hack * Add the 'ONE' concretization policy * Rm unused function * Update concretization; add comments * Add ONE policy test * Create a base class for all concretization exceptions * Remove Armv7Cpu._concretize_registers * Check for enabled logging in a more idiomatic way * [wip] intermediate testing commit * Reimplement hooks and execution with unicorn * Add a DMB (mem barrier) instruction; nop * simplify instruction resolution * improve unicorn error handling * explicitly delete emu * Handle ARM helpers inline * map fetched memory * Narrow exception handling * Update DMB docs; make __kuser_dmb match real implementation * Fix typo; add comment; remove extraneous parameter * typos++ --- manticore/core/cpu/abstractcpu.py | 205 ++-- manticore/core/cpu/arm.py | 28 +- manticore/core/cpu/x86.py | 26 - manticore/core/executor.py | 4 +- manticore/core/memory.py | 19 +- manticore/core/smtlib/expression.py | 14 +- manticore/core/state.py | 12 +- manticore/models/linux.py | 125 ++- manticore/utils/emulate.py | 263 +++++ tests/test_armv7cpu.py | 30 +- tests/test_cpu_manual.py | 4 +- tests/test_state.py | 8 + tests/test_unicorn.py | 1399 +++++++++++++++++++++++++++ 13 files changed, 1870 insertions(+), 267 deletions(-) create mode 100644 manticore/utils/emulate.py create mode 100644 tests/test_unicorn.py 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) +