├── ethereum_dasm ├── output │ ├── __init__.py │ └── console.py ├── symbolic │ ├── __init__.py │ ├── simplify.py │ └── mythril.py ├── __init__.py ├── __main__.py ├── asm │ ├── __init__.py │ ├── registry.py │ └── instructions.py ├── utils │ ├── __init__.py │ ├── signatures.py │ ├── api.py │ ├── data.py │ └── colors.py └── evmdasm.py ├── requirements.txt ├── requirements_all_featues.txt ├── setup.py ├── .gitignore ├── LICENSE └── README.md /ethereum_dasm/output/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ethereum_dasm/symbolic/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | evmdasm 2 | requests 3 | colorama 4 | tabulate 5 | -------------------------------------------------------------------------------- /requirements_all_featues.txt: -------------------------------------------------------------------------------- 1 | evmdasm 2 | requests 3 | colorama 4 | tabulate 5 | mythril 6 | ethereum-input-decoder 7 | pyetherchain 8 | -------------------------------------------------------------------------------- /ethereum_dasm/__init__.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # Author : 4 | from ethereum_dasm import evmdasm -------------------------------------------------------------------------------- /ethereum_dasm/__main__.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # Author : 4 | from ethereum_dasm import evmdasm 5 | 6 | if __name__ == '__main__': 7 | evmdasm.main() 8 | -------------------------------------------------------------------------------- /ethereum_dasm/asm/__init__.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # Author : 4 | from .instructions import BasicBlock, Instruction 5 | 6 | 7 | __ALL__ = ["BasicBlock", "Instruction"] 8 | -------------------------------------------------------------------------------- /ethereum_dasm/utils/__init__.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # Author : 4 | from .api import EthJsonRpc 5 | from .data import hex_decode, is_ascii_subsequence, is_all_ascii, str_to_bytes, bytes_to_str 6 | from . import signatures 7 | 8 | 9 | __ALL__ = ["EthJsonRpc", "hex_decode", "is_ascii_subsequence", "is_all_ascii", "str_to_bytes", "bytes_to_str", "signatures"] 10 | -------------------------------------------------------------------------------- /ethereum_dasm/asm/registry.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # Author : 4 | 5 | from .instructions import Instruction 6 | 7 | import evmdasm.registry 8 | 9 | registry = evmdasm.registry.InstructionRegistry(instructions=evmdasm.registry.INSTRUCTIONS, _template_cls=Instruction) 10 | 11 | # rebuild the registry with our extended Instruction class. (clone with our class as template) 12 | INSTRUCTIONS = registry.instructions 13 | INSTRUCTIONS_BY_OPCODE = registry.by_opcode 14 | INSTRUCTIONS_BY_NAME = registry.by_name 15 | INSTRUCTIONS_BY_CATEGORY = registry.by_category 16 | INSTRUCTION_MARKS_BASICBLOCK_END = registry.instruction_marks_basicblock_end 17 | create_instruction = registry.create_instruction 18 | -------------------------------------------------------------------------------- /ethereum_dasm/utils/signatures.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # Author : 4 | 5 | 6 | try: 7 | import ethereum_input_decoder 8 | except ImportError: 9 | ethereum_input_decoder = None 10 | 11 | 12 | cache_lookup_function_signature = {} # memcache for lookup_function_signature 13 | 14 | 15 | def lookup_function_signature(sighash): 16 | if not ethereum_input_decoder: 17 | return [] 18 | cache_hit = cache_lookup_function_signature.get(sighash) 19 | if cache_hit: 20 | return cache_hit 21 | cache_lookup_function_signature[sighash] = list(ethereum_input_decoder.decoder.FourByteDirectory.lookup_signatures(sighash)) 22 | return cache_lookup_function_signature[sighash] 23 | -------------------------------------------------------------------------------- /ethereum_dasm/utils/api.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # Author : 4 | import requests 5 | 6 | 7 | class EthJsonRpc(object): 8 | 9 | def __init__(self, url): 10 | self.url = url 11 | self.id = 1 12 | self.session = requests.session() 13 | 14 | def call(self, method, params=None): 15 | 16 | params = params or [] 17 | data = { 18 | 'jsonrpc': '2.0', 19 | 'method': method, 20 | 'params': params, 21 | 'id': self.id, 22 | } 23 | headers = {'Content-Type': 'application/json'} 24 | resp = self.session.post(self.url, headers=headers, json=data) 25 | 26 | self.id += 1 27 | return resp.json() 28 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | from setuptools import setup, find_packages 6 | 7 | def read(fname): 8 | return open(os.path.join(os.path.dirname(__file__), fname)).read() 9 | 10 | version="0.1.5" 11 | 12 | setup( 13 | name="ethereum-dasm", 14 | version=version, 15 | packages=find_packages(), 16 | author="tintinweb", 17 | author_email="tintinweb@oststrom.com", 18 | description=( 19 | "An ethereum bytecode disassembler with static and dynamic analysis features"), 20 | license="GPLv2", 21 | keywords=["ethereum", "blockchain", "evm", "disassembler", "decompiler", "pseudocode"], 22 | url="https://github.com/tintinweb/ethereum-dasm", 23 | download_url="https://github.com/tintinweb/ethereum-dasm/tarball/v%s"%version, 24 | #python setup.py register -r https://testpypi.python.org/pypi 25 | long_description=read("README.md") if os.path.isfile("README.md") else "", 26 | long_description_content_type='text/markdown', 27 | install_requires=["evmdasm", 28 | "colorama", 29 | "requests", 30 | "tabulate"], 31 | #package_data={}, 32 | extras_require={"mythril": ["mythril"], # for laser-ethereum 33 | "abidecoder": ["ethereum-input-decoder", "pyetherchain"],}, 34 | ) 35 | -------------------------------------------------------------------------------- /ethereum_dasm/utils/data.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # Author : 4 | import binascii 5 | 6 | 7 | def hex_decode(s): 8 | try: 9 | return bytes.fromhex(s).decode('ascii') 10 | except (NameError, AttributeError): 11 | return s.decode("hex") 12 | except UnicodeDecodeError: 13 | return '' # invalid 14 | 15 | 16 | def is_ascii_subsequence(s, min_percent=0.51): 17 | if len(s) == 0: 18 | return False 19 | if isinstance(s, bytes): 20 | s = s.rstrip(b'\x00') # ignore zero padding 21 | if not len(s): 22 | return False 23 | return [128 > c > 0x20 for c in s].count(True) / float(len(s)) >= min_percent 24 | return [128 > ord(c) > 0x20 for c in s].count(True) / float(len(s)) >= min_percent 25 | 26 | 27 | def is_all_ascii(s): 28 | if isinstance(s, bytes): 29 | return all(128 > c > 0x20 for c in s) 30 | return all(128 > ord(c) > 0x20 for c in s) 31 | 32 | 33 | def str_to_bytes(s): 34 | """ 35 | Convert 0xHexString to bytes 36 | :param s: 0x hexstring 37 | :return: byte sequence 38 | """ 39 | try: 40 | return bytes.fromhex(s.replace("0x", "")) 41 | except (NameError, AttributeError): 42 | return s.decode("hex") 43 | 44 | 45 | def bytes_to_str(s, prefix="0x"): 46 | return "%s%s" % (prefix,binascii.hexlify(s).decode("utf-8")) 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | -------------------------------------------------------------------------------- /ethereum_dasm/utils/colors.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # Author : 4 | 5 | from ethereum_dasm.asm.registry import * 6 | 7 | try: 8 | # optional terminal colors 9 | import colorama 10 | 11 | colorama.init(autoreset=True) 12 | except ImportError: 13 | colorama = None 14 | 15 | 16 | class Color: 17 | """ 18 | Fore: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET. 19 | Back: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET. 20 | Style: DIM, NORMAL, BRIGHT, RESET_ALL 21 | """ 22 | 23 | @staticmethod 24 | def location(s): 25 | if not colorama: 26 | return s 27 | return colorama.Fore.WHITE + colorama.Style.BRIGHT + s + colorama.Style.RESET_ALL 28 | 29 | @staticmethod 30 | def instruction(s): 31 | if not colorama: 32 | return s 33 | 34 | if any(s.startswith(i) for i in INSTRUCTION_MARKS_BASICBLOCK_END): 35 | return colorama.Fore.YELLOW + colorama.Style.BRIGHT + s + colorama.Style.RESET_ALL 36 | elif s in ("ADD", "MUL", "SUB", "DIV", "SDIV", "MOD", "SMOD", "ADDMOD", "MULMOD", "EXP", "SIGNEXTEND"): 37 | return colorama.Fore.CYAN + s + colorama.Style.RESET_ALL 38 | elif s in ("LT", "GT", "SLT", "SGT", "EQ", "ISZERO", "AND", "OR", "XOR", "NOT", "BYTE", "SHL", "SHR", "SAR"): 39 | return colorama.Fore.MAGENTA + colorama.Style.BRIGHT + s + colorama.Style.RESET_ALL 40 | elif any(s.startswith(i) for i in ("PUSH", "POP", "SWAP", "DUP")): 41 | return colorama.Back.RESET + colorama.Fore.YELLOW + colorama.Style.DIM + s + colorama.Style.RESET_ALL 42 | elif any(s.startswith(i) for i in ("CREATE", "CALL", "CALLCODE", "DELEGATECALL", "STATICCALL", "REVERT", 43 | "SELFDESTRUCT", "CALLDATALOAD", "CALLDATACOPY", "EXTCODECOPY")): 44 | return colorama.Back.RESET + colorama.Fore.RED + colorama.Style.BRIGHT + s + colorama.Style.RESET_ALL 45 | elif any(i in s for i in ("LOAD", "STORE", "LOG")): 46 | return colorama.Back.RESET + colorama.Fore.GREEN + colorama.Style.NORMAL + s + colorama.Style.RESET_ALL 47 | elif s.startswith("UNKNOWN"): 48 | return colorama.Back.LIGHTRED_EX + s + colorama.Style.RESET_ALL 49 | 50 | return s 51 | 52 | @staticmethod 53 | def address(s, fmt="3d"): 54 | if not colorama: 55 | return fmt % s 56 | 57 | return colorama.Back.RESET + colorama.Fore.GREEN + colorama.Style.DIM + fmt % s + colorama.Style.RESET_ALL 58 | 59 | @staticmethod 60 | def gas(s, fmt="%3d"): 61 | if not colorama: 62 | return fmt % s 63 | 64 | if s <= 0: 65 | return colorama.Fore.YELLOW + colorama.Style.DIM + colorama.Back.LIGHTYELLOW_EX + fmt % s + colorama.Style.RESET_ALL 66 | elif s < 10: 67 | return colorama.Back.RESET + colorama.Fore.YELLOW + colorama.Style.DIM + fmt % s + colorama.Style.RESET_ALL 68 | elif s < 30: 69 | return colorama.Back.RESET + colorama.Fore.YELLOW + colorama.Style.NORMAL + fmt % s + colorama.Style.RESET_ALL 70 | else: 71 | return colorama.Fore.YELLOW + colorama.Style.BRIGHT + fmt % s + colorama.Style.RESET_ALL 72 | 73 | @staticmethod 74 | def description(s): 75 | if not colorama: 76 | return s 77 | return colorama.Style.DIM + colorama.Fore.GREEN + s + colorama.Style.RESET_ALL -------------------------------------------------------------------------------- /ethereum_dasm/asm/instructions.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # Author : 4 | """ 5 | Verbose EthereumVM Disassembler 6 | 7 | OPCODES taken from: 8 | https://github.com/ethereum/go-ethereum/blob/master/core/vm/opcodes.go 9 | https://github.com/ethereum/yellowpaper/blob/master/Paper.tex 10 | """ 11 | 12 | import logging 13 | import random 14 | import ethereum_dasm.utils as utils 15 | import evmdasm.instructions 16 | 17 | logger = logging.getLogger(__name__) 18 | 19 | 20 | class BasicBlock(object): 21 | 22 | def __init__(self, address=None, name=None, instructions=None): 23 | self.instructions = instructions or [] 24 | self.address = address 25 | self.name = name 26 | self.annotations = [] 27 | 28 | self.next = None 29 | self.previous = None 30 | 31 | def __repr__(self): 32 | return "" % (self.address, len(self.instructions)) 33 | 34 | def skip_to(self, names): 35 | for instr in self.instructions: 36 | if any(instr.name==name for name in names): 37 | return instr 38 | return None 39 | 40 | def find_sequence(self, seq, prn=lambda a,b:a==b): 41 | i_instr = iter(self.instructions) 42 | 43 | try: 44 | i = 0 45 | seqlen = len(seq) 46 | while True: 47 | instr = next(i_instr) 48 | if prn(instr.name,seq[i]): 49 | i += 1 50 | elif i > 0: 51 | # abort no match 52 | break 53 | if i+1==seqlen: 54 | return True 55 | except StopIteration: 56 | print("STOPITER") 57 | pass 58 | return False 59 | 60 | 61 | class Instruction(evmdasm.instructions.Instruction): 62 | 63 | def __init__(self, opcode, name, length_of_operand=0, description=None, args=None, returns=None, gas=-1, 64 | category=None, pops=None, pushes=None, fork=None): 65 | super().__init__(opcode=opcode, name=name, 66 | length_of_operand=length_of_operand, 67 | description=description, 68 | args=args, returns=returns, 69 | gas=gas, category=category, 70 | pops=pops, pushes=pushes, fork=fork) 71 | 72 | # additional attribs 73 | self.annotations = [] 74 | self.xrefs = set([]) 75 | self.jumpto = None 76 | self.basicblock = None 77 | 78 | @staticmethod 79 | def from_evmdasm_instruction(evmdasm_instruction): 80 | return Instruction() 81 | 82 | def randomize_operand(self): 83 | self.operand_bytes = bytes(bytearray(random.getrandbits(8) for _ in range(self.length_of_operand))) 84 | return self 85 | 86 | def describe_operand(self, resolve_funcsig=False): 87 | if not self.operand: 88 | str_operand = '' 89 | elif resolve_funcsig and len(self.operand) == 8 and self.address < 0x100: 90 | # speed improvment: its very unlikely that there will be funcsigs after addr 400 91 | # 4bytes, could be a func-sig 92 | logger.info("online sighash lookup for: %s"%self.operand) 93 | pot_funcsigs = utils.signatures.lookup_function_signature(self.operand) 94 | if len(pot_funcsigs) == 0: 95 | ascii = '' 96 | elif len(pot_funcsigs) == 1: 97 | ascii = ' --> \'function %s\'' % pot_funcsigs[0] 98 | else: 99 | ascii = ' --> *ambiguous* \'function %s\'' % pot_funcsigs[0] 100 | 101 | str_operand = "0x%s%s" % (self.operand, ascii) 102 | elif len(self.operand_bytes) >= 4: 103 | try: 104 | ascii = ' (%r)' % self.operand_bytes.decode("utf-8") \ 105 | if self.operand_bytes and utils.is_ascii_subsequence(self.operand_bytes) else '' 106 | except UnicodeDecodeError: 107 | ascii = '' 108 | str_operand = "0x%s%s" % (self.operand, ascii) 109 | else: 110 | ascii = '' 111 | str_operand = "0x%s%s" % (self.operand, ascii) 112 | 113 | extra = "@%s" % hex(self.jumpto) if self.jumpto else '' 114 | return "%s%s" % (str_operand, extra) 115 | -------------------------------------------------------------------------------- /ethereum_dasm/output/console.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # Author : 4 | """ 5 | console output writer 6 | """ 7 | 8 | 9 | from ethereum_dasm import utils 10 | from ethereum_dasm.utils import colors 11 | import ethereum_dasm.asm.registry as registry 12 | import textwrap 13 | import tabulate 14 | tabulate.PRESERVE_WHITESPACE = True 15 | 16 | 17 | def format_comment_block(it, indent=0): 18 | s = [indent * " " + "/*******************************************************************"] 19 | for i in it: 20 | s.append((2+indent)*" "+ "%s" % i) 21 | s.append(indent*" "+ "*******************************************************************/") 22 | return "\n".join(s) 23 | 24 | 25 | class EVMDasmPrinter: 26 | """ utility class for different output formats 27 | """ 28 | 29 | @staticmethod 30 | def listing(evmcode, json=False): 31 | for i, nm in enumerate(evmcode.instructions): 32 | if json: 33 | print(json.dumps({"name":nm.name, "operand":nm.operand})) 34 | else: 35 | print("%s %s" % (nm.name, nm.operand)) 36 | 37 | @staticmethod 38 | def detailed(evmcode, resolve_funcsig=False): 39 | print("%-3s %-4s %-3s %-15s %-36s %-30s %s" % ( 40 | "Inst", "addr", " hex ", "mnemonic", "operand", "xrefs", "description")) 41 | print("-" * 150) 42 | # listify it in order to resolve xrefs, jumps 43 | for i, nm in enumerate(evmcode.instructions): 44 | if nm.name == "JUMPDEST": 45 | print(":loc_%s %s" % (hex(nm.address), "(%s)"%' --> '.join(nm.annotations))) 46 | try: 47 | operand = ','.join('%s@%s' % (x.name, hex(x.address)) for x in nm.xrefs) if nm.xrefs else '' 48 | print("%4d [%3d 0x%0.3x] %-15s %-36s %-30s # %s %s" % (i, nm.address, nm.address, nm.name, 49 | nm.describe_operand(resolve_funcsig=resolve_funcsig), # todo: get rid of this and use evmcode.name_at(nm.address) 50 | operand, 51 | nm.description, 52 | "returns: %r" % nm.returns if nm.returns else "")) 53 | except Exception as e: 54 | print(e) 55 | if nm.name in registry.INSTRUCTION_MARKS_BASICBLOCK_END: 56 | print("") 57 | 58 | @staticmethod 59 | def basicblocks_detailed(evmcode, resolve_funcsig=False): 60 | 61 | # todo - remove annotations from objects and track them in evmcode 62 | print("%-4s %-4s %-5s %-3s | %-15s %-36s %-30s %s" % ( 63 | "Inst", "addr", " hex ", "gas", "mnemonic", "operand", "xrefs", "description")) 64 | print("-" * 150) 65 | 66 | i = 0 67 | for bb in evmcode.basicblocks: 68 | # every basicblock 69 | print("%s %s" % (utils.colors.Color.location(":loc_%s" % hex(bb.address)), 70 | "\n"+format_comment_block(bb.instructions[0].annotations, indent=2)+"\n" if bb.instructions[0].annotations else "")) 71 | for nm in bb.instructions: 72 | try: 73 | operand = ','.join('%s@%s' % (x.name, hex(x.address)) for x in nm.xrefs) if nm.xrefs else '' 74 | print("%4d [%-4s %-4s] %-3s | %-15s %-36s %-30s # %-60s %-30s %s" % (i, 75 | utils.colors.Color.address(nm.address, fmt="%4d"), 76 | utils.colors.Color.address(nm.address, fmt="0x%0.4x"), 77 | utils.colors.Color.gas(nm.gas), utils.colors.Color.instruction(nm.name), 78 | nm.describe_operand( 79 | resolve_funcsig=resolve_funcsig), # todo: get rid of this and use evmcode.name_at(nm.address) 80 | operand, 81 | nm.description, 82 | "returns: %s" % ', '.join(map(str,nm.returns)) if nm.returns else "", 83 | "args: %s" % ', '.join(map(str,nm.args)) if nm.args else "")) 84 | 85 | except Exception as e: 86 | print(e) 87 | 88 | i += 1 89 | if nm.name in registry.INSTRUCTION_MARKS_BASICBLOCK_END: 90 | print("") 91 | 92 | @staticmethod 93 | def basicblocks_detailed_tabulate(evmcode, resolve_funcsig=False): 94 | headers = [" %-4s %-4s %-5s %-3s | %-15s %-36s" % ("Inst", "addr", " hex ", "gas", "mnemonic", "operand"), 95 | "xrefs", "description", "retval", "args"] 96 | 97 | lines = [] 98 | # todo - remove annotations from objects and track them in evmcode 99 | 100 | i = 0 101 | for bb in evmcode.basicblocks: 102 | # every basicblock 103 | 104 | lines.append(["%s %s" % (utils.colors.Color.location(":loc_%s" % hex(bb.address)), 105 | "\n" + format_comment_block(bb.instructions[0].annotations, indent=2) + "\n" if 106 | bb.instructions[0].annotations else "")]) 107 | 108 | for nm in bb.instructions: 109 | try: 110 | operand = ','.join('%s@%s' % (x.name, utils.colors.Color.location(hex(x.address))) for x in nm.xrefs) if nm.xrefs else '' 111 | 112 | lines.append([" %4d [%-4s %-4s ] %-3s | %-15s %-36s" % (i, 113 | utils.colors.Color.address(nm.address, fmt="%4d"), 114 | utils.colors.Color.address(nm.address, fmt="0x%0.4x"), 115 | utils.colors.Color.gas(nm.gas), 116 | utils.colors.Color.instruction(nm.name), 117 | nm.describe_operand(resolve_funcsig=resolve_funcsig),), 118 | 119 | # todo: get rid of this and use evmcode.name_at(nm.address) 120 | operand, 121 | '\n '.join(textwrap.wrap("%s %s" % (utils.colors.Color.description("#"), nm.description))), 122 | '\n'.join(textwrap.wrap(', '.join(map(str,nm.returns)) if nm.returns else "", width=20)), 123 | '\n'.join(textwrap.wrap(', '.join(map(str,nm.args)) if nm.args else "", width=20))]) 124 | 125 | except Exception as e: 126 | print(e) 127 | 128 | i += 1 129 | if nm.name in registry.INSTRUCTION_MARKS_BASICBLOCK_END: 130 | lines.append([" "]) 131 | print(tabulate.tabulate(lines, headers=headers, numalign="right")) -------------------------------------------------------------------------------- /ethereum_dasm/symbolic/simplify.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # Author : 4 | 5 | import z3 6 | import logging 7 | import mythril.laser.smt 8 | 9 | logger = logging.getLogger(__name__) 10 | 11 | def pp_hexint(a): 12 | return z3.z3printer.to_format(get_z3_value_hex(a)) 13 | 14 | z3.z3printer._Formatter.pp_bv = pp_hexint 15 | z3.z3printer._Formatter.pp_int = pp_hexint 16 | #z3.z3printer._z3_op_to_str[] 17 | 18 | 19 | def format_comment_block(it, indent=0): 20 | s = [indent * " " + "/*******************************************************************"] 21 | for i in it: 22 | s.append((2+indent)*" "+ "%s" % i) 23 | s.append(indent*" "+ "*******************************************************************/") 24 | return "\n".join(s) 25 | 26 | def get_z3_value_hex(item): 27 | 28 | if (type(item) == int): 29 | return hex(item) 30 | 31 | elif (type(item) == mythril.laser.smt.bitvec.BitVec and not item.symbolic): 32 | return hex(item.value) 33 | elif (type(item) == z3.BitVecNumRef): 34 | return hex(item.as_long()) 35 | 36 | try: 37 | return hex(z3.simplify(item).as_long()) 38 | except AttributeError: 39 | return str(z3.simplify(item)).replace("\n","\n ") 40 | except z3.Z3Exception: 41 | return str(item).replace("\n","\n ") 42 | 43 | def get_z3_value(item): 44 | 45 | if (type(item) == int): 46 | return item 47 | 48 | elif (type(item) == mythril.laser.smt.bitvec.BitVec and not item.symbolic): 49 | return item.value 50 | elif (type(item) == z3.BitVecNumRef): 51 | return item.as_long() 52 | 53 | try: 54 | return z3.simplify(item).as_long() 55 | except AttributeError: 56 | return str(z3.simplify(item)) 57 | except z3.Z3Exception: 58 | return str(item) 59 | 60 | 61 | class MetaCode: 62 | 63 | @staticmethod 64 | def jump(asm_stmt): 65 | name = asm_stmt.evmcode.name_for_location.get(asm_stmt.instr.jumpto) 66 | name = "goto function_%s (LOC_%%(evm.pc)s)" % name if name else "goto LOC_%(evm.pc)s" 67 | 68 | return PseudoCodeStatement(asm=asm_stmt, fmt=name) 69 | 70 | @staticmethod 71 | def jumpi(asm_stmt): 72 | name = asm_stmt.evmcode.name_for_location.get(asm_stmt.instr.jumpto) 73 | name = "if (%%(condition)s) goto function_%s (LOC_%%(evm.pc)s)" % name if name else "if (%(condition)s) goto LOC_%(evm.pc)s" 74 | return PseudoCodeStatement(asm=asm_stmt, fmt=name) 75 | 76 | @staticmethod 77 | def jumpdest(asm_stmt): 78 | # funcsig = asm_stmt.evmcode.function_at.get(asm_stmt.instr.address) 79 | annotations = "\n"+format_comment_block(asm_stmt.instr.annotations, indent=2)+"\n" if asm_stmt.instr.annotations else "" 80 | 81 | name = asm_stmt.evmcode.name_for_location.get(asm_stmt.instr.address) 82 | name = "function_%s (LOC_%s)"%(name,hex(asm_stmt.instr.address)) if name else "LOC_%s"%hex(asm_stmt.instr.address) 83 | 84 | return PseudoCodeStatement(asm=asm_stmt, fmt=":%s %s"%(name,annotations)) 85 | 86 | @staticmethod 87 | def cat_terminate(asm_stmt): 88 | return PseudoCodeStatement(asm=asm_stmt) 89 | 90 | @staticmethod 91 | def cat_event(asm_stmt): 92 | return PseudoCodeStatement(asm=asm_stmt) 93 | 94 | @staticmethod 95 | def cat_system(asm_stmt): 96 | return PseudoCodeStatement(asm=asm_stmt) 97 | 98 | @staticmethod 99 | def cat_storage(asm_stmt): 100 | if "STORE" not in asm_stmt.instr.name: 101 | return 102 | return PseudoCodeStatement(asm=asm_stmt, 103 | fmt=asm_stmt.instr.category+"[%(loc)s] = %(value)s") 104 | 105 | @staticmethod 106 | def cat_memory(asm_stmt): 107 | if "STORE" not in asm_stmt.instr.name: 108 | return 109 | return PseudoCodeStatement(asm=asm_stmt, 110 | fmt=asm_stmt.instr.category + "[%(offset)s] = %(value)s") 111 | 112 | 113 | 114 | class AsmStatement(object): 115 | 116 | def __init__(self, instr, stack, evmcode): 117 | self.instr = instr 118 | self.stack = stack 119 | self.evmcode = evmcode 120 | 121 | self.args = tuple([]) 122 | self.args_as_dict = {} 123 | self.returns = tuple([]) 124 | self.returns_as_dict = {} 125 | try: 126 | self.stack_next = self.evmcode.symbolic_state_at[instr.next.address][0][1].mstate.stack 127 | except (KeyError, AttributeError): 128 | # keyerror: no state available 129 | # attributeerror: instr.next is none (last instr) 130 | self.stack_next = None # stack unavailable. 131 | 132 | if self.stack: 133 | self.args = tuple(zip(map(str,instr.args), [get_z3_value_hex(self.stack[len(self.stack) - 1 - i]) for i in range(len(instr.args))])) 134 | self.args_as_dict = dict(self.args) 135 | 136 | if self.stack_next: 137 | self.returns = tuple(zip(map(str,instr.returns), [get_z3_value_hex(self.stack_next[len(self.stack_next) - 1 - i]) for i in range(len(instr.returns))])) 138 | else: 139 | self.returns = tuple(zip(map(str,instr.returns), ['?'] * len(instr.returns))) 140 | self.returns_as_dict = dict(self.returns) 141 | 142 | self._pseudocode = None 143 | 144 | def __str__(self): 145 | return "%s (%s) returns (%s)" % (self.instr, 146 | ", ".join("%s=%s"%(a, " ".join(l.strip() for l in get_z3_value_hex(b).split("\n"))) for a,b in self.args), # can be multiline... z3.PP formatter ftw 147 | ", ".join("%s=%s" % (a, " ".join(l.strip() for l in get_z3_value_hex(b).split("\n"))) for a, b in self.returns)) 148 | 149 | def __repr__(self): 150 | return self.__str__() 151 | 152 | @property 153 | def as_pseudocode(self): 154 | if self._pseudocode is not None: 155 | return self._pseudocode 156 | 157 | f = getattr(MetaCode, self.instr.name.lower(), getattr(MetaCode, "cat_%s" % self.instr.category.lower(), None)) 158 | if not f: 159 | self._pseudocode = False # mark as not resolveable 160 | else: 161 | self._pseudocode = f(self) 162 | 163 | return self._pseudocode 164 | 165 | 166 | class PseudoCodeStatement(object): 167 | 168 | def __init__(self, asm, fmt=None): 169 | self.asm = asm 170 | self.fmt = fmt 171 | 172 | def __str__(self): 173 | if self.fmt: 174 | # use fmt if specified 175 | return self.fmt % self.asm.args_as_dict if self.asm.args else self.fmt 176 | 177 | return "%s(%s)" % (self.asm.instr.name, 178 | (", ".join("%s=%s" % (a, b) for a, b in self.asm.args))) 179 | 180 | 181 | def simplify(evmcode, show_asm=False, show_pseudocode=True, show_unreachable=False): 182 | """ 183 | 184 | skip: 185 | * arithmetic 186 | * bitwise 187 | skip: (symbolic) 188 | * envinfo 189 | * sha3 190 | * blockinfo 191 | yield: 192 | * mem|storage store 193 | * JUMP(i) 194 | * JUMPDEST 195 | * LOG() -->cat event 196 | * SYSTEM_OPERATIONS --> 197 | * TERMINATE --> 198 | 199 | :param evmcode: 200 | :return: 201 | """ 202 | yield ":init" 203 | for instr in evmcode.instructions: 204 | try: 205 | asm_stmt = AsmStatement(instr=instr, 206 | stack=evmcode.symbolic_state_at[instr.address][0][1].mstate.stack if not instr.name in ("JUMPDEST",) else None, 207 | evmcode=evmcode) 208 | except KeyError as ke: 209 | if show_unreachable: 210 | yield("\t//UnreachableCodeException @:LOC_%s [!!!!] <-- exception: %r --> %r" % (hex(instr.address), ke, instr)) 211 | continue 212 | 213 | if show_asm and asm_stmt: 214 | yield "\t//%s"%asm_stmt 215 | 216 | if not show_pseudocode: 217 | continue 218 | 219 | if asm_stmt.as_pseudocode: 220 | # yield "// LOC_%s " % (hex(instr.address)) 221 | if instr.category=="label": 222 | yield "\n%s"%asm_stmt.as_pseudocode # yield blank line before printing label 223 | else: 224 | yield "\t%s"%"\n\t".join(str(asm_stmt.as_pseudocode).splitlines()) 225 | if instr.category=="terminate": 226 | yield "\t/******* <> *******/" 227 | 228 | -------------------------------------------------------------------------------- /ethereum_dasm/symbolic/mythril.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # Author : 4 | from mythril.analysis.symbolic import SymExecWrapper 5 | from mythril.ethereum.evmcontract import EVMContract 6 | 7 | from collections import defaultdict 8 | 9 | 10 | class DiGraph(object): 11 | 12 | def __init__(self): 13 | self._graph = defaultdict(list) 14 | self._conditions = {} 15 | self._first_added = None 16 | 17 | @property 18 | def nodes(self): 19 | """ returns the vertices of a graph """ 20 | return list(self._graph.keys()) 21 | 22 | @property 23 | def edges(self): 24 | """ returns the edges of a graph """ 25 | edges = [] 26 | for vertex, neighbours in self._graph.items(): 27 | for neighbour in neighbours: 28 | if {neighbour, vertex} not in edges: 29 | edges.append({vertex, neighbour}) 30 | return edges 31 | 32 | def add_node(self, vertex): 33 | if not self._first_added: 34 | self._first_added = vertex # remember first vertex 35 | self._graph[vertex] = [] 36 | 37 | def add_edge(self, frm, to, condition): 38 | if not self._first_added: 39 | self._first_added = frm # remember first vertex 40 | 41 | self._graph[frm].append(to) 42 | self._conditions[(frm, to)] = condition 43 | 44 | def iterate_graph(self, start=None): 45 | start = start or self._first_added 46 | 47 | yield start 48 | # {start:[vertex1,vertex2,...] 49 | for vertex in self._graph[start]: 50 | for v in self.iterate_graph(vertex): 51 | yield v 52 | 53 | def __repr__(self): 54 | return "<%s nodes:%d edges:%d>" %(self.__class__.__name__, 55 | len(self.nodes), 56 | len(self.edges)) 57 | 58 | def find_isolated_vertices(self): 59 | """ returns a list of isolated vertices. """ 60 | graph = self._graph 61 | isolated = [] 62 | for vertex in graph: 63 | print(isolated, vertex) 64 | if not graph[vertex]: 65 | isolated += [vertex] 66 | return isolated 67 | 68 | def find_path(self, start_vertex, end_vertex, _path=None): 69 | """ find a path from start_vertex to end_vertex 70 | in graph """ 71 | _path = _path or [] 72 | graph = self._graph 73 | _path = _path + [start_vertex] 74 | if start_vertex == end_vertex: 75 | return _path 76 | if start_vertex not in graph: 77 | return None 78 | for vertex in graph[start_vertex]: 79 | if vertex not in _path: 80 | extended_path = self.find_path(vertex, 81 | end_vertex, 82 | _path) 83 | if extended_path: 84 | return extended_path 85 | return None 86 | 87 | def find_all_paths(self, start_vertex, end_vertex, _path=None): 88 | """ find all paths from start_vertex to 89 | end_vertex in graph """ 90 | _path = _path or [] 91 | graph = self._graph 92 | _path = _path + [start_vertex] 93 | if start_vertex == end_vertex or ( 94 | not end_vertex and not graph[start_vertex]): # start_vertex == [] <-- no more vertices 95 | return [_path] 96 | if start_vertex not in graph: 97 | return [] 98 | paths = [] 99 | 100 | for vertex in graph[start_vertex]: 101 | if vertex not in _path: 102 | extended_paths = self.find_all_paths(vertex, 103 | end_vertex, 104 | _path) 105 | for p in extended_paths: 106 | paths.append(p) 107 | return paths 108 | 109 | 110 | class MythrilSymExecGraph(DiGraph): 111 | 112 | def __init__(self): 113 | super().__init__() 114 | 115 | def get_block_and_state_by_address(self, address, prn_cmp=None): 116 | if not prn_cmp: 117 | prn_cmp = lambda x, y: x == y 118 | 119 | # find all basicblocks that contain instructions from a specific address 120 | for n in self.nodes: 121 | for s in n.states: 122 | instr = s.get_current_instruction() 123 | if prn_cmp(instr["address"], address): 124 | yield n, s 125 | break 126 | 127 | def get_block_and_state_by_instruction_name(self, name, prn_cmp=None): 128 | """ 129 | 130 | # find all pushes 131 | print(list(s.get_current_instruction() for b,s in graph.get_block_and_state_by_instruction_name("PUSH",prn_cmp=lambda x,y:x.startswith(y)))) 132 | 133 | :param name: 134 | :param prn_cmp: 135 | :return: 136 | """ 137 | if not prn_cmp: 138 | prn_cmp = lambda x, y: x == y 139 | 140 | # find all basicblocks that contain instructions from a specific address 141 | for n in self.nodes: 142 | for s in n.states: 143 | instr = s.get_current_instruction() 144 | if prn_cmp(instr["opcode"], name): 145 | yield n, s 146 | break 147 | 148 | def get_block_by_uid(self, uid): 149 | return next(b for b in self.nodes if b.uid == uid) 150 | 151 | def get_streams(self, start=None): 152 | """ 153 | Get all possible paths/forks/execution streams from starting block end of execution 154 | 155 | Example: 156 | for stream in graph.get_streams(graph.get_basicblock_by_uid(0)): 157 | print([s.uid for s in stream]) 158 | 159 | [0, 1] 160 | [0, 2, 3, 33, 35, 36, 38] 161 | [0, 2, 3, 33, 35, 37] 162 | [0, 2, 3, 34] 163 | [0, 2, 4, 5, 29, 31, 32] 164 | [0, 2, 4, 5, 30] 165 | [0, 2, 4, 6, 7, 25, 27, 28] 166 | [0, 2, 4, 6, 7, 26] 167 | [0, 2, 4, 6, 8, 9, 11, 13, 14, 16, 18, 20, 21, 23] 168 | [0, 2, 4, 6, 8, 9, 11, 13, 14, 16, 18, 20, 21, 24] 169 | [0, 2, 4, 6, 8, 9, 11, 13, 14, 16, 18, 20, 22] 170 | [0, 2, 4, 6, 8, 9, 11, 13, 14, 16, 19] 171 | [0, 2, 4, 6, 8, 9, 11, 13, 14, 17] 172 | [0, 2, 4, 6, 8, 9, 11, 13, 15] 173 | [0, 2, 4, 6, 8, 9, 12] 174 | [0, 2, 4, 6, 8, 10] 175 | 176 | 177 | :param start: 178 | :return: 179 | """ 180 | start = start or self.get_block_by_uid(0) 181 | return self.find_all_paths(start_vertex=start, end_vertex=None) 182 | 183 | def get_stream_by_block(self, block, start=None): 184 | start = start or self.get_block_by_uid(0) 185 | 186 | all_streams = self.get_streams(start=start) 187 | 188 | result_streams = [] 189 | 190 | for s in all_streams: 191 | if block in s: 192 | result_streams.append(s) 193 | continue 194 | return result_streams 195 | 196 | def iterate_blocks(self, start): 197 | start = start or self.get_block_by_uid(0) 198 | 199 | return self.iterate_graph(start=start) 200 | 201 | def iterate_blocks_and_states(self): 202 | for node in self.nodes: 203 | for state in node.states: 204 | yield node, state 205 | 206 | 207 | def symbolic_execute(code, address): 208 | contract = EVMContract(code) 209 | sym = SymExecWrapper(contract=contract, address=address, strategy="dfs") 210 | 211 | # populate graph object 212 | graph = MythrilSymExecGraph() 213 | for n in sym.nodes.values(): 214 | # g.add all nodes - just in case one is not connected which should not happen 215 | graph.add_node(n) 216 | 217 | for e in sym.edges: 218 | graph.add_edge(sym.nodes[e.node_from], sym.nodes[e.node_to], e) 219 | 220 | """ 221 | print(graph.iterate_graph()) 222 | print(sorted([x.uid for x in graph.nodes])) 223 | print(graph._first_added.uid) 224 | print(next(graph.get_basicblocks_by_address(0))) 225 | print(graph.get_basicblock_by_uid(0)) 226 | print(list(n.uid for n in graph.iterate_graph(graph.get_basicblock_by_uid(0)))) 227 | 228 | print(graph.find_all_paths(graph.get_basicblock_by_uid(0), graph.get_basicblock_by_uid(37))) 229 | print(graph.find_all_paths(graph.get_basicblock_by_uid(0),None)) 230 | print("streams") 231 | for stream in graph.get_streams(graph.get_basicblock_by_uid(0)): 232 | print([s.uid for s in stream]) 233 | 234 | print(list(s.get_current_instruction() for b,s in graph.get_block_and_state_by_instruction_name("PUSH",prn_cmp=lambda x,y:x.startswith(y)))) 235 | 236 | print(graph.get_stream_by_block(block=graph.get_block_by_uid(2))) 237 | """ 238 | # only work on the graph from now 239 | return graph 240 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /ethereum_dasm/evmdasm.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # Author : 4 | """ 5 | Verbose EthereumVM Disassembler 6 | 7 | OPCODES taken from: 8 | https://github.com/ethereum/go-ethereum/blob/master/core/vm/opcodes.go 9 | https://github.com/ethereum/yellowpaper/blob/master/Paper.tex 10 | """ 11 | 12 | import logging 13 | import sys 14 | import os 15 | import time 16 | from evmdasm.disassembler import EvmDisassembler 17 | 18 | from ethereum_dasm import utils 19 | from ethereum_dasm.utils import colors 20 | from ethereum_dasm.asm import BasicBlock 21 | import ethereum_dasm.asm.registry as registry 22 | from ethereum_dasm.output import console 23 | 24 | 25 | 26 | try: 27 | import ethereum_dasm.symbolic.simplify as evmsimplify 28 | from ethereum_dasm.symbolic.simplify import get_z3_value 29 | supports_z3 = True 30 | except ImportError as ie: 31 | get_z3_value = lambda x: str(x) 32 | supports_z3 = False 33 | 34 | 35 | 36 | 37 | logger = logging.getLogger(__name__) 38 | 39 | try: 40 | from ethereum_dasm.symbolic.mythril import symbolic_execute 41 | import z3 42 | except ImportError as ie: 43 | symbolic_execute = None 44 | z3 = None 45 | logger.warning("Symbolic Execution not available: %s" % ie) 46 | 47 | try: 48 | import pyetherchain.pyetherchain as pyetherchain 49 | except ImportError as ie: 50 | pyetherchain = None 51 | logger.warning("pyetherchain not available: %s" % ie) 52 | 53 | 54 | 55 | 56 | class Contract(object): 57 | """ 58 | Main Input Class 59 | """ 60 | 61 | def __init__(self, bytecode=None, address=None, static_analysis=True, dynamic_analysis=True, network="mainnet", simplify_show_unreachable=False, simplify_show_asm=False): 62 | if not bytecode and not address: 63 | raise Exception("missing bytecode or contract address") 64 | 65 | if not bytecode: 66 | api = utils.EthJsonRpc("https://%s.infura.io/"%network) 67 | bytecode = api.call(method="eth_getCode", params=[address, "latest"])["result"] 68 | 69 | self.address = address 70 | self.bytecode, self.auxdata = Contract.get_auxdata(bytecode) 71 | self._evmcode = EvmCode(contract=self, 72 | static_analysis=static_analysis, dynamic_analysis=dynamic_analysis) # do not reference this directly, always use self.disassembly() 73 | 74 | self._simplify_show_unreachable, self._simplify_show_asm = simplify_show_unreachable, simplify_show_asm 75 | 76 | @property 77 | def disassembly(self): 78 | if not self._evmcode.instructions: 79 | self._evmcode.disassemble(bytecode=self.bytecode) 80 | return self._evmcode 81 | 82 | @property 83 | def simplified(self): 84 | return evmsimplify.simplify(self.disassembly, 85 | show_pseudocode=True, 86 | show_asm=self._simplify_show_asm, 87 | show_unreachable=self._simplify_show_unreachable) 88 | 89 | @property 90 | def methods(self): 91 | return self.disassembly.functions 92 | 93 | def guess_abi(self, txs=30): 94 | """ 95 | Guess the evmbytecodes ABI 96 | 97 | :return: 98 | """ 99 | 100 | """ 101 | 1) get all sighashes from disassembly 102 | 2) get all potential signatures 103 | 3) (optional) build the dict with sighashes we've already seen and verified on e.g. etherscan.io 104 | 4) build abi.json 105 | """ 106 | #todo: implement - this is just a quick hack 107 | 108 | if not self.address: 109 | raise Exception("address required.") 110 | 111 | 112 | etherchain = pyetherchain.EtherChain() 113 | account = etherchain.account(self.address) 114 | 115 | # try to get abi from etherchain 116 | try: 117 | abi = account.api.get_account_abi(account.address) 118 | if abi: 119 | return abi 120 | except Exception as e: 121 | # todo - JSONDecodeError or pyetherchainerror 122 | logger.warning("guess_abi: %r"%e) 123 | 124 | # guess abi from transactions 125 | # get all transaction inputs 126 | tx_inputs = set([]) 127 | for t in account.transactions( 128 | start=0, length=txs, direction="in")["data"]: 129 | tx = pyetherchain.EtherChainTransaction(tx=t["parenthash"]) 130 | if tx[0]["input"].strip(): 131 | tx_inputs.add(tx[0]["input"]) 132 | 133 | 134 | logger.debug("resolving %d unique inputs" % len(tx_inputs)) 135 | 136 | online_sighash_to_pseudo_abi = {} 137 | 138 | for tx_input in tx_inputs: 139 | pseudo_abi = utils.signatures.ethereum_input_decoder.decoder.FourByteDirectory.get_pseudo_abi_for_input(utils.str_to_bytes(tx_input)) 140 | lst_pa = list(pseudo_abi) 141 | # todo: hacky 142 | if lst_pa: 143 | online_sighash_to_pseudo_abi[lst_pa[0]["signature"]] = lst_pa 144 | 145 | # combine evm info with input decoder info 146 | abi_out = [] 147 | for sighash, evmdismethod in self.disassembly.functions.items(): 148 | # constant, inputs{name,type}, name, outputs{name,type}, payable, stateMutability, type, signature 149 | # "address":loc, 150 | # "signature_ascii":ascii, 151 | # "signatures": pot_funcsigs, 152 | # "payable": True,#functions are payable by default 153 | # "inputs": []} 154 | if not sighash.startswith("0x"): 155 | sighash = "0x%s"%sighash 156 | online_methods = online_sighash_to_pseudo_abi.get(sighash, []) 157 | online_input_method = {} 158 | for pa in online_methods: 159 | # list of online pseudo abis 160 | # check nr of parameters 161 | if len(pa["inputs"]) == len(evmdismethod.get("inputs")): 162 | online_input_method = pa 163 | break 164 | online_method = {} 165 | for pa in evmdismethod.get("signatures",[]): 166 | if len(pa["inputs"]) == len(evmdismethod.get("inputs")): 167 | online_method = pa 168 | break 169 | 170 | abi_method = {"signature": sighash, 171 | "address": evmdismethod.get("address"), 172 | "constant": None, # we can probably check this this statically (no STORE) or dynamically 173 | "name": online_input_method.get("name", online_method.get("name")), # from 4byte.directory 174 | "payable": evmdismethod.get("payable"), # from static analysis 175 | "stateMutability": None, # no clue 176 | "type": "function", 177 | "inputs": online_input_method.get("inputs", evmdismethod.get("inputs", online_method.get("inputs"))), 178 | "outputs":[], 179 | } 180 | abi_out.append(abi_method) 181 | return abi_out 182 | 183 | @staticmethod 184 | def get_auxdata(bytecode): 185 | # auxdata format: 0xa1 0x65 'b' 'z' 'z' 'r' '0' 0x58 0x20 <32 bytes swarm hash> 0x00 0x29 186 | signature_hexstr = 'a165%x%x%x%x%x5820'%(ord('b'),ord('z'),ord('z'),ord('r'),ord('0')) 187 | auxdata_index = bytecode.rfind(signature_hexstr) 188 | if auxdata_index < 0: 189 | return bytecode, {} 190 | # verify consistency 191 | auxdata={} 192 | auxdata['raw'] = bytecode[auxdata_index:] 193 | auxdata['swarm'] = auxdata['raw'][len(signature_hexstr):len(signature_hexstr)+32*2] 194 | 195 | if not auxdata['raw'][len(signature_hexstr)+32*2:len(signature_hexstr)+(32+2)*2]=="0029": 196 | logger.error("invalid auxdata format!") 197 | return bytecode, {} 198 | 199 | # cut auxdata from bytecode 200 | bytecode = bytecode[:auxdata_index] 201 | return bytecode, auxdata 202 | 203 | def trace_transaction(self, tx): 204 | api = utils.EthJsonRpc("https://%s.infura.io/" % "mainnet") 205 | bytecode = api.call(method="debug_traceTransaction", params=[tx, {"disableStorage": False, 206 | "disableMemory": False, 207 | "disableStack": False, 208 | "tracer": None, 209 | "timeout": None}]) 210 | 211 | 212 | 213 | class EvmCode(object): 214 | """ 215 | Main Disassembler Class 216 | 217 | designed like wdasm/idapro with instructions and annotations/tables with extra info per address 218 | """ 219 | def __init__(self, contract, debug=False, static_analysis=True, dynamic_analysis=True): 220 | # args 221 | self.contract = contract 222 | self.debug = debug 223 | self.enable_static_analysis = static_analysis 224 | self.enable_dynamic_analysis = dynamic_analysis 225 | 226 | # init 227 | self.disassembler = EvmDisassembler(debug=debug, _registry=registry.registry) 228 | 229 | self.first = None # first instruction 230 | self.last = None # last instruction 231 | 232 | self.instructions = [] # instructions as list to quickly jump to instruction at index 233 | self.instruction_at = {} # address:instruction - quickly jump to instruction at address 234 | 235 | self.basicblocks = [] # basicblocks as list to quickly iter basicblocks sequentially 236 | self.basicblock_at = {} # address:basicblock - quickly jump to basicblock at address 237 | # todo: basicblock with forking 238 | 239 | self.jumptable = {} # address:JUMP(I) - quickly find jump instructions (instr.jumpto holds the destination) 240 | self.function_call_dispatcher_jumptable = {} # address:JUMP(I) - of the dispatcher block 0 241 | self.xrefs_at = {} # address:set(ref istruction,ref instruction) - address to instruction for JUMP xrefs 242 | 243 | self.functions = {} # sighash: {"address","signature_ascii", "signatures", "payable", "inputs", "constant"} 244 | self.function_at = {} # address (JUMPDEST): sighash 245 | 246 | self.name_for_location = {} # address: function_name 247 | self.name_at = {} # address:name # todo - to be implemented: replace the "operand" overwrite in instruction() with a global table for this 248 | self.comments_at = {} # address:[comment,] todo implement in favor of instruction.annotation/basicblock.annotation 249 | self.annotations_at = {} # address:[annot,] todo implement in favor of instruction.annotation/basicblock.annotation 250 | 251 | self.strings_at = {} # collect potential strings in operands 252 | 253 | self.symbolic_state_at = {} # address: [(node,state)] (1:n mapping) - the symbolic execution state at address (can be multiple as it is a graph) 254 | 255 | #self.gas_at = {} # from symbolic execution: initial_gas - state.mstate.gas (gas up to instruction at address) # todo maybe implement 256 | 257 | self.duration = None 258 | 259 | def assemble(self, instructions=None): 260 | instructions = instructions or self.instructions 261 | return '0x' + ''.join(inst.serialize() for inst in instructions) 262 | 263 | def disassemble(self, bytecode=None): 264 | """ 265 | Disassemble bytecode to [Instructions,] 266 | :param bytecode: evm bytecode 267 | :param analyze: perform analysis steps 268 | :return: iterator to instructions 269 | 270 | Example: 271 | 272 | for inst in self.disassembler.disassemble(bytecode): 273 | # return them as we process them 274 | yield inst 275 | """ 276 | if not bytecode: 277 | return self.iterate() 278 | 279 | t_start = time.time() 280 | self.instructions = list(self.disassembler.disassemble(bytecode)) 281 | 282 | self.instruction_at = dict((instr.address, instr) for instr in self.instructions) 283 | 284 | self.first = self.instructions[0] 285 | self.last = self.instructions[-1] 286 | 287 | self.basicblocks = list(EvmCode._get_basicblocks(self.instructions)) 288 | self.basicblock_at = dict((bb.address, bb) for bb in self.basicblocks) 289 | 290 | self.jumptable = dict((instr.address, instr) for instr in self.instructions if instr.name in ("JUMP", "JUMPI")) 291 | 292 | # track strings >2 chars allascii 293 | self.strings_at = dict((instr.address, instr) for instr in self.instructions if instr.operand_bytes and len(instr.operand_bytes)>=2 and utils.is_all_ascii(instr.operand_bytes)) 294 | 295 | # ---> run analysis <--- 296 | self.analyze_static() 297 | self.analyze_dynamic() # dynamic would be sufficient 298 | 299 | 300 | # timekeeping 301 | self.duration = time.time() - t_start 302 | 303 | # current = self.first 304 | return self.iterate() 305 | 306 | def iterate(self, first=None): 307 | """ 308 | Iterate Blocks or Instructions (linked via .next) 309 | :param first: instruction 310 | :return: iterator 311 | """ 312 | 313 | current = first or self.first 314 | yield current 315 | while current.next: 316 | current = current.next 317 | yield current 318 | 319 | def find(self, instruction, start=None): 320 | current = start or self.first 321 | while current: 322 | if instruction == current.name: 323 | yield current 324 | current = current.next 325 | 326 | def find_within_address_range(self, start=0, end=65535): 327 | current = start or self.first 328 | while current: 329 | if start <= current.address < end: 330 | yield current 331 | current = current.next 332 | 333 | def insert(self, instruction, index): 334 | self.instructions.insert(index, instruction) 335 | # fix jumptable in current code. adding length of current jump 336 | self.reload() 337 | 338 | def reload(self): 339 | self.disassemble(bytecode=self.assemble()) # reanalyze 340 | 341 | @staticmethod 342 | def _get_basicblocks(disasm): 343 | # listify it in order to resolve xrefs, jumps 344 | current_basicblock = BasicBlock(address=0, name="init") 345 | 346 | for i, nm in enumerate(disasm): 347 | if nm.name == "JUMPDEST": 348 | # jumpdest belongs to the new basicblock (marks the start) 349 | yield current_basicblock 350 | prevs_basicblock = current_basicblock 351 | current_basicblock = BasicBlock(address=nm.address, 352 | name="loc_%s" % hex(nm.address)) 353 | 354 | # dbly linked for navigation <3 355 | prevs_basicblock.next = current_basicblock 356 | current_basicblock.previous = prevs_basicblock 357 | 358 | # add to current basicblock 359 | current_basicblock.instructions.append(nm) 360 | nm.basicblock = current_basicblock 361 | 362 | # yield the last basicblock 363 | yield current_basicblock 364 | 365 | def analyze_static(self): 366 | if not self.enable_static_analysis: 367 | return 368 | self._static_update_xrefs() # update statically known XREFs 369 | self._reconstruct_function_signatures() # find function signatures 370 | 371 | def _static_update_xrefs(self): 372 | """ 373 | update instruction.jumpto and self.xrefs with JUMP based xref information 374 | limitation: can only resolve XREFS based on [PUSH,JUMP(I)] 375 | :return: 376 | """ 377 | # find all JUMP, JUMPI's 378 | for loc, instruction in self.jumptable.items(): 379 | if instruction.previous and instruction.previous.name.startswith("PUSH"): 380 | instruction.jumpto = int(instruction.previous.operand, 16) 381 | target_instruction = self.instruction_at.get(instruction.jumpto) 382 | if target_instruction and target_instruction.name == "JUMPDEST": 383 | # valid address, valid target 384 | self.xrefs_at.setdefault(instruction.jumpto, set([])) 385 | self.xrefs_at[instruction.jumpto] = instruction 386 | target_instruction.xrefs.add(instruction) 387 | else: 388 | logger.error("invalid jump at %s" % instruction) 389 | 390 | def _reconstruct_function_signatures(self): 391 | 392 | # find CALLDATALOAD 393 | # find PUSH4 * JUMPI 394 | # check next block for CALLDATALOAD to get number of args and length 395 | 396 | # get first instruction 397 | # check first 3 blocks: 398 | instr_calldataload = self.basicblock_at[0].skip_to(["CALLDATALOAD"]) 399 | 400 | if not instr_calldataload or instr_calldataload.name != "CALLDATALOAD": 401 | return # todo remove 402 | raise Exception("missing CALLDATALOAD") 403 | 404 | loc_to_sighash = {} 405 | 406 | while instr_calldataload.name in ["CALLDATALOAD", "PUSH4", "EQ", "PUSH1", "PUSH2", "PUSH3", "JUMPI"]: 407 | # abort on jumpdest 408 | ipushfuncsig = instr_calldataload.skip_to( 409 | ["CALLDATALOAD", "PUSH4", "EQ", "PUSH1", "PUSH2", "PUSH3", "JUMPI", "JUMPDEST"]) 410 | ieq = ipushfuncsig.skip_to( 411 | ["CALLDATALOAD", "PUSH4", "EQ", "PUSH1", "PUSH2", "PUSH3", "JUMPI", "JUMPDEST"]) 412 | ipushaddr = ieq.skip_to( 413 | ["CALLDATALOAD", "PUSH4", "EQ", "PUSH1", "PUSH2", "PUSH3", "JUMPI", "JUMPDEST"]) 414 | ijumpi = ipushaddr.skip_to( 415 | ["CALLDATALOAD", "PUSH4", "EQ", "PUSH1", "PUSH2", "PUSH3", "JUMPI", "JUMPDEST"]) 416 | 417 | if not ipushfuncsig.name.startswith("PUSH") or ieq.name != "EQ" or not ipushaddr.name.startswith( 418 | "PUSH") or ijumpi.name != "JUMPI": 419 | break 420 | 421 | loc_to_sighash[ijumpi.jumpto] = ipushfuncsig.operand 422 | # todo: save name_at[ipushfuncsig.address] 423 | 424 | # next instruction 425 | instr_calldataload = ijumpi 426 | 427 | # got all location to sighashes 428 | # resolve sighash 429 | for loc, sighash in loc_to_sighash.items(): 430 | pot_funcsigs = utils.signatures.lookup_function_signature(sighash) 431 | if len(pot_funcsigs) == 0: 432 | ascii = "function 0x%s" % sighash 433 | elif len(pot_funcsigs) == 1: 434 | ascii = 'function %s' % pot_funcsigs[0] 435 | else: 436 | ascii = "function 0x%s"%sighash 437 | 438 | # todo: fixme 439 | self.functions[sighash] = {"address":loc, 440 | "signature_ascii": ascii, 441 | "signatures_ascii": pot_funcsigs, 442 | "signatures": [utils.signatures.ethereum_input_decoder.decoder.FourByteDirectory.parse_text_signature(s) for s in pot_funcsigs], 443 | "payable": True,#functions are payable by default 444 | "constant": None, # no write storage (replaced by view/pure) todo 445 | "view": None, #no write storage todo 446 | "pure": None, #no read/write storage todo 447 | "inputs": []} 448 | 449 | # find next JUMP (marks end of funchead) 450 | args = [] 451 | 452 | instr = self.instruction_at[loc] 453 | 454 | end_addr = instr.skip_to(["JUMP"]).address 455 | while instr.address < end_addr: 456 | instr = instr.skip_to(["CALLDATALOAD"]) 457 | 458 | if not instr or not instr.previous or instr.address >= end_addr: 459 | break 460 | 461 | p_instr = instr.previous 462 | while p_instr and p_instr.address>loc and not "JUMP" in p_instr.name and not "CALLDATALOAD" in p_instr.name: 463 | if p_instr.name.startswith("PUSH"): 464 | cdl_offset = int(p_instr.operand, 16) 465 | args.append(cdl_offset) 466 | break 467 | p_instr = p_instr.previous 468 | instr = instr.next 469 | 470 | if args: 471 | num_args = len(args) 472 | sizes = [] 473 | 474 | prevs = args[0] 475 | for nr,offset in enumerate(args): 476 | if nr==0: continue 477 | sizes.append("bytes%d"%(offset-prevs)) 478 | prevs = offset 479 | 480 | for _ in range(num_args-len(sizes)): 481 | sizes.append("") 482 | 483 | self.functions[sighash]["inputs"] = sizes 484 | # 485 | # 486 | # check if payable 487 | # 488 | if self.basicblock_at[loc].find_sequence(["JUMPDEST", "CALLVALUE", "ISZERO", "PUSH", "JUMPI", "PUSH", "DUP", "REVERT"], 489 | prn=lambda a, b: a.startswith(b)): # we do not need an exact match 490 | self.functions[sighash]["payable"] = False 491 | 492 | 493 | # add annotations 494 | f = self.functions[sighash] 495 | self.instruction_at[loc].annotations.append(f["signature_ascii"]) 496 | 497 | self.instruction_at[loc].annotations.append("payable: %r" % f["payable"]) 498 | self.instruction_at[loc].annotations.append("inputs: (%d) %r" % (len(f["inputs"]), f["inputs"])) 499 | if f["signatures_ascii"]: 500 | self.instruction_at[loc].annotations.append("potential signatures: %r" % f["signatures_ascii"]) 501 | 502 | self.function_at[loc] = f 503 | if f["signatures"]: 504 | self.name_for_location[loc] = "/*OR*/".join(_["name"] for _ in f["signatures"]) # in case multiple sigs returned 505 | else: 506 | # alt. use sighash as a label for location 507 | self.name_for_location[loc] = "0x%s" % sighash 508 | 509 | 510 | def analyze_dynamic(self): 511 | if not symbolic_execute or not self.enable_dynamic_analysis: 512 | return 513 | 514 | # run the symbolic execution 515 | logger.debug("running symbolic execution") 516 | symgraph = symbolic_execute(code=self.contract.bytecode, address=self.contract.address) 517 | logger.debug(repr(symgraph)) 518 | 519 | # assign every address a block and states 520 | for node, state in symgraph.iterate_blocks_and_states(): 521 | instr = state.get_current_instruction() 522 | self.symbolic_state_at.setdefault(instr['address'], []) 523 | self.symbolic_state_at[instr['address']].append((node, state)) 524 | 525 | 526 | #print(set(self.symbolic_state_at.keys()).symmetric_difference(set(self.instruction_at.keys()))) 527 | #print(self.symbolic_state_at.keys()) 528 | #print(self.instruction_at.keys()) 529 | #input("--") 530 | # ----------------------------- 531 | # analysis: jumps 532 | # ----------------------------- 533 | # fix jump destinations 534 | # find all JUMP(I) s and get the location from stack 535 | for addr, instr in self.jumptable.items(): # all JUMP/I s 536 | # get symbolic stack 537 | for node, state in self.symbolic_state_at.get(addr,[]): # todo investigate keyerror 538 | dst = get_z3_value(state.mstate.stack[-1]) 539 | #dst = z3.simplify().as_long() if z3.is_expr(state.mstate.stack[-1]) else get_z3_value(state.mstate.stack[-1]) 540 | if instr.jumpto and instr.jumpto != dst: # todo: needs to be a set 541 | logger.warning("Symbolic JUMP destination different: %s != %s" % (instr.jumpto, dst)) 542 | instr.jumpto = dst 543 | 544 | # ----------------------------------------------- 545 | # analysis: calldataloads / function signatures 546 | # ----------------------------------------------- 547 | 548 | 549 | # get calldataloads per block 550 | calldataloads = {} # block: [instr,offset] 551 | 552 | for instr in self.find("CALLDATALOAD"): 553 | # get symbolic stack 554 | for node, state in self.symbolic_state_at.get(instr.address, []): 555 | cdl_offset = get_z3_value(state.mstate.stack[-1]) # get last item from stack (=offset) 556 | # calldata offset 557 | calldataloads.setdefault(node,[]) 558 | calldataloads[node].append((instr, cdl_offset)) 559 | # todo: implement like reconstruct method 560 | 561 | # ----------------------------------------------- 562 | # analysis: function modifiers reconstruction 563 | # ----------------------------------------------- 564 | 565 | # todo implement the rest 566 | 567 | 568 | def main(): 569 | logging.basicConfig(format="%(levelname)-7s - %(message)s", level=logging.WARNING) 570 | from optparse import OptionParser 571 | usage = """usage: %prog [options] 572 | 573 | example: %prog [-L -F -v] 574 | %prog [-L -F -v] # read from stdin 575 | %prog [-L -F -a
] # fetch contract code from infura.io 576 | """ 577 | parser = OptionParser(usage=usage) 578 | loglevels = ['CRITICAL', 'FATAL', 'ERROR', 'WARNING', 'WARN', 'INFO', 'DEBUG', 'NOTSET'] 579 | parser.add_option("-v", "--verbosity", default="critical", 580 | help="available loglevels: %s [default: %%default]" % ','.join(l.lower() for l in loglevels)) 581 | parser.add_option("-L", "--listing", action="store_true", dest="listing", 582 | help="disables table mode, outputs assembly only") 583 | parser.add_option("-F", "--no-online-lookup", action="store_false", default=True, dest="function_signature_lookup", 584 | help="disable online function signature lookup") 585 | parser.add_option("-a", "--address", 586 | help="fetch contract bytecode from address") 587 | parser.add_option("-C", "--no-color", dest="no_color", default=False, action="store_true", 588 | help="disable color mode (requires pip install colorama)") 589 | parser.add_option("-A", "--guess-abi", dest="guess_abi", default=False, action="store_true", 590 | help="guess the ABI for that contract") 591 | parser.add_option("-D", "--no-dynamic-analysis", dest="dynamic_analysis", default=True, action="store_false", 592 | help="disable dynamic analysis / symolic execution") 593 | parser.add_option("-S", "--no-static-analysis", dest="static_analysis", default=True, action="store_false", 594 | help="disable static analysis") 595 | parser.add_option("-s", "--simplify", dest="simplify", default=False, action="store_true", 596 | help="simplify disassembly to human readable code") 597 | parser.add_option("-x", "--simplify-show-asm", dest="simplify_show_asm", default=False, action="store_true", 598 | help="simplify: show or hide asm annotations in simplified code") 599 | parser.add_option("-y", "--simplify-show-unreachable", dest="simplify_show_unreachable", default=False, action="store_true", 600 | help="simplify: show or hide annotations for unreachable instructions in simplified code") 601 | parser.add_option("-n", "--network", dest="network", default="mainnet", 602 | help="network for address lookup (default: mainnet, ropsten, rinkeby, kovan") 603 | 604 | # parse args 605 | (options, args) = parser.parse_args() 606 | 607 | if options.verbosity.upper() in loglevels: 608 | options.verbosity = getattr(logging, options.verbosity.upper()) 609 | logger.setLevel(options.verbosity) 610 | else: 611 | parser.error("invalid verbosity selected. please check --help") 612 | 613 | if options.no_color: 614 | utils.colors.colorama = None # override the import to disable colorama 615 | 616 | if not options.dynamic_analysis: 617 | symbolic_execute = None # hack hack 618 | 619 | if options.function_signature_lookup and not utils.signatures.ethereum_input_decoder: 620 | logger.warning("ethereum_input_decoder package not installed. function signature lookup not available.(pip install ethereum-input-decoder)") 621 | 622 | if options.simplify_show_asm or options.simplify_show_unreachable: 623 | options.simplify = True 624 | 625 | # get bytecode from stdin, or arg:file or arg:bytcode 626 | 627 | if options.address: 628 | contract = Contract(address=options.address, 629 | network=options.network, 630 | static_analysis=options.static_analysis, dynamic_analysis=options.dynamic_analysis, 631 | simplify_show_unreachable=options.simplify_show_unreachable, simplify_show_asm=options.simplify_show_asm) 632 | elif not args: 633 | contract = Contract(bytecode=sys.stdin.read().strip(), 634 | network=options.network, 635 | static_analysis=options.static_analysis, dynamic_analysis=options.dynamic_analysis, 636 | simplify_show_unreachable=options.simplify_show_unreachable, simplify_show_asm=options.simplify_show_asm) 637 | else: 638 | if os.path.isfile(args[0]): 639 | contract = Contract(bytecode=open(args[0], 'r').read(), 640 | network=options.network, 641 | static_analysis=options.static_analysis, dynamic_analysis=options.dynamic_analysis, 642 | simplify_show_unreachable=options.simplify_show_unreachable, simplify_show_asm=options.simplify_show_asm) 643 | else: 644 | contract = Contract(bytecode=args[0], 645 | network=options.network, 646 | static_analysis=options.static_analysis, dynamic_analysis=options.dynamic_analysis, 647 | simplify_show_unreachable=options.simplify_show_unreachable, simplify_show_asm=options.simplify_show_asm) 648 | 649 | #logger.debug(INSTRUCTIONS_BY_OPCODE) 650 | 651 | # print dissasembly 652 | if options.listing: 653 | console.EVMDasmPrinter.listing(contract.disassembly) 654 | else: 655 | #evm_dasm.disassemble(contract.bytecode) 656 | console.EVMDasmPrinter.basicblocks_detailed_tabulate(contract.disassembly, 657 | resolve_funcsig=options.function_signature_lookup) 658 | #print(evm_dasm.functions) ## print detected functions 659 | #EVMDasmPrinter.detailed(evm_dasm.disassemble(evmcode), resolve_funcsig=options.function_signature_lookup) 660 | 661 | logger.info("finished in %0.3f seconds." % contract.disassembly.duration) 662 | # post a notification that disassembly might be incorrect due to errors 663 | if contract.disassembly.disassembler.errors: 664 | logger.warning("disassembly finished with %d errors" % len(contract.disassembly.disassembler.errors)) 665 | if options.verbosity >= 30: 666 | logger.warning("use -v INFO to see the errors") 667 | else: 668 | for e in contract.disassembly.disassembler.errors: 669 | logger.info(e) 670 | 671 | logger.info("AUXDATA: %r"%contract.auxdata) 672 | 673 | if options.guess_abi: 674 | print("=" * 30) 675 | print("reconstructed ABI:") 676 | print(contract.guess_abi()) 677 | 678 | # ------ testing area 679 | 680 | if options.simplify: 681 | print("======================[simplified]") 682 | print("") 683 | for x in contract.simplified: 684 | print(x) 685 | 686 | # ------ testing area 687 | 688 | # quick check 689 | is_equal_assemble_disassemble = contract.bytecode.strip() == ''.join(contract.disassembly.assemble(contract.disassembly.disassemble())) 690 | logger.debug("assemble(disassemble(evmcode))==%s"%is_equal_assemble_disassemble) 691 | if not is_equal_assemble_disassemble: 692 | logger.debug("original: %s" % contract.bytecode.strip()) 693 | logger.debug("reassembled: %s" % ''.join(contract.disassembly.assemble(contract.disassembly.disassemble()))) 694 | 695 | # -- exit -- 696 | sys.exit(len(contract.disassembly.disassembler.errors)) 697 | 698 | 699 | if __name__ == "__main__": 700 | main() 701 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ethereum-dasm 2 | ethereum evm bytecode disassembler with static- and dynamic-analysis and function signature lookup 3 | 4 | [https://github.com/ethereum/] [https://www.ethereum.org/] 5 | 6 | ![windows](https://user-images.githubusercontent.com/2865694/42905736-28e71672-8ad9-11e8-9a2d-baa6f4260dbc.png) 7 | 8 | **disassembles evm bytecode** 9 | 10 | [![asciicast](https://asciinema.org/a/256036.png)](https://asciinema.org/a/256036) 11 | 12 | **decompile a contract to pseudocode** 13 | 14 | [![asciicast](https://asciinema.org/a/0xJvR55Zr7svqibS0oLHA2vY5.png)](https://asciinema.org/a/0xJvR55Zr7svqibS0oLHA2vY5) 15 | 16 | 17 | ## install 18 | 19 | ### pip 20 | ``` 21 | #> pip3 install "ethereum-dasm[mythril,abidecoder]" 22 | #> python3 -m ethereum_dasm --help # verify installation 23 | ``` 24 | 25 | ### code 26 | ``` 27 | #> pip3 install -r requirements*.txt 28 | #> python3 setup.py install 29 | ``` 30 | 31 | ``` 32 | #> python3 -m ethereum_dasm -a 0x44919b8026f38d70437a8eb3be47b06ab1c3e4bf # jusst to verify installation 33 | ``` 34 | 35 | ## usage 36 | 37 | ``` 38 | Usage: ethereum_dasm.py [options] 39 | 40 | example: ethereum_dasm.py [-L -F -v] 41 | ethereum_dasm.py [-L -F -v] # read from stdin 42 | ethereum_dasm.py [-L -F -a
] # fetch contract code from infura.io 43 | 44 | 45 | Options: 46 | -h, --help show this help message and exit 47 | -v VERBOSITY, --verbosity=VERBOSITY 48 | available loglevels: 49 | critical,fatal,error,warning,warn,info,debug,notset 50 | [default: critical] 51 | -L, --listing disables table mode, outputs assembly only 52 | -F, --no-online-lookup 53 | disable online function signature lookup 54 | -a ADDRESS, --address=ADDRESS 55 | fetch contract bytecode from address 56 | -C, --no-color disable color mode (requires pip install colorama) 57 | -A, --guess-abi guess the ABI for that contract 58 | -D, --no-dynamic-analysis 59 | disable dynamic analysis / symolic execution 60 | -S, --no-static-analysis 61 | disable static analysis 62 | -s, --simplify simplify disassembly to human readable code 63 | -x, --simplify-show-asm 64 | simplify: show or hide asm annotations in simplified 65 | code 66 | -y, --simplify-show-unreachable 67 | simplify: show or hide annotations for unreachable 68 | instructions in simplified code 69 | -n NETWORK, --network=NETWORK 70 | network for address lookup (default: mainnet, ropsten, 71 | rinkeby, kovan 72 | ``` 73 | 74 | #> echo "0x12345678" | python3 -m ethereum_dasm 75 | #> python3 -m ethereum_dasm 0x12345678 76 | #> python3 -m ethereum_dasm ether_contract.evm 77 | #> python3 -m ethereum_dasm -a 78 | #> python3 -m ethereum_dasm -a -A 79 | #> python3 -m ethereum_dasm -a --simplify [--simplify-show-asm, --simplify-show-unreachable] 80 | 81 | ## features 82 | 83 | * disassemble evm bytecode 84 | * decompile evm bytecode to pseudocode 85 | * provide the path to the ethereum vm bytecode or the bytecode as an argument to evmdasm. Tries to read from stdin by default. 86 | * returns !=0 on errors 87 | * various output modes (`-L` to switch from table to listing mode) 88 | * color-mode 89 | * invalid opcodes are prefixed with `UNKNOWN_` 90 | * gas consumption for instruction 91 | * return value and arguments for instruction 92 | * basic jump/xref analysis 93 | * basicblocking 94 | * online function signature and method name lookup, operand annotations 95 | * payable modifier detection 96 | * dynamic analysis based on symbolic execution (depends on mythril) 97 | * ABI.json download (from etherchain.org) 98 | * ABI.json reconstruction from static analysis of the evm bytecode 99 | * main interface `Contract()`. 100 | 101 | ## examples 102 | 103 | **abi online lookup or reconstruction** 104 | ```#>python3 -m ethereum_dasm -a 0x44919b8026f38d70437a8eb3be47b06ab1c3e4bf -A``` 105 | ```python 106 | # [....] 107 | [{'stateMutability': 'nonpayable', 'constant': False, 'type': 'function', 'name': 'enter', 'signature': '0x124c32a1', 'outputs': [{'type': 'bool', 'name': ''}], 'inputs': [{'type': 'bytes32', 'name': '_passcode' 108 | }, {'type': 'bytes8', 'name': '_gateKey'}], 'payable': False}, {'stateMutability': 'pure', 'constant': True, 'type': 'function', 'name': 'maxEntrants', 'signature': '0x60643652', 'outputs': [{'type': 'uint8', 'n 109 | ame': ''}], 'inputs': [], 'payable': False}, {'stateMutability': 'view', 'constant': True, 'type': 'function', 'name': 'totalEntrants', 'signature': '0x694463a2', 'outputs': [{'type': 'uint8', 'name': ''}], 'inp 110 | uts': [], 'payable': False}, {'stateMutability': 'nonpayable', 'constant': False, 'type': 'function', 'name': 'assignAll', 'signature': '0x90ae631d', 'outputs': [{'type': 'bool', 'name': ''}], 'inputs': [], 'pay 111 | able': False}, {'inputs': [], 'stateMutability': 'nonpayable', 'payable': False, 'type': 'constructor'}] 112 | ``` 113 | 114 | **abi reconstruction from dasm if abi is not available** 115 | `#> python3 -m ethereum_dasm -a 0x8f8bed23a644f3bbb4e227e28704c050e67c35be -A` 116 | ``` 117 | [{'signature': '0x95d89b41', 'outputs': [], 'stateMutability': None, 'name': 'symbol', 'constant': None, 'inputs': [], 'payable': True, 'type': 'function', 'address': 598}, {'signature': '0x095ea7b3', 'outputs': 118 | [], 'stateMutability': None, 'name': 'approve', 'constant': None, 'inputs': ['bytes32', ''], 'payable': True, 'type': 'function', 'address': 328}, {'signature': '0x313ce567', 'outputs': [], 'stateMutab 119 | ility': None, 'name': 'decimals', 'constant': None, 'inputs': [], 'payable': True, 'type': 'function', 'address': 486}, {'signature': '0xdd62ed3e', 'outputs': [], 'stateMutability': None, 'name': 'allowance', 'c 120 | onstant': None, 'inputs': ['bytes32', ''], 'payable': True, 'type': 'function', 'address': 691}, {'signature': '0x66188463', 'outputs': [], 'stateMutability': None, 'name': 'decreaseApproval', 'constant 121 | ': None, 'inputs': ['bytes32', ''], 'payable': True, 'type': 'function', 'address': 529}, {'signature': '0x23b872dd', 'outputs': [], 'stateMutability': None, 'name': 'transferFrom', 'constant': None, 'i 122 | nputs': ['bytes32', 'bytes32', ''], 'payable': True, 'type': 'function', 'address': 423}, {'signature': '0x2ff2e9dc', 'outputs': [], 'stateMutability': None, 'name': 'INITIAL_SUPPLY', 'constant': None, 123 | 'inputs': [], 'payable': True, 'type': 'function', 'address': 465}, {'signature': '0x06fdde03', 'outputs': [], 'stateMutability': None, 'name': 'name', 'constant': None, 'inputs': [], 'payable': True, 'type': 'f 124 | unction', 'address': 190}, {'signature': '0x18160ddd', 'outputs': [], 'stateMutability': None, 'name': 'totalSupply', 'constant': None, 'inputs': [], 'payable': True, 'type': 'function', 'address': 384}, {'signa 125 | ture': '0x70a08231', 'outputs': [], 'stateMutability': None, 'name': 'balanceOf', 'constant': None, 'inputs': [''], 'payable': True, 'type': 'function', 'address': 565}, {'signature': '0xa9059cbb', 'out 126 | puts': [], 'stateMutability': None, 'name': 'transfer', 'constant': None, 'inputs': [{'type': 'address', 'name': 'arg0'}, {'type': 'uint256', 'name': 'arg1'}], 'payable': True, 'type': 'function', 'address': 619 127 | }, {'signature': '0xd73dd623', 'outputs': [], 'stateMutability': None, 'name': 'increaseApproval', 'constant': None, 'inputs': ['bytes32', ''], 'payable': True, 'type': 'function', 'address': 655}] 128 | ``` 129 | 130 | 131 | **detailed listing** 132 | 133 | ```python3 -m ethereum_dasm -a 0x44919b8026f38d70437a8eb3be47b06ab1c3e4bf -A --no-color ``` 134 | ```python 135 | Inst addr hex gas | mnemonic operand xrefs description retval args 136 | ------------------------------------------------------------------------------------------------ ----------- -------------------------------------------------------------------- --------------- -------------------- 137 | :loc_0x0 138 | 0 [ 0 0x0000 ] 3 | PUSH1 0x60 # Place 1 byte item on stack. item 139 | 1 [ 2 0x0002 ] 3 | PUSH1 0x40 # Place 1 byte item on stack. item 140 | 2 [ 4 0x0004 ] 3 | MSTORE # Save word to memory. value, offset 141 | 3 [ 5 0x0005 ] 3 | PUSH1 0x04 # Place 1 byte item on stack. item 142 | 4 [ 7 0x0007 ] 2 | CALLDATASIZE # Get size of input data in current environment. msg.data.length 143 | 5 [ 8 0x0008 ] 3 | LT # Lesser-than comparison flag a, b 144 | 6 [ 9 0x0009 ] 3 | PUSH2 0x0048 # Place 2-byte item on stack. item 145 | 7 [ 12 0x000c ] 10 | JUMPI @0x48 # Conditionally alter the program counter. evm.pc, condition 146 | 8 [ 13 0x000d ] 3 | PUSH4 0xffffffff # Place 4-byte item on stack. item 147 | 9 [ 18 0x0012 ] 3 | PUSH1 0xe0 # Place 1 byte item on stack. item 148 | 10 [ 20 0x0014 ] 3 | PUSH1 0x02 # Place 1 byte item on stack. item 149 | 11 [ 22 0x0016 ] 10 | EXP # Exponential operation. result base, exponent 150 | 12 [ 23 0x0017 ] 3 | PUSH1 0x00 # Place 1 byte item on stack. item 151 | 13 [ 25 0x0019 ] 3 | CALLDATALOAD # Get input data of current environment. msg.data unknown 152 | 14 [ 26 0x001a ] 5 | DIV # Integer division operation. result a, b 153 | 15 [ 27 0x001b ] 3 | AND # Bitwise AND operation. result a, b 154 | 16 [ 28 0x001c ] 3 | PUSH4 0x124c32a1 --> 'function enter(bytes32,bytes8)' # Place 4-byte item on stack. item 155 | 17 [ 33 0x0021 ] 3 | DUP2 # Duplicate 2nd stack item. 156 | 18 [ 34 0x0022 ] 3 | EQ # Equality comparison flag a, b 157 | 19 [ 35 0x0023 ] 3 | PUSH2 0x004d # Place 2-byte item on stack. item 158 | 20 [ 38 0x0026 ] 10 | JUMPI @0x4d # Conditionally alter the program counter. evm.pc, condition 159 | 21 [ 39 0x0027 ] 3 | DUP1 # Duplicate 1st stack item. 160 | 22 [ 40 0x0028 ] 3 | PUSH4 0x60643652 --> 'function maxEntrants()' # Place 4-byte item on stack. item 161 | 23 [ 45 0x002d ] 3 | EQ # Equality comparison flag a, b 162 | 24 [ 46 0x002e ] 3 | PUSH2 0x0095 # Place 2-byte item on stack. item 163 | 25 [ 49 0x0031 ] 10 | JUMPI @0x95 # Conditionally alter the program counter. evm.pc, condition 164 | 26 [ 50 0x0032 ] 3 | DUP1 # Duplicate 1st stack item. 165 | 27 [ 51 0x0033 ] 3 | PUSH4 0x694463a2 --> 'function totalEntrants()' # Place 4-byte item on stack. item 166 | 28 [ 56 0x0038 ] 3 | EQ # Equality comparison flag a, b 167 | 29 [ 57 0x0039 ] 3 | PUSH2 0x00be # Place 2-byte item on stack. item 168 | 30 [ 60 0x003c ] 10 | JUMPI @0xbe # Conditionally alter the program counter. evm.pc, condition 169 | 31 [ 61 0x003d ] 3 | DUP1 # Duplicate 1st stack item. 170 | 32 [ 62 0x003e ] 3 | PUSH4 0x90ae631d --> 'function assignAll()' # Place 4-byte item on stack. item 171 | 33 [ 67 0x0043 ] 3 | EQ # Equality comparison flag a, b 172 | 34 [ 68 0x0044 ] 3 | PUSH2 0x00d1 # Place 2-byte item on stack. item 173 | 35 [ 71 0x0047 ] 10 | JUMPI @0xd1 # Conditionally alter the program counter. evm.pc, condition 174 | 175 | :loc_0x48 176 | 36 [ 72 0x0048 ] 1 | JUMPDEST JUMPI@0xc # Mark a valid destination for jumps. 177 | 37 [ 73 0x0049 ] 3 | PUSH1 0x00 # Place 1 byte item on stack. item 178 | 38 [ 75 0x004b ] 3 | DUP1 # Duplicate 1st stack item. 179 | 39 [ 76 0x004c ] 0 | REVERT # throw an error offset, size 180 | :loc_0x4d 181 | /******************************************************************* 182 | function enter(bytes32,bytes8) 183 | payable: False 184 | inputs: (2) ['bytes32', ''] 185 | potential signatures: ['enter(bytes32,bytes8)'] 186 | *******************************************************************/ 187 | 40 [ 77 0x004d ] 1 | JUMPDEST JUMPI@0x26 # Mark a valid destination for jumps. 188 | 41 [ 78 0x004e ] 2 | CALLVALUE # Get deposited value by the instruction/transaction responsible for msg.value 189 | this execution. 190 | 42 [ 79 0x004f ] 3 | ISZERO # Simple not operator flag a 191 | 43 [ 80 0x0050 ] 3 | PUSH2 0x0058 # Place 2-byte item on stack. item 192 | 44 [ 83 0x0053 ] 10 | JUMPI @0x58 # Conditionally alter the program counter. evm.pc, condition 193 | 45 [ 84 0x0054 ] 3 | PUSH1 0x00 # Place 1 byte item on stack. item 194 | 46 [ 86 0x0056 ] 3 | DUP1 # Duplicate 1st stack item. 195 | 47 [ 87 0x0057 ] 0 | REVERT # throw an error offset, size 196 | :loc_0x58 197 | 48 [ 88 0x0058 ] 1 | JUMPDEST JUMPI@0x53 # Mark a valid destination for jumps. 198 | 49 [ 89 0x0059 ] 3 | PUSH2 0x0081 # Place 2-byte item on stack. item 199 | 50 [ 92 0x005c ] 3 | PUSH1 0x04 # Place 1 byte item on stack. item 200 | 51 [ 94 0x005e ] 3 | CALLDATALOAD # Get input data of current environment. msg.data unknown 201 | 52 [ 95 0x005f ] 3 | PUSH24 0xffffffffffffffffffffffffffffffffffffffffffffffff # Place 24-byte item on stack. item 202 | 53 [ 120 0x0078 ] 3 | NOT # Bitwise NOT operation. result a, b 203 | 54 [ 121 0x0079 ] 3 | PUSH1 0x24 # Place 1 byte item on stack. item 204 | 55 [ 123 0x007b ] 3 | CALLDATALOAD # Get input data of current environment. msg.data unknown 205 | 56 [ 124 0x007c ] 3 | AND # Bitwise AND operation. result a, b 206 | 57 [ 125 0x007d ] 3 | PUSH2 0x00e4 # Place 2-byte item on stack. item 207 | 58 [ 128 0x0080 ] 8 | JUMP @0xe4 # Alter the program counter. evm.pc 208 | 209 | :loc_0x81 210 | 59 [ 129 0x0081 ] 1 | JUMPDEST # Mark a valid destination for jumps. 211 | 60 [ 130 0x0082 ] 3 | PUSH1 0x40 # Place 1 byte item on stack. item 212 | 61 [ 132 0x0084 ] 3 | MLOAD # Load word from memory. offset 213 | 62 [ 133 0x0085 ] 3 | SWAP1 # Exchange 1st and 2nd stack items. 214 | 63 [ 134 0x0086 ] 3 | ISZERO # Simple not operator flag a 215 | 64 [ 135 0x0087 ] 3 | ISZERO # Simple not operator flag a 216 | 65 [ 136 0x0088 ] 3 | DUP2 # Duplicate 2nd stack item. 217 | 66 [ 137 0x0089 ] 3 | MSTORE # Save word to memory. value, offset 218 | 67 [ 138 0x008a ] 3 | PUSH1 0x20 # Place 1 byte item on stack. item 219 | 68 [ 140 0x008c ] 3 | ADD # Addition operation. result a, b 220 | 69 [ 141 0x008d ] 3 | PUSH1 0x40 # Place 1 byte item on stack. item 221 | 70 [ 143 0x008f ] 3 | MLOAD # Load word from memory. offset 222 | 71 [ 144 0x0090 ] 3 | DUP1 # Duplicate 1st stack item. 223 | 72 [ 145 0x0091 ] 3 | SWAP2 # Exchange 1st and 3rd stack items. 224 | 73 [ 146 0x0092 ] 3 | SUB # Subtraction operation. result a, b 225 | 74 [ 147 0x0093 ] 3 | SWAP1 # Exchange 1st and 2nd stack items. 226 | 75 [ 148 0x0094 ] 0 | RETURN # Halt execution returning output data. offset, size 227 | 228 | :loc_0x95 229 | /******************************************************************* 230 | function maxEntrants() 231 | payable: False 232 | inputs: (0) [] 233 | potential signatures: ['maxEntrants()'] 234 | *******************************************************************/ 235 | 76 [ 149 0x0095 ] 1 | JUMPDEST JUMPI@0x31 # Mark a valid destination for jumps. 236 | 77 [ 150 0x0096 ] 2 | CALLVALUE # Get deposited value by the instruction/transaction responsible for msg.value 237 | this execution. 238 | 78 [ 151 0x0097 ] 3 | ISZERO # Simple not operator flag a 239 | 79 [ 152 0x0098 ] 3 | PUSH2 0x00a0 # Place 2-byte item on stack. item 240 | 80 [ 155 0x009b ] 10 | JUMPI @0xa0 # Conditionally alter the program counter. evm.pc, condition 241 | 81 [ 156 0x009c ] 3 | PUSH1 0x00 # Place 1 byte item on stack. item 242 | 82 [ 158 0x009e ] 3 | DUP1 # Duplicate 1st stack item. 243 | 83 [ 159 0x009f ] 0 | REVERT # throw an error offset, size 244 | :loc_0xa0 245 | 84 [ 160 0x00a0 ] 1 | JUMPDEST JUMPI@0x9b # Mark a valid destination for jumps. 246 | 85 [ 161 0x00a1 ] 3 | PUSH2 0x00a8 # Place 2-byte item on stack. item 247 | 86 [ 164 0x00a4 ] 3 | PUSH2 0x0314 # Place 2-byte item on stack. item 248 | 87 [ 167 0x00a7 ] 8 | JUMP @0x314 # Alter the program counter. evm.pc 249 | 250 | :loc_0xa8 251 | 88 [ 168 0x00a8 ] 1 | JUMPDEST # Mark a valid destination for jumps. 252 | 89 [ 169 0x00a9 ] 3 | PUSH1 0x40 # Place 1 byte item on stack. item 253 | 90 [ 171 0x00ab ] 3 | MLOAD # Load word from memory. offset 254 | 91 [ 172 0x00ac ] 3 | PUSH1 0xff # Place 1 byte item on stack. item 255 | 92 [ 174 0x00ae ] 3 | SWAP1 # Exchange 1st and 2nd stack items. 256 | 93 [ 175 0x00af ] 3 | SWAP2 # Exchange 1st and 3rd stack items. 257 | 94 [ 176 0x00b0 ] 3 | AND # Bitwise AND operation. result a, b 258 | 95 [ 177 0x00b1 ] 3 | DUP2 # Duplicate 2nd stack item. 259 | 96 [ 178 0x00b2 ] 3 | MSTORE # Save word to memory. value, offset 260 | 97 [ 179 0x00b3 ] 3 | PUSH1 0x20 # Place 1 byte item on stack. item 261 | 98 [ 181 0x00b5 ] 3 | ADD # Addition operation. result a, b 262 | 99 [ 182 0x00b6 ] 3 | PUSH1 0x40 # Place 1 byte item on stack. item 263 | 100 [ 184 0x00b8 ] 3 | MLOAD # Load word from memory. offset 264 | 101 [ 185 0x00b9 ] 3 | DUP1 # Duplicate 1st stack item. 265 | 102 [ 186 0x00ba ] 3 | SWAP2 # Exchange 1st and 3rd stack items. 266 | 103 [ 187 0x00bb ] 3 | SUB # Subtraction operation. result a, b 267 | 104 [ 188 0x00bc ] 3 | SWAP1 # Exchange 1st and 2nd stack items. 268 | 105 [ 189 0x00bd ] 0 | RETURN # Halt execution returning output data. offset, size 269 | 270 | :loc_0xbe 271 | /******************************************************************* 272 | function totalEntrants() 273 | payable: False 274 | inputs: (0) [] 275 | potential signatures: ['totalEntrants()'] 276 | *******************************************************************/ 277 | 106 [ 190 0x00be ] 1 | JUMPDEST JUMPI@0x3c # Mark a valid destination for jumps. 278 | 107 [ 191 0x00bf ] 2 | CALLVALUE # Get deposited value by the instruction/transaction responsible for msg.value 279 | this execution. 280 | 108 [ 192 0x00c0 ] 3 | ISZERO # Simple not operator flag a 281 | 109 [ 193 0x00c1 ] 3 | PUSH2 0x00c9 # Place 2-byte item on stack. item 282 | 110 [ 196 0x00c4 ] 10 | JUMPI @0xc9 # Conditionally alter the program counter. evm.pc, condition 283 | 111 [ 197 0x00c5 ] 3 | PUSH1 0x00 # Place 1 byte item on stack. item 284 | 112 [ 199 0x00c7 ] 3 | DUP1 # Duplicate 1st stack item. 285 | 113 [ 200 0x00c8 ] 0 | REVERT # throw an error offset, size 286 | :loc_0xc9 287 | 114 [ 201 0x00c9 ] 1 | JUMPDEST JUMPI@0xc4 # Mark a valid destination for jumps. 288 | 115 [ 202 0x00ca ] 3 | PUSH2 0x00a8 # Place 2-byte item on stack. item 289 | 116 [ 205 0x00cd ] 3 | PUSH2 0x031a # Place 2-byte item on stack. item 290 | 117 [ 208 0x00d0 ] 8 | JUMP @0x31a # Alter the program counter. evm.pc 291 | 292 | :loc_0xd1 293 | /******************************************************************* 294 | function assignAll() 295 | payable: False 296 | inputs: (0) [] 297 | potential signatures: ['assignAll()'] 298 | *******************************************************************/ 299 | 118 [ 209 0x00d1 ] 1 | JUMPDEST JUMPI@0x47 # Mark a valid destination for jumps. 300 | 119 [ 210 0x00d2 ] 2 | CALLVALUE # Get deposited value by the instruction/transaction responsible for msg.value 301 | this execution. 302 | 120 [ 211 0x00d3 ] 3 | ISZERO # Simple not operator flag a 303 | 121 [ 212 0x00d4 ] 3 | PUSH2 0x00dc # Place 2-byte item on stack. item 304 | 122 [ 215 0x00d7 ] 10 | JUMPI @0xdc # Conditionally alter the program counter. evm.pc, condition 305 | 123 [ 216 0x00d8 ] 3 | PUSH1 0x00 # Place 1 byte item on stack. item 306 | 124 [ 218 0x00da ] 3 | DUP1 # Duplicate 1st stack item. 307 | 125 [ 219 0x00db ] 0 | REVERT # throw an error offset, size 308 | :loc_0xdc 309 | 126 [ 220 0x00dc ] 1 | JUMPDEST JUMPI@0xd7 # Mark a valid destination for jumps. 310 | 127 [ 221 0x00dd ] 3 | PUSH2 0x0081 # Place 2-byte item on stack. item 311 | 128 [ 224 0x00e0 ] 3 | PUSH2 0x0320 # Place 2-byte item on stack. item 312 | 129 [ 227 0x00e3 ] 8 | JUMP @0x320 # Alter the program counter. evm.pc 313 | 314 | :loc_0xe4 315 | 130 [ 228 0x00e4 ] 1 | JUMPDEST JUMP@0x80 # Mark a valid destination for jumps. 316 | 131 [ 229 0x00e5 ] 3 | PUSH1 0x00 # Place 1 byte item on stack. item 317 | 132 [ 231 0x00e7 ] 2 | ORIGIN # Get execution origination address. tx.origin 318 | 133 [ 232 0x00e8 ] 3 | PUSH1 0x01 # Place 1 byte item on stack. item 319 | 134 [ 234 0x00ea ] 3 | PUSH1 0xa0 # Place 1 byte item on stack. item 320 | 135 [ 236 0x00ec ] 3 | PUSH1 0x02 # Place 1 byte item on stack. item 321 | 136 [ 238 0x00ee ] 10 | EXP # Exponential operation. result base, exponent 322 | 137 [ 239 0x00ef ] 3 | SUB # Subtraction operation. result a, b 323 | 138 [ 240 0x00f0 ] 3 | AND # Bitwise AND operation. result a, b 324 | 139 [ 241 0x00f1 ] 2 | CALLER # Get caller address.This is the address of the account that is msg.sender 325 | directly responsible for this execution. 326 | 140 [ 242 0x00f2 ] 3 | PUSH1 0x01 # Place 1 byte item on stack. item 327 | 141 [ 244 0x00f4 ] 3 | PUSH1 0xa0 # Place 1 byte item on stack. item 328 | 142 [ 246 0x00f6 ] 3 | PUSH1 0x02 # Place 1 byte item on stack. item 329 | 143 [ 248 0x00f8 ] 10 | EXP # Exponential operation. result base, exponent 330 | 144 [ 249 0x00f9 ] 3 | SUB # Subtraction operation. result a, b 331 | 145 [ 250 0x00fa ] 3 | AND # Bitwise AND operation. result a, b 332 | 146 [ 251 0x00fb ] 3 | EQ # Equality comparison flag a, b 333 | 147 [ 252 0x00fc ] 3 | ISZERO # Simple not operator flag a 334 | 148 [ 253 0x00fd ] 3 | ISZERO # Simple not operator flag a 335 | 149 [ 254 0x00fe ] 3 | ISZERO # Simple not operator flag a 336 | 150 [ 255 0x00ff ] 3 | PUSH2 0x0107 # Place 2-byte item on stack. item 337 | 151 [ 258 0x0102 ] 10 | JUMPI @0x107 # Conditionally alter the program counter. evm.pc, condition 338 | 152 [ 259 0x0103 ] 3 | PUSH1 0x00 # Place 1 byte item on stack. item 339 | 153 [ 261 0x0105 ] 3 | DUP1 # Duplicate 1st stack item. 340 | 154 [ 262 0x0106 ] 0 | REVERT # throw an error offset, size 341 | 342 | [...] 343 | 344 | :loc_0x5d5 345 | 954 [1493 0x05d5 ] 1 | JUMPDEST JUMPI@0x5c9 # Mark a valid destination for jumps. 346 | 955 [1494 0x05d6 ] 2 | POP # Remove item from stack. #dummy 347 | 956 [1495 0x05d7 ] 3 | SWAP1 # Exchange 1st and 2nd stack items. 348 | 957 [1496 0x05d8 ] 8 | JUMP # Alter the program counter. evm.pc 349 | 958 [1497 0x05d9 ] 0 | STOP # Halts execution. 350 | 959 [1498 0x05da ] 750 | LOG1 0x65 # Append log record with one topic. start, size, topic1 351 | 960 [1500 0x05dc ] 3 | PUSH3 0x7a7a72 # Place 3-byte item on stack. item 352 | 961 [1504 0x05e0 ] 2 | ADDRESS # Get address of currently executing account. this.address 353 | 962 [1505 0x05e1 ] 2 | PC # Get the value of the program counter prior to the increment. evm.pc 354 | 963 [1506 0x05e2 ] 30 | SHA3 # Compute Keccak-256 hash. flag offset, size 355 | 964 [1507 0x05e3 ] -1 | UNKNOWN_0xf # Invalid opcode 356 | 965 [1508 0x05e4 ] 3 | SWAP4 # Exchange 1st and 5th stack items. 357 | 966 [1509 0x05e5 ] 3 | SWAP14 # Exchange 1st and 15th stack items. 358 | 967 [1510 0x05e6 ] 2 | CALLDATASIZE # Get size of input data in current environment. msg.data.length 359 | 968 [1511 0x05e7 ] -1 | UNKNOWN_0xe1 # Invalid opcode 360 | 969 [1512 0x05e8 ] -1 | UNKNOWN_0xcb # Invalid opcode 361 | 970 [1513 0x05e9 ] 3 | PUSH22 0xc68ec825da862dc7082ea12aea89eb3783b2e0423cd2 # Place 22-byte item on stack. item 362 | 971 [1536 0x0600 ] 30 | SHA3 # Compute Keccak-256 hash. flag offset, size 363 | 972 [1537 0x0601 ] -1 | UNKNOWN_0x28 # Invalid opcode 364 | 973 [1538 0x0602 ] 3 | PUSH16 0x0029 # Place 16-byte item on stack. item 365 | ============================== 366 | reconstructed ABI: 367 | [{'name': 'enter', 'stateMutability': 'nonpayable', 'signature': '0x124c32a1', 'payable': False, 'inputs': [{'name': '_passcode', 'type': 'bytes32'}, {'name': '_gateKey', 'type': 'bytes8'}], 'constant': False, 'type': 'function', 'outputs': [{'name': '', 'type': 'bool'}]}, {'name': 'maxEntrants', 'stateMutability': 'pure', 'signature': '0x60643652', 'payable': False, 'inputs': [], 'constant': True, 'type': 'function', 'outputs': [{'name': '', 'type': 'uint8'}]}, {'name': 'totalEntrants', 'stateMutability': 'view', 'signature': '0x694463a2', 'payable': False, 'inputs': [], 'constant': True, 'type': 'function', 'outputs': [{'name': '', 'type': 'uint8'}]}, {'name': 'assignAll', 'stateMutability': 'nonpayable', 'signature': '0x90ae631d', 'payable': False, 'inputs': [], 'constant': False, 'type': 'function', 'outputs': [{'name': '', 'type': 'bool'}]}, {'stateMutability': 'nonpayable', 'type': 'constructor', 'payable': False, 'inputs': []}] 368 | 369 | ``` 370 | 371 | 372 | **decompile to pseudocode** 373 | 374 | `python3 -m ethereum_dasm -a 0x44919b8026f38d70437a8eb3be47b06ab1c3e4bf --simplify #[--simplify-show-asm, --simplify-show-unreachable]` 375 | ```php 376 | 377 | ======================[simplified] 378 | 379 | :init 380 | memory[0x40] = 0x60 381 | if (Not(ULE(0x4, 1_calldatasize))) goto LOC_0x48 382 | if (And(If(1_calldatasize <= 0x3, 0x0, 1_calldata[0x3]) == 0xa1, 383 | If(1_calldatasize <= 0x2, 0x0, 1_calldata[0x2]) == 0x32, 384 | If(1_calldatasize <= 0x1, 0x0, 1_calldata[0x1]) == 0x4c, 385 | If(1_calldatasize <= 0x0, 0x0, 1_calldata[0x0]) == 0x12)) goto function_enter (LOC_0x4d) 386 | if (And(If(1_calldatasize <= 0x3, 0x0, 1_calldata[0x3]) == 0x52, 387 | If(1_calldatasize <= 0x2, 0x0, 1_calldata[0x2]) == 0x36, 388 | If(1_calldatasize <= 0x1, 0x0, 1_calldata[0x1]) == 0x64, 389 | If(1_calldatasize <= 0x0, 0x0, 1_calldata[0x0]) == 0x60)) goto function_maxEntrants (LOC_0x95) 390 | if (And(If(1_calldatasize <= 0x3, 0x0, 1_calldata[0x3]) == 0xa2, 391 | If(1_calldatasize <= 0x2, 0x0, 1_calldata[0x2]) == 0x63, 392 | If(1_calldatasize <= 0x1, 0x0, 1_calldata[0x1]) == 0x44, 393 | If(1_calldatasize <= 0x0, 0x0, 1_calldata[0x0]) == 0x69)) goto function_totalEntrants (LOC_0xbe) 394 | if (And(If(1_calldatasize <= 0x3, 0x0, 1_calldata[0x3]) == 0x1d, 395 | If(1_calldatasize <= 0x2, 0x0, 1_calldata[0x2]) == 0x63, 396 | If(1_calldatasize <= 0x1, 0x0, 1_calldata[0x1]) == 0xae, 397 | If(1_calldatasize <= 0x0, 0x0, 1_calldata[0x0]) == 0x90)) goto function_assignAll (LOC_0xd1) 398 | 399 | :LOC_0x48 400 | REVERT(offset=0x0, size=0x0) 401 | /******* <> *******/ 402 | 403 | :function_enter (LOC_0x4d) 404 | /******************************************************************* 405 | function enter(bytes32,bytes8) 406 | payable: False 407 | inputs: (2) ['bytes32', ''] 408 | potential signatures: ['enter(bytes32,bytes8)'] 409 | *******************************************************************/ 410 | 411 | if (call_value1 == 0x0) goto LOC_0x58 412 | REVERT(offset=0x0, size=0x0) 413 | /******* <> *******/ 414 | 415 | :LOC_0x58 416 | goto LOC_0xe4 417 | 418 | :LOC_0x81 419 | 420 | :function_maxEntrants (LOC_0x95) 421 | /******************************************************************* 422 | function maxEntrants() 423 | payable: False 424 | inputs: (0) [] 425 | potential signatures: ['maxEntrants()'] 426 | *******************************************************************/ 427 | 428 | if (call_value1 == 0x0) goto LOC_0xa0 429 | REVERT(offset=0x0, size=0x0) 430 | /******* <> *******/ 431 | 432 | :LOC_0xa0 433 | goto LOC_0x314 434 | 435 | :LOC_0xa8 436 | memory[0x60] = 0xfa 437 | RETURN(offset=0x60, size=0x20) 438 | /******* <> *******/ 439 | 440 | :function_totalEntrants (LOC_0xbe) 441 | /******************************************************************* 442 | function totalEntrants() 443 | payable: False 444 | inputs: (0) [] 445 | potential signatures: ['totalEntrants()'] 446 | *******************************************************************/ 447 | 448 | if (call_value1 == 0x0) goto LOC_0xc9 449 | REVERT(offset=0x0, size=0x0) 450 | /******* <> *******/ 451 | 452 | :LOC_0xc9 453 | goto LOC_0x31a 454 | 455 | :function_assignAll (LOC_0xd1) 456 | /******************************************************************* 457 | function assignAll() 458 | payable: False 459 | inputs: (0) [] 460 | potential signatures: ['assignAll()'] 461 | *******************************************************************/ 462 | 463 | if (call_value1 == 0x0) goto LOC_0xdc 464 | REVERT(offset=0x0, size=0x0) 465 | /******* <> *******/ 466 | 467 | :LOC_0xdc 468 | goto LOC_0x320 469 | 470 | :LOC_0xe4 471 | if (Not(Extract(159, 0, origin1) == 472 | 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef)) goto LOC_0x107 473 | REVERT(offset=0x0, size=0x0) 474 | /******* <> *******/ 475 | 476 | :LOC_0x107 477 | if (True) goto LOC_0x114 478 | 479 | :LOC_0x114 480 | if (bvurem_i(1_gas, 0x1fff) == 0x0) goto LOC_0x11f 481 | REVERT(offset=0x0, size=0x0) 482 | /******* <> *******/ 483 | 484 | :LOC_0x11f 485 | if (And(If(1_calldatasize <= 0x29, 0x0, 1_calldata[0x29]) == 0x0, 486 | If(1_calldatasize <= 0x28, 0x0, 1_calldata[0x28]) == 0x0)) goto LOC_0x154 487 | REVERT(offset=0x0, size=0x0) 488 | /******* <> *******/ 489 | 490 | :LOC_0x154 491 | if (Not(And(If(1_calldatasize <= 0x27, 0x0, 1_calldata[0x27]) == 492 | 0x0, 493 | If(1_calldatasize <= 0x26, 0x0, 1_calldata[0x26]) == 494 | 0x0, 495 | If(1_calldatasize <= 0x25, 0x0, 1_calldata[0x25]) == 496 | 0x0, 497 | If(1_calldatasize <= 0x24, 0x0, 1_calldata[0x24]) == 498 | 0x0))) goto LOC_0x18f 499 | REVERT(offset=0x0, size=0x0) 500 | /******* <> *******/ 501 | 502 | :LOC_0x18f 503 | if (And(Extract(7, 0, origin1) == 504 | If(1_calldatasize <= 0x2b, 0x0, 1_calldata[0x2b]), 505 | Extract(15, 8, origin1) == 506 | If(1_calldatasize <= 0x2a, 0x0, 1_calldata[0x2a]), 507 | If(1_calldatasize <= 0x29, 0x0, 1_calldata[0x29]) == 0x0, 508 | If(1_calldatasize <= 0x28, 0x0, 1_calldata[0x28]) == 0x0)) goto LOC_0x1c0 509 | REVERT(offset=0x0, size=0x0) 510 | /******* <> *******/ 511 | 512 | :LOC_0x1c0 513 | if (True) goto LOC_0x1d1 514 | 515 | :LOC_0x1d1 516 | memory[0x0] = 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef 517 | memory[0x20] = 0x3 518 | if (True) goto LOC_0x1f7 519 | 520 | :LOC_0x1f7 521 | memory[0x0] = Concat(0x0, Extract(159, 0, origin1)) 522 | memory[0x20] = 0x3 523 | if (True) goto LOC_0x21d 524 | 525 | :LOC_0x21d 526 | memory[0x60] = Concat(If(1_calldatasize <= 0x4, 0x0, 1_calldata[0x4]), 527 | If(1_calldatasize <= 0x5, 0x0, 1_calldata[0x5]), 528 | If(1_calldatasize <= 0x6, 0x0, 1_calldata[0x6]), 529 | If(1_calldatasize <= 0x7, 0x0, 1_calldata[0x7]), 530 | If(1_calldatasize <= 0x8, 0x0, 1_calldata[0x8]), 531 | If(1_calldatasize <= 0x9, 0x0, 1_calldata[0x9]), 532 | If(1_calldatasize <= 0xa, 0x0, 1_calldata[0xa]), 533 | If(1_calldatasize <= 0xb, 0x0, 1_calldata[0xb]), 534 | If(1_calldatasize <= 0xc, 0x0, 1_calldata[0xc]), 535 | If(1_calldatasize <= 0xd, 0x0, 1_calldata[0xd]), 536 | If(1_calldatasize <= 0xe, 0x0, 1_calldata[0xe]), 537 | If(1_calldatasize <= 0xf, 0x0, 1_calldata[0xf]), 538 | If(1_calldatasize <= 0x10, 0x0, 1_calldata[0x10]), 539 | If(1_calldatasize <= 0x11, 0x0, 1_calldata[0x11]), 540 | If(1_calldatasize <= 0x12, 0x0, 1_calldata[0x12]), 541 | If(1_calldatasize <= 0x13, 0x0, 1_calldata[0x13]), 542 | If(1_calldatasize <= 0x14, 0x0, 1_calldata[0x14]), 543 | If(1_calldatasize <= 0x15, 0x0, 1_calldata[0x15]), 544 | If(1_calldatasize <= 0x16, 0x0, 1_calldata[0x16]), 545 | If(1_calldatasize <= 0x17, 0x0, 1_calldata[0x17]), 546 | If(1_calldatasize <= 0x18, 0x0, 1_calldata[0x18]), 547 | If(1_calldatasize <= 0x19, 0x0, 1_calldata[0x19]), 548 | If(1_calldatasize <= 0x1a, 0x0, 1_calldata[0x1a]), 549 | If(1_calldatasize <= 0x1b, 0x0, 1_calldata[0x1b]), 550 | If(1_calldatasize <= 0x1c, 0x0, 1_calldata[0x1c]), 551 | If(1_calldatasize <= 0x1d, 0x0, 1_calldata[0x1d]), 552 | If(1_calldatasize <= 0x1e, 0x0, 1_calldata[0x1e]), 553 | If(1_calldatasize <= 0x1f, 0x0, 1_calldata[0x1f]), 554 | If(1_calldatasize <= 0x20, 0x0, 1_calldata[0x20]), 555 | If(1_calldatasize <= 0x21, 0x0, 1_calldata[0x21]), 556 | If(1_calldatasize <= 0x22, 0x0, 1_calldata[0x22]), 557 | If(1_calldatasize <= 0x23, 0x0, 1_calldata[0x23])) 558 | memory[0x0] = KECCAC[If(1_calldatasize_<=_0x4,_0x0,_1_calldata[0x4])] 559 | memory[0x20] = 0x4 560 | if (False) goto LOC_0x257 561 | REVERT(offset=0x0, size=0x0) 562 | /******* <> *******/ 563 | 564 | :LOC_0x257 565 | 566 | :LOC_0x275 567 | 568 | :LOC_0x2d6 569 | 570 | :LOC_0x314 571 | 572 | :LOC_0x317 573 | goto LOC_0xa8 574 | 575 | :LOC_0x31a 576 | goto LOC_0xa8 577 | 578 | :LOC_0x320 579 | if (False) goto LOC_0x338 580 | REVERT(offset=0x0, size=0x0) 581 | /******* <> *******/ 582 | 583 | :LOC_0x338 584 | 585 | :LOC_0x347 586 | 587 | :LOC_0x3a8 588 | 589 | :LOC_0x3b9 590 | 591 | :LOC_0x3d8 592 | 593 | :LOC_0x3e0 594 | 595 | :LOC_0x3f5 596 | 597 | :LOC_0x459 598 | 599 | :LOC_0x46a 600 | 601 | :LOC_0x4de 602 | 603 | :LOC_0x541 604 | 605 | :LOC_0x552 606 | 607 | :LOC_0x556 608 | 609 | :LOC_0x55e 610 | 611 | :LOC_0x588 612 | 613 | :LOC_0x592 614 | 615 | :LOC_0x5b6 616 | 617 | :LOC_0x5bb 618 | 619 | :LOC_0x5c1 620 | 621 | :LOC_0x5d5 622 | ``` 623 | --------------------------------------------------------------------------------