Security analysis tool for EVM bytecode. Supports smart contracts built for Ethereum, Hedera, Quorum, Vechain, Roostock, Tron and other EVM-compatible blockchains.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
mythril/myth

224 lines
6.7 KiB

7 years ago
#!/usr/bin/env python
7 years ago
"""mythril.py: Bug hunting on the Ethereum blockchain
7 years ago
http://www.github.com/b-mueller/mythril
"""
from mythril.ether import evm, util
7 years ago
from mythril.disassembler.callgraph import generate_callgraph
from mythril.ether.contractstorage import get_persistent_storage
from mythril.ether.ethcontract import ETHContract
from mythril.ether.util import compile_solidity
from mythril.rpc.client import EthJsonRpc
7 years ago
from mythril.ipc.client import EthIpc
from ethereum import utils
from laser.ethereum import laserfree
7 years ago
import logging
7 years ago
import sys
import argparse
import os
import re
7 years ago
7 years ago
7 years ago
def searchCallback(code_hash, code, addresses, balances):
print("Matched contract with code hash " + code_hash)
7 years ago
for i in range(0, len(addresses)):
print("Address: " + addresses[i] + ", balance: " + str(balances[i]))
7 years ago
def exitWithError(message):
print(message)
sys.exit()
7 years ago
parser = argparse.ArgumentParser(description='Bug hunting on the Ethereum blockchain')
7 years ago
commands = parser.add_argument_group('commands')
commands.add_argument('-d', '--disassemble', action='store_true', help='disassemble')
7 years ago
commands.add_argument('-g', '--graph', help='generate a control flow graph', metavar='OUTPUT_FILE')
commands.add_argument('-x', '--fire-lasers', action='store_true', help='detect vulnerabilities')
commands.add_argument('-t', '--trace', action='store_true', help='trace, use with -c or -a and --data (optional)')
commands.add_argument('-s', '--search', help='search the contract database')
commands.add_argument('--xrefs', action='store_true', help='get xrefs from a contract, use with -c or -a')
commands.add_argument('--hash', help='calculate function signature hash', metavar='SIGNATURE')
commands.add_argument('--init-db', action='store_true', help='initialize the contract database')
inputs = parser.add_argument_group('input arguments')
inputs.add_argument('-c', '--code', help='hex-encoded bytecode string ("6060604052...")', metavar='BYTECODE')
inputs.add_argument('-i', '--infiles', help='comma-separated list of solidity files', metavar='INPUT_FILES')
inputs.add_argument('-a', '--address', help='pull contract from the mainnet', metavar='CONTRACT_ADDRESS')
inputs.add_argument('--data', help='message call input data for tracing')
options = parser.add_argument_group('options')
options.add_argument('--sync-all', action='store_true', help='Also sync contracts with zero balance')
options.add_argument('--rpchost', default='127.0.0.1', help='RPC host')
options.add_argument('--rpcport', type=int, default=8545, help='RPC port')
options.add_argument('--rpctls', type=bool, default=False, help='RPC port')
options.add_argument('--ipc', help='use IPC interface instead of RPC', action='store_true')
options.add_argument('--enable-physics', type=bool, default=False, help='enable graph physics simulation')
options.add_argument('-v', type=int, help='log level (0-2)', metavar='LOG_LEVEL')
# Get config values
7 years ago
try:
db_dir = os.environ['DB_DIR']
except KeyError:
7 years ago
db_dir = None
7 years ago
try:
solc_binary = os.environ['SOLC']
except KeyError:
solc_binary = 'solc'
args = parser.parse_args()
if not (args.search or args.init_db or args.hash or args.disassemble or args.graph or args.xrefs or args.fire_lasers or args.trace):
parser.print_help()
sys.exit()
7 years ago
if (args.v):
if (0 <= args.v < 3):
logging.basicConfig(level=[logging.NOTSET, logging.INFO, logging.DEBUG][args.v])
elif (args.hash):
print("0x" + utils.sha3(args.hash)[:4].hex())
sys.exit()
# Database search ops
if args.search or args.init_db:
contract_storage = get_persistent_storage(db_dir)
if (args.search):
try:
contract_storage.search(args.search, searchCallback)
except SyntaxError:
exitWithError("Syntax error in search expression.")
elif (args.init_db):
contract_storage.initialize(args.rpchost, args.rpcport, args.rpctls, args.sync_all, args.ipc)
sys.exit()
7 years ago
# Establish RPC/IPC connection if necessary
if (args.address or args.infiles):
7 years ago
if args.ipc:
try:
eth = EthIpc()
7 years ago
7 years ago
except Exception as e:
exitWithError("Error establishing IPC connection: " + str(e))
else:
7 years ago
try:
7 years ago
eth = EthJsonRpc(args.rpchost, args.rpcport, args.rpctls)
7 years ago
except Exception as e:
exitWithError("Error establishing RPC connection: " + str(e))
7 years ago
# Load input contracts
contracts = []
if (args.code):
7 years ago
contracts.append(ETHContract(args.code, address = util.get_random_address()))
elif (args.address):
7 years ago
contracts.append(ETHContract(eth.eth_getCode(args.address), address = args.address))
elif (args.infiles):
files = args.infiles.split(",")
counter = 0
for file in files:
name, bytecode = compile_solidity(solc_binary, file)
# Max. 16 contracts supported
contract = ETHContract(bytecode, name = name, address = "0x" + hex(counter)[2:] * 40)
counter += 1
contracts.append(contract)
logging.info(contract.name + " at " + contract.address)
else:
exitWithError("No input bytecode. Please provide EVM code via -c BYTECODE, -a ADDRESS, or -i SOLIDITY_FILES")
# Commands
7 years ago
if (args.disassemble):
7 years ago
easm_text = contracts[0].get_easm()
sys.stdout.write(easm_text)
elif (args.graph):
if args.enable_physics is not None:
physics = True
modules = []
for contract in contracts:
7 years ago
print(str(contract.as_dict()))
modules.append(contract.as_dict())
html = generate_callgraph(modules, args.enable_physics)
try:
with open(args.graph, "w") as f:
f.write(html)
except Exception as e:
print("Error saving graph: " + str(e))
elif (args.xrefs):
contract = ETHContract(bytecode)
print("\n".join(contract.get_xrefs()))
7 years ago
elif (args.fire_lasers):
modules = []
for contract in contracts:
modules.append(contract.as_dict())
laserfree.fire(modules)
7 years ago
elif (args.trace):
7 years ago
if (args.code):
bytecode = args.code
7 years ago
elif (args.address):
7 years ago
if args.ipc:
eth = EthIpc()
bytecode = eth.eth_getCode(args.address)
7 years ago
7 years ago
else:
7 years ago
eth = EthJsonRpc(args.rpchost, args.rpcport, args.rpctls)
bytecode = eth.eth_getCode(args.address)
7 years ago
else:
exitWithError("Disassembler: Provide the input bytecode via -c BYTECODE or --id ID")
7 years ago
if (args.data):
trace = evm.trace(bytecode, args.data)
7 years ago
else:
trace = evm.trace(bytecode)
for i in trace:
if (re.match(r'^PUSH.*', i['op'])):
print(str(i['pc']) + " " + i['op'] + " " + i['pushvalue'] + ";\tSTACK: " + i['stack'])
else:
print(str(i['pc']) + " " + i['op'] + ";\tSTACK: " + i['stack'])
7 years ago
else:
parser.print_help()