Merge branch 'master' into issue/576-add-new-cli-option-to-pass-contract-creation-bytecode

pull/596/head
Maxime Biais 6 years ago
commit fac974080b
No known key found for this signature in database
GPG Key ID: F3EA910F71E3C894
  1. 20
      README.md
  2. 4
      mythril/analysis/modules/dependence_on_predictable_vars.py
  3. 4
      mythril/analysis/modules/ether_send.py
  4. 2
      mythril/analysis/modules/multiple_sends.py
  5. 1
      mythril/analysis/symbolic.py
  6. 8
      mythril/analysis/traceexplore.py
  7. 2
      mythril/disassembler/disassembly.py
  8. 2
      mythril/ether/ethcontract.py
  9. 29
      mythril/ether/evm.py
  10. 17
      mythril/ethereum/interface/leveldb/accountindexing.py
  11. 61
      mythril/ethereum/interface/leveldb/client.py
  12. 32
      mythril/ethereum/interface/leveldb/state.py
  13. 14
      mythril/interfaces/cli.py
  14. 2
      mythril/laser/ethereum/call.py
  15. 10
      mythril/laser/ethereum/instructions.py
  16. 2
      mythril/laser/ethereum/state.py
  17. 2
      mythril/laser/ethereum/svm.py
  18. 22
      mythril/mythril.py
  19. 3
      mythril/support/signatures.py
  20. 1
      requirements.txt
  21. 3
      setup.py
  22. BIN
      static/mythril.png
  23. BIN
      static/mythril_new.png
  24. 2
      tests/analysis/test_delegatecall.py
  25. 2
      tests/laser/state/mstack_test.py
  26. 2
      tests/laser/transaction/symbolic_test.py
  27. 2
      tests/report_test.py
  28. 3
      tests/taint_runner_test.py

