def SHA3(self, gstate, index, length): if svm_utils.is_bv_concrete(index) and svm_utils.is_bv_concrete(length): index = svm_utils.get_concrete_int(index) length = svm_utils.get_concrete_int(length) if length == 0: gstate.mstate.stack.append(z3.BitVecVal(SHA_EMPTY_ARGS_VALUE, 256)) return data = z3.simplify(svm_utils.get_memory_data(gstate.mstate.memory, index, length)) sha_constraints, hash_vector = svm_utils.symbolic_keccak(self, data) gstate.wstate.constraints.extend(sha_constraints) gstate.mstate.stack.append(hash_vector) return hash_vector = self.svm.sym_bv_generator.get_sym_bitvec(constraints.ConstraintType.SHA3, gstate.wstate.gen, unique=True) logging.debug('SHA index or len not resolved. Using the symbolic vector') gstate.mstate.stack.append(hash_vector) return
def BALANCE(self, gstate, address): if svm_utils.is_bv_concrete(address) and svm_utils.get_concrete_int(address) in gstate.wstate.address_to_account: balance = gstate.wstate.address_to_account[svm_utils.get_concrete_int(address)].balance gstate.mstate.stack.append(balance) return address = str(z3.simplify(address)).replace(' ', '_') gstate.mstate.stack.append(self.svm.sym_bv_generator.get_sym_bitvec(constraints.ConstraintType.BALANCE, gstate.wstate.gen, unique=True))
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 abstract(self, label_suffix): old_storage = self.storage self.storage = AbstractStorage(f'abstract_storage{label_suffix}') for map_id in self.mapping_id_to_sum: if svm_utils.is_bv_concrete(map_id): map_id_string = svm_utils.get_concrete_int(map_id) else: map_id_string = str(z3.simplify(map_id)) raise Exception("pdb") label = f'abstract_sum_{map_id_string}{label_suffix}' self.mapping_id_to_sum[map_id] = z3.BitVec(label, 256) self.balance = z3.BitVec(f'gstate_balance{label_suffix}', 256)
def RETURN(self, gstate, offset, length): if not svm_utils.is_bv_concrete(length): # raise SVMRuntimeError('Non concrete return length') length = 32 length = svm_utils.get_concrete_int(length) return_data_bytes = [z3.Select(gstate.mstate.memory, i+offset) for i in range(length)] return_data = z3.Concat(return_data_bytes) assert return_data.size() == length*8 gstate.return_data = return_data gstate.exit_code = 1 gstate.halt = True return [gstate]
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 CODECOPY(self, gstate, dest_offset, offset, length): active_address = gstate.environment.active_address active_account = gstate.wstate.address_to_account[active_address] offset = svm_utils.get_concrete_int(offset) if svm_utils.is_bv_concrete(length): length = svm_utils.get_concrete_int(length) else: length = 0 assert isinstance(gstate.environment.runtime_bytecode_bytes, list) bytecode_bytes = gstate.environment.runtime_bytecode_bytes for i in range(length): if offset + i < len(bytecode_bytes): data_byte = bytecode_bytes[offset+i] else: data_byte = z3.BitVecVal(0, 8) gstate.mstate.memory = z3.Store(gstate.mstate.memory, dest_offset+i, data_byte) gstate.mstate.memory_dict[svm_utils.get_concrete_int(dest_offset+i)] = data_byte
def extract_index_features(index): current_index = index features = [] if current_index.decl().name() == 'bvadd': current_index = current_index.arg(0) if current_index.decl().name().startswith('sha'): current_index = current_index.arg(0) features.append(current_index.size()) else: return features if current_index.decl().name() == 'concat': args = [current_index.arg(i) for i in range(current_index.num_args())] if svm_utils.is_bv_concrete(args[-1]): features.append(args[-1]) current_index = z3.Concat(args[0:-1]) if len(args) > 2 else args[0] else: return features elif is_bv_concrete(current_index): if current_index.size() > 256: prefix, suffix = split_bv(current_index, 256) suffix = z3.simplify(suffix) features.append(suffix) current_index = z3.simplify(prefix) elif current_index.size() == 256: features.append(current_index) return features else: features.append(current_index) return features else: return features if current_index.decl().name().startswith('sha'): print(current_index) current_index = current_index.arg(0) features.append(current_index.size()) else: return features return features
def symbolic_keccak(svm, data): sha_constraints = [] sha_func, sha_func_inv = constraints.get_sha_functions(data.size()) hash_vector = sha_func(data) sha_constraints.append(sha_func_inv(sha_func(data)) == data) hash_vector_features = extract_index_features(hash_vector) data_concrete = svm_utils.is_bv_concrete(data) if data_concrete: concrete_data = svm_utils.get_concrete_int(data) data_bytes = ethereum.utils.zpad( ethereum.utils.int_to_bytes(concrete_data), data.size() // 8) hash_value = int.from_bytes(ethereum.utils.sha3_256(data_bytes), 'big') SIZE_PER_SHA_LEN = 2**100 limit_left = 1024 + SIZE_PER_SHA_LEN * data.size() limit_right = limit_left + SIZE_PER_SHA_LEN if not data_concrete: sha_constraints.append(z3.ULT(limit_left, hash_vector)) sha_constraints.append(z3.ULT(hash_vector, limit_right)) # last 4 bits are 0 => hashes are 16 words between each other sha_constraints.append(z3.Extract(3, 0, hash_vector) == 0) elif data_concrete: storage_range = limit_right - limit_left scaled_hash_value = limit_left + int( (hash_value / svm_utils.TT256M1) * storage_range) scaled_hash_value = scaled_hash_value // 16 * 16 sha_constraints.append( hash_vector == z3.BitVecVal(scaled_hash_value, VECTOR_LEN)) # elif storage_node == svm.storage_root and data_concrete: # hash_value = hash_value // 16 * 16 # sha_constraints.append(hash_vector == z3.BitVecVal(hash_value, VECTOR_LEN)) return sha_constraints, hash_vector
def _CALL(self, gstate, gas, to, value, meminstart, meminsize, outmemstart, memoutsize): new_gstates = [] active_account = gstate.wstate.address_to_account[gstate.environment.active_address] environment = gstate.environment mstate = gstate.mstate wstate = gstate.wstate active_account.balance -= value if z3.is_true(z3.simplify(to == 0x0)): mstate.stack.append(z3.BitVecVal(0, 256)) elif z3.is_true(z3.simplify(to == 0x1)): raise Exception('not implemented') elif z3.is_true(z3.simplify(to == 0x2)): raise Exception('not implemented') elif svm_utils.is_bv_concrete(meminsize): meminsize = svm_utils.get_concrete_int(meminsize) gstate.return_data = None callee_calldata = None in_data_bytes = [z3.Select(mstate.memory, meminstart+i) for i in range(meminsize)] if meminsize else [] if meminsize != 0: callee_calldata = z3.Concat(in_data_bytes) if len(in_data_bytes) > 1 else in_data_bytes[0] for callee_address, callee_account in wstate.address_to_account.items(): callee_constraint = z3.simplify(to == callee_address) if z3.is_false(callee_constraint): continue if callee_address == active_account.address: if svm_utils.is_bv_concrete(gstate.environment.sender) and\ svm_utils.get_concrete_int(gstate.environment.sender) == callee_address: gstate.halt = True return [] callee_environment = Environment(active_address=callee_address, sender=z3.BitVecVal(active_account.address, 256), calldata=callee_calldata, gasprice=environment.gasprice, callvalue=value, origin=environment.origin, calldata_type=CalldataType.DEFINED, disassembly=callee_account.contract.disassembly, runtime_bytecode_bytes=list(callee_account.contract.disassembly.bytecode), timestamp=environment.timestamp) child_wstate = copy(wstate) child_wstate.constraints.append(to == callee_address) if not svm_utils.check_wstate_reachable(child_wstate, 100): continue child_gstate = GlobalState(child_wstate, callee_environment) intermediate_gstates = self.execute_gstate(child_gstate) for new_gstate in intermediate_gstates: new_gstate.mstate = deepcopy(gstate.mstate) new_gstate.mstate.stack.append(new_gstate.exit_code) new_gstate.exit_code = None 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_gstate.has_storage_changed |= gstate.has_storage_changed if new_gstate.return_data is not None: return_data = new_gstate.return_data memoutsize = svm_utils.get_concrete_int(memoutsize) outmemstart = svm_utils.get_concrete_int(outmemstart) return_data_bytes = svm_utils.split_bv_into_bytes(return_data) for i in range(min(return_data.size()//8, memoutsize)): return_byte = return_data_bytes[i] new_gstate.mstate.memory = z3.Store(new_gstate.mstate.memory, i + outmemstart, return_byte) new_gstates.extend(self.execute_gstate(new_gstate)) ret = self.svm.sym_bv_generator.get_sym_bitvec(constraints.ConstraintType.RETURN_VALUE, wstate.gen, unique=True, acc=active_account.id) gstate.return_data = None to_constraint = z3.Or([to == p for p in self.svm.possible_caller_addresses]) wstate.constraints.append(to_constraint) wstate.has_symbolic_calls = True return_data = self.svm.sym_bv_generator.get_sym_bitvec(constraints.ConstraintType.RETURNDATA, wstate.gen, unique=True, bv_size=SYMBOLIC_CALL_RETURNDATA_SIZE_BYTES*8, acc=active_account.id) gstate.return_data = return_data concrete_memoutsize = svm_utils.get_concrete_int(memoutsize) if concrete_memoutsize != 0: return_data_bytes = svm_utils.split_bv_into_bytes(return_data) memoutsize = svm_utils.get_concrete_int(memoutsize) for i in range(min(return_data.size()//8, memoutsize)): return_byte = return_data_bytes[i] mstate.memory = z3.Store(mstate.memory, i+outmemstart, return_byte) gstate.has_storage_changed = True mstate.stack.append(ret) return new_gstates
def MSTORE8(self, gstate, index, value): data = z3.Extract(7, 0, value) if svm_utils.is_bv_concrete(index): gstate.mstate.memory_dict[svm_utils.get_concrete_int(index)] = data gstate.mstate.memory = z3.Store(gstate.mstate.memory, index, data)