def CALLDATALOAD(self, gstate, index): logging.debug('CALLDATA index:' + str(index)) if gstate.environment.calldata is None: raise SVMRuntimeError('CALLDATA is not set') index_concrete = svm_utils.is_bv_concrete(index) if gstate.environment.calldata_type == CalldataType.UNDEFINED: data_bytes = [] # for i in range(32): # label = 'calldata_{}_{}'.format(wstate.gen, i + svm_utils.get_concrete_int(index)) # data_bytes.append(z3.BitVec(label, 8)) # data = z3.Concat(data_bytes) if index_concrete: data = self.svm.sym_bv_generator.get_sym_bitvec(constraints.ConstraintType.CALLDATA, gstate.wstate.gen, index=svm_utils.get_concrete_int(index)) gstate.mstate.stack.append(data) else: data = self.svm.sym_bv_generator.get_sym_bitvec(constraints.ConstraintType.CALLDATA, gstate.wstate.gen, unique=True) gstate.mstate.stack.append(data) elif gstate.environment.calldata_type == CalldataType.DEFINED: assert gstate.environment.calldata.sort().name() == 'bv', 'CALLDATA sort mismatch' index = svm_utils.get_concrete_int(z3.simplify(index)) calldatasize = gstate.environment.calldata.size() assert index < calldatasize calldata_start = calldatasize-index*8 calldata_stop = max(0, calldata_start - 256) calldata = z3.Extract(calldata_start-1, calldata_stop, gstate.environment.calldata) calldata = svm_utils.zpad_bv_left(calldata, 256) gstate.mstate.stack.append(calldata) else: raise SVMRuntimeError('Unknown calldata type')
def CREATE(self, gstate, balance, offset, length): new_gstates = [] active_address = gstate.environment.active_address active_account = gstate.wstate.address_to_account[active_address] if gstate.wstate.gen != 0: raise SVMRuntimeError('Dynamic contract creation is not supported') offset = svm_utils.get_concrete_int(offset) length = svm_utils.get_concrete_int(length) create_contract_bytes = [] concrete_prefix_bytes = [] concrete_prefix_stop = False for index in range(0, length): memory_byte = gstate.mstate.memory_dict[index+offset] memory_byte = svm_utils.get_concrete_int(memory_byte) if svm_utils.is_bv_concrete(memory_byte) else memory_byte create_contract_bytes.append(memory_byte) concrete_prefix_stop = concrete_prefix_stop or not isinstance(memory_byte, int) if not concrete_prefix_stop: concrete_prefix_bytes.append(memory_byte) found_swarmhashes = asm.find_swarmhashes(bytes(concrete_prefix_bytes)) if not len(found_swarmhashes): raise SVMRuntimeError('CREATE found no swarmhashes in bytecode') create_contract = self.svm.swarm_hash_tuple_to_contract[tuple(found_swarmhashes)] create_address = utils.get_next_contract_address() create_account = Account(create_address, create_contract, balance=balance) logging.debug(f'CREATE created contract {create_account.contract.name}') create_enviroment = Environment(active_address=create_address, sender=z3.BitVecVal(active_account.address, 256), calldata=[], gasprice=gstate.environment.gasprice, callvalue=balance, origin=gstate.environment.origin, calldata_type=CalldataType.UNDEFINED, disassembly=create_account.contract.creation_disassembly, runtime_bytecode_bytes=create_contract_bytes, timestamp=gstate.environment.timestamp) child_wstate = copy(gstate.wstate) child_wstate.address_to_account[create_address] = create_account create_gstate = GlobalState(child_wstate, create_enviroment) create_gstate.has_storage_changed = True intermediate_gstates = self.execute_gstate(create_gstate) if len(intermediate_gstates) == 0: raise SVMRuntimeError('CREATE has no feasible blocks') for new_gstate in intermediate_gstates: new_gstate.mstate = deepcopy(gstate.mstate) new_gstate.mstate.stack.append(z3.BitVecVal(create_address, 256)) new_gstate.pc_addr_to_depth = copy(gstate.pc_addr_to_depth) new_gstate.mstate.pc += 1 new_gstate.halt = False new_gstate.environment = gstate.environment new_gstates.extend(self.execute_gstate(new_gstate)) logging.debug(f'returning FROM CREATE contract {create_account.contract.name}') gstate.halt = True return new_gstates
def JUMP(self, gstate, jump_addr): if not svm_utils.is_bv_concrete(jump_addr): if svm_utils.check_wstate_reachable(gstate.wstate, 100): logging.warning('JUMP to invalid address') return # if gstate.pc_addr_to_depth.setdefault((gstate.mstate.pc, jump_addr), 0) < self.svm.max_jump_depth: jump_addr = svm_utils.get_concrete_int(jump_addr) instr_idx = svm_utils.get_instruction_index(gstate.environment.disassembly.instruction_list, jump_addr) if instr_idx is None: raise SVMRuntimeError('JUMP to invalid address') dest_opcode = gstate.environment.disassembly.instruction_list[instr_idx]['opcode'] if dest_opcode != 'JUMPDEST': raise SVMRuntimeError('JUMP to invalid address') gstate.mstate.pc = instr_idx return self.execute_gstate(gstate)
def CALLDATACOPY(self, gstate, dest_offset, offset, length): length_concrete = svm_utils.is_bv_concrete(length) if not length_concrete: logging.warning('Symbolic calldata size') length = z3.BitVecVal(64, 256) if gstate.environment.calldata_type == CalldataType.UNDEFINED: length = svm_utils.get_concrete_int(length) if svm_utils.is_bv_concrete(offset): offset = svm_utils.get_concrete_int(offset) for i in range(length): data_word = self.svm.sym_bv_generator.get_sym_bitvec(constraints.ConstraintType.CALLDATA, gstate.wstate.gen, index=offset+(i//32)) slot = i % 32 data_bytes = svm_utils.split_bv_into_bytes(data_word) data = data_bytes[slot] gstate.mstate.memory = z3.Store(gstate.mstate.memory, dest_offset+i, data) else: for i in range(length): data = self.svm.sym_bv_generator.get_sym_bitvec(constraints.ConstraintType.CALLDATA, gstate.wstate.gen, unique=True, bv_size=8) gstate.mstate.memory = z3.Store(gstate.mstate.memory, dest_offset+i, data) elif gstate.environment.calldata_type == CalldataType.DEFINED: length = svm_utils.get_concrete_int(length) offset_concrete = svm_utils.is_bv_concrete(offset) calldata_bytes = svm_utils.split_bv_into_bytes(gstate.environment.calldata) offset_concrete = svm_utils.is_bv_concrete(offset) for i in range(length): gstate.mstate.memory = z3.Store(gstate.mstate.memory, dest_offset+i, calldata_bytes[offset_concrete+i]) else: raise SVMRuntimeError('Unknown calldata type')
def SIGNEXTEND(self, gstate, a, b): if svm_utils.is_bv_concrete(a) and svm_utils.is_bv_concrete(b): a = svm_utils.get_concrete_int(a) b = svm_utils.get_concrete_int(b) if a <= 31: testbit = a * 8 + 7 if b & (1 << testbit): gstate.mstate.stack.append(b | (TT256 - (1 << testbit))) else: gstate.mstate.stack.append(b & ((1 << testbit) - 1)) else: gstate.mstate.stack.append(b) else: raise SVMRuntimeError('SIGNEXTEND error')
def CALLDATASIZE(self, gstate): active_address = gstate.environment.active_address active_account = gstate.wstate.address_to_account[active_address] if gstate.environment.calldata_type == CalldataType.UNDEFINED: gstate.mstate.stack.append(self.svm.sym_bv_generator.get_sym_bitvec(constraints.ConstraintType.CALLDATASIZE, gstate.wstate.gen, acc=active_account.id)) elif gstate.environment.calldata_type == CalldataType.DEFINED: if gstate.environment.calldata is not None: gstate.mstate.stack.append(z3.BitVecVal(gstate.environment.calldata.size() // 8, 256)) else: gstate.mstate.stack.append(z3.BitVecVal(0, 256)) else: raise SVMRuntimeError('Unknown calldata type')
def evaluate(self, gstate): stack_len_start = len(gstate.mstate.stack) self.pre_evaluate(gstate) if gstate.halt: return instr = gstate.environment.disassembly.instruction_list[gstate.mstate.pc] instr_address = instr['address'] op = instr['opcode'] match = re.match(r'^(PUSH|DUP|LOG|SWAP)\d{1,2}', op) op = match.group(1) if match else op eval_func = getattr(self, op, None) if eval_func is None: raise SVMRuntimeError(f'op evaluator not found: {op}') active_account = gstate.wstate.address_to_account[gstate.environment.active_address] current_func = '?' if gstate.wstate.trace is None else gstate.wstate.trace arg = instr.get('argument', '') arg = (arg[0:10] + '..') if len(arg) > 12 else arg.ljust(12) logging.debug(f'{BColors.BLUE}{BColors.BOLD}OP{BColors.ENDC} ' f'{op.ljust(12)}\t' f'{arg},\t' f'addr={instr_address},\t' f'pc={gstate.mstate.pc},\t' f'contract={active_account.contract.name}\t' f'func={current_func}\t' f'sender={gstate.environment.sender}') arglist = inspect.getargspec(eval_func).args try: stack_args = [gstate.mstate.stack.pop() for arg in arglist[2:]] res = eval_func(gstate, *stack_args) gstate.mstate.pc += 1 stack_len_stop = len(gstate.mstate.stack) self.op_to_stack_len[op] = (stack_len_stop - stack_len_start) return res except Exception as e: s = z3.Solver() s.add(gstate.wstate.constraints) if s.check() == z3.sat: raise e
def DELEGATECALL(self, gstate, gas, to, meminstart, meminsize, outmemstart, memoutsize): value = 0 raise SVMRuntimeError('DELEGATECALL not implemented')
def CALLCODE(self, gstate, gas, to, value, meminstart, meminsize, outmemstart, memoutsize): raise SVMRuntimeError('CALLCODE not implemented')
def JUMPI(self, gstate, jump_addr, condition): new_gstates = [] active_address = gstate.environment.active_address active_account = gstate.wstate.address_to_account[active_address] mstate = gstate.mstate jump_addr = svm_utils.get_concrete_int(jump_addr) function_entry = False gstate.jump_count += 1 if (gstate.jump_count % 3 == 0 and gstate.jump_count > 20 and not svm_utils.check_wstate_reachable(gstate.wstate, 100)): gstate.halt = True return if gstate.pc_addr_to_depth.setdefault((gstate.mstate.pc, jump_addr), 0) >= 3: gstate.halt = True return increase_depth = True current_contract = active_account.contract line = solidity_utils.offset_to_line(current_contract.src_code, mstate.pc, current_contract.src_map) src_code = current_contract.src_code.split('\n')[line].strip() concrete_cond = False if 'assert' in src_code: increase_depth = False if type(condition) == bool: increase_depth = False else: simplified_cond = z3.simplify(condition) if z3.is_true(simplified_cond) or z3.is_false(simplified_cond): concrete_cond = True increase_depth = False if increase_depth: gstate.pc_addr_to_depth[(mstate.pc, jump_addr)] = gstate.pc_addr_to_depth[(mstate.pc, jump_addr)] + 1 instr_idx = svm_utils.get_instruction_index(gstate.environment.disassembly.instruction_list, jump_addr) if instr_idx is None: raise SVMRuntimeError('JUMP to invalid address') dest_opcode = gstate.environment.disassembly.instruction_list[instr_idx]['opcode'] if dest_opcode != 'JUMPDEST': raise SVMRuntimeError('JUMP to invalid address') condition = z3.BoolVal(condition) if isinstance(condition, bool) else condition condition = (condition == 0) if isinstance(condition, z3.BitVecRef) else condition assert isinstance(condition ,z3.BoolRef), 'Invalid condition types!' if not z3.is_false(z3.simplify(condition)): true_gstate = copy(gstate) true_gstate.mstate.pc = instr_idx true_gstate.wstate.constraints.append(condition) if true_gstate.wstate.trace is None: jump_func_name, jump_func_hash = gstate.environment.extract_func_name_hash(gstate.mstate.pc) if jump_func_name: jump_trace = make_trace(active_account, jump_func_name) logging.debug('Entering function %s', jump_trace) true_gstate.wstate.trace = jump_trace new_gstates.extend(self.execute_gstate(true_gstate)) negated_condition = z3.Not(condition) if not z3.is_false(z3.simplify(negated_condition)): false_gstate = copy(gstate) false_gstate.mstate.pc += 1 false_gstate.wstate.constraints.append(negated_condition) new_gstates.extend(self.execute_gstate(false_gstate)) gstate.halt = True return new_gstates
def EXTCODECOPY(self, gstate, start, s2, size): raise SVMRuntimeError('not implemented')