@ -1,17 +1,23 @@
# Mythril OSS [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Mythril%20-%20Security%20Analyzer%20for%20Ethereum%20Smart%20Contracts&url=https://www.github.com/ConsenSys/mythril) # Mythril Classic
<p align="center">
<img src="/static/mythril_new.png" height="320px"/>
</p>
[![Discord](https://img.shields.io/discord/481002907366588416.svg)](https://discord.gg/E3YrVtG) [![Discord](https://img.shields.io/discord/481002907366588416.svg)](https://discord.gg/E3YrVtG)
[![PyPI](https://badge.fury.io/py/mythril.svg)](https://pypi.python.org/pypi/mythril) [![PyPI](https://badge.fury.io/py/mythril.svg)](https://pypi.python.org/pypi/mythril)
![Master Build Status](https://img.shields.io/circleci/project/github/ConsenSys/mythril/master.svg) ![Master Build Status](https://img.shields.io/circleci/project/github/ConsenSys/mythril-classic/master.svg)
[![Waffle.io - Columns and their card count](https://badge.waffle.io/ConsenSys/mythril.svg?columns=In%20Progress)](https://waffle.io/ConsenSys/mythril) [![Waffle.io - Columns and their card count](https://badge.waffle.io/ConsenSys/mythril-classic.svg?columns=In%20Progress)](https://waffle.io/ConsenSys/mythril-classic/)
[![Sonarcloud - Maintainability](https://sonarcloud.io/api/project_badges/measure?project=mythril&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=mythril) [![Sonarcloud - Maintainability](https://sonarcloud.io/api/project_badges/measure?project=mythril&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=mythril)
[![PyPI Statistics](https://pypistats.com/badge/mythril.svg)](https://pypistats.com/package/mythril) [![PyPI Statistics](https://pypistats.com/badge/mythril.svg)](https://pypistats.com/package/mythril)
<img height="120px" align="right" src="https://github.com/ConsenSys/mythril/raw/master/static/mythril.png" alt="mythril" />
Mythril OSS is the classic security analysis tool for Ethereum smart contracts. It uses concolic analysis, taint analysis and control flow checking to detect a variety of security vulnerabilities. Mythril Classic is an open-source security analysis tool for Ethereum smart contracts. It uses concolic analysis, taint analysis and control flow checking to detect a variety of security vulnerabilities.
Whether you want to contribute, need support, or want to learn what we have cooking for the future, our [Discord server](https://discord.gg/E3YrVtG) will serve your needs! Whether you want to contribute, need support, or want to learn what we have cooking for the future, our [Discord server](https://discord.gg/E3YrVtG) will serve your needs!
Oh and by the way, we're building an easy-to-use SaaS solution and tools ecosystem for Ethereum developers called [Mythril Platform](https://mythril.ai). You should definitely check that out as well. Oh and by the way, we're also building an easy-to-use security analysis platform (a.k.a. "the INFURA for smart contract security") that anybody can use to create purpose-built security tools. It's called [Mythril Platform](https://mythril.ai) and you should definitely [check it out](https://media.consensys.net/mythril-platform-api-is-upping-the-smart-contract-security-game-eee1d2642488).
## Installation and setup ## Installation and setup
@ -31,7 +37,7 @@ See the [Wiki](https://github.com/ConsenSys/mythril/wiki/Installation-and-Setup)
## Usage ## Usage
Instructions for using the 'myth' tool are found on the [Wiki](https://github.com/ConsenSys/mythril/wiki). Instructions for using the 'myth' tool are found on the [Wiki](https://github.com/ConsenSys/mythril-classic/wiki).
For support or general discussions please join the Mythril community on [Discord](https://discord.gg/E3YrVtG). For support or general discussions please join the Mythril community on [Discord](https://discord.gg/E3YrVtG).

@ -120,8 +120,8 @@ def solve(call):
model = solver.get_model(call.node.constraints) model = solver.get_model(call.node.constraints)
logging.debug("[DEPENDENCE_ON_PREDICTABLE_VARS] MODEL: " + str(model)) logging.debug("[DEPENDENCE_ON_PREDICTABLE_VARS] MODEL: " + str(model))
for d in model.decls(): for decl in model.decls():
logging.debug("[DEPENDENCE_ON_PREDICTABLE_VARS] main model: %s = 0x%x" % (d.name(), model[d].as_long())) logging.debug("[DEPENDENCE_ON_PREDICTABLE_VARS] main model: %s = 0x%x" % (decl.name(), model[decl].as_long()))
return True return True
except UnsatError: except UnsatError:

@ -111,8 +111,8 @@ def execute(statespace):
try: try:
model = solver.get_model(node.constraints) model = solver.get_model(node.constraints)
for d in model.decls(): for decl in model.decls():
logging.debug("[ETHER_SEND] main model: %s = 0x%x" % (d.name(), model[d].as_long())) logging.debug("[ETHER_SEND] main model: %s = 0x%x" % (decl.name(), model[decl].as_long()))
debug = "SOLVER OUTPUT:\n" + solver.pretty_print_model(model) debug = "SOLVER OUTPUT:\n" + solver.pretty_print_model(model)

@ -38,7 +38,7 @@ def execute(statespace):
def _explore_nodes(call, statespace): def _explore_nodes(call, statespace):
children = _child_nodes(statespace, call.node) children = _child_nodes(statespace, call.node)
sending_children = list(filter(lambda call: call.node in children, statespace.calls)) sending_children = list(filter(lambda c: c.node in children, statespace.calls))
return sending_children return sending_children

@ -16,7 +16,6 @@ class SymExecWrapper:
def __init__(self, contract, address, strategy, dynloader=None, max_depth=22, def __init__(self, contract, address, strategy, dynloader=None, max_depth=22,
execution_timeout=None, create_timeout=None, max_transaction_count=3): execution_timeout=None, create_timeout=None, max_transaction_count=3):
s_strategy = None
if strategy == 'dfs': if strategy == 'dfs':
s_strategy = DepthFirstSearchStrategy s_strategy = DepthFirstSearchStrategy
elif strategy == 'bfs': elif strategy == 'bfs':

@ -13,8 +13,8 @@ colors = [
{'border': '#4753bf', 'background': '#3b46a1', 'highlight': {'border': '#fff', 'background': '#424db3'}}, {'border': '#4753bf', 'background': '#3b46a1', 'highlight': {'border': '#fff', 'background': '#424db3'}},
] ]
def get_serializable_statespace(statespace):
def get_serializable_statespace(statespace):
nodes = [] nodes = []
edges = [] edges = []
@ -40,10 +40,10 @@ def get_serializable_statespace(statespace):
color = color_map[node.get_cfg_dict()['contract_name']] color = color_map[node.get_cfg_dict()['contract_name']]
def get_state_accounts(state): def get_state_accounts(node_state):
state_accounts = [] state_accounts = []
for key in state.accounts: for key in node_state.accounts:
account = state.accounts[key].as_dict account = node_state.accounts[key].as_dict
account.pop('code', None) account.pop('code', None)
account['balance'] = str(account['balance']) account['balance'] = str(account['balance'])

@ -5,7 +5,7 @@ import logging
class Disassembly(object): class Disassembly(object):
def __init__(self, code, enable_online_lookup=True): def __init__(self, code, enable_online_lookup=False):
self.instruction_list = asm.disassemble(util.safe_decode(code)) self.instruction_list = asm.disassemble(util.safe_decode(code))
self.func_hashes = [] self.func_hashes = []
self.func_to_addr = {} self.func_to_addr = {}

@ -6,7 +6,7 @@ import re
class ETHContract(persistent.Persistent): class ETHContract(persistent.Persistent):
def __init__(self, code="", creation_code="", name="Unknown", enable_online_lookup=True): def __init__(self, code="", creation_code="", name="Unknown", enable_online_lookup=False):
# Workaround: We currently do not support compile-time linking. # Workaround: We currently do not support compile-time linking.
# Dynamic contract addresses of the format __[contract-name]_____________ are replaced with a generic address # Dynamic contract addresses of the format __[contract-name]_____________ are replaced with a generic address

@ -7,10 +7,8 @@ from io import StringIO
import re import re
def trace(code, calldata = ""): def trace(code, calldata=""):
log_handlers = ['eth.vm.op', 'eth.vm.op.stack', 'eth.vm.op.memory', 'eth.vm.op.storage'] log_handlers = ['eth.vm.op', 'eth.vm.op.stack', 'eth.vm.op.memory', 'eth.vm.op.storage']
output = StringIO() output = StringIO()
stream_handler = StreamHandler(output) stream_handler = StreamHandler(output)
@ -20,56 +18,41 @@ def trace(code, calldata = ""):
log_vm_op.addHandler(stream_handler) log_vm_op.addHandler(stream_handler)
addr = bytes.fromhex('0123456789ABCDEF0123456789ABCDEF01234567') addr = bytes.fromhex('0123456789ABCDEF0123456789ABCDEF01234567')
state = State() state = State()
ext = messages.VMExt(state, transactions.Transaction(0, 0, 21000, addr, 0, addr)) ext = messages.VMExt(state, transactions.Transaction(0, 0, 21000, addr, 0, addr))
message = vm.Message(addr, addr, 0, 21000, calldata) message = vm.Message(addr, addr, 0, 21000, calldata)
vm.vm_execute(ext, message, util.safe_decode(code))
res, gas, dat = vm.vm_execute(ext, message, util.safe_decode(code))
stream_handler.flush() stream_handler.flush()
ret = output.getvalue() ret = output.getvalue()
lines = ret.split("\n") lines = ret.split("\n")
trace = [] state_trace = []
for line in lines: for line in lines:
m = re.search(r'pc=b\'(\d+)\'.*op=([A-Z0-9]+)', line) m = re.search(r'pc=b\'(\d+)\'.*op=([A-Z0-9]+)', line)
if m: if m:
pc = m.group(1) pc = m.group(1)
op = m.group(2) op = m.group(2)
m = re.match(r'.*stack=(\[.*?\])', line) m = re.match(r'.*stack=(\[.*?\])', line)
if m: if m:
stackitems = re.findall(r'b\'(\d+)\'', m.group(1)) stackitems = re.findall(r'b\'(\d+)\'', m.group(1))
stack = "[" stack = "["
if len(stackitems): if len(stackitems):
for i in range(0, len(stackitems) - 1): for i in range(0, len(stackitems) - 1):
stack += hex(int(stackitems[i])) + ", " stack += hex(int(stackitems[i])) + ", "
stack += hex(int(stackitems[-1])) stack += hex(int(stackitems[-1]))
stack += "]" stack += "]"
else: else:
stack = "[]" stack = "[]"
if re.match(r'^PUSH.*', op): if re.match(r'^PUSH.*', op):
val = re.search(r'pushvalue=(\d+)', line).group(1) val = re.search(r'pushvalue=(\d+)', line).group(1)
pushvalue = hex(int(val)) pushvalue = hex(int(val))
trace.append({'pc': pc, 'op': op, 'stack': stack, 'pushvalue': pushvalue}) state_trace.append({'pc': pc, 'op': op, 'stack': stack, 'pushvalue': pushvalue})
else: else:
trace.append({'pc': pc, 'op': op, 'stack': stack}) state_trace.append({'pc': pc, 'op': op, 'stack': stack})
return trace return state_trace

@ -63,16 +63,15 @@ class AccountIndexer(object):
def get_contract_by_hash(self, contract_hash): def get_contract_by_hash(self, contract_hash):
""" """
get mapped address by its hash, if not found try indexing get mapped contract_address by its hash, if not found try indexing
""" """
address = self.db.reader._get_address_by_hash(contract_hash) contract_address = self.db.reader._get_address_by_hash(contract_hash)
if address is not None: if contract_address is not None:
return address return contract_address
else: else:
raise AddressNotFoundError raise AddressNotFoundError
return self.db.reader._get_address_by_hash(contract_hash)
def _process(self, startblock): def _process(self, startblock):
""" """
Processesing method Processesing method
@ -82,9 +81,9 @@ class AccountIndexer(object):
addresses = [] addresses = []
for blockNum in range(startblock, startblock + BATCH_SIZE): for blockNum in range(startblock, startblock + BATCH_SIZE):
hash = self.db.reader._get_block_hash(blockNum) block_hash = self.db.reader._get_block_hash(blockNum)
if hash is not None: if block_hash is not None:
receipts = self.db.reader._get_block_receipts(hash, blockNum) receipts = self.db.reader._get_block_receipts(block_hash, blockNum)
for receipt in receipts: for receipt in receipts:
if receipt.contractAddress is not None and not all(b == 0 for b in receipt.contractAddress): if receipt.contractAddress is not None and not all(b == 0 for b in receipt.contractAddress):

@ -79,52 +79,43 @@ class LevelDBReader(object):
gets head block header gets head block header
""" """
if not self.head_block_header: if not self.head_block_header:
hash = self.db.get(head_header_key) block_hash = self.db.get(head_header_key)
num = self._get_block_number(hash) num = self._get_block_number(block_hash)
self.head_block_header = self._get_block_header(hash, num) self.head_block_header = self._get_block_header(block_hash, num)
# find header with valid state # find header with valid state
while not self.db.get(self.head_block_header.state_root) and self.head_block_header.prevhash is not None: while not self.db.get(self.head_block_header.state_root) and self.head_block_header.prevhash is not None:
hash = self.head_block_header.prevhash block_hash = self.head_block_header.prevhash
num = self._get_block_number(hash) num = self._get_block_number(block_hash)
self.head_block_header = self._get_block_header(hash, num) self.head_block_header = self._get_block_header(block_hash, num)
return self.head_block_header return self.head_block_header
def _get_block_number(self, hash): def _get_block_number(self, block_hash):
""" """Get block number by its hash"""
gets block number by hash number_key = block_hash_prefix + block_hash
"""
number_key = block_hash_prefix + hash
return self.db.get(number_key) return self.db.get(number_key)
def _get_block_header(self, hash, num): def _get_block_header(self, block_hash, num):
""" """Get block header by block header hash & number"""
get block header by block header hash & number header_key = header_prefix + num + block_hash
"""
header_key = header_prefix + num + hash
block_header_data = self.db.get(header_key) block_header_data = self.db.get(header_key)
header = rlp.decode(block_header_data, sedes=BlockHeader) header = rlp.decode(block_header_data, sedes=BlockHeader)
return header return header
def _get_address_by_hash(self, hash): def _get_address_by_hash(self, block_hash):
""" """Get mapped address by its hash"""
get mapped address by its hash address_key = address_prefix + block_hash
"""
address_key = address_prefix + hash
return self.db.get(address_key) return self.db.get(address_key)
def _get_last_indexed_number(self): def _get_last_indexed_number(self):
""" """Get latest indexed block number"""
latest indexed block number
"""
return self.db.get(address_mapping_head_key) return self.db.get(address_mapping_head_key)
def _get_block_receipts(self, hash, num): def _get_block_receipts(self, block_hash, num):
""" """Get block transaction receipts by block header hash & number"""
get block transaction receipts by block header hash & number
"""
number = _format_block_number(num) number = _format_block_number(num)
receipts_key = block_receipts_prefix + number + hash receipts_key = block_receipts_prefix + number + block_hash
receipts_data = self.db.get(receipts_key) receipts_data = self.db.get(receipts_key)
receipts = rlp.decode(receipts_data, sedes=CountableList(ReceiptForStorage)) receipts = rlp.decode(receipts_data, sedes=CountableList(ReceiptForStorage))
return receipts return receipts
@ -216,12 +207,10 @@ class EthLevelDB(object):
if not cnt % 1000: if not cnt % 1000:
logging.info("Searched %d contracts" % cnt) logging.info("Searched %d contracts" % cnt)
def contract_hash_to_address(self, hash): def contract_hash_to_address(self, contract_hash):
""" """Tries to find corresponding account address"""
tries to find corresponding account address
"""
address_hash = binascii.a2b_hex(utils.remove_0x_head(hash)) address_hash = binascii.a2b_hex(utils.remove_0x_head(contract_hash))
indexer = AccountIndexer(self) indexer = AccountIndexer(self)
return _encode_hex(indexer.get_contract_by_hash(address_hash)) return _encode_hex(indexer.get_contract_by_hash(address_hash))
@ -230,9 +219,9 @@ class EthLevelDB(object):
""" """
gets block header by block number gets block header by block number
""" """
hash = self.reader._get_block_hash(number) block_hash = self.reader._get_block_hash(number)
block_number = _format_block_number(number) block_number = _format_block_number(number)
return self.reader._get_block_header(hash, block_number) return self.reader._get_block_header(block_hash, block_number)
def eth_getBlockByNumber(self, number): def eth_getBlockByNumber(self, number):
""" """

@ -43,9 +43,9 @@ class Account(rlp.Serializable):
('code_hash', hash32) ('code_hash', hash32)
] ]
def __init__(self, nonce, balance, storage, code_hash, db, address): def __init__(self, nonce, balance, storage, code_hash, db, addr):
self.db = db self.db = db
self.address = address self.address = addr
super(Account, self).__init__(nonce, balance, storage, code_hash) super(Account, self).__init__(nonce, balance, storage, code_hash)
self.storage_cache = {} self.storage_cache = {}
self.storage_trie = SecureTrie(Trie(self.db)) self.storage_trie = SecureTrie(Trie(self.db))
@ -73,12 +73,12 @@ class Account(rlp.Serializable):
return self.storage_cache[key] return self.storage_cache[key]
@classmethod @classmethod
def blank_account(cls, db, address, initial_nonce=0): def blank_account(cls, db, addr, initial_nonce=0):
""" """
creates a blank account creates a blank account
""" """
db.put(BLANK_HASH, b'') db.put(BLANK_HASH, b'')
o = cls(initial_nonce, 0, trie.BLANK_ROOT, BLANK_HASH, db, address) o = cls(initial_nonce, 0, trie.BLANK_ROOT, BLANK_HASH, db, addr)
o.existent_at_start = False o.existent_at_start = False
return o return o
@ -100,21 +100,21 @@ class State:
self.journal = [] self.journal = []
self.cache = {} self.cache = {}
def get_and_cache_account(self, address): def get_and_cache_account(self, addr):
""" """Gets and caches an account for an addres, creates blank if not found"""
gets and caches an account for an addres, creates blank if not found
""" if addr in self.cache:
if address in self.cache: return self.cache[addr]
return self.cache[address] rlpdata = self.secure_trie.get(addr)
rlpdata = self.secure_trie.get(address) if rlpdata == trie.BLANK_NODE and len(addr) == 32: # support for hashed addresses
if rlpdata == trie.BLANK_NODE and len(address) == 32: # support for hashed addresses rlpdata = self.trie.get(addr)
rlpdata = self.trie.get(address)
if rlpdata != trie.BLANK_NODE: if rlpdata != trie.BLANK_NODE:
o = rlp.decode(rlpdata, Account, db=self.db, address=address) o = rlp.decode(rlpdata, Account, db=self.db, address=addr)
else: else:
o = Account.blank_account( o = Account.blank_account(
self.db, address, 0) self.db, addr, 0)
self.cache[address] = o self.cache[addr] = o
o._mutable = True o._mutable = True
o._cached_rlp = None o._cached_rlp = None
return o return o

@ -15,10 +15,11 @@ import argparse
from mythril.exceptions import CriticalError, AddressNotFoundError from mythril.exceptions import CriticalError, AddressNotFoundError
from mythril.mythril import Mythril from mythril.mythril import Mythril
from mythril.version import VERSION from mythril.version import VERSION
import mythril.support.signatures as sigs
def exit_with_error(format, message): def exit_with_error(format_, message):
if format == 'text' or format == 'markdown': if format_ == 'text' or format_ == 'markdown':
print(message) print(message)
else: else:
result = {'success': False, 'error': str(message), 'issues': []} result = {'success': False, 'error': str(message), 'issues': []}
@ -79,6 +80,7 @@ def main():
options.add_argument('--phrack', action='store_true', help='Phrack-style call graph') options.add_argument('--phrack', action='store_true', help='Phrack-style call graph')
options.add_argument('--enable-physics', action='store_true', help='enable graph physics simulation') options.add_argument('--enable-physics', action='store_true', help='enable graph physics simulation')
options.add_argument('-v', type=int, help='log level (0-2)', metavar='LOG_LEVEL') options.add_argument('-v', type=int, help='log level (0-2)', metavar='LOG_LEVEL')
options.add_argument('-q', '--query-signature', action='store_true', help='Lookup function signatures through www.4byte.directory')
rpc = parser.add_argument_group('RPC options') rpc = parser.add_argument_group('RPC options')
rpc.add_argument('-i', action='store_true', help='Preset: Infura Node service (Mainnet)') rpc.add_argument('-i', action='store_true', help='Preset: Infura Node service (Mainnet)')
@ -103,6 +105,7 @@ def main():
parser.print_help() parser.print_help()
sys.exit() sys.exit()
if args.v: if args.v:
if 0 <= args.v < 3: if 0 <= args.v < 3:
coloredlogs.install( coloredlogs.install(
@ -112,6 +115,10 @@ def main():
else: else:
exit_with_error(args.outform, "Invalid -v value, you can find valid values in usage") exit_with_error(args.outform, "Invalid -v value, you can find valid values in usage")
if args.query_signature:
if sigs.ethereum_input_decoder == None:
exit_with_error(args.outform, "The --query-signature function requires the python package ethereum-input-decoder")
# -- commands -- # -- commands --
if args.hash: if args.hash:
print(Mythril.hash_for_function_signature(args.hash)) print(Mythril.hash_for_function_signature(args.hash))
@ -123,7 +130,8 @@ def main():
# solc_args = None, dynld = None, max_recursion_depth = 12): # solc_args = None, dynld = None, max_recursion_depth = 12):
mythril = Mythril(solv=args.solv, dynld=args.dynld, mythril = Mythril(solv=args.solv, dynld=args.dynld,
solc_args=args.solc_args) solc_args=args.solc_args,
enable_online_lookup=args.query_signature)
if args.dynld and not (args.rpc or args.i): if args.dynld and not (args.rpc or args.i):
mythril.set_api_from_config_path() mythril.set_api_from_config_path()

@ -99,7 +99,7 @@ def get_callee_account(global_state, callee_address, dynamic_loader):
try: try:
code = dynamic_loader.dynld(environment.active_account.address, callee_address) code = dynamic_loader.dynld(environment.active_account.address, callee_address)
except Exception as e: except Exception:
logging.debug("Unable to execute dynamic loader.") logging.debug("Unable to execute dynamic loader.")
raise ValueError() raise ValueError()
if code is None: if code is None:

@ -497,7 +497,6 @@ class Instruction:
global keccak_function_manager global keccak_function_manager
state = global_state.mstate state = global_state.mstate
environment = global_state.environment
op0, op1 = state.stack.pop(), state.stack.pop() op0, op1 = state.stack.pop(), state.stack.pop()
try: try:
@ -701,12 +700,9 @@ class Instruction:
try: try:
# Attempt to concretize value # Attempt to concretize value
_bytes = util.concrete_int_to_bytes(value) _bytes = util.concrete_int_to_bytes(value)
state.memory[mstart: mstart + len(_bytes)] = _bytes
state.memory[mstart:mstart+len(_bytes)] = _bytes except:
except (AttributeError, TypeError):
try: try:
state.memory[mstart] = value state.memory[mstart] = value
except TypeError: except TypeError:
@ -948,7 +944,7 @@ class Instruction:
state = global_state.mstate state = global_state.mstate
dpth = int(self.op_code[3:]) dpth = int(self.op_code[3:])
state.stack.pop(), state.stack.pop() state.stack.pop(), state.stack.pop()
[state.stack.pop() for x in range(dpth)] [state.stack.pop() for _ in range(dpth)]
# Not supported # Not supported
return [global_state] return [global_state]

@ -278,10 +278,10 @@ class GlobalState:
def new_bitvec(self, name, size=256): def new_bitvec(self, name, size=256):
transaction_id = self.current_transaction.id transaction_id = self.current_transaction.id
node_id = self.node.uid
return BitVec("{}_{}".format(transaction_id, name), size) return BitVec("{}_{}".format(transaction_id, name), size)
class WorldState: class WorldState:
""" """
The WorldState class represents the world state as described in the yellow paper The WorldState class represents the world state as described in the yellow paper

@ -74,7 +74,7 @@ class LaserEVM:
logging.info("Finished contract creation, found {} open states".format(len(self.open_states))) logging.info("Finished contract creation, found {} open states".format(len(self.open_states)))
if len(self.open_states) == 0: if len(self.open_states) == 0:
logging.warning("No contract was created during the execution of contract creation " logging.warning("No contract was created during the execution of contract creation "
"Increase the resources for creation execution (--max-depth or --create_timeout)") "Increase the resources for creation execution (--max-depth or --create-timeout)")
# Reset code coverage # Reset code coverage
self.coverage = {} self.coverage = {}

@ -76,18 +76,20 @@ class Mythril(object):
""" """
def __init__(self, solv=None, def __init__(self, solv=None,
solc_args=None, dynld=False): solc_args=None, dynld=False,
enable_online_lookup=False):
self.solv = solv self.solv = solv
self.solc_args = solc_args self.solc_args = solc_args
self.dynld = dynld self.dynld = dynld
self.enable_online_lookup = enable_online_lookup
self.mythril_dir = self._init_mythril_dir() self.mythril_dir = self._init_mythril_dir()
self.sigs = signatures.SignatureDb() self.sigs = signatures.SignatureDb(enable_online_lookup=self.enable_online_lookup)
try: try:
self.sigs.open() # tries mythril_dir/signatures.json by default (provide path= arg to make this configurable) self.sigs.open() # tries mythril_dir/signatures.json by default (provide path= arg to make this configurable)
except FileNotFoundError as fnfe: except FileNotFoundError:
logging.info( logging.info(
"No signature database found. Creating database if sigs are loaded in: " + self.sigs.signatures_file + "\n" + "No signature database found. Creating database if sigs are loaded in: " + self.sigs.signatures_file + "\n" +
"Consider replacing it with the pre-initialized database at https://raw.githubusercontent.com/ConsenSys/mythril/master/signatures.json") "Consider replacing it with the pre-initialized database at https://raw.githubusercontent.com/ConsenSys/mythril/master/signatures.json")
@ -261,8 +263,7 @@ class Mythril(object):
def search_db(self, search): def search_db(self, search):
def search_callback(contract, address, balance): def search_callback(_, address, balance):
print("Address: " + address + ", balance: " + str(balance)) print("Address: " + address + ", balance: " + str(balance))
try: try:
@ -280,10 +281,9 @@ class Mythril(object):
def load_from_bytecode(self, code, is_contract_creation=False): def load_from_bytecode(self, code, is_contract_creation=False):
address = util.get_indexed_address(0) address = util.get_indexed_address(0)
if (is_contract_creation): if (is_contract_creation):
self.contracts.append(ETHContract(creation_code=code, name="MAIN")) self.contracts.append(ETHContract(creation_code=code, name="MAIN", enable_online_lookup=self.enable_online_lookup))
else: else:
self.contracts.append(ETHContract(code=code, name="MAIN")) self.contracts.append(ETHContract(code=code, name="MAIN", enable_online_lookup=self.enable_online_lookup))
print(self.contracts)
return address, self.contracts[-1] # return address and contract object return address, self.contracts[-1] # return address and contract object
def load_from_address(self, address): def load_from_address(self, address):
@ -294,7 +294,7 @@ class Mythril(object):
code = self.eth.eth_getCode(address) code = self.eth.eth_getCode(address)
except FileNotFoundError as e: except FileNotFoundError as e:
raise CriticalError("IPC error: " + str(e)) raise CriticalError("IPC error: " + str(e))
except ConnectionError as e: except ConnectionError:
raise CriticalError("Could not connect to RPC server. Make sure that your node is running and that RPC parameters are set correctly.") raise CriticalError("Could not connect to RPC server. Make sure that your node is running and that RPC parameters are set correctly.")
except Exception as e: except Exception as e:
raise CriticalError("IPC / RPC error: " + str(e)) raise CriticalError("IPC / RPC error: " + str(e))
@ -302,7 +302,7 @@ class Mythril(object):
if code == "0x" or code == "0x0": if code == "0x" or code == "0x0":
raise CriticalError("Received an empty response from eth_getCode. Check the contract address and verify that you are on the correct chain.") raise CriticalError("Received an empty response from eth_getCode. Check the contract address and verify that you are on the correct chain.")
else: else:
self.contracts.append(ETHContract(code, name=address)) self.contracts.append(ETHContract(code, name=address, enable_online_lookup=self.enable_online_lookup))
return address, self.contracts[-1] # return address and contract object return address, self.contracts[-1] # return address and contract object
def load_from_solidity(self, solidity_files): def load_from_solidity(self, solidity_files):
@ -439,7 +439,7 @@ class Mythril(object):
outtxt.append("{}: {}".format(hex(i), self.eth.eth_getStorageAt(address, i))) outtxt.append("{}: {}".format(hex(i), self.eth.eth_getStorageAt(address, i)))
except FileNotFoundError as e: except FileNotFoundError as e:
raise CriticalError("IPC error: " + str(e)) raise CriticalError("IPC error: " + str(e))
except ConnectionError as e: except ConnectionError:
raise CriticalError("Could not connect to RPC server. Make sure that your node is running and that RPC parameters are set correctly.") raise CriticalError("Could not connect to RPC server. Make sure that your node is running and that RPC parameters are set correctly.")
return '\n'.join(outtxt) return '\n'.join(outtxt)

@ -11,7 +11,6 @@ from subprocess import Popen, PIPE
from mythril.exceptions import CompilerError from mythril.exceptions import CompilerError
# todo: tintinweb - make this a normal requirement? (deps: eth-abi and requests, both already required by mythril)
try: try:
# load if available but do not fail # load if available but do not fail
import ethereum_input_decoder import ethereum_input_decoder
@ -54,7 +53,7 @@ except ImportError:
class SignatureDb(object): class SignatureDb(object):
def __init__(self, enable_online_lookup=True): def __init__(self, enable_online_lookup=False):
""" """
Constr Constr
:param enable_online_lookup: enable onlien signature hash lookup :param enable_online_lookup: enable onlien signature hash lookup

@ -4,6 +4,7 @@ coverage
eth_abi>=1.0.0 eth_abi>=1.0.0
eth-account>=0.1.0a2 eth-account>=0.1.0a2
ethereum>=2.3.2 ethereum>=2.3.2
ethereum-input-decoder>=0.2.2
eth-hash>=0.1.0 eth-hash>=0.1.0
eth-keyfile>=0.5.1 eth-keyfile>=0.5.1
eth-keys>=0.2.0b3 eth-keys>=0.2.0b3

@ -104,7 +104,8 @@ setup(
'py-flags', 'py-flags',
'mock', 'mock',
'configparser>=3.5.0', 'configparser>=3.5.0',
'persistent>=4.2.0' 'persistent>=4.2.0',
'ethereum-input-decoder>=0.2.2'
], ],
tests_require=[ tests_require=[

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

@ -189,7 +189,7 @@ def test_delegate_call(sym_mock, concrete_mock, curr_instruction):
statespace.calls = [call] statespace.calls = [call]
# act # act
issues = execute(statespace) execute(statespace)
# assert # assert
assert concrete_mock.call_count == 1 assert concrete_mock.call_count == 1

@ -45,7 +45,7 @@ class MachineStackTest(BaseTestCase):
mstack = MachineStack([0, 1]) mstack = MachineStack([0, 1])
with pytest.raises(NotImplementedError): with pytest.raises(NotImplementedError):
mstack = mstack + [2] mstack + [2]
@staticmethod @staticmethod
def test_mstack_no_support_iadd(): def test_mstack_no_support_iadd():

@ -49,7 +49,7 @@ def test_execute_contract_creation(mocked_setup: MagicMock):
mocked_setup.side_effect = _is_contract_creation mocked_setup.side_effect = _is_contract_creation
# Act # Act
new_account = execute_contract_creation(laser_evm, "606000") execute_contract_creation(laser_evm, "606000")
# Assert # Assert
# mocked_setup.assert_called() # mocked_setup.assert_called()

@ -23,7 +23,7 @@ def _fix_debug_data(json_str):
def _generate_report(input_file): def _generate_report(input_file):
contract = ETHContract(input_file.read_text()) contract = ETHContract(input_file.read_text(), enable_online_lookup=False)
sym = SymExecWrapper(contract, address=(util.get_indexed_address(0)), strategy="dfs", execution_timeout=30) sym = SymExecWrapper(contract, address=(util.get_indexed_address(0)), strategy="dfs", execution_timeout=30)
issues = fire_lasers(sym) issues = fire_lasers(sym)

@ -6,6 +6,7 @@ from mythril.laser.ethereum.cfg import Node, Edge
from mythril.laser.ethereum.state import MachineState, Account, Environment, GlobalState from mythril.laser.ethereum.state import MachineState, Account, Environment, GlobalState
from mythril.laser.ethereum.svm import LaserEVM from mythril.laser.ethereum.svm import LaserEVM
def test_execute_state(mocker): def test_execute_state(mocker):
record = TaintRecord() record = TaintRecord()
record.stack = [True, False, True] record.stack = [True, False, True]
@ -54,8 +55,6 @@ def test_execute_node(mocker):
assert state_1 in record.states assert state_1 in record.states
def test_execute(mocker): def test_execute(mocker):
active_account = Account('0x00') active_account = Account('0x00')
environment = Environment(active_account, None, None, None, None, None) environment = Environment(active_account, None, None, None, None, None)

Loading…
Cancel
Save