def decode(code): dis = Disassembly(code) asm = dis.get_easm() tmp = dict() for line in asm.splitlines(): lst = line.split() tmp[lst[0]] = ' '.join(lst[1:]) return (json.dumps(dis.address_to_function_name), json.dumps(tmp))
def test_easm_from_solidity_files(self): for input_file in TESTDATA_INPUTS.iterdir(): output_expected = TESTDATA_OUTPUTS_EXPECTED / (input_file.name + ".easm") output_current = TESTDATA_OUTPUTS_CURRENT / (input_file.name + ".easm") code = input_file.read_text() disassembly = Disassembly(code) output_current.write_text(disassembly.get_easm()) if not (output_expected.read_text() == output_current.read_text()): self.found_changed_files(input_file, output_expected, output_current) self.assert_and_show_changed_files()
class ETHContract(persistent.Persistent): def __init__(self, code, creation_code="", name="Unknown"): self.creation_code = creation_code self.name = name # Workaround: We currently do not support compile-time linking. # Dynamic contract addresses of the format __[contract-name]_____________ are replaced with a generic address code = re.sub(r'(_+.*_+)', 'aa' * 20, code) self.code = code self.disassembly = Disassembly(self.code) def as_dict(self): return { 'address': self.address, 'name': self.name, 'code': self.code, 'creation_code': self.creation_code, 'disassembly': self.disassembly } def get_easm(self): return self.disassembly.get_easm() def matches_expression(self, expression): str_eval = '' easm_code = None tokens = filter( None, re.split("(and|or|not)", expression.replace(" ", ""), re.IGNORECASE)) for token in tokens: if token in ("and", "or", "not"): str_eval += " " + token + " " continue m = re.match(r'^code#([a-zA-Z0-9\s,\[\]]+)#', token) if (m): if easm_code is None: easm_code = self.get_easm() code = m.group(1).replace(",", "\\n") str_eval += "\"" + code + "\" in easm_code" continue m = re.match(r'^func#([a-zA-Z0-9\s_,(\\)\[\]]+)#$', token) if (m): sign_hash = "0x" + utils.sha3(m.group(1))[:4].hex() str_eval += "\"" + sign_hash + "\" in self.disassembly.func_hashes" continue return eval(str_eval.strip())
class EVMContract(persistent.Persistent): """This class represents an address with associated code (Smart Contract).""" def __init__( self, code="", creation_code="", name="Unknown", enable_online_lookup=False ): """Create a new contract. Workaround: We currently do not support compile-time linking. Dynamic contract addresses of the format __[contract-name]_____________ are replaced with a generic address Apply this for creation_code & code :param code: :param creation_code: :param name: :param enable_online_lookup: """ creation_code = re.sub(r"(_{2}.{38})", "aa" * 20, creation_code) code = re.sub(r"(_{2}.{38})", "aa" * 20, code) self.creation_code = creation_code self.name = name self.code = code self.disassembly = Disassembly(code, enable_online_lookup=enable_online_lookup) self.creation_disassembly = Disassembly( creation_code, enable_online_lookup=enable_online_lookup ) @property def bytecode_hash(self): """ :return: runtime bytecode hash """ return get_code_hash(self.code) @property def creation_bytecode_hash(self): """ :return: Creation bytecode hash """ return get_code_hash(self.creation_code) def as_dict(self): """ :return: """ return { "name": self.name, "code": self.code, "creation_code": self.creation_code, "disassembly": self.disassembly, } def get_easm(self): """ :return: """ return self.disassembly.get_easm() def get_creation_easm(self): """ :return: """ return self.creation_disassembly.get_easm() def matches_expression(self, expression): """ :param expression: :return: """ str_eval = "" easm_code = None tokens = re.split("\s+(and|or|not)\s+", expression, re.IGNORECASE) for token in tokens: if token in ("and", "or", "not"): str_eval += " " + token + " " continue m = re.match(r"^code#([a-zA-Z0-9\s,\[\]]+)#", token) if m: if easm_code is None: easm_code = self.get_easm() code = m.group(1).replace(",", "\\n") str_eval += '"' + code + '" in easm_code' continue m = re.match(r"^func#([a-zA-Z0-9\s_,(\\)\[\]]+)#$", token) if m: sign_hash = "0x" + utils.sha3(m.group(1))[:4].hex() str_eval += '"' + sign_hash + '" in self.disassembly.func_hashes' continue return eval(str_eval.strip())
class ETHContract(persistent.Persistent): def __init__(self, code, creation_code="", name="Unknown", enable_online_lookup=False): # Workaround: We currently do not support compile-time linking. # Dynamic contract addresses of the format __[contract-name]_____________ are replaced with a generic address # Apply this for creation_code & code creation_code = re.sub(r"(_{2}.{38})", "aa" * 20, creation_code) code = re.sub(r"(_{2}.{38})", "aa" * 20, code) self.creation_code = creation_code self.name = name self.code = code self.disassembly = Disassembly( code, enable_online_lookup=enable_online_lookup) self.creation_disassembly = Disassembly( creation_code, enable_online_lookup=enable_online_lookup) def as_dict(self): return { "address": self.address, "name": self.name, "code": self.code, "creation_code": self.creation_code, "disassembly": self.disassembly, } def get_easm(self): return self.disassembly.get_easm() def matches_expression(self, expression): str_eval = "" easm_code = None tokens = re.split("\s+(and|or|not)\s+", expression, re.IGNORECASE) for token in tokens: if token in ("and", "or", "not"): str_eval += " " + token + " " continue m = re.match(r"^code#([a-zA-Z0-9\s,\[\]]+)#", token) if m: if easm_code is None: easm_code = self.get_easm() code = m.group(1).replace(",", "\\n") str_eval += '"' + code + '" in easm_code' continue m = re.match(r"^func#([a-zA-Z0-9\s_,(\\)\[\]]+)#$", token) if m: sign_hash = "0x" + utils.sha3(m.group(1))[:4].hex() str_eval += '"' + sign_hash + '" in self.disassembly.func_hashes' continue return eval(str_eval.strip())
In this example, we'll only check one of the refernced contracts contains the initWallet() function If it does, we save the disassembly and callgraph for further analysis ''' for xref in xrefs: code = homestead.eth_getCode(xref) disassembly = Disassembly(code) if contract.matches_expression("func#initWallet(address[],uint256,uint256)#"): print ("initWallet() in referenced library contract: " + xref) # Save list of contracts that forward calls to this library contract cwd = os.getcwd() with open("contracts_calling_" + xref + ".txt", "w") as f: addresses = contract_storage.instance_lists[k].addresses f.write("\n".join(addresses)) easm = disassembly.get_easm() with open("library_" + xref + ".easm", "w") as f: f.write(easm) generate_callgraph(disassembly, os.path.join(cwd, "library_" + xref))