from manticore import Manticore from manticore.core.smtlib import ConstraintSet, Operators, solver, issymbolic, Array, Expression, Constant from manticore.core.smtlib.visitors import arithmetic_simplifier from manticore.platforms import evm from manticore.platforms.evm import pack_msb from manticore.core.state import State import tempfile from subprocess import Popen, PIPE import sha3 import json class EVMContract(object): def __init__(self, address, seth=None, default_caller=None): self._default_caller = default_caller self._seth=seth self._address=address self._hashes = {} self._caller = None self._value = 0 name, source_code, init_bytecode, metadata, metadata_runtime, hashes = self._seth.context['seth']['metadata'][address] for signature in hashes.keys(): func_name = str(signature.split('(')[0]) self._hashes[func_name] = signature, hashes[signature] def __int__(self): return self._address @property def address(self): return self._address def value(self, value): self._value = value return self def caller(self, caller): self._caller = caller return self def __getattribute__(self, name): if not name.startswith('_') and name in self._hashes.keys(): def f(*args, **kwargs): caller = kwargs.get('caller', self._caller) value = kwargs.get('value', self._value) tx_data = self._seth.make_function_call(str(self._hashes[name][0]),*args) if caller is not None: caller = int(caller) else: caller = self._default_caller self._seth.transaction(caller=caller, address=self._address, value=value, data=tx_data ) self._caller = None self._value = 0 return f else: return object.__getattribute__(self, name) class ManticoreEVM(Manticore): class SByte(): def __init__(self, size=1): self.size=size def __mul__(self, reps): return Symbol(self.size*reps) SCHAR = SByte(1) SUINT = SByte(32) SValue = None @staticmethod def pack_msb(value): return ''.join(ManticoreEVM.serialize_uint(value)) @staticmethod def serialize(value): if isinstance(value, (str,tuple)): return ManticoreEVM.serialize_string(value) if isinstance(value, (list)): return ManticoreEVM.serialize_array(value) if isinstance(value, (int, long)): return ManticoreEVM.serialize_uint(value) if isinstance(value, ManticoreEVM.SByte): return ManticoreEVM.serialize_uint(value.size) + (None,)*value.size + (('\x00',)*(32-(value.size%32))) if value is None: return (None,)*32 @staticmethod def serialize_uint(value, size=32): '''takes an int and packs it into a 32 byte string, msb first''' assert size >=1 bytes = [] for position in range(size): bytes.append( Operators.EXTRACT(value, position*8, 8) ) chars = map(Operators.CHR, bytes) return tuple(reversed(chars)) @staticmethod def serialize_string(value): assert isinstance(value, (str,tuple)) return ManticoreEVM.serialize_uint(len(value)) + tuple(value) + tuple('\x00'*(32-(len(value)%32))) @staticmethod def serialize_array(value): assert isinstance(value, list) serialized = [ManticoreEVM.serialize_uint(len(value))] for item in value: serialized.append(ManticoreEVM.serialize(item)) return reduce(lambda x,y: x+y, serialized) @staticmethod def make_function_id( method_name): s = sha3.keccak_256() s.update(method_name) return s.hexdigest()[:8].decode('hex') @staticmethod def make_function_arguments(*args): if len(args) == 0: return () args = list(args) for i in range(len(args)): if isinstance(args[i], EVMContract): args[i] = int(args[i]) result = [] dynamic_args = [] dynamic_offset = 32*len(args) for arg in args: if isinstance(arg, (list, tuple, str, ManticoreEVM.SByte)): result.append(ManticoreEVM.serialize(dynamic_offset)) serialized_arg = ManticoreEVM.serialize(arg) dynamic_args.append(serialized_arg) assert len(serialized_arg)%32 ==0 dynamic_offset += len(serialized_arg) else: result.append(ManticoreEVM.serialize(arg)) for arg in dynamic_args: result.append(arg) return reduce(lambda x,y: x+y, result) @staticmethod def make_function_call(method_name, *args): function_id = ManticoreEVM.make_function_id(method_name) def check_bitsize(value, size): if isinstance(value, BitVec): return value.size==size return (value & ~((1<" raise NotImplemented print "TRANSACTION ", tx_num, '-', ty try: md_name, md_source_code, md_init_bytecode, md_metadata, md_metadata_runtime, md_hashes= self.context['seth']['metadata'][address] except: md_name, md_source_code, md_init_bytecode, md_metadata, md_metadata_runtime, md_hashes = None,None,None,None,None,None print '\t From: 0x%x'%x(caller) print '\t To: 0x%x'%x(address) print '\t Value: %d wei'%x(value) xdata = x(data).encode('hex') print '\t Data:', xdata if ty == 'CALL': print '\t Function: ', done = False rhashes = dict((hsh, signature) for signature, hsh in md_hashes.iteritems()) try: signature = rhashes.get(xdata[:8], '{fallback}()') done = True func_name = signature.split('(')[0] print func_name,'(', types = signature.split('(')[1][:-1].split(',') off = 8 for ty in types: if off != 8: print ',', val, off = consume_type(ty, xdata, off) print val, print ')' except Exception,e: print e, xdata def coverage(self, account_address): account_address = int(account_address) #This will just pick one of the running states. #This assumes the code and the accounts are the same in all versions of the world world = self.get_world(self.running_state_ids[0]) seen = self.context['coverage'] #.union( self.context.get('code_data', set())) runtime_bytecode = world.storage[account_address]['code'] class bcolors: HEADER = '\033[95m' OKBLUE = '\033[94m' OKGREEN = '\033[92m' WARNING = '\033[93m' FAIL = '\033[91m' ENDC = '\033[0m' BOLD = '\033[1m' UNDERLINE = '\033[4m' end = None if ''.join(runtime_bytecode[-44: -34]) =='\x00\xa1\x65\x62\x7a\x7a\x72\x30\x58\x20' \ and ''.join(runtime_bytecode[-2:]) =='\x00\x29': end = -9-33-2 output = '' offset = 0 count = 0 total = 0 for i in evm.EVMDecoder.decode_all(runtime_bytecode[:end]) : if (account_address, offset) in seen: output += bcolors.OKGREEN output += "** 0x%04x %s\n"%(offset, i) output += bcolors.ENDC count += 1 else: output += " 0x%04x %s\n"%(offset, i) total += 1 offset += i.size output += "Total assembler lines: %d\n"% total output += "Total assembler lines visited: %d\n"% count output += "Coverage: %2.2f%%\n"% (count*100.0/total) return output def symbolic_sha3(self, state, data, known_hashes): with self.locked_context('known_sha3', set) as known_sha3: state.platform._sha3.update(known_sha3) def concrete_sha3(self, state, buf, value): with self.locked_context('known_sha3', set) as known_sha3: known_sha3.add((buf,value))