Merge pull request #1 from b-mueller/master

merge upstream
pull/13/head
step21 7 years ago committed by GitHub
commit dfecf9da26
  1. 2
      .gitignore
  2. 89
      README.md
  3. 133
      myth
  4. 202
      mythril/disassembler/callgraph.py
  5. 2
      mythril/disassembler/disassembly.py
  6. 38
      mythril/ether/contractstorage.py
  7. 5
      mythril/ether/util.py
  8. 3
      requirements.txt
  9. 6
      setup.py
  10. BIN
      static/callgraph.png
  11. BIN
      static/callgraph6.png
  12. 121
      static/mythril.html

2
.gitignore vendored

@ -10,3 +10,5 @@ dist
*.rst *.rst
*.lock *.lock
*.svg *.svg
laser*
lol.py

@ -20,47 +20,24 @@ $ cd mythril
$ python setup.py install $ python setup.py install
``` ```
Note that Mythril requires Python 3.5 to work.
You also need a [go-ethereum](https://github.com/ethereum/go-ethereum) node that is synced with the network (note that Mythril uses non-standard RPC APIs only supported by go-ethereum, so other clients likely won't work). Start the node as follows: You also need a [go-ethereum](https://github.com/ethereum/go-ethereum) node that is synced with the network (note that Mythril uses non-standard RPC APIs only supported by go-ethereum, so other clients likely won't work). Start the node as follows:
```bash ```bash
$ geth --rpc --rpcapi eth,admin,debug --syncmode fast $ geth --rpc --rpcapi eth,debug --syncmode fast
``` ```
### Database initialization
Mythril builds its own contract database to enable fast search operations. This is to enable operations like those described in the [legendary "Mitch Brenner" blog post](https://medium.com/@rtaylor30/how-i-snatched-your-153-037-eth-after-a-bad-tinder-date-d1d84422a50b) in seconds instead of days. Unfortunately, the initial sync process is slow. You don't need to sync the whole blockchain right away though: If you abort the syncing process with `ctrl+c`, it will be auto-resumed the next time you run the `--init-db` command.
```bash
$ myth --init-db
Starting synchronization from latest block: 4323706
Processing block 4323000, 3 individual contracts in database
(...)
```
Note that syncing doesn't take quite as long as it first seems, because the blocks get smaller towards the beginning of the chain.
The default behavior is to only sync contracts with a non-zero balance. You can disable this behavior with the `--sync-all` flag, but be aware that this will result in a huge (as in: dozens of GB) database.
## Command line usage ## Command line usage
The Mythril command line tool (aptly named `myth`) allows you to conveniently access some of Mythril's functionality. The Mythril command line tool (aptly named `myth`) allows you to conveniently access some of Mythril's functionality.
### Searching the database
The search feature allows you to find contract instances that contain specific function calls and opcode sequences. It supports simple boolean expressions, such as:
```bash
$ myth --search "func#changeMultisig(address)#"
$ myth --search "code#PUSH1 0x50,POP#"
$ myth --search "func#changeMultisig(address)# and code#PUSH1 0x50#"
```
### Disassembler ### Disassembler
Use the `-d` flag to disassemble code. The disassembler accepts a bytecode string or a contract address as its input. Use the `-d` flag to disassemble code. The disassembler accepts a bytecode string or a contract address as its input.
```bash ```bash
$ myth -d -c "$ ./myth -d -c "5060" $ myth -d -c "0x6060"
0 PUSH1 0x60 0 PUSH1 0x60
``` ```
@ -77,67 +54,61 @@ $ myth -d -a "0x2a0c0dbecc7e4d658f48e01e3fa353f44050c208"
1137 ISZERO 1137 ISZERO
``` ```
Adding the `-g FILENAME` option will output a call graph: ### Control flow graph
Mythril integrates the LASER symbolic virtual machine. Right now, this is mainly used for CFG generation. The `-g FILENAME` option generates an [interactive jsViz graph](http://htmlpreview.github.io/?https://github.com/b-mueller/mythril/blob/master/static/mythril.html):
```bash ```bash
$ myth -d -a "0xFa52274DD61E1643d2205169732f29114BC240b3" -g ./graph.svg $ myth -g ./graph.html -a "0xFa52274DD61E1643d2205169732f29114BC240b3"
``` ```
![callgraph](https://raw.githubusercontent.com/b-mueller/mythril/master/static/callgraph.png "Call graph") ![callgraph](https://raw.githubusercontent.com/b-mueller/mythril/master/static/callgraph6.png "Call graph")
Note that currently, Mythril only processes `JUMP` and `JUMPI` instructions with immediately preceding `PUSH`, but doesn't understand dynamic jumps and function calls. The "bounce" effect, while awesome (and thus enabled by default), sometimes messes up the graph layout. If that happens, disable the effect with the `--disable-physics` flag.
### Tracing Code ### Contract search
You can run a code trace in the PyEthereum virtual machine. Optionally, input data can be passed via the `--data` flag. Mythril builds its own contract database to enable fast search operations. This is to enable operations like those described in the [legendary "Mitch Brenner" blog post](https://medium.com/@rtaylor30/how-i-snatched-your-153-037-eth-after-a-bad-tinder-date-d1d84422a50b) in ~~seconds~~ minutes instead of days. Unfortunately, the initial sync process is slow. You don't need to sync the whole blockchain right away though: If you abort the syncing process with `ctrl+c`, it will be auto-resumed the next time you run the `--init-db` command.
```bash ```bash
$ myth -t -c "0x60506050" $ myth --init-db
0 PUSH1 0x50; STACK: [] Starting synchronization from latest block: 4323706
2 PUSH1 0x50; STACK: [0x50] Processing block 4323000, 3 individual contracts in database
$ myth -t -a "0x3665f2bf19ee5e207645f3e635bf0f4961d661c0"
0 PUSH1 0x60; STACK: []
2 PUSH1 0x40; STACK: [0x60]
4 MSTORE; STACK: [0x60, 0x40]
5 CALLDATASIZE; STACK: []
(...) (...)
``` ```
#### Finding cross-references Mythril retrieves contract data over RPC by default. You can switch to IPC using the `--ipc` flag.
It is often useful to find other contracts referenced by a particular contract. Let's assume you want to search for contracts that fulfill conditions similar to the [Parity Multisig Wallet Bug](http://hackingdistributed.com/2017/07/22/deep-dive-parity-bug/). First, you want to find a list of contracts that use the `DELEGATECALL` opcode: The default behavior is to only sync contracts with a non-zero balance. You can disable this behavior with the `--sync-all` flag, but be aware that this will result in a huge (as in: dozens of GB) database.
#### Searching from the command line
The search feature allows you to find contract instances that contain specific function calls and opcode sequences. It supports simple boolean expressions, such as:
```bash ```bash
$ myth --search "code#DELEGATECALL#" $ myth --search "func#changeMultisig(address)#"
Matched contract with code hash 07459966443977122e639cbf7804c446 $ myth --search "code#PUSH1 0x50,POP#"
Address: 0x76799f77587738bfeef09452df215b63d2cfb08a, balance: 1000000000000000 $ myth --search "func#changeMultisig(address)# and code#PUSH1 0x50#"
Address: 0x3582d2a3b67d63ed10f1ecaef0dca71b9283b543, balance: 92000000000000000000
Address: 0x4b9bc00c35f7cee95c65c3c9836040c37dec9772, balance: 89000000000000000000
Address: 0x156d5687a201affb3f1e632dcfb9fde4b0128211, balance: 29500000000000000000
(...)
``` ```
Note that "code hash" in the above output refers to the contract's index in the database. The following lines ("Address: ...") list instances of same contract deployed on the blockchain. #### Finding cross-references
You can then use the `--xrefs` flag to find the addresses of referenced contracts: It is often useful to find other contracts referenced by a particular contract. E.g.:
```bash ```bash
$ myth --search "code#DELEGATECALL#"
Matched contract with code hash 07459966443977122e639cbf7804c446
Address: 0x76799f77587738bfeef09452df215b63d2cfb08a, balance: 1000000000000000
$ myth --xrefs 07459966443977122e639cbf7804c446 $ myth --xrefs 07459966443977122e639cbf7804c446
5b9e8728e316bbeb692d22daaab74f6cbf2c4691 5b9e8728e316bbeb692d22daaab74f6cbf2c4691
``` ```
## Custom scripts
By combining Mythril and [PyEthereum](https://github.com/ethereum/pyethereum) modules, you can automate more complex static and dynamic analysis tasks. Here is an [example](https://github.com/b-mueller/mythril/blob/master/examples/find-fallback-dcl.py).
## Issues ## Issues
The RPC database sync solution is not very efficient. I explored some other options, including: The database sync is currently not very efficient.
- Using PyEthereum: I encountered issues syncing PyEthereum with Homestead. Also, PyEthApp only supports Python 2.7, which causes issues with other important packages. - Using PyEthereum: I encountered issues syncing PyEthereum with Homestead. Also, PyEthApp only supports Python 2.7, which causes issues with other important packages.
- Accessing the Go-Ethereum LevelDB: This would be a great option. However, PyEthereum database code seems unable to deal with Go-Ethereum's LevelDB. It would take quite a bit of effort to figure this out. - Accessing the Go-Ethereum LevelDB: This would be a great option. However, PyEthereum database code seems unable to deal with Go-Ethereum's LevelDB. It would take quite a bit of effort to figure this out.
- IPC might allow for faster sync then RPC - haven't tried it yet.
I'm writing this in my spare time, so contributors would be highly welcome! I'm writing this in my spare time, so contributors would be highly welcome!

133
myth

@ -11,12 +11,15 @@ from mythril.ether.contractstorage import get_persistent_storage
from mythril.rpc.client import EthJsonRpc from mythril.rpc.client import EthJsonRpc
from mythril.ipc.client import EthIpc from mythril.ipc.client import EthIpc
from ethereum import utils from ethereum import utils
from laser.ethereum import laserfree
import logging
import binascii import binascii
import sys import sys
import argparse import argparse
import os import os
import re import re
def searchCallback(code_hash, code, addresses, balances): def searchCallback(code_hash, code, addresses, balances):
print("Matched contract with code hash " + code_hash ) print("Matched contract with code hash " + code_hash )
@ -31,32 +34,43 @@ def exitWithError(message):
parser = argparse.ArgumentParser(description='Bug hunting on the Ethereum blockchain') parser = argparse.ArgumentParser(description='Bug hunting on the Ethereum blockchain')
parser.add_argument('-d', '--disassemble', action='store_true', help='disassemble, specify input with -c or -a')
parser.add_argument('-t', '--trace', action='store_true', help='trace, use with -c or -a and --data (optional)') commands = parser.add_argument_group('commands')
parser.add_argument('-c', '--code', help='hex-encoded bytecode string ("6060604052...")', metavar='BYTECODE') commands.add_argument('-d', '--disassemble', action='store_true', help='disassemble, specify input with -c or -a')
parser.add_argument('-a', '--address', help='contract address') commands.add_argument('-t', '--trace', action='store_true', help='trace, use with -c or -a and --data (optional)')
parser.add_argument('-o', '--outfile') commands.add_argument('-g', '--graph', help='generate a call graph', metavar='OUTPUT_FILE')
parser.add_argument('-g', '--graph', help='when disassembling, also generate a callgraph', metavar='OUTPUT_FILE') commands.add_argument('-l', '--fire-lasers', action='store_true', help='detect vulnerabilities, use with -c or -a')
parser.add_argument('--ipc', help='use ipc interface', action='store_true') commands.add_argument('-s', '--search', help='search the contract database')
parser.add_argument('--data', help='message call input data for tracing') commands.add_argument('--xrefs', help='get xrefs from contract in database', metavar='CONTRACT_HASH')
parser.add_argument('--search', help='search the contract database') commands.add_argument('--hash', help='calculate function signature hash', metavar='SIGNATURE')
parser.add_argument('--xrefs', help='get xrefs from contract in database', metavar='CONTRACT_HASH') commands.add_argument('--init-db', action='store_true', help='initialize the contract database')
parser.add_argument('--hash', help='calculate function signature hash', metavar='SIGNATURE')
parser.add_argument('--init-db', action='store_true', help='Initialize the contract database') inputs = parser.add_argument_group('input arguments')
parser.add_argument('--sync-all', action='store_true', help='Also sync contracts with zero balance') inputs.add_argument('-c', '--code', help='hex-encoded bytecode string ("6060604052...")', metavar='BYTECODE')
parser.add_argument('--rpchost', default='127.0.0.1', help='RPC host') inputs.add_argument('-a', '--address', help='contract address')
parser.add_argument('--rpcport', type=int, default=8545, help='RPC port') 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('--ipc', help='use IPC interface instead of RPC', action='store_true')
options.add_argument('--disable-physics', action='store_true', help='disable graph physics simulation')
options.add_argument('-v', type=int, help='log level (0-2)', metavar='LOG_LEVEL')
try: try:
db_dir = os.environ['DB_DIR'] db_dir = os.environ['DB_DIR']
except KeyError: except KeyError:
db_dir = None db_dir = None
contract_storage = get_persistent_storage(db_dir)
args = parser.parse_args() args = parser.parse_args()
if (args.disassemble): if (args.v):
if (0 <= args.v < 3):
logging.basicConfig(level=[logging.NOTSET, logging.INFO, logging.DEBUG][args.v])
if (args.disassemble or args.graph or args.fire_lasers):
if (args.code): if (args.code):
encoded_bytecode = args.code encoded_bytecode = args.code
@ -68,7 +82,7 @@ if (args.disassemble):
encoded_bytecode = eth.eth_getCode(args.address) encoded_bytecode = eth.eth_getCode(args.address)
except Exception as e: except Exception as e:
exitWithError("Exception loading bytecode via IPC: " + str(e)) exitWithError("Exception loading bytecode via IPC: " + str(e))
else:
try: try:
eth = EthJsonRpc(args.rpchost, args.rpcport) eth = EthJsonRpc(args.rpchost, args.rpcport)
@ -78,24 +92,37 @@ if (args.disassemble):
exitWithError("Exception loading bytecode via RPC: " + str(e)) exitWithError("Exception loading bytecode via RPC: " + str(e))
else: else:
exitWithError("Disassembler: Provide the input bytecode via -c BYTECODE or --id ID") exitWithError("No input bytecode. Please provide the code via -c BYTECODE or -a address")
try: try:
disassembly = Disassembly(encoded_bytecode) disassembly = Disassembly(encoded_bytecode)
# instruction_list = asm.disassemble(util.safe_decode(encoded_bytecode))
except binascii.Error: except binascii.Error:
exitWithError("Disassembler: Invalid code string.") exitWithError("Disassembler: Invalid code string.")
easm_text = disassembly.get_easm() if (args.disassemble):
if (args.outfile): easm_text = disassembly.get_easm()
util.string_to_file(args.outfile, easm_text)
else:
sys.stdout.write(easm_text) sys.stdout.write(easm_text)
if (args.graph): elif (args.graph):
generate_callgraph(disassembly, args.graph)
if (args.disable_physics):
physics = False
else:
physics = True
html = generate_callgraph(disassembly, physics)
try:
with open(args.graph, "w") as f:
f.write(html)
except Exception as e:
print("Error saving graph: " + str(e))
elif (args.fire_lasers):
laserfree.fire(disassembly)
elif (args.trace): elif (args.trace):
@ -126,34 +153,36 @@ elif (args.trace):
else: else:
print(str(i['pc']) + " " + i['op'] + ";\tSTACK: " + i['stack']) print(str(i['pc']) + " " + i['op'] + ";\tSTACK: " + i['stack'])
elif (args.search): else:
contract_storage = get_persistent_storage(db_dir)
if (args.search):
try: try:
contract_storage.search(args.search, searchCallback) contract_storage.search(args.search, searchCallback)
except SyntaxError: except SyntaxError:
exitWithError("Syntax error in search expression.") exitWithError("Syntax error in search expression.")
elif (args.xrefs): elif (args.xrefs):
try: try:
contract_hash = util.safe_decode(args.xrefs) contract_hash = util.safe_decode(args.xrefs)
except binascii.Error: except binascii.Error:
exitWithError("Invalid contract hash.") exitWithError("Invalid contract hash.")
try: try:
contract = contract_storage.get_contract_by_hash(contract_hash) contract = contract_storage.get_contract_by_hash(contract_hash)
print("\n".join(contract.get_xrefs())) print("\n".join(contract.get_xrefs()))
except KeyError: except KeyError:
exitWithError("Contract not found in the database.") exitWithError("Contract not found in the database.")
elif (args.init_db):
if args.ipc:
contract_storage.initialize(args.rpchost, args.rpcport, args.sync_all, args.ipc)
else:
contract_storage.initialize(args.rpchost, args.rpcport, args.sync_all, args.ipc)
elif (args.hash): elif (args.init_db):
print("0x" + utils.sha3(args.hash)[:4].hex()) if args.ipc:
contract_storage.initialize(args.rpchost, args.rpcport, args.sync_all, args.ipc)
else:
contract_storage.initialize(args.rpchost, args.rpcport, args.sync_all, args.ipc)
else: elif (args.hash):
parser.print_help() print("0x" + utils.sha3(args.hash)[:4].hex())
else:
parser.print_help()

@ -1,64 +1,140 @@
import graphviz as gv from laser.ethereum import svm
from z3 import Z3Exception, simplify
import re
styles = {
'graph': {
'overlap': 'false',
'fontsize': '16',
'fontcolor': 'white',
'bgcolor': '#333333',
'concentrate':'true',
},
'nodes': {
'fontname': 'Helvetica',
'shape': 'box',
'fontcolor': 'white',
'color': 'white',
'style': 'filled',
'fillcolor': '#006699',
},
'edges': {
'style': 'dashed',
'dir': 'forward',
'color': 'white',
'arrowhead': 'normal',
'fontname': 'Courier',
'fontsize': '12',
'fontcolor': 'white',
}
}
def apply_styles(graph, styles):
graph.graph_attr.update(
('graph' in styles and styles['graph']) or {}
)
graph.node_attr.update(
('nodes' in styles and styles['nodes']) or {}
)
graph.edge_attr.update(
('edges' in styles and styles['edges']) or {}
)
return graph
def generate_callgraph(disassembly, file):
graph = gv.Graph(format='svg')
index = 0
for block in disassembly.blocks:
easm = block.get_easm().replace("\n", "\l")
graph.node(str(index), easm)
index += 1
for xref in disassembly.xrefs:
graph.edge(str(xref[0]), str(xref[1]))
graph = apply_styles(graph, styles)
graph.render(file)
graph_html = '''<html>
<head>
<style type="text/css">
#mynetwork {
background-color: #000000;
}
body {
background-color: #000000;
color: #ffffff;
}
</style>
<link href="https://cdnjs.cloudflare.com/ajax/libs/vis/4.21.0/vis.min.css" rel="stylesheet" type="text/css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vis/4.21.0/vis.min.js"></script>
<script>
var options = {
autoResize: true,
height: '100%',
width: '100%',
manipulation: false,
height: '90%',
layout: {
randomSeed: undefined,
improvedLayout:true,
hierarchical: {
enabled:true,
levelSeparation: 450,
nodeSpacing: 200,
treeSpacing: 100,
blockShifting: true,
edgeMinimization: true,
parentCentralization: false,
direction: 'LR', // UD, DU, LR, RL
sortMethod: 'directed' // hubsize, directed
}
},
nodes:{
borderWidth: 1,
borderWidthSelected: 2,
chosen: true,
shape: 'box',
font: {
align: 'left',
color: '#FFFFFF',
},
color: {
border: '#26996f',
background: '#1f7e5b',
highlight: {
border: '#26996f',
background: '#28a16f'
}
}
},
edges:{
font: {
color: '#ffffff',
size: 12, // px
face: 'arial',
background: 'none',
strokeWidth: 0, // px
strokeColor: '#ffffff',
align: 'horizontal',
multi: false,
vadjust: 0,
}
},
physics:{
enabled: [ENABLE_PHYSICS],
}
}
[JS]
</script>
</head>
<body>
<p>Mythril / Ethereum LASER Symbolic VM</p>
<p><div id="mynetwork"></div><br /></p>
<script type="text/javascript">
var container = document.getElementById('mynetwork');
var data = {'nodes': nodes, 'edges': edges}
var gph = new vis.Network(container, data, options);
</script>
</body>
</html>
'''
def serialize(_svm):
nodes = []
edges = []
for n in _svm.nodes:
code = _svm.nodes[n].as_dict()['code']
code = re.sub("([0-9a-f]{8})[0-9a-f]+", lambda m: m.group(1) + "(...)", code)
nodes.append("{id: " + str(_svm.nodes[n].as_dict()['id']) + ", size: 150, 'label': '" + code + "'}")
for edge in _svm.edges:
if (edge.condition is None):
label = ""
else:
try:
label = str(simplify(edge.condition)).replace("\n", "")
except Z3Exception:
label = str(edge.condition).replace("\n", "")
label = re.sub("[^_]([[\d]{2}\d+)", lambda m: hex(int(m.group(1))), label)
code = re.sub("([0-9a-f]{8})[0-9a-f]+", lambda m: m.group(1) + "(...)", code)
edges.append("{from: " + str(edge.as_dict()['from']) + ', to: ' + str(edge.as_dict()['to']) + ", 'arrows': 'to', 'label': '" + label + "', 'smooth': {'type': 'cubicBezier'}}")
return "var nodes = [\n" + ",\n".join(nodes) + "\n];\nvar edges = [\n" + ",\n".join(edges) + "\n];"
def generate_callgraph(disassembly, physics):
_svm = svm.SVM(disassembly)
_svm.sym_exec()
html = graph_html.replace("[JS]", serialize(_svm))
html = html.replace("[ENABLE_PHYSICS]", str(physics).lower())
return html

@ -48,7 +48,7 @@ class Disassembly:
try: try:
func_name = signatures[func_hash] func_name = signatures[func_hash]
except KeyError: except KeyError:
func_name = "UNK_" + func_hash func_name = "_function_" + func_hash
try: try:
offset = self.instruction_list[i+2]['argument'] offset = self.instruction_list[i+2]['argument']

@ -74,31 +74,33 @@ class ContractStorage(persistent.Persistent):
receipt = eth.eth_getTransactionReceipt(tx['hash']) receipt = eth.eth_getTransactionReceipt(tx['hash'])
contract_address = receipt['contractAddress'] if receipt is not None:
contract_code = eth.eth_getCode(contract_address) contract_address = receipt['contractAddress']
contract_balance = eth.eth_getBalance(contract_address)
if not contract_balance or sync_all: contract_code = eth.eth_getCode(contract_address)
# skip contracts with zero balance (disable with --sync-all) contract_balance = eth.eth_getBalance(contract_address)
continue
code = ETHContract(contract_code, tx['input']) if not contract_balance or sync_all:
# skip contracts with zero balance (disable with --sync-all)
continue
m = hashlib.md5() code = ETHContract(contract_code, tx['input'])
m.update(contract_code.encode('UTF-8'))
contract_hash = m.digest()
try: m = hashlib.md5()
self.contracts[contract_hash] m.update(contract_code.encode('UTF-8'))
except KeyError: contract_hash = m.digest()
self.contracts[contract_hash] = code
m = InstanceList()
self.instance_lists[contract_hash] = m
self.instance_lists[contract_hash].add(contract_address, contract_balance) try:
self.contracts[contract_hash]
except KeyError:
self.contracts[contract_hash] = code
m = InstanceList()
self.instance_lists[contract_hash] = m
transaction.commit() self.instance_lists[contract_hash].add(contract_address, contract_balance)
transaction.commit()
self.last_block = blockNum self.last_block = blockNum
blockNum -= 1 blockNum -= 1

@ -4,7 +4,6 @@ from ethereum.abi import encode_abi, encode_int
from ethereum.utils import zpad from ethereum.utils import zpad
from ethereum.abi import method_id from ethereum.abi import method_id
def safe_decode(hex_encoded_string): def safe_decode(hex_encoded_string):
# print(type(hex_encoded_string)) # print(type(hex_encoded_string))
@ -35,6 +34,10 @@ def bytecode_from_blockchain(creation_tx_hash, ipc, rpc_host='127.0.0.1', rpc_po
raise RuntimeError("Transaction trace didn't return any bytecode") raise RuntimeError("Transaction trace didn't return any bytecode")
def fire_lasers(disassembly):
return laserfree.analysis(disassembly)
def encode_calldata(func_name, arg_types, args): def encode_calldata(func_name, arg_types, args):
mid = method_id(func_name, arg_types) mid = method_id(func_name, arg_types)

@ -1,4 +1,5 @@
ethereum>=2.0.4 ethereum>=2.0.4
ZODB>=5.3.0 ZODB>=5.3.0
graphviz>=0.8 z3-solver>=4.5
web3 web3
laser-ethereum>=0.1.4

@ -219,7 +219,7 @@ security community.
setup( setup(
name='mythril', name='mythril',
version='0.3.15', version='0.5.6',
description='A reversing and bug hunting framework for the Ethereum blockchain', description='A reversing and bug hunting framework for the Ethereum blockchain',
long_description=long_description, long_description=long_description,
@ -253,8 +253,10 @@ setup(
install_requires=[ install_requires=[
'ethereum>=2.0.4', 'ethereum>=2.0.4',
'web3',
'ZODB>=5.3.0', 'ZODB>=5.3.0',
'graphviz>=0.8' 'z3-solver>=4.5',
'laser-ethereum>=0.1.6'
], ],
python_requires='>=3.5', python_requires='>=3.5',

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

@ -0,0 +1,121 @@
<html>
<head>
<style type="text/css">
#mynetwork {
background-color: #000000;
}
body {
background-color: #000000;
color: #ffffff;
}
</style>
<link href="https://cdnjs.cloudflare.com/ajax/libs/vis/4.21.0/vis.min.css" rel="stylesheet" type="text/css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vis/4.21.0/vis.min.js"></script>
<script>
var options = {
autoResize: true,
height: '100%',
width: '100%',
manipulation: false,
height: '90%',
layout: {
randomSeed: undefined,
improvedLayout:true,
hierarchical: {
enabled:true,
levelSeparation: 450,
nodeSpacing: 200,
treeSpacing: 100,
blockShifting: true,
edgeMinimization: true,
parentCentralization: false,
direction: 'LR', // UD, DU, LR, RL
sortMethod: 'directed' // hubsize, directed
}
},
nodes:{
borderWidth: 1,
borderWidthSelected: 2,
chosen: true,
shape: 'box',
font: {
align: 'left',
color: '#FFFFFF',
},
color: {
border: '#26996f',
background: '#1f7e5b',
highlight: {
border: '#26996f',
background: '#28a16f'
}
}
},
edges:{
font: {
color: '#ffffff',
size: 12, // px
face: 'arial',
background: 'none',
strokeWidth: 0, // px
strokeColor: '#ffffff',
align: 'horizontal',
multi: false,
vadjust: 0,
}
},
physics:{
enabled: true,
}
}
var nodes = [
{id: 0, size: 150, 'label': '0 PUSH1 0x60\n2 PUSH1 0x40\n4 MSTORE\n5 PUSH1 0x04\n7 CALLDATASIZE\n8 LT\n9 PUSH2 0x0057\n12 JUMPI\n13 PUSH1 0x00\n15 CALLDATALOAD\n16 PUSH29 0x01000000(...)\n46 SWAP1\n47 DIV\n48 PUSH4 0xffffffff\n53 AND\n54 DUP1\n55 PUSH4 0x3ccfd60b\n60 EQ\n61 PUSH2 0x005c\n64 JUMPI\n65 DUP1\n66 PUSH4 0xf8b2cb4f\n71 EQ\n72 PUSH2 0x0071\n75 JUMPI\n76 DUP1\n77 PUSH4 0xfcddd056\n82 EQ\n83 PUSH2 0x00be\n86 JUMPI\n87 JUMPDEST\n88 PUSH1 0x00\n90 DUP1\n91 REVERT\n'},
{id: 198, size: 150, 'label': '198 JUMPDEST\n199 STOP\n'},
{id: 113, size: 150, 'label': '113 getBalance(address)\n114 CALLVALUE\n115 ISZERO\n116 PUSH2 0x007c\n119 JUMPI\n120 PUSH1 0x00\n122 DUP1\n123 REVERT\n'},
{id: 168, size: 150, 'label': '168 JUMPDEST\n169 PUSH1 0x40\n171 MLOAD\n172 DUP1\n173 DUP3\n174 DUP2\n175 MSTORE\n176 PUSH1 0x20\n178 ADD\n179 SWAP2\n180 POP\n181 POP\n182 PUSH1 0x40\n184 MLOAD\n185 DUP1\n186 SWAP2\n187 SUB\n188 SWAP1\n189 RETURN\n'},
{id: 92, size: 150, 'label': '92 withdraw()\n93 CALLVALUE\n94 ISZERO\n95 PUSH2 0x0067\n98 JUMPI\n99 PUSH1 0x00\n101 DUP1\n102 REVERT\n'},
{id: 103, size: 150, 'label': '103 JUMPDEST\n104 PUSH2 0x006f\n107 PUSH2 0x00c8\n110 JUMP\n'},
{id: 190, size: 150, 'label': '190 _function_0xfcddd056\n191 PUSH2 0x00c6\n194 PUSH2 0x021c\n197 JUMP\n'},
{id: 398, size: 150, 'label': '398 JUMPDEST\n399 PUSH1 0x00\n401 DUP1\n402 PUSH1 0x00\n404 CALLER\n405 PUSH20 0xffffffff(...)\n426 AND\n427 PUSH20 0xffffffff(...)\n448 AND\n449 DUP2\n450 MSTORE\n451 PUSH1 0x20\n453 ADD\n454 SWAP1\n455 DUP2\n456 MSTORE\n457 PUSH1 0x20\n459 ADD\n460 PUSH1 0x00\n462 SHA3\n463 DUP2\n464 SWAP1\n465 SSTORE\n466 POP\n467 JUMP\n'},
{id: 111, size: 150, 'label': '111 JUMPDEST\n112 STOP\n'},
{id: 200, size: 150, 'label': '200 JUMPDEST\n201 PUSH1 0x00\n203 DUP1\n204 PUSH1 0x00\n206 CALLER\n207 PUSH20 0xffffffff(...)\n228 AND\n229 PUSH20 0xffffffff(...)\n250 AND\n251 DUP2\n252 MSTORE\n253 PUSH1 0x20\n255 ADD\n256 SWAP1\n257 DUP2\n258 MSTORE\n259 PUSH1 0x20\n261 ADD\n262 PUSH1 0x00\n264 SHA3\n265 SLOAD\n266 GT\n267 ISZERO\n268 ISZERO\n269 PUSH2 0x0115\n272 JUMPI\n273 PUSH1 0x00\n275 DUP1\n276 REVERT\n'},
{id: 468, size: 150, 'label': '468 JUMPDEST\n469 PUSH1 0x00\n471 DUP1\n472 PUSH1 0x00\n474 DUP4\n475 PUSH20 0xffffffff(...)\n496 AND\n497 PUSH20 0xffffffff(...)\n518 AND\n519 DUP2\n520 MSTORE\n521 PUSH1 0x20\n523 ADD\n524 SWAP1\n525 DUP2\n526 MSTORE\n527 PUSH1 0x20\n529 ADD\n530 PUSH1 0x00\n532 SHA3\n533 SLOAD\n534 SWAP1\n535 POP\n536 SWAP2\n537 SWAP1\n538 POP\n539 JUMP\n'},
{id: 277, size: 150, 'label': '277 JUMPDEST\n278 CALLER\n279 PUSH20 0xffffffff(...)\n300 AND\n301 PUSH1 0x00\n303 DUP1\n304 CALLER\n305 PUSH20 0xffffffff(...)\n326 AND\n327 PUSH20 0xffffffff(...)\n348 AND\n349 DUP2\n350 MSTORE\n351 PUSH1 0x20\n353 ADD\n354 SWAP1\n355 DUP2\n356 MSTORE\n357 PUSH1 0x20\n359 ADD\n360 PUSH1 0x00\n362 SHA3\n363 SLOAD\n364 PUSH1 0x40\n366 MLOAD\n367 PUSH1 0x00\n369 PUSH1 0x40\n371 MLOAD\n372 DUP1\n373 DUP4\n374 SUB\n375 DUP2\n376 DUP6\n377 DUP8\n378 PUSH2 0x8796\n381 GAS\n382 SUB\n383 CALL\n384 SWAP3\n385 POP\n386 POP\n387 POP\n388 ISZERO\n389 ISZERO\n390 PUSH2 0x018e\n393 JUMPI\n394 PUSH1 0x00\n396 DUP1\n397 REVERT\n'},
{id: 87, size: 150, 'label': '87 JUMPDEST\n88 PUSH1 0x00\n90 DUP1\n91 REVERT\n'},
{id: 124, size: 150, 'label': '124 JUMPDEST\n125 PUSH2 0x00a8\n128 PUSH1 0x04\n130 DUP1\n131 DUP1\n132 CALLDATALOAD\n133 PUSH20 0xffffffff(...)\n154 AND\n155 SWAP1\n156 PUSH1 0x20\n158 ADD\n159 SWAP1\n160 SWAP2\n161 SWAP1\n162 POP\n163 POP\n164 PUSH2 0x01d4\n167 JUMP\n'},
{id: 540, size: 150, 'label': '540 JUMPDEST\n541 CALLVALUE\n542 PUSH1 0x00\n544 DUP1\n545 CALLER\n546 PUSH20 0xffffffff(...)\n567 AND\n568 PUSH20 0xffffffff(...)\n589 AND\n590 DUP2\n591 MSTORE\n592 PUSH1 0x20\n594 ADD\n595 SWAP1\n596 DUP2\n597 MSTORE\n598 PUSH1 0x20\n600 ADD\n601 PUSH1 0x00\n603 SHA3\n604 PUSH1 0x00\n606 DUP3\n607 DUP3\n608 SLOAD\n609 ADD\n610 SWAP3\n611 POP\n612 POP\n613 DUP2\n614 SWAP1\n615 SSTORE\n616 POP\n617 JUMP\n'}
];
var edges = [
{from: 0, to: 87, 'arrows': 'to', 'label': 'Not(ULE(4, calldatasize))', 'smooth': {'type': 'cubicBezier'}},
{from: 398, to: 111, 'arrows': 'to', 'label': '', 'smooth': {'type': 'cubicBezier'}},
{from: 277, to: 398, 'arrows': 'to', 'label': 'Not(retval_384_115 == 0)', 'smooth': {'type': 'cubicBezier'}},
{from: 200, to: 277, 'arrows': 'to', 'label': 'Not(storage_sha_hash == 0)', 'smooth': {'type': 'cubicBezier'}},
{from: 103, to: 200, 'arrows': 'to', 'label': '', 'smooth': {'type': 'cubicBezier'}},
{from: 92, to: 103, 'arrows': 'to', 'label': 'callvalue == 0', 'smooth': {'type': 'cubicBezier'}},
{from: 0, to: 92, 'arrows': 'to', 'label': 'Extract0xff,0xe0, calldata_0) ==0x3ccfd60b', 'smooth': {'type': 'cubicBezier'}},
{from: 468, to: 168, 'arrows': 'to', 'label': '', 'smooth': {'type': 'cubicBezier'}},
{from: 124, to: 468, 'arrows': 'to', 'label': '', 'smooth': {'type': 'cubicBezier'}},
{from: 113, to: 124, 'arrows': 'to', 'label': 'callvalue == 0', 'smooth': {'type': 'cubicBezier'}},
{from: 0, to: 113, 'arrows': 'to', 'label': 'Extract0xff,0xe0, calldata_0) ==0xf8b2cb4f', 'smooth': {'type': 'cubicBezier'}},
{from: 540, to: 198, 'arrows': 'to', 'label': '', 'smooth': {'type': 'cubicBezier'}},
{from: 190, to: 540, 'arrows': 'to', 'label': '', 'smooth': {'type': 'cubicBezier'}},
{from: 0, to: 190, 'arrows': 'to', 'label': 'Extract0xff,0xe0, calldata_0) ==0xfcddd056', 'smooth': {'type': 'cubicBezier'}}
];
</script>
</head>
<body>
<p>Mythril / Ethereum LASER Symbolic VM</p>
<p><div id="mynetwork"></div><br /></p>
<script type="text/javascript">
var container = document.getElementById('mynetwork');
var data = {'nodes': nodes, 'edges': edges}
var gph = new vis.Network(container, data, options);
</script>
</body>
</html>
Loading…
Cancel
Save