Dev arm regfile refactor (#57)

* RegisterFile refactor everywhere

* Update arm_rf tests

* Refactor Operand in both x86 and arm

* Add APSR support (#53)

* Add APSR support

* #issuecomment-284826572

* APSR tests

* cspr to aspr

* RegisterFile refactor everywhere

* Update arm_rf tests

* Refactor Operand in both x86 and arm

* Rebased and NotImplementedError fix

* ARM register initialization compressed

* Fix merging bugs

* Assimilate PR comments

* Assimilate PR comments2

* The new style. future is now

* WIP

* Improve quick start ,add asciinema (#58)

* Towards a helloworld world

* Rm requirements.txt (#56)

* Print workspace dir by default (#55)

* Print workspace dir by default

* Change initial cli output

* refactor or or or

* WIP

* Add arm rf testcase

* ARM aliases fix

* debug print removed

* fix tests

* Remove unicorn script from travis build (#64)

* remove unicorn script from travis build

* remove unicorn script entirely

* Rename test -> tests (#66)

* rename test -> tests

* re-add ignored tests

* Switch instructions to prefer virtualenv (#65)

* switch instructions to prefer virtualenv

* document use of virtualenvwrapper

* Make cpuid more forgiving (#67)

* Make cpuid more forgiving

* error() to warning()

* Add CPUID EAX=0xd ECX=0,1

* Let logger handle the format string iff needed

* to hex

* Rm unused files (#76)

* Move state into own file (#75)

* Move State from executor.py

* Fix import

* relative import

* Rm unnecessary code (#80)

* Add example crackme (#77)

* add example crackme

* make filename and directory structure more accurate

* Fix state.branches (#74)

* init

* rm old code

pretty sure it's wrong

* Clean

* rm

* Rename record_fork, move to ConcretizeRegister exception handler

It doesn't need to be in fork() because it's only relevant for when
we fork due to PC.

* Add test for record_branches

* Add back record_branches which got removed in rebase

Rebase went oddly because State got moved to a new file

* unnecesary tuple()

* necesary tuple

* remove unnecessary keys()

* Use OrderedDict (#84)

It can be useful to know elements in `branches` towards the end are
more recently discovered branches

* arm: Fix broken dynamic APSR (#78)

* Refactor Register (#82)

* Fix register read bug

If we store a BitVec, we do NOT want to return bool() of it, which
returns True, which is totally incorrect. We do however, want to return
a symbolic Bool of it, if it is a 1 bit register (flag).

* Improve

* Raise error

* Fix register tests

Changed the interfaces:
- removed nbits param (never used)
- no longer raise AssertionError when overflowing a flag reg, just
  truncate
- rename test funcs to be more descriptive

* rm unused import

* Add symbolic tests

* Rm no longer applicable test

* Add docs (#61)

* Add sphinx-quickstart generated docs

* Add api.rst

* Edit index.rst

* Add autodocumenting Manticore class

* Update Manticore docstring

* Doc verbosity

* Doc hook and add_hook

* More docs

* Add Sphinx dev dep

For building docs

* RegisterFile refactor everywhere

* Update arm_rf tests

* Refactor Operand in both x86 and arm

* RegisterFile refactor everywhere

* Refactor Operand in both x86 and arm

* Rebased and NotImplementedError fix

* ARM register initialization compressed

* Fix merging bugs

* Assimilate PR comments

* Assimilate PR comments2

* The new style. future is now

* WIP

* Towards a helloworld world

* refactor or or or

* WIP

* Add arm rf testcase

* ARM aliases fix

* debug print removed

* fix tests

* unnecesary tuple()

* necesary tuple

* remove unnecessary keys()

* rebased

* Remove test

* https://github.com/trailofbits/manticore/pull/57#pullrequestreview-27971778

* https://github.com/trailofbits/manticore/pull/57#discussion_r107820815, https://github.com/trailofbits/manticore/pull/57#discussion_r107820331

* https://github.com/trailofbits/manticore/pull/57#discussion_r107821090

* https://github.com/trailofbits/manticore/pull/57#discussion_r107821066

* https://github.com/trailofbits/manticore/pull/57#discussion_r107821919

* OOps search and replace

* (invalid) -> None

* The (invalid) -> None

* None vs. invalid

* In armpy we know that STACK is SP so lets try to eliminate cpu.STACK in favor of x.SP

* remove ugly hex

* Removed redundant import and comment
This commit is contained in:
feliam
2017-03-24 14:44:24 -03:00
committed by GitHub
parent a12aae80d1
commit 7dd0879918
6 changed files with 559 additions and 749 deletions
+47 -62
View File
@@ -26,19 +26,16 @@ MU = {
SANE_SIZES = {8, 16, 32, 64, 80, 128, 256}
# This encapsulates how to acccess operands (regs/mem/immediates) for differents cpus
class Operand(object):
__metaclass__ = ABCMeta
def _reg_name(self, reg_id):
return reg_id
class MemSpec(object):
''' Auxiliary class wraps capstone operand 'mem' attribute. This will return register names instead of Ids '''
def __init__(self, parent):
self.parent = parent
segment = property( lambda self: self.parent._reg_name(self.parent.op.mem.segment) )
base = property( lambda self: self.parent._reg_name(self.parent.op.mem.base) )
index = property( lambda self: self.parent._reg_name(self.parent.op.mem.index) )
scale = property( lambda self: self.parent._reg_name(self.parent.op.mem.scale) )
disp = property( lambda self: self.parent._reg_name(self.parent.op.mem.disp) )
scale = property( lambda self: self.parent.op.mem.scale )
disp = property( lambda self: self.parent.op.mem.disp )
def __init__(self, cpu, op, **kwargs):
'''
@@ -50,87 +47,84 @@ class Operand(object):
@param cpu: A Cpu oinstance
@param op: a Capstone operand (eew)
'''
self.cpu=cpu
self.op=op
if op.type == X86_OP_MEM:
self.mem = self.__class__.MemSpec(self)
assert isinstance(cpu, Cpu)
assert isinstance(op, (X86Op, ArmOp))
self.cpu = cpu
self.op = op
self.mem = Operand.MemSpec(self)
def _reg_name(self, reg_id):
''' Translates a capstone register ID into the register name '''
cs_reg_name = self.cpu.instruction.reg_name(reg_id)
if cs_reg_name is None or cs_reg_name.lower() == '(invalid)':
return None
return self.cpu._regfile._alias(cs_reg_name.upper())
def __getattr__(self, name):
return getattr(self.op, name)
@abstractmethod
@property
def size(self):
''' Return bit size of operand '''
raise NotImplementedError
def address(self):
''' On a memory operand it returns the effective address '''
pass
raise NotImplementedError
@abstractmethod
def read(self):
''' It reads the operand value from the registers or memory '''
pass
raise NotImplementedError
@abstractmethod
def write(self, value):
''' It writes the value ofspecific type to the registers or memory '''
pass
raise NotImplementedError
# Basic register file structure not actully need to abstract as it's used only from the cpu implementation
class RegisterFile(object):
def __init__(self, aliases=None):
if aliases is None:
aliases = {}
self._aliases = aliases
''''dict mapping from alias register name ('PC') to actual register name
('RSP'), which can be passed into reg_id()
'''
''''dict mapping from alias register name ('PC') to actual register name ('RIP') '''
def _alias(self, register):
'''Get register canonical alias. ex. PC->RIP or PC->R15 '''
return self._aliases.get(register, register)
#@abstractmethod
def write(self, reg_id, value):
''' Write value to the register reg_id
@param reg_id: a register id. Must be listed on all_registers
def write(self, register, value):
''' Write value to the specified register
@param register: a register id. Must be listed on all_registers
@param value: a value of the expected type
@return the value actually written to the register
'''
pass
#@abstractmethod
def read(self, reg_id):
''' Read value from the register identified by reg_id
@param reg_id: a register id. Must be listed on all_registers
def read(self, register):
''' Read value from specified register
@param register: a register name. Must be listed on all_registers
@return the register value
'''
pass
#@abstractmethod
def reg_name(self, reg_id):
''' Gives a string representation (name) of a register (ID->name)
@param reg_id: a register ID
'''
pass
#@abstractmethod
def reg_id(self, reg_name):
''' Gives the register ID for a string representation of a register (name->ID)
@param reg_name: a string representation of reg_id register'''
pass
@property
def all_registers(self):
''' Lists all possible register names (Including aliases) '''
pass
return tuple(self._aliases)
@property
def canonical_registers(self):
''' List the minimal most beautiful set of registers needed '''
pass
def __contains__(self, reg_id):
def __contains__(self, register):
''' Check for register validity
@param reg_id: a register ID
@param register: a register name
'''
return reg_id in self.all_registers
return self._alias(register) in self.all_registers
############################################################################
# Abstract cpu encapsulating common cpu methods used by models and executor.
class Cpu(object):
@@ -192,43 +186,36 @@ class Cpu(object):
return self._regfile.all_registers
#this operates on names
def write_register(self, name, value):
def write_register(self, register, value):
''' A convenient method to write a register by name (this accepts alias)
@param name a register name as listed in all_registers
@param register a register name as listed in all_registers
@param value a value
@return It will return the written value possibly croped
'''
reg_id = self._regfile.reg_id(name)
return self._regfile.write(reg_id, value)
return self._regfile.write(register, value)
def read_register(self, name):
def read_register(self, register):
''' A convenient method to read a register by name (this accepts alias)
@param name a register name as listed in all_registers
@param register a register name as listed in all_registers
@param value a value
@return It will return the written value possibly croped
'''
reg_id = self._regfile.reg_id(name)
return self._regfile.read(reg_id)
return self._regfile.read(register)
# Pythonic acces to registers and aliases
def __getattr__(self, name):
''' A pythonic version of read_register '''
assert name != '_regfile'
if hasattr(self, '_regfile') and name in self.all_registers:
if hasattr(self, '_regfile') and name in self._regfile:
return self.read_register(name)
raise AttributeError(name)
def __setattr__(self, name, value):
''' A pythonic version of write_register '''
if hasattr(self, '_regfile') and name in self.all_registers:
if hasattr(self, '_regfile') and name in self._regfile:
return self.write_register(name, value)
object.__setattr__(self, name, value)
def getCanonicalRegisters(self):
values = [self.read_register(rname) for rname in self.canonical_registers]
d = dict(zip(self.canonical_registers, values))
return d
#############################
# Memory access
@@ -372,9 +359,7 @@ class Cpu(object):
if logger.level == logging.DEBUG :
for l in str(self).split('\n'):
logger.debug(l)
implementation(*instruction.operands)
self._icount+=1
@abstractmethod
@@ -533,7 +518,7 @@ class InvalidPCException(Exception):
super(InvalidPCException, self).__init__("Trying to execute invalid memory @%08x"%pc)
self.pc=pc
class InstructionNotImplemented(Exception):
class InstructionNotImplementedError(Exception):
''' Exception raised when you try to execute an instruction that is
not yet implemented in the emulator.
Go to cpu.py and add it!
+111 -263
View File
@@ -12,25 +12,6 @@ from bitwise import *
from capstone import *
from capstone.arm import *
from capstone.x86 import *
# no emulator by default
try:
from unicorn import *
from unicorn.x86_const import *
from unicorn.arm_const import *
except:
pass
MU = None
# Custom Constants (avoid conflicts with capstone's arm constants)
ARM_REG_APSR_N = 1000
ARM_REG_APSR_Z = 1001
ARM_REG_APSR_C = 1002
ARM_REG_APSR_V = 1003
import logging
logger = logging.getLogger("CPU")
@@ -70,28 +51,31 @@ class Armv7Operand(Operand):
def __init__(self, cpu, op, **kwargs):
super(Armv7Operand, self).__init__(cpu, op, **kwargs)
@property
def size(self):
assert self.op.type == ARM_OP_REG
if self.op.reg >= ARM_REG_D0 and self.op.reg <= ARM_REG_D31:
return 8
return 64
else:
return 4
#FIXME check other types of operand sizes
return 32
def read(self, nbits=None, withCarry=False):
carry = self.cpu.regfile.read(ARM_REG_APSR_C)
carry = self.cpu.regfile.read('APSR_C')
if self.op.type == ARM_OP_REG:
reg = self.cpu.regfile.read(self.op.reg)
register = self._reg_name(self.op.reg)
value = self.cpu.regfile.read(register)
# XXX This can be an offset of 8, depending on ARM mode
if self.op.reg == ARM_REG_R15:
reg += 4
if register in ('PC', 'R15'):
value += 4
if self.is_shifted():
shift = self.op.shift
reg, carry = self.cpu._Shift(reg, shift.type, shift.value, carry)
value, carry = self.cpu._Shift(value, shift.type, shift.value, carry)
if self.op.subtracted:
reg = -reg
value = -value
if withCarry:
return reg, carry
return reg
return value, carry
return value
elif self.op.type == ARM_OP_IMM:
imm = self.op.imm
if self.op.subtracted:
@@ -110,7 +94,8 @@ class Armv7Operand(Operand):
def write(self, value, nbits=None):
if self.op.type == ARM_OP_REG:
self.cpu.regfile.write(self.op.reg, value)
register = self._reg_name(self.op.reg)
self.cpu.regfile.write(register, value)
elif self.op.type == ARM_OP_MEM:
raise NotImplementedError('need to impl arm store mem')
else:
@@ -120,7 +105,8 @@ class Armv7Operand(Operand):
if self.op.type == ARM_OP_REG:
self.write(value)
elif self.op.type == ARM_OP_MEM:
self.cpu.regfile.write(self.op.mem.base, value)
register = self._reg_name(self.op.mem.base)
self.cpu.regfile.write(register, value)
else:
raise NotImplementedError("writeback Operand unknown type", self.op.type)
@@ -136,29 +122,27 @@ class Armv7Operand(Operand):
def get_mem_offset(self):
assert self.op.type == ARM_OP_MEM
mem = self.op.mem
off = 0
if mem.index:
idx = mem.scale * self.cpu.regfile.read(mem.index)
carry = self.cpu.regfile.read(ARM_REG_APSR_C)
if self.mem.index is not None:
idx = self.mem.scale * self.cpu.regfile.read(self.mem.index)
carry = self.cpu.regfile.read('APSR_C')
if self.is_shifted():
shift = self.op.shift
idx, carry = self.cpu._Shift(idx, shift.type, shift.value, carry)
off = idx
else:
off = mem.disp
off = self.mem.disp
return -off if self.op.subtracted else off
def get_mem_base_addr(self):
assert self.op.type == ARM_OP_MEM
mem = self.op.mem
base = self.cpu.regfile.read(mem.base)
base = self.cpu.regfile.read(self.mem.base)
# If pc is the base, we need to correct for the fact that the ARM
# spec defines PC to point to the current insn + 8, which we are not
# compliant with (we do current insn + 4)
return base+4 if mem.base == ARM_REG_PC else base
return base+4 if self.mem.base in ('PC', 'R15') else base
def _getExpandImmCarry(self, carryIn):
'''Manually compute the carry bit produced by expanding an immediate
@@ -172,90 +156,47 @@ class Armv7Operand(Operand):
class Armv7RegisterFile(RegisterFile):
REGMAP = {
ARM_REG_R0: 0,
ARM_REG_R1: 1,
ARM_REG_R2: 2,
ARM_REG_R3: 3,
ARM_REG_R4: 4,
ARM_REG_R5: 5,
ARM_REG_R6: 6,
ARM_REG_R7: 7,
ARM_REG_R8: 8,
ARM_REG_R9: 9,
ARM_REG_R10: 10,
ARM_REG_R11: 11,
ARM_REG_R12: 12,
ARM_REG_R13: 13, # alias: ARM_REG_SP
ARM_REG_R14: 14, # alias: ARM_REG_LR
ARM_REG_R15: 15, # alias: ARM_REG_PC
ARM_REG_D0: 16,
ARM_REG_D1: 17,
ARM_REG_D2: 18,
ARM_REG_D3: 19,
ARM_REG_D4: 20,
ARM_REG_D5: 21,
ARM_REG_D6: 22,
ARM_REG_D7: 23,
ARM_REG_D8: 24,
ARM_REG_D9: 25,
ARM_REG_D10: 26,
ARM_REG_D11: 27,
ARM_REG_D12: 28,
ARM_REG_D13: 29,
ARM_REG_D14: 30,
ARM_REG_D15: 31,
ARM_REG_D16: 32,
ARM_REG_D17: 33,
ARM_REG_D18: 34,
ARM_REG_D19: 35,
ARM_REG_D20: 36,
ARM_REG_D21: 37,
ARM_REG_D22: 38,
ARM_REG_D23: 39,
ARM_REG_D24: 40,
ARM_REG_D25: 41,
ARM_REG_D26: 42,
ARM_REG_D27: 43,
ARM_REG_D28: 44,
ARM_REG_D29: 45,
ARM_REG_D30: 46,
ARM_REG_D31: 47,
ARM_REG_APSR_N: 48,
ARM_REG_APSR_Z: 49,
ARM_REG_APSR_C: 50,
ARM_REG_APSR_V: 51,
}
def __init__(self):
'''ARM Register file abstraction. GPRs use ints for read/write. APSR
flags allow writes of bool/{1, 0} but always read bools.
'''
super(Armv7RegisterFile, self).__init__( )
self.aliases = { 'STACK': 'SP',
'PC': 'PC', # these three lines are technically unnecessary, but explicit is
'SP': 'SP', # better than implicit
'LR': 'LR', }#)
gpr = [Register(32) for x in xrange(16)]
vec = [Register(64) for x in xrange(32)]
bit_flags = [Register(1) for x in xrange(4)]
self.regs = gpr + vec + bit_flags
super(Armv7RegisterFile, self).__init__({ 'SB':'R9',
'SL':'R10',
'FP':'R11',
'IP': 'R12',
'STACK': 'R13',
'SP': 'R13',
'LR': 'R14',
'PC': 'R15', } )
self._regs = { }
#32 bit registers
for reg_name in ( 'R0', 'R1', 'R2', 'R3', 'R4', 'R5', 'R6', 'R7', 'R8',
'R9', 'R10', 'R11', 'R12', 'R13', 'R14', 'R15' ):
self._regs[reg_name] = Register(32)
#64 bit registers
for reg_name in ( 'D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8',
'D9', 'D10', 'D11', 'D12', 'D13', 'D14', 'D15', 'D16',
'D17', 'D18', 'D19', 'D20', 'D21', 'D22', 'D23', 'D24',
'D25', 'D26', 'D27', 'D28', 'D29', 'D30', 'D31'):
self._regs[reg_name] = Register(64)
#Flags
self._regs['APSR_N'] = Register(1)
self._regs['APSR_Z'] = Register(1)
self._regs['APSR_C'] = Register(1)
self._regs['APSR_V'] = Register(1)
def _read_APSR(self):
N = self.read(ARM_REG_APSR_N)
Z = self.read(ARM_REG_APSR_Z)
C = self.read(ARM_REG_APSR_C)
V = self.read(ARM_REG_APSR_V)
apsr = 0
def make_apsr_flag(flag_expr, offset):
'Helper for constructing an expression for the APSR register'
return Operators.ITEBV(32, flag_expr,
BitVecConstant(32, 1 << offset),
BitVecConstant(32, 0))
apsr = 0
N = self.read('APSR_N')
Z = self.read('APSR_Z')
C = self.read('APSR_C')
V = self.read('APSR_V')
if any(issymbolic(x) for x in [N, Z, C, V]):
apsr = (make_apsr_flag(N, 31) |
make_apsr_flag(Z, 30) |
@@ -268,50 +209,44 @@ class Armv7RegisterFile(RegisterFile):
if V: apsr |= 1 << 28
return apsr
def _write_APSR(self, apsr):
''' Auxiliar function - Writes flags from a full APSR (only 4 msb used) '''
V = Operators.EXTRACT(apsr, 28, 1)
C = Operators.EXTRACT(apsr, 29, 1)
Z = Operators.EXTRACT(apsr, 30, 1)
N = Operators.EXTRACT(apsr, 31, 1)
self.write(ARM_REG_APSR_V, V)
self.write(ARM_REG_APSR_C, C)
self.write(ARM_REG_APSR_Z, Z)
self.write(ARM_REG_APSR_N, N)
self.write('APSR_V', V)
self.write('APSR_C', C)
self.write('APSR_Z', Z)
self.write('APSR_N', N)
def read(self, reg_id):
if reg_id == ARM_REG_APSR:
def read(self, register):
assert register in self
if register == 'APSR':
return self._read_APSR()
return self.regs[self.REGMAP[reg_id]].read()
register = self._alias(register)
return self._regs[register].read()
def write(self, reg_id, val):
if reg_id == ARM_REG_APSR:
return self._write_APSR(val)
reg_offset = self.REGMAP[reg_id]
reg = self.regs[reg_offset]
reg.write(val)
def write(self, register, value):
assert register in self
if register == 'APSR':
return self._write_APSR(value)
register = self._alias(register)
self._regs[register].write(value)
@property
def all_registers(self):
return ('R0','R1','R2','R3','R4','R5','R6','R7','R8','R9','R10','R11','R12','R13','R14','R15','D0','D1','D2',
return super(Armv7RegisterFile, self).all_registers + \
('R0','R1','R2','R3','R4','R5','R6','R7','R8','R9','R10','R11','R12','R13','R14','R15','D0','D1','D2',
'D3','D4','D5','D6','D7','D8','D9','D10','D11','D12','D13','D14','D15','D16','D17','D18','D19','D20',
'D21','D22','D23','D24','D25','D26','D27','D28','D29','D30','D31','APSR_N','APSR_Z','APSR_C','APSR_V') + ('STACK','PC','SP','LR')
'D21','D22','D23','D24','D25','D26','D27','D28','D29','D30','D31','APSR','APSR_N','APSR_Z','APSR_C','APSR_V')
@property
def canonical_registers(self):
return ('R0','R1','R2','R3','R4','R5','R6','R7','R8','R9','R10','R11','R12', 'SP', 'LR', 'PC', 'APSR')
return ('R0','R1','R2','R3','R4','R5','R6','R7','R8','R9','R10','R11','R12','R13','R14','R15','APSR')
def reg_name(self, reg_id):
reg_offset = self.REGMAP[reg_id]
return self.all_registers[reg_offset]
def reg_id(self, reg_name):
reg_name = self.aliases.get(reg_name, reg_name)
return globals()['ARM_REG_'+reg_name]
def __contains__(self, reg_id):
return reg_id in self.all_registers
class Armv7Cpu(Cpu):
'''Note: In this implementation, PC contains address of current
@@ -361,22 +296,6 @@ class Armv7Cpu(Cpu):
logger.info("Emulator wants this regs %r", reg_values)
return reg_values
def stack_get(self):
return self.STACK
def stack_set(self, value):
self.STACK = value
def pc_get(self):
return self.PC
def pc_set(self, value):
self.PC=value
def stack_sub(self, value):
self.STACK -= value
def stack_add(self, value):
self.STACK += value
# Flags that are the result of arithmetic instructions. Unconditionally
# set, but conditionally committed.
@@ -395,7 +314,7 @@ class Armv7Cpu(Cpu):
'''
unupdated_flags = self._last_flags.viewkeys() - flags.viewkeys()
for flag in unupdated_flags:
flag_name = globals()['ARM_REG_APSR_%s'%(flag,)]
flag_name = 'APSR_{}'.format(flag)
self._last_flags[flag] = self.regfile.read(flag_name)
self._last_flags.update(flags)
@@ -404,7 +323,7 @@ class Armv7Cpu(Cpu):
if self.instruction.mnemonic == 'adc':
return
for flag, val in self._last_flags.iteritems():
flag_name = globals()['ARM_REG_APSR_%s'%(flag,)]
flag_name = 'APSR_{}'.format(flag)
self.regfile.write(flag_name, val)
@@ -416,7 +335,8 @@ class Armv7Cpu(Cpu):
if _type in (ARM_SFT_RRX, ARM_SFT_RRX_REG) and amount != 1:
amount = 1
if _type in range(ARM_SFT_ASR_REG, ARM_SFT_RRX_REG + 1):
elif _type in range(ARM_SFT_ASR_REG, ARM_SFT_RRX_REG + 1):
amount = cpu.instruction.reg_name(amount).upper()
amount = Operators.EXTRACT(cpu.regfile.read(amount), 0, 8)
if amount == 0:
@@ -441,29 +361,29 @@ class Armv7Cpu(Cpu):
# TODO add to abstract cpu, and potentially remove stacksub/add from it?
def stack_push(self, data):
if isinstance(data, (int, long)):
self.stack_sub(self.address_bit_size/8)
self.write_int(self.stack_get(), data, self.address_bit_size)
self.SP -= self.address_bit_size/8
self.write_int(self.SP, data, self.address_bit_size)
elif isinstance(data, BitVec):
self.stack_sub(data.size/8)
self.write_int(self.stack_get(), data, data.size)
self.SP -= data.size/8
self.write_int(self.SP, data, data.size)
elif isinstance(data, str):
self.stack_sub(len(data))
self.write(self.stack_get(), data)
self.SP -= len(data)
self.write(self.SP, data)
else:
raise NotImplementedError('unsupported type for stack push data')
return self.stack_get()
return self.SP
def stack_peek(self, nbytes=4):
return self.read(self.stack_get(), nbytes)
return self.read(self.SP, nbytes)
def stack_pop(self, nbytes=4):
# TODO is the distinction between load and read really in the op size?
nbits = nbytes * 8
if nbits == self.address_bit_size:
val = self.read_int(self.stack_get(), nbits)
val = self.read_int(self.SP, nbits)
else:
val = self.read(self.stack_get(), nbytes)
self.stack_add(nbytes)
val = self.read(self.SP, nbytes)
self.SP += nbytes
return val
def read(self, addr, nbytes):
@@ -485,28 +405,6 @@ class Armv7Cpu(Cpu):
return 'ASR'
return OP_NAME_MAP.get(name, name)
def readOperand(self, op):
if op.type == ARM_OP_REG:
return self.regfile.read(op.reg)
elif op.type == ARM_OP_IMM:
return op.imm
elif op.type == ARM_OP_MEM:
raise NotImplementedError('need to impl arm load mem')
else:
raise NotImplementedError("readOperand unknown type", op.type)
def writeOperand(self, op, value):
if op.type == ARM_OP_REG:
self.regfile.write(op.reg, value)
elif op.type == ARM_OP_MEM:
raise NotImplementedError('need to impl arm store mem')
else:
raise NotImplementedError("writeOperand unknown type", op.type)
def getOperandAddress(self, op):
# TODO IMPLEMENT
return -1
def _wrap_operands(self, ops):
return [Armv7Operand(self, op) for op in ops]
@@ -524,10 +422,10 @@ class Armv7Cpu(Cpu):
cpu._force_next = False
return True
C = cpu.regfile.read(ARM_REG_APSR_C)
N = cpu.regfile.read(ARM_REG_APSR_N)
V = cpu.regfile.read(ARM_REG_APSR_V)
Z = cpu.regfile.read(ARM_REG_APSR_Z)
C = cpu.regfile.read('APSR_C')
N = cpu.regfile.read('APSR_N')
V = cpu.regfile.read('APSR_V')
Z = cpu.regfile.read('APSR_Z')
if cc == ARM_CC_AL: ret = True
elif cc == ARM_CC_EQ: ret = Z
@@ -553,58 +451,23 @@ class Armv7Cpu(Cpu):
return ret
def getCanonicalRegisters(cpu):
#TODO: Clean up the way this interacts with the regfile
names = ['r%d'%(i,) for i in range(13)] + ['sp', 'lr', 'pc']
values = [cpu.regfile.regs[i].read() for i in range(16)]
d = dict(zip(names, values))
N = cpu.regfile.read(ARM_REG_APSR_N)
Z = cpu.regfile.read(ARM_REG_APSR_Z)
C = cpu.regfile.read(ARM_REG_APSR_C)
V = cpu.regfile.read(ARM_REG_APSR_V)
cpsr = 0
def make_cpsr_flag(flag_expr, offset):
'Helper for constructing an expression for the CPSR register'
return Operators.ITEBV(cpu.address_bit_size, flag_expr,
BitVecConstant(cpu.address_bit_size, 1 << offset),
BitVecConstant(cpu.address_bit_size, 0))
if any(issymbolic(x) for x in [N, Z, C, V]):
cpsr = (make_cpsr_flag(N, 31) |
make_cpsr_flag(Z, 30) |
make_cpsr_flag(C, 29) |
make_cpsr_flag(V, 28))
else:
if N: cpsr |= 1 << 31
if Z: cpsr |= 1 << 30
if C: cpsr |= 1 << 29
if V: cpsr |= 1 << 28
d['cpsr'] = cpsr
return d
def getSyscallRetReg(self):
return 'r0'
def get_syscall_description(cpu):
# EABI standards:
# syscall # is in R7
# arguments are passed in R0-R6
# retval is passed in R0
index = cpu.regfile.read(ARM_REG_R7)
index = cpu.regfile.read('R7')
arg_indeces = [globals()['ARM_REG_R%d' % i] for i in range(0, 7)]
arg_indeces = ['R{}'.format(i) for i in range(0, 7)]
arguments = [cpu.regfile.read(idx) for idx in arg_indeces]
def writeResult(result, cpu = cpu):
cpu.regfile.write(ARM_REG_R0, result)
cpu.regfile.write('R0', result)
return (index, arguments, writeResult)
def getSyscallResult(self):
return self.regfile.read(ARM_REG_R0)
return self.regfile.read('R0')
@instruction
def MOV(cpu, dest, src):
@@ -707,7 +570,7 @@ class Armv7Cpu(Cpu):
@instruction
def ADC(cpu, dest, op1, op2):
carry = cpu.regfile.read(ARM_REG_APSR_C)
carry = cpu.regfile.read('APSR_C')
result, carry, overflow = cpu._ADD(op1.read(), op2.read(), carry)
dest.write(result)
return result, carry, overflow
@@ -732,7 +595,7 @@ class Armv7Cpu(Cpu):
@instruction
def SBC(cpu, dest, src, add):
carry = cpu.regfile.read(ARM_REG_APSR_C)
carry = cpu.regfile.read('APSR_C')
result, carry, overflow = cpu._ADD(src.read(), ~add.read(), carry)
dest.write(result)
return result, carry, overflow
@@ -749,13 +612,13 @@ class Armv7Cpu(Cpu):
@instruction
def BLE(cpu, dest):
cpu.PC = Operators.ITEBV(cpu.address_bit_size,
cpu.regfile.read(ARM_REG_APSR_Z), dest.read(), cpu.PC)
cpu.regfile.read('APSR_Z'), dest.read(), cpu.PC)
@instruction
def BL(cpu, label):
next_instr_addr = cpu.regfile.read(ARM_REG_PC)
cpu.regfile.write(ARM_REG_LR, next_instr_addr)
cpu.regfile.write(ARM_REG_PC, label.read())
next_instr_addr = cpu.regfile.read('PC')
cpu.regfile.write('LR', next_instr_addr)
cpu.regfile.write('PC', label.read())
@instruction
@@ -765,9 +628,9 @@ class Armv7Cpu(Cpu):
## THUMB and regular modes, so we use the addresses as is. TODO: Handle
## thumb correctly and fix this
target = dest.read()
next_instr_addr = cpu.regfile.read(ARM_REG_PC) #- 2
cpu.regfile.write(ARM_REG_LR, next_instr_addr) # | 1)
cpu.regfile.write(ARM_REG_PC, target & ~1)
next_instr_addr = cpu.regfile.read('PC') #- 2
cpu.regfile.write('LR', next_instr_addr) # | 1)
cpu.regfile.write('PC', target & ~1)
@instruction
def CMP(cpu, reg, cmp):
@@ -776,28 +639,14 @@ class Armv7Cpu(Cpu):
@instruction
def POP(cpu, *regs):
sp = cpu.stack_get()
invalid = 0
# "The SP can only be in the list before ARMv7. ARM deprecates any use
# of ARM instructions that include the SP, and the value of the SP after
# such an instruction is UNKNOWN" (pg A8-537)
valid = lambda r: r.op.type == ARM_OP_REG and r.op.reg != ARM_REG_SP
for reg in regs:
assert valid(reg)
val = cpu.stack_pop(cpu.address_bit_size / 8)
reg.write(val)
@instruction
def PUSH(cpu, *regs):
# ARM deprecates the use of ARM instructions that include the PC in the
# list (pg A8-539)
valid = lambda r: r.op.type == ARM_OP_REG and r.op.reg != ARM_REG_PC
high_to_low_regs = regs[::-1]
for reg in high_to_low_regs:
assert valid(reg)
cpu.stack_push(reg.read())
@@ -927,10 +776,9 @@ class Armv7Cpu(Cpu):
elif insn_id == ARM_INS_LSR:
srtype = ARM_SFT_LSR_REG
carry = cpu.regfile.read(ARM_REG_APSR_C)
carry = cpu.regfile.read('APSR_C')
if rest:
amount_val = rest[0].op.reg
result, carry = cpu._Shift(op.read(), srtype, amount_val, carry)
result, carry = cpu._Shift(op.read(), srtype, rest[0].op.reg, carry)
else:
result, carry = op.read(withCarry=True)
dest.write(result)
@@ -988,9 +836,8 @@ class Armv7Cpu(Cpu):
def _VSTM(cpu, address, *regs):
for reg in regs:
size = reg.size()
cpu.write_int(address, reg.read(), size * 8)
address += size
cpu.write_int(address, reg.read(), reg.size)
address += reg.size/8
return address
@@ -1010,3 +857,4 @@ class Armv7Cpu(Cpu):
@instruction
def STCL(cpu, *operands):
pass
+16 -31
View File
@@ -443,16 +443,11 @@ class AMD64RegFile(RegisterFile):
for reg in ('FPSW', 'FPTAG', 'FPCW'):
self._registers[reg] = 0
self._cache = {}
for name in ('AF', 'CF', 'DF', 'IF', 'OF', 'PF', 'SF', 'ZF'):
self.write(name, False)
def reg_name(self, reg_id):
return reg_id
def reg_id(self, reg_name):
return self._aliases.get(reg_name, reg_name)
@property
def all_registers(self):
return tuple( self._table.keys() +
@@ -462,8 +457,8 @@ class AMD64RegFile(RegisterFile):
def canonical_registers(self):
return self._canonical_registers
def __contains__(self, reg_id):
return reg_id in self.all_registers
def __contains__(self, register):
return register in self.all_registers
def _set_bv(self, register_id, register_size, offset, size, reset, value):
if isinstance(value, (int,long)):
@@ -550,9 +545,8 @@ class AMD64RegFile(RegisterFile):
for flag, offset in self._flags.iteritems():
self.write(flag, Operators.EXTRACT(res, offset, 1))
def write(self, reg_id, value):
name = self.reg_name(reg_id)
def write(self, name, value):
name = self._alias(name)
if name in ('ST0', 'ST1', 'ST2', 'ST3', 'ST4', 'ST5', 'ST6', 'ST7'):
name = 'FP%d' % ((self.read('TOP') + int(name[2]) ) & 7)
@@ -574,15 +568,14 @@ class AMD64RegFile(RegisterFile):
self._update_cache(name, value)
return value
def _update_cache(self,name, value):
def _update_cache(self, name, value):
self._cache[name] = value
for affected in self._affects[name]:
assert affected != name
self._cache.pop(affected, None)
def read(self, reg_id):
name = self.reg_name(reg_id)
def read(self, name):
name = self._alias(name)
if name in ('ST0', 'ST1', 'ST2', 'ST3', 'ST4', 'ST5', 'ST6', 'ST7'):
name = 'FP%d' % ((self.read('TOP') + int(name[2]) ) & 7)
if name in self._cache:
@@ -605,24 +598,16 @@ class AMD64RegFile(RegisterFile):
###########################
# Operand Wrapper
class AMD64Operand(Operand):
def _reg_name(self, reg_id):
if reg_id <= 0 :
return '(invalid)'
return self.cpu.instruction.reg_name(reg_id).upper()
''' This class deals with capstone X86 operands '''
def __init__(self, cpu, op, **kwargs):
super(AMD64Operand, self).__init__(cpu, op, **kwargs)
self.cpu=cpu
self.op=op
if op.type == X86_OP_MEM:
self.mem = AMD64Operand.MemSpec(self)
#################################3
# Operand access
def address(self):
cpu, o = self.cpu, self.op
address = 0
if o.mem.segment != 0:
if self.mem.segment is not None:
seg = self.mem.segment
base, size, ty = cpu.get_descriptor(cpu.read_register(seg))
address += base #todo check limits and perms
@@ -630,18 +615,18 @@ class AMD64Operand(Operand):
#FIXME inspect operand or cpu.instruction and decide
# the correct default segment for instruction
seg = 'DS'
if o.mem.base != 0 and self.mem.base in ['SP', 'ESP', 'EBP']:
if self.mem.base is not None and self.mem.base in ['SP', 'ESP', 'EBP']:
seg = 'SS'
base, size, ty = cpu.get_descriptor(cpu.read_register(seg))
address += base #todo check limits and perms
if o.mem.base != 0:
if self.mem.base is not None:
base = self.mem.base
address += cpu.read_register(base)
if o.mem.index != 0:
if self.mem.index is not None:
index = self.mem.index
address += o.mem.scale*cpu.read_register(index)
if o.mem.disp != 0:
address += o.mem.disp
address += self.mem.scale*cpu.read_register(index)
address += self.mem.disp
return address & ((1<<cpu.address_bit_size)-1)
+11 -9
View File
@@ -16,6 +16,7 @@ from ..core.cpu.arm import *
from ..core.executor import SyscallNotImplemented, ProcessExit
logger = logging.getLogger("MODEL")
class RestartSyscall(Exception):
pass
@@ -1125,10 +1126,10 @@ class Linux(object):
@param path: the "link path id"
@param buf: the buffer where the bytes will be putted.
@param bufsize: the max size for read the link.
@todo: Out eax number of bytes actually sent | EAGAIN | EBADF | EFAULT | EINTR | EINVAL | EIO | ENOSPC | EPIPE
@todo: Out eax number of bytes actually sent | EAGAIN | EBADF | EFAULT | EINTR | errno.EINVAL | EIO | ENOSPC | EPIPE
'''
if bufsize <= 0:
return -EINVAL
return -errno.EINVAL
filename = self._read_string(cpu, path)
data = os.readlink(filename)[:bufsize]
cpu.write_bytes(buf, data)
@@ -1598,23 +1599,24 @@ class Linux(object):
self.awake(procid)
def handleInvalidPC(self, e):
#FIXME THIS IS ARM SPECIFIC
cpu = self.current
if cpu.PC == self.ARM_GET_TLS:
if hasattr(self, 'tls_value'):
cpu.regfile.write(ARM_REG_R0, self.tls_value)
cpu.regfile.write('R0', self.tls_value)
elif cpu.PC == self.ARM_CMPXCHG:
oldval = cpu.regfile.read(ARM_REG_R0)
newval = cpu.regfile.read(ARM_REG_R1)
ptr = cpu.regfile.read(ARM_REG_R2)
oldval = cpu.regfile.read('R0')
newval = cpu.regfile.read('R1')
ptr = cpu.regfile.read('R2')
existing = cpu.read_int(ptr, cpu.address_bit_size)
ret = 1
if existing == oldval:
ret = 0
cpu.regfile.write(ARM_REG_APSR_C, 1)
cpu.regfile.write('APSR_C', 1)
cpu.write_int(ptr, newval, cpu.address_bit_size)
cpu.regfile.write(ARM_REG_R0, ret)
cpu.regfile.write('R0', ret)
elif cpu.PC == self.ARM_MEM_BARRIER:
# Apply any needed memory barrier to preserve consistency with data
# modified manually and __kuser_cmpxchg usage. Nop in our case, just
@@ -1624,7 +1626,7 @@ class Linux(object):
raise e
# Return normally
lr = cpu.regfile.read(ARM_REG_R14) # ARM_REG_LR
lr = cpu.regfile.read('R14') # 'LR'
cpu.PC = lr
+329 -348
View File
File diff suppressed because it is too large Load Diff
+45 -36
View File
@@ -10,57 +10,66 @@ class Armv7RF(unittest.TestCase):
self.r = RF()
def test_init_state(self):
self.assertEqual(self.r.read(ARM_REG_R0), 0)
self.assertEqual(self.r.read('R0'), 0)
def test_write_read(self):
self.r.write(ARM_REG_R0, 1)
self.assertEqual(self.r.read(ARM_REG_R0), 1)
self.r.write('R0', 1)
self.assertEqual(self.r.read('R0'), 1)
def test_write_read_sp(self):
self.r.write(ARM_REG_SP, 1)
self.assertEqual(self.r.read(ARM_REG_SP), 1)
self.r.write('SP', 1)
self.assertEqual(self.r.read('SP'), 1)
def test_flag_wr(self):
self.r.write(ARM_REG_APSR_Z, True)
self.assertEqual(self.r.read(ARM_REG_APSR_Z), True)
self.r.write('APSR_Z', True)
self.assertEqual(self.r.read('APSR_Z'), True)
def test_flag_wr_f(self):
self.r.write(ARM_REG_APSR_Z, False)
self.assertEqual(self.r.read(ARM_REG_APSR_Z), False)
def test_reg_name(self):
self.assertEqual(self.r.reg_name(ARM_REG_R0), 'R0')
self.r.write('APSR_Z', False)
self.assertEqual(self.r.read('APSR_Z'), False)
def test_bad_reg_name(self):
with self.assertRaises(KeyError):
nonexistant_id = 9999
self.r.reg_name(nonexistant_id)
def test_reg_id(self):
self.assertEqual(self.r.reg_id('R0'), ARM_REG_R0)
def test_bad_reg_id(self):
with self.assertRaises(KeyError):
self.r.reg_id('XXX')
with self.assertRaises(AssertionError):
nonexistant_reg = "Pc"
self.r.read(nonexistant_reg)
def test_flag_wr(self):
self.r.write(ARM_REG_APSR, 0xffffffff)
self.assertEqual(self.r.read(ARM_REG_APSR), 0xf0000000) #4 more significant bits used
self.assertEqual(self.r.read(ARM_REG_APSR_V), True)
self.assertEqual(self.r.read(ARM_REG_APSR_C), True)
self.assertEqual(self.r.read(ARM_REG_APSR_Z), True)
self.assertEqual(self.r.read(ARM_REG_APSR_N), True)
self.r.write('APSR', 0xffffffff)
self.assertEqual(self.r.read('APSR'), 0xf0000000) #4 more significant bits used
self.assertEqual(self.r.read('APSR_V'), True)
self.assertEqual(self.r.read('APSR_C'), True)
self.assertEqual(self.r.read('APSR_Z'), True)
self.assertEqual(self.r.read('APSR_N'), True)
self.r.write(ARM_REG_APSR_N, False)
self.assertEqual(self.r.read(ARM_REG_APSR), 0x70000000)
self.r.write('APSR_N', False)
self.assertEqual(self.r.read('APSR'), 0x70000000)
self.r.write(ARM_REG_APSR_Z, False)
self.assertEqual(self.r.read(ARM_REG_APSR), 0x30000000)
self.r.write('APSR_Z', False)
self.assertEqual(self.r.read('APSR'), 0x30000000)
self.r.write(ARM_REG_APSR_C, False)
self.assertEqual(self.r.read(ARM_REG_APSR), 0x10000000)
self.r.write('APSR_C', False)
self.assertEqual(self.r.read('APSR'), 0x10000000)
self.r.write(ARM_REG_APSR_V, False)
self.assertEqual(self.r.read(ARM_REG_APSR), 0x00000000)
self.r.write('APSR_V', False)
self.assertEqual(self.r.read('APSR'), 0x00000000)
def test_register_independence_wr(self):
regs = ( 'R0', 'R1', 'R2', 'R3', 'R4', 'R5', 'R6', 'R7', 'R8',
'R9', 'R10', 'R11', 'R12', 'R13', 'R14', 'R15' )
aliases = {'SB':'R9', 'SL':'R10', 'FP':'R11', 'IP': 'R12', 'STACK': 'R13', 'SP': 'R13', 'LR': 'R14', 'PC': 'R15' }
for j in xrange(16):
for i in xrange(16):
if i == j:
self.r.write(regs[i], 0x41424344)
else:
self.r.write(regs[i], 0)
for a,b in aliases.items():
self.assertEqual(self.r.read(a), self.r.read(b))
for i in xrange(16):
if i == j:
self.assertEqual(self.r.read(regs[i]), 0x41424344 )
else:
self.assertEqual(self.r.read(regs[i]), 0x00000000 )