def test_ADD_1(self): instruction = EVMAsm.disassemble_one(b"\x60\x10") self.assertEqual( EVMAsm.Instruction(0x60, "PUSH", 1, 0, 1, 3, "Place 1 byte item on stack.", 16, 0), instruction) instruction = EVMAsm.assemble_one("PUSH1 0x10") self.assertEqual( instruction, EVMAsm.Instruction(0x60, "PUSH", 1, 0, 1, 3, "Place 1 byte item on stack.", 16, 0)) instructions1 = EVMAsm.disassemble_all(b"\x30\x31") instructions2 = EVMAsm.assemble_all("ADDRESS\nBALANCE") self.assertTrue( all(a == b for a, b in zip(instructions1, instructions2))) # High level simple assembler/disassembler bytecode = EVMAsm.assemble_hex("""PUSH1 0x80 BLOCKHASH MSTORE PUSH1 0x2 PUSH2 0x100 """) self.assertEqual(bytecode, "0x608040526002610100") asmcode = EVMAsm.disassemble_hex("0x608040526002610100") self.assertEqual( asmcode, """PUSH1 0x80\nBLOCKHASH\nMSTORE\nPUSH1 0x2\nPUSH2 0x100""")
def test_ADD_1(self): instruction = EVMAsm.disassemble_one(b'\x60\x10') self.assertEqual( EVMAsm.Instruction(0x60, 'PUSH', 1, 0, 1, 0, 'Place 1 byte item on stack.', 16, 0), instruction) instruction = EVMAsm.assemble_one('PUSH1 0x10') EVMAsm.Instruction(0x60, 'PUSH', 1, 0, 1, 0, 'Place 1 byte item on stack.', 16, 0) instructions1 = EVMAsm.disassemble_all(b'\x30\x31') instructions2 = EVMAsm.assemble_all('ADDRESS\nBALANCE') self.assertTrue( all(a == b for a, b in zip(instructions1, instructions2))) # High level simple assembler/disassembler bytecode = EVMAsm.assemble_hex("""PUSH1 0x80 BLOCKHASH MSTORE PUSH1 0x2 PUSH2 0x100 """) self.assertEqual(bytecode, '0x608040526002610100') asmcode = EVMAsm.disassemble_hex('0x608040526002610100') self.assertEqual( asmcode, '''PUSH1 0x80\nBLOCKHASH\nMSTORE\nPUSH1 0x2\nPUSH2 0x100''')
def compute_basic_blocks(self): ''' Split instructions into BasicBlock Args: self: CFG Returns: None ''' # Do nothing if basic_blocks already exist if self._basic_blocks: return bb = BasicBlock() for instruction in disassemble_all(self.bytecode): self._instructions[instruction.pc] = instruction if instruction.name == 'JUMPDEST': # JUMPDEST indicates a new BasicBlock. Set the end pc # of the current block, and switch to a new one. if bb._instructions: self._basic_blocks[bb.end.pc] = bb bb = BasicBlock() self._basic_blocks[instruction.pc] = bb bb.add_instruction(instruction) if bb.start.pc == instruction.pc: self._basic_blocks[instruction.pc] = bb if bb.end.name in BASIC_BLOCK_END: self._basic_blocks[bb.end.pc] = bb bb = BasicBlock()
def disassemble_all( bytecode: Iterable, pc: int = 0, fork=pyevmasm.DEFAULT_FORK) -> Iterable[EVMInstruction]: ''' Decode all instructions in bytecode :param bytecode: an evm bytecode (binary) :param pc: program counter of the first instruction in the bytecode(optional) :type bytecode: iterator/sequence/str :return: An generator of Instruction objects Example use:: >>> for inst in EVMAsm.decode_all(bytecode): ... print inst ... PUSH1 0x60 PUSH1 0x40 MSTORE PUSH1 0x2 PUSH2 0x108 PUSH1 0x0 POP SSTORE PUSH1 0x40 MLOAD ''' instructions = pyevmasm.disassemble_all(bytecode, pc, fork) return EVMAsm.convert_multiple_instructions_to_evminstructions( instructions)
def __build_source_map(self, bytecode, srcmap): # https://solidity.readthedocs.io/en/develop/miscellaneous.html#source-mappings new_srcmap = {} bytecode = self._without_metadata(bytecode) if self.source_code and srcmap: asm_offset = 0 asm_pos = 0 md = dict(enumerate(srcmap[asm_pos].split(':'))) byte_offset = int(md.get(0, 0)) # is the byte-offset to the start of the range in the source file source_len = int(md.get(1, 0)) # is the length of the source range in bytes file_index = int(md.get(2, 0)) # is the source index over sourceList jump_type = md.get(3, None) # this can be either i, o or - signifying whether a jump instruction goes into a function, returns from a function or is a regular jump as part of e.g. a loop pos_to_offset = {} for i in EVMAsm.disassemble_all(bytecode): pos_to_offset[asm_pos] = asm_offset asm_pos += 1 asm_offset += i.size for asm_pos, md in enumerate(srcmap): if len(md): d = {p: k for p, k in enumerate(md.split(':')) if k} byte_offset = int(d.get(0, byte_offset)) source_len = int(d.get(1, source_len)) file_index = int(d.get(2, file_index)) jump_type = d.get(3, jump_type) new_srcmap[pos_to_offset[asm_pos]] = (byte_offset, source_len, file_index, jump_type) return new_srcmap
def _is_revert_bb(self, state, pc): world = state.platform def read_code(_pc=None): while True: yield to_constant(world.current_vm.read_code(_pc)[0]) _pc += 1 for inst in EVMAsm.disassemble_all(read_code(pc), pc): if inst.name == "REVERT": return True if inst.is_terminator: return False
def generate_evm_code(raw_code, storage): contracts = {} for contract in raw_code: contracts[contract] = list(pyevmasm.disassemble_all( raw_code[contract])) impls = [] contract_info = [] for contract in sorted(contracts): if contract not in storage: storage[contract] = {} impls.append( generate_contract_code( AVMLabel("contract_entry_" + str(contract)), contracts[contract], contract, )) contract_info.append({ "code_point": AVMLabel("contract_entry_" + str(contract)), "contractID": contract, "storage": storage[contract], "code_size": len(contracts[contract]) + sum(op.operand_size for op in contracts[contract]), "code_hash": int.from_bytes(eth_utils.crypto.keccak(raw_code[contract]), byteorder="big"), "code": byterange.frombytes(bytes.fromhex(raw_code[contract].hex())), }) contract_info.append({ "code_point": value.Tuple([]), "contractID": 100, "storage": {}, "code_size": 1, "code_hash": 1, "code": byterange.frombytes(b""), }) def initialization(vm): os.initialize(vm, contract_info) vm.jump_direct(AVMLabel("run_loop_start")) main_code = [] main_code.append(compile_block(run_loop_start)) main_code += impls return compile_block(initialization), BlockStatement(main_code)
print("\twrites to stack:", instruction.writes_to_stack) print("\treads from stack:", instruction.reads_from_stack) print("\twrites to memory:", instruction.writes_to_memory) print("\treads from memory:", instruction.reads_from_memory) print("\twrites to storage:", instruction.writes_to_storage) print("\treads from storage:", instruction.reads_from_storage) print("\tis terminator", instruction.is_terminator) instruction = ea.disassemble_one("\x60\x10") printi(instruction) instruction = ea.assemble_one("PUSH1 0x10") printi(instruction) for instruction in ea.disassemble_all("\x30\x31"): printi(instruction) for instruction in ea.assemble_all("ADDRESS\nBALANCE"): printi(instruction) # High level simple assembler/disassembler print( ea.assemble_hex("""PUSH1 0x60 BLOCKHASH MSTORE PUSH1 0x2 PUSH2 0x100 """)) print(ea.disassemble_hex("0x606040526002610100"))
print('\twrites to stack:', instruction.writes_to_stack) print('\treads from stack:', instruction.reads_from_stack) print('\twrites to memory:', instruction.writes_to_memory) print('\treads from memory:', instruction.reads_from_memory) print('\twrites to storage:', instruction.writes_to_storage) print('\treads from storage:', instruction.reads_from_storage) print('\tis terminator', instruction.is_terminator) instruction = ea.disassemble_one('\x60\x10') printi(instruction) instruction = ea.assemble_one('PUSH1 0x10') printi(instruction) for instruction in ea.disassemble_all('\x30\x31'): printi(instruction) for instruction in ea.assemble_all('ADDRESS\nBALANCE'): printi(instruction) #High level simple assembler/disassembler print(ea.assemble_hex( """PUSH1 0x60 BLOCKHASH MSTORE PUSH1 0x2 PUSH2 0x100 """ ))
def generate_evm_code(raw_code, storage): contracts = {} for contract in raw_code: contracts[contract] = list(pyevmasm.disassemble_all( raw_code[contract])) code_tuples_data = {} for contract in raw_code: code_tuples_data[contract] = byterange.frombytes( bytes.fromhex(raw_code[contract].hex())) code_tuples_func = make_bst_lookup(code_tuples_data) @modifies_stack([value.IntType()], 1) def code_tuples(vm): code_tuples_func(vm) code_hashes_data = {} for contract in raw_code: code_hashes_data[contract] = int.from_bytes(eth_utils.crypto.keccak( raw_code[contract]), byteorder="big") code_hashes_func = make_bst_lookup(code_hashes_data) @modifies_stack([value.IntType()], [value.ValueType()]) def code_hashes(vm): code_hashes_func(vm) contract_dispatch = {} for contract in contracts: contract_dispatch[contract] = AVMLabel("contract_entry_" + str(contract)) contract_dispatch_func = make_bst_lookup(contract_dispatch) @modifies_stack([value.IntType()], 1) def dispatch_contract(vm): contract_dispatch_func(vm) code_sizes = {} for contract in contracts: code_sizes[contract] = len(contracts[contract]) + sum( op.operand_size for op in contracts[contract]) # Give the interrupt contract address a nonzero size code_sizes[0x01] = 1 code_size_func = make_bst_lookup(code_sizes) @modifies_stack([value.IntType()], 1) def code_size(vm): code_size_func(vm) impls = [] contract_info = [] for contract in sorted(contracts): if contract not in storage: storage[contract] = {} impls.append( generate_contract_code( AVMLabel("contract_entry_" + str(contract)), contracts[contract], code_tuples_data[contract], contract, code_size, code_tuples, code_hashes, dispatch_contract, )) contract_info.append({ "contractID": contract, "storage": storage[contract] }) def initialization(vm): os.initialize(vm, contract_info) vm.jump_direct(AVMLabel("run_loop_start")) def run_loop_start(vm): vm.set_label(AVMLabel("run_loop_start")) os.get_next_message(vm) execution.setup_initial_call(vm, dispatch_contract) vm.push(AVMLabel("run_loop_start")) vm.jump() main_code = [] main_code.append(compile_block(run_loop_start)) main_code += impls return compile_block(initialization), BlockStatement(main_code)
def printb(bytecode): insns = list( disassemble_all(binascii.unhexlify(bytecode), fork=DEFAULT_FORK)) for i in insns: print("%08x: %s" % (i.pc, str(i)))