def __init__(self, code: str, enable_online_lookup: bool = False): self.bytecode = code self.instruction_list = asm.disassemble(util.safe_decode(code)) self.func_hashes = [] self.function_name_to_address = {} self.address_to_function_name = {} signatures = SignatureDb( enable_online_lookup=enable_online_lookup ) # control if you want to have online signature hash lookups try: signatures.open() # open from default locations except FileNotFoundError: logging.info( "Missing function signature file. Resolving of function names from signature file disabled." ) # Need to take from PUSH1 to PUSH4 because solc seems to remove excess 0s at the beginning for optimizing jump_table_indices = asm.find_opcode_sequence( [("PUSH1", "PUSH2", "PUSH3", "PUSH4"), ("EQ", )], self.instruction_list) for index in jump_table_indices: function_hash, jump_target, function_name = get_function_info( index, self.instruction_list, signatures) self.func_hashes.append(function_hash) if jump_target is not None and function_name is not None: self.function_name_to_address[function_name] = jump_target self.address_to_function_name[jump_target] = function_name signatures.write( ) # store resolved signatures (potentially resolved online)
def __init__(self, code, enable_online_lookup=True): self.instruction_list = asm.disassemble(util.safe_decode(code)) self.func_hashes = [] self.func_to_addr = {} self.addr_to_func = {} self.bytecode = code signatures = SignatureDb( enable_online_lookup=enable_online_lookup ) # control if you want to have online sighash lookups try: signatures.open() # open from default locations except FileNotFoundError: logging.info( "Missing function signature file. Resolving of function names from signature file disabled." ) # Parse jump table & resolve function names # Need to take from PUSH1 to PUSH4 because solc seems to remove excess 0s at the beginning for optimizing jmptable_indices = asm.find_opcode_sequence( [("PUSH1", "PUSH2", "PUSH3", "PUSH4"), ("EQ", )], self.instruction_list) for i in jmptable_indices: func_hash = self.instruction_list[i]['argument'] # Append with missing 0s at the beginning func_hash = "0x" + func_hash[2:].rjust(8, "0") self.func_hashes.append(func_hash) try: # tries local cache, file and optional online lookup # may return more than one function signature. since we cannot probe for the correct one we'll use the first func_names = signatures.get(func_hash) if len(func_names) > 1: # ambigious result func_name = "**ambiguous** %s" % func_names[ 0] # return first hit but note that result was ambiguous else: # only one item func_name = func_names[0] except KeyError: func_name = "_function_" + func_hash try: offset = self.instruction_list[i + 2]['argument'] jump_target = int(offset, 16) self.func_to_addr[func_name] = jump_target self.addr_to_func[jump_target] = func_name except: continue signatures.write( ) # store resolved signatures (potentially resolved online)
def __init__(self, code): self.instruction_list = asm.disassemble(util.safe_decode(code)) self.xrefs = [] self.func_to_addr = {} self.addr_to_func = {} self.bytecode = code try: mythril_dir = os.environ['MYTHRIL_DIR'] except KeyError: mythril_dir = os.path.join(os.path.expanduser('~'), ".mythril") # Load function signatures signatures_file = os.path.join(mythril_dir, 'signatures.json') if not os.path.exists(signatures_file): logging.info( "Missing function signature file. Resolving of function names disabled." ) signatures = {} else: with open(signatures_file) as f: signatures = json.load(f) # Parse jump table & resolve function names jmptable_indices = asm.find_opcode_sequence(["PUSH4", "EQ"], self.instruction_list) for i in jmptable_indices: func_hash = self.instruction_list[i]['argument'] try: func_name = signatures[func_hash] except KeyError: func_name = "_function_" + func_hash try: offset = self.instruction_list[i + 2]['argument'] jump_target = int(offset, 16) self.func_to_addr[func_name] = jump_target self.addr_to_func[jump_target] = func_name except: continue
def __init__(self, code): self.instruction_list = asm.disassemble(util.safe_decode(code)) self.blocks = [] self.xrefs = [] self.func_to_addr = {} self.addr_to_func = {} # Parse jump table & resolve function names script_dir = os.path.dirname(os.path.realpath(__file__)) signature_file = os.path.join(script_dir, 'signatures.json') with open(signature_file) as f: signatures = json.load(f) jmptable_indices = asm.find_opcode_sequence(["PUSH4", "EQ"], self.instruction_list) for i in jmptable_indices: func_hash = self.instruction_list[i]['argument'] try: func_name = signatures[func_hash] except KeyError: func_name = "UNK_" + func_hash try: offset = self.instruction_list[i+2]['argument'] jump_target = int(offset, 16) self.func_to_addr[func_name] = jump_target self.addr_to_func[jump_target] = func_name except: continue # Parse instructions into basic blocks current_block = Block(0, 0, "PROLOGUE") index = 0 blocklen = 0 blocknumber = 1 for instruction in self.instruction_list: if (instruction['opcode'] == "JUMPDEST"): try: func_name = "- FUNCTION " + self.addr_to_func[instruction['address']] + " -" except KeyError: func_name = "- JUMPDEST_UNK -" current_block.update_length(blocklen) self.blocks.append(current_block) current_block = Block(blocknumber, index, func_name) blocklen = 0 blocknumber += 1 current_block.instruction_list.append(instruction) blocklen += 1 index += 1 # Add the last block current_block.update_length(blocklen) self.blocks.append(current_block) # Resolve cross-references for block in self.blocks: jmp_indices = asm.find_opcode_sequence(["JUMP"], block.instruction_list) jmp_indices += asm.find_opcode_sequence(["JUMPI"], block.instruction_list) for i in jmp_indices: try: dest_hex = block.instruction_list[i - 1]['argument'] dest = int(dest_hex[2:], 16) except: continue j = 0 try: while(self.blocks[j].end_addr < dest): j += 1 except IndexError: continue if not (block.id, self.blocks[j].id) in self.xrefs: self.xrefs.append((block.id, self.blocks[j].id)) # if the last instruction isn't an unconditional jump or halt, also add a reference to the following block try: if (block.id < len(self.blocks)) and (block.instruction_list[block.length - 1]['opcode'] not in ['JUMP', 'STOP', 'THROW', 'REVERT', 'INVALID']): if not (block.id, self.blocks[j].id) in self.xrefs: self.xrefs.append((block.id, block.id + 1)) except UnboundLocalError: # quickfix continue