Fix merge conflict with master

pull/555/head
Dominik Muhs 6 years ago
commit 4ee2fdb5fa
  1. 6
      README.md
  2. 8
      mythril/analysis/symbolic.py
  3. 4
      mythril/ether/asm.py
  4. 13
      mythril/ether/ethcontract.py
  5. 2
      mythril/ether/evm.py
  6. 2
      mythril/ether/util.py
  7. 7
      mythril/interfaces/cli.py
  8. 10
      mythril/laser/ethereum/instructions.py
  9. 3
      mythril/laser/ethereum/taint_analysis.py
  10. 4
      mythril/laser/ethereum/util.py
  11. 23
      mythril/leveldb/accountindexing.py
  12. 105
      mythril/leveldb/client.py
  13. 18
      mythril/leveldb/eth_db.py
  14. 36
      mythril/leveldb/state.py
  15. 9
      mythril/mythril.py
  16. 28
      mythril/rpc/base_client.py
  17. 4
      mythril/rpc/client.py
  18. 16
      mythril/rpc/utils.py
  19. 4
      mythril/support/loader.py
  20. 1
      requirements.txt
  21. 1
      setup.py
  22. 12
      tests/native_test.py

@ -2,16 +2,16 @@
[![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/master.svg)
[![Waffle.io - Columns and their card count](https://badge.waffle.io/ConsenSys/mythril.svg?columns=all)](https://waffle.io/ConsenSys/mythril) [![Waffle.io - Columns and their card count](https://badge.waffle.io/ConsenSys/mythril.svg?columns=In%20Progress)](https://waffle.io/ConsenSys/mythril)
[![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)
<img height="120px" align="right" src="https://github.com/ConsenSys/mythril/raw/master/static/mythril.png" alt="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 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.
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 now building a whole security tools ecosystem with [Mythril Platform](https://mythril.ai). You should definitely check that out as well. 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.
## Installation and setup ## Installation and setup

@ -9,9 +9,9 @@ from mythril.laser.ethereum.strategy.basic import DepthFirstSearchStrategy, Brea
class SymExecWrapper: class SymExecWrapper:
''' """
Wrapper class for the LASER Symbolic virtual machine. Symbolically executes the code and does a bit of pre-analysis for convenience. Wrapper class for the LASER Symbolic virtual machine. Symbolically executes the code and does a bit of pre-analysis for convenience.
''' """
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): execution_timeout=None, create_timeout=None):
@ -66,7 +66,7 @@ class SymExecWrapper:
# ignore prebuilts # ignore prebuilts
continue continue
if (meminstart.type == VarType.CONCRETE and meminsz.type == VarType.CONCRETE): if meminstart.type == VarType.CONCRETE and meminsz.type == VarType.CONCRETE:
self.calls.append(Call(self.nodes[key], state, state_index, op, to, gas, value, state.mstate.memory[meminstart.val:meminsz.val * 4])) self.calls.append(Call(self.nodes[key], state, state_index, op, to, gas, value, state.mstate.memory[meminstart.val:meminsz.val * 4]))
else: else:
self.calls.append(Call(self.nodes[key], state, state_index, op, to, gas, value)) self.calls.append(Call(self.nodes[key], state, state_index, op, to, gas, value))
@ -104,7 +104,7 @@ class SymExecWrapper:
taint = True taint = True
for constraint in s.node.constraints: for constraint in s.node.constraints:
if ("caller" in str(constraint)): if "caller" in str(constraint):
taint = False taint = False
break break

@ -80,7 +80,7 @@ def find_opcode_sequence(pattern, instruction_list):
matched = False matched = False
break break
if (matched): if matched:
match_indexes.append(i) match_indexes.append(i)
return match_indexes return match_indexes
@ -102,7 +102,7 @@ def disassemble(bytecode):
instruction = {'address': addr} instruction = {'address': addr}
try: try:
if (sys.version_info > (3, 0)): if sys.version_info > (3, 0):
opcode = opcodes[bytecode[addr]] opcode = opcodes[bytecode[addr]]
else: else:
opcode = opcodes[ord(bytecode[addr])] opcode = opcodes[ord(bytecode[addr])]

@ -7,15 +7,16 @@ 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=True):
self.creation_code = creation_code
self.name = name
# 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
# Apply this for creation_code & code
creation_code = re.sub(r'(_+.*_+)', 'aa' * 20, creation_code)
code = re.sub(r'(_+.*_+)', 'aa' * 20, code) code = re.sub(r'(_+.*_+)', 'aa' * 20, code)
self.creation_code = creation_code
self.name = name
self.code = code self.code = code
self.disassembly = Disassembly(code, enable_online_lookup=enable_online_lookup) self.disassembly = Disassembly(code, enable_online_lookup=enable_online_lookup)
self.creation_disassembly = Disassembly(creation_code, enable_online_lookup=enable_online_lookup) self.creation_disassembly = Disassembly(creation_code, enable_online_lookup=enable_online_lookup)
@ -49,7 +50,7 @@ class ETHContract(persistent.Persistent):
m = re.match(r'^code#([a-zA-Z0-9\s,\[\]]+)#', token) m = re.match(r'^code#([a-zA-Z0-9\s,\[\]]+)#', token)
if (m): if m:
if easm_code is None: if easm_code is None:
easm_code = self.get_easm() easm_code = self.get_easm()
@ -59,7 +60,7 @@ class ETHContract(persistent.Persistent):
m = re.match(r'^func#([a-zA-Z0-9\s_,(\\)\[\]]+)#$', token) m = re.match(r'^func#([a-zA-Z0-9\s_,(\\)\[\]]+)#$', token)
if (m): if m:
sign_hash = "0x" + utils.sha3(m.group(1))[:4].hex() sign_hash = "0x" + utils.sha3(m.group(1))[:4].hex()

@ -37,7 +37,7 @@ def trace(code, calldata=""):
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):

@ -10,7 +10,7 @@ import json
def safe_decode(hex_encoded_string): def safe_decode(hex_encoded_string):
if (hex_encoded_string.startswith("0x")): if hex_encoded_string.startswith("0x"):
return bytes.fromhex(hex_encoded_string[2:]) return bytes.fromhex(hex_encoded_string[2:])
else: else:
return bytes.fromhex(hex_encoded_string) return bytes.fromhex(hex_encoded_string)

@ -5,7 +5,7 @@
http://www.github.com/ConsenSys/mythril http://www.github.com/ConsenSys/mythril
""" """
import logging import logging, coloredlogs
import json import json
import sys import sys
import argparse import argparse
@ -103,7 +103,10 @@ def main():
if args.v: if args.v:
if 0 <= args.v < 3: if 0 <= args.v < 3:
logging.basicConfig(level=[logging.NOTSET, logging.INFO, logging.DEBUG][args.v]) coloredlogs.install(
fmt='%(name)s[%(process)d] %(levelname)s %(message)s',
level=[logging.NOTSET, logging.INFO, logging.DEBUG][args.v]
)
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")

@ -773,7 +773,8 @@ class Instruction:
return self._sload_helper(global_state, str(index)) return self._sload_helper(global_state, str(index))
def _sload_helper(self, global_state, index, constraints=None): @staticmethod
def _sload_helper(global_state, index, constraints=None):
try: try:
data = global_state.environment.active_account.storage[index] data = global_state.environment.active_account.storage[index]
except KeyError: except KeyError:
@ -786,8 +787,8 @@ class Instruction:
global_state.mstate.stack.append(data) global_state.mstate.stack.append(data)
return [global_state] return [global_state]
@staticmethod
def _get_constraints(self, keccak_keys, this_key, argument): def _get_constraints(keccak_keys, this_key, argument):
global keccak_function_manager global keccak_function_manager
for keccak_key in keccak_keys: for keccak_key in keccak_keys:
if keccak_key == this_key: if keccak_key == this_key:
@ -837,7 +838,8 @@ class Instruction:
return self._sstore_helper(global_state, str(index), value) return self._sstore_helper(global_state, str(index), value)
def _sstore_helper(self, global_state, index, value, constraint=None): @staticmethod
def _sstore_helper(global_state, index, value, constraint=None):
try: try:
global_state.environment.active_account = deepcopy(global_state.environment.active_account) global_state.environment.active_account = deepcopy(global_state.environment.active_account)
global_state.accounts[ global_state.accounts[

@ -109,7 +109,8 @@ class TaintRunner:
records = TaintRunner.execute_node(node, record, index) records = TaintRunner.execute_node(node, record, index)
result.add_records(records) result.add_records(records)
if len(records) == 0: # continue if there is no record to work on
continue
children = TaintRunner.children(node, statespace, environment, transaction_stack_length) children = TaintRunner.children(node, statespace, environment, transaction_stack_length)
for child in children: for child in children:
current_nodes.append((child, records[-1], 0)) current_nodes.append((child, records[-1], 0))

@ -16,7 +16,7 @@ def sha3(seed):
def safe_decode(hex_encoded_string): def safe_decode(hex_encoded_string):
if (hex_encoded_string.startswith("0x")): if hex_encoded_string.startswith("0x"):
return bytes.fromhex(hex_encoded_string[2:]) return bytes.fromhex(hex_encoded_string[2:])
else: else:
return bytes.fromhex(hex_encoded_string) return bytes.fromhex(hex_encoded_string)
@ -99,7 +99,7 @@ def concrete_int_to_bytes(val):
# logging.debug("concrete_int_to_bytes " + str(val)) # logging.debug("concrete_int_to_bytes " + str(val))
if (type(val) == int): if type(val) == int:
return val.to_bytes(32, byteorder='big') return val.to_bytes(32, byteorder='big')
return (simplify(val).as_long()).to_bytes(32, byteorder='big') return (simplify(val).as_long()).to_bytes(32, byteorder='big')

@ -34,9 +34,9 @@ class CountableList(object):
class ReceiptForStorage(rlp.Serializable): class ReceiptForStorage(rlp.Serializable):
''' """
Receipt format stored in levelDB Receipt format stored in levelDB
''' """
fields = [ fields = [
('state_root', binary), ('state_root', binary),
@ -50,9 +50,9 @@ class ReceiptForStorage(rlp.Serializable):
class AccountIndexer(object): class AccountIndexer(object):
''' """
Updates address index Updates address index
''' """
def __init__(self, ethDB): def __init__(self, ethDB):
self.db = ethDB self.db = ethDB
@ -62,19 +62,20 @@ class AccountIndexer(object):
self.updateIfNeeded() self.updateIfNeeded()
def get_contract_by_hash(self, contract_hash): def get_contract_by_hash(self, contract_hash):
''' """
get mapped contract_address by its hash, if not found try indexing get mapped contract_address by its hash, if not found try indexing
''' """
contract_address = self.db.reader._get_address_by_hash(contract_hash) contract_address = self.db.reader._get_address_by_hash(contract_hash)
if contract_address is not None: if contract_address is not None:
return contract_address return contract_address
else: else:
raise AddressNotFoundError raise AddressNotFoundError
def _process(self, startblock): def _process(self, startblock):
''' """
Processesing method Processesing method
''' """
logging.debug("Processing blocks %d to %d" % (startblock, startblock + BATCH_SIZE)) logging.debug("Processing blocks %d to %d" % (startblock, startblock + BATCH_SIZE))
addresses = [] addresses = []
@ -94,9 +95,9 @@ class AccountIndexer(object):
return addresses return addresses
def updateIfNeeded(self): def updateIfNeeded(self):
''' """
update address index update address index
''' """
headBlock = self.db.reader._get_head_block() headBlock = self.db.reader._get_head_block()
if headBlock is not None: if headBlock is not None:
# avoid restarting search if head block is same & we already initialized # avoid restarting search if head block is same & we already initialized
@ -126,7 +127,7 @@ class AccountIndexer(object):
count = 0 count = 0
processed = 0 processed = 0
while (blockNum <= self.lastBlock): while blockNum <= self.lastBlock:
# leveldb cannot be accessed on multiple processes (not even readonly) # leveldb cannot be accessed on multiple processes (not even readonly)
# multithread version performs significantly worse than serial # multithread version performs significantly worse than serial
try: try:

@ -26,23 +26,23 @@ address_mapping_head_key = b'accountMapping' # head (latest) number of indexed
def _format_block_number(number): def _format_block_number(number):
''' """
formats block number to uint64 big endian formats block number to uint64 big endian
''' """
return utils.zpad(utils.int_to_big_endian(number), 8) return utils.zpad(utils.int_to_big_endian(number), 8)
def _encode_hex(v): def _encode_hex(v):
''' """
encodes hash as hex encodes hash as hex
''' """
return '0x' + utils.encode_hex(v) return '0x' + utils.encode_hex(v)
class LevelDBReader(object): class LevelDBReader(object):
''' """
level db reading interface, can be used with snapshot level db reading interface, can be used with snapshot
''' """
def __init__(self, db): def __init__(self, db):
self.db = db self.db = db
@ -50,34 +50,34 @@ class LevelDBReader(object):
self.head_state = None self.head_state = None
def _get_head_state(self): def _get_head_state(self):
''' """
gets head state gets head state
''' """
if not self.head_state: if not self.head_state:
root = self._get_head_block().state_root root = self._get_head_block().state_root
self.head_state = State(self.db, root) self.head_state = State(self.db, root)
return self.head_state return self.head_state
def _get_account(self, address): def _get_account(self, address):
''' """
gets account by address gets account by address
''' """
state = self._get_head_state() state = self._get_head_state()
account_address = binascii.a2b_hex(utils.remove_0x_head(address)) account_address = binascii.a2b_hex(utils.remove_0x_head(address))
return state.get_and_cache_account(account_address) return state.get_and_cache_account(account_address)
def _get_block_hash(self, number): def _get_block_hash(self, number):
''' """
gets block hash by block number gets block hash by block number
''' """
num = _format_block_number(number) num = _format_block_number(number)
hash_key = header_prefix + num + num_suffix hash_key = header_prefix + num + num_suffix
return self.db.get(hash_key) return self.db.get(hash_key)
def _get_head_block(self): def _get_head_block(self):
''' """
gets head block header gets head block header
''' """
if not self.head_block_header: if not self.head_block_header:
block_hash = self.db.get(head_header_key) block_hash = self.db.get(head_header_key)
num = self._get_block_number(block_hash) num = self._get_block_number(block_hash)
@ -91,38 +91,29 @@ class LevelDBReader(object):
return self.head_block_header return self.head_block_header
def _get_block_number(self, block_hash): def _get_block_number(self, block_hash):
''' """Get block number by its hash"""
gets block number by its hash
'''
number_key = block_hash_prefix + block_hash number_key = block_hash_prefix + block_hash
return self.db.get(number_key) return self.db.get(number_key)
def _get_block_header(self, block_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 + block_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, block_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 + block_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, block_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 + block_hash receipts_key = block_receipts_prefix + number + block_hash
receipts_data = self.db.get(receipts_key) receipts_data = self.db.get(receipts_key)
@ -131,44 +122,44 @@ class LevelDBReader(object):
class LevelDBWriter(object): class LevelDBWriter(object):
''' """
level db writing interface level db writing interface
''' """
def __init__(self, db): def __init__(self, db):
self.db = db self.db = db
self.wb = None self.wb = None
def _set_last_indexed_number(self, number): def _set_last_indexed_number(self, number):
''' """
sets latest indexed block number sets latest indexed block number
''' """
return self.db.put(address_mapping_head_key, _format_block_number(number)) return self.db.put(address_mapping_head_key, _format_block_number(number))
def _start_writing(self): def _start_writing(self):
''' """
start writing a batch start writing a batch
''' """
self.wb = self.db.write_batch() self.wb = self.db.write_batch()
def _commit_batch(self): def _commit_batch(self):
''' """
commit batch commit batch
''' """
self.wb.write() self.wb.write()
def _store_account_address(self, address): def _store_account_address(self, address):
''' """
get block transaction receipts by block header hash & number get block transaction receipts by block header hash & number
''' """
address_key = address_prefix + utils.sha3(address) address_key = address_prefix + utils.sha3(address)
self.wb.put(address_key, address) self.wb.put(address_key, address)
class EthLevelDB(object): class EthLevelDB(object):
''' """
Go-Ethereum LevelDB client class Go-Ethereum LevelDB client class
''' """
def __init__(self, path): def __init__(self, path):
self.path = path self.path = path
@ -177,9 +168,9 @@ class EthLevelDB(object):
self.writer = LevelDBWriter(self.db) self.writer = LevelDBWriter(self.db)
def get_contracts(self): def get_contracts(self):
''' """
iterate through all contracts iterate through all contracts
''' """
for account in self.reader._get_head_state().get_all_accounts(): for account in self.reader._get_head_state().get_all_accounts():
if account.code is not None: if account.code is not None:
code = _encode_hex(account.code) code = _encode_hex(account.code)
@ -188,9 +179,9 @@ class EthLevelDB(object):
yield contract, account.address, account.balance yield contract, account.address, account.balance
def search(self, expression, callback_func): def search(self, expression, callback_func):
''' """
searches through all contract accounts searches through all contract accounts
''' """
cnt = 0 cnt = 0
indexer = AccountIndexer(self) indexer = AccountIndexer(self)
@ -217,9 +208,7 @@ class EthLevelDB(object):
logging.info("Searched %d contracts" % cnt) logging.info("Searched %d contracts" % cnt)
def contract_hash_to_address(self, contract_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(contract_hash)) address_hash = binascii.a2b_hex(utils.remove_0x_head(contract_hash))
indexer = AccountIndexer(self) indexer = AccountIndexer(self)
@ -227,17 +216,17 @@ class EthLevelDB(object):
return _encode_hex(indexer.get_contract_by_hash(address_hash)) return _encode_hex(indexer.get_contract_by_hash(address_hash))
def eth_getBlockHeaderByNumber(self, number): def eth_getBlockHeaderByNumber(self, number):
''' """
gets block header by block number gets block header by block number
''' """
block_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(block_hash, block_number) return self.reader._get_block_header(block_hash, block_number)
def eth_getBlockByNumber(self, number): def eth_getBlockByNumber(self, number):
''' """
gets block body by block number gets block body by block number
''' """
block_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)
body_key = body_prefix + block_number + block_hash body_key = body_prefix + block_number + block_hash
@ -246,22 +235,22 @@ class EthLevelDB(object):
return body return body
def eth_getCode(self, address): def eth_getCode(self, address):
''' """
gets account code gets account code
''' """
account = self.reader._get_account(address) account = self.reader._get_account(address)
return _encode_hex(account.code) return _encode_hex(account.code)
def eth_getBalance(self, address): def eth_getBalance(self, address):
''' """
gets account balance gets account balance
''' """
account = self.reader._get_account(address) account = self.reader._get_account(address)
return account.balance return account.balance
def eth_getStorageAt(self, address, position): def eth_getStorageAt(self, address, position):
''' """
gets account storage data at position gets account storage data at position
''' """
account = self.reader._get_account(address) account = self.reader._get_account(address)
return _encode_hex(utils.zpad(utils.encode_int(account.get_storage_data(position)), 32)) return _encode_hex(utils.zpad(utils.encode_int(account.get_storage_data(position)), 32))

@ -3,27 +3,27 @@ from ethereum.db import BaseDB
class ETH_DB(BaseDB): class ETH_DB(BaseDB):
''' """
adopts pythereum BaseDB using plyvel adopts pythereum BaseDB using plyvel
''' """
def __init__(self, path): def __init__(self, path):
self.db = plyvel.DB(path) self.db = plyvel.DB(path)
def get(self, key): def get(self, key):
''' """
gets value for key gets value for key
''' """
return self.db.get(key) return self.db.get(key)
def put(self, key, value): def put(self, key, value):
''' """
puts value for key puts value for key
''' """
self.db.put(key, value) self.db.put(key, value)
def write_batch(self): def write_batch(self):
''' """
start writing a batch start writing a batch
''' """
return self.db.write_batch() return self.db.write_batch()

@ -32,9 +32,9 @@ STATE_DEFAULTS = {
class Account(rlp.Serializable): class Account(rlp.Serializable):
''' """
adjusted account from ethereum.state adjusted account from ethereum.state
''' """
fields = [ fields = [
('nonce', big_endian_int), ('nonce', big_endian_int),
@ -57,15 +57,15 @@ class Account(rlp.Serializable):
@property @property
def code(self): def code(self):
''' """
code rlp data code rlp data
''' """
return self.db.get(self.code_hash) return self.db.get(self.code_hash)
def get_storage_data(self, key): def get_storage_data(self, key):
''' """
get storage data get storage data
''' """
if key not in self.storage_cache: if key not in self.storage_cache:
v = self.storage_trie.get(utils.encode_int32(key)) v = self.storage_trie.get(utils.encode_int32(key))
self.storage_cache[key] = utils.big_endian_to_int( self.storage_cache[key] = utils.big_endian_to_int(
@ -74,24 +74,24 @@ class Account(rlp.Serializable):
@classmethod @classmethod
def blank_account(cls, db, addr, 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, addr) 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
def is_blank(self): def is_blank(self):
''' """
checks if is a blank account checks if is a blank account
''' """
return self.nonce == 0 and self.balance == 0 and self.code_hash == BLANK_HASH return self.nonce == 0 and self.balance == 0 and self.code_hash == BLANK_HASH
class State(): class State:
''' """
adjusted state from ethereum.state adjusted state from ethereum.state
''' """
def __init__(self, db, root): def __init__(self, db, root):
self.db = db self.db = db
@ -101,14 +101,14 @@ class State():
self.cache = {} self.cache = {}
def get_and_cache_account(self, addr): 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 addr in self.cache:
return self.cache[addr] return self.cache[addr]
rlpdata = self.secure_trie.get(addr) rlpdata = self.secure_trie.get(addr)
if rlpdata == trie.BLANK_NODE and len(addr) == 32: # support for hashed addresses if rlpdata == trie.BLANK_NODE and len(addr) == 32: # support for hashed addresses
rlpdata = self.trie.get(addr) rlpdata = self.trie.get(addr)
if rlpdata != trie.BLANK_NODE: if rlpdata != trie.BLANK_NODE:
o = rlp.decode(rlpdata, Account, db=self.db, address=addr) o = rlp.decode(rlpdata, Account, db=self.db, address=addr)
else: else:
@ -120,9 +120,9 @@ class State():
return o return o
def get_all_accounts(self): def get_all_accounts(self):
''' """
iterates through trie to and yields non-blank leafs as accounts iterates through trie to and yields non-blank leafs as accounts
''' """
for address_hash, rlpdata in self.secure_trie.trie.iter_branch(): for address_hash, rlpdata in self.secure_trie.trie.iter_branch():
if rlpdata != trie.BLANK_NODE: if rlpdata != trie.BLANK_NODE:
yield rlp.decode(rlpdata, Account, db=self.db, address=address_hash) yield rlp.decode(rlpdata, Account, db=self.db, address=address_hash)

@ -103,7 +103,8 @@ class Mythril(object):
self.contracts = [] # loaded contracts self.contracts = [] # loaded contracts
def _init_mythril_dir(self): @staticmethod
def _init_mythril_dir():
try: try:
mythril_dir = os.environ['MYTHRIL_DIR'] mythril_dir = os.environ['MYTHRIL_DIR']
except KeyError: except KeyError:
@ -179,7 +180,8 @@ class Mythril(object):
def analyze_truffle_project(self, *args, **kwargs): def analyze_truffle_project(self, *args, **kwargs):
return analyze_truffle_project(self.sigs, *args, **kwargs) # just passthru by passing signatures for now return analyze_truffle_project(self.sigs, *args, **kwargs) # just passthru by passing signatures for now
def _init_solc_binary(self, version): @staticmethod
def _init_solc_binary(version):
# Figure out solc binary and version # Figure out solc binary and version
# Only proper versions are supported. No nightlies, commits etc (such as available in remix) # Only proper versions are supported. No nightlies, commits etc (such as available in remix)
@ -434,7 +436,8 @@ class Mythril(object):
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)
def disassemble(self, contract): @staticmethod
def disassemble(contract):
return contract.get_easm() return contract.get_easm()
@staticmethod @staticmethod

@ -20,64 +20,64 @@ class BaseClient(object):
pass pass
def eth_coinbase(self): def eth_coinbase(self):
''' """
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_coinbase https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_coinbase
TESTED TESTED
''' """
return self._call('eth_coinbase') return self._call('eth_coinbase')
def eth_blockNumber(self): def eth_blockNumber(self):
''' """
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_blocknumber https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_blocknumber
TESTED TESTED
''' """
return hex_to_dec(self._call('eth_blockNumber')) return hex_to_dec(self._call('eth_blockNumber'))
def eth_getBalance(self, address=None, block=BLOCK_TAG_LATEST): def eth_getBalance(self, address=None, block=BLOCK_TAG_LATEST):
''' """
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getbalance https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getbalance
TESTED TESTED
''' """
address = address or self.eth_coinbase() address = address or self.eth_coinbase()
block = validate_block(block) block = validate_block(block)
return hex_to_dec(self._call('eth_getBalance', [address, block])) return hex_to_dec(self._call('eth_getBalance', [address, block]))
def eth_getStorageAt(self, address=None, position=0, block=BLOCK_TAG_LATEST): def eth_getStorageAt(self, address=None, position=0, block=BLOCK_TAG_LATEST):
''' """
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getstorageat https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getstorageat
TESTED TESTED
''' """
block = validate_block(block) block = validate_block(block)
return self._call('eth_getStorageAt', [address, hex(position), block]) return self._call('eth_getStorageAt', [address, hex(position), block])
def eth_getCode(self, address, default_block=BLOCK_TAG_LATEST): def eth_getCode(self, address, default_block=BLOCK_TAG_LATEST):
''' """
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getcode https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getcode
NEEDS TESTING NEEDS TESTING
''' """
if isinstance(default_block, str): if isinstance(default_block, str):
if default_block not in BLOCK_TAGS: if default_block not in BLOCK_TAGS:
raise ValueError raise ValueError
return self._call('eth_getCode', [address, default_block]) return self._call('eth_getCode', [address, default_block])
def eth_getBlockByNumber(self, block=BLOCK_TAG_LATEST, tx_objects=True): def eth_getBlockByNumber(self, block=BLOCK_TAG_LATEST, tx_objects=True):
''' """
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getblockbynumber https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getblockbynumber
TESTED TESTED
''' """
block = validate_block(block) block = validate_block(block)
return self._call('eth_getBlockByNumber', [block, tx_objects]) return self._call('eth_getBlockByNumber', [block, tx_objects])
def eth_getTransactionReceipt(self, tx_hash): def eth_getTransactionReceipt(self, tx_hash):
''' """
https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactionreceipt https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactionreceipt
TESTED TESTED
''' """
return self._call('eth_getTransactionReceipt', [tx_hash]) return self._call('eth_getTransactionReceipt', [tx_hash])

@ -17,9 +17,9 @@ JSON_MEDIA_TYPE = 'application/json'
This code is adapted from: https://github.com/ConsenSys/ethjsonrpc This code is adapted from: https://github.com/ConsenSys/ethjsonrpc
''' '''
class EthJsonRpc(BaseClient): class EthJsonRpc(BaseClient):
''' """
Ethereum JSON-RPC client class Ethereum JSON-RPC client class
''' """
def __init__(self, host='localhost', port=GETH_DEFAULT_RPC_PORT, tls=False): def __init__(self, host='localhost', port=GETH_DEFAULT_RPC_PORT, tls=False):
self.host = host self.host = host

@ -2,17 +2,17 @@ from .constants import BLOCK_TAGS
def hex_to_dec(x): def hex_to_dec(x):
''' """
Convert hex to decimal Convert hex to decimal
''' """
return int(x, 16) return int(x, 16)
def clean_hex(d): def clean_hex(d):
''' """
Convert decimal to hex and remove the "L" suffix that is appended to large Convert decimal to hex and remove the "L" suffix that is appended to large
numbers numbers
''' """
return hex(d).rstrip('L') return hex(d).rstrip('L')
def validate_block(block): def validate_block(block):
@ -25,14 +25,14 @@ def validate_block(block):
def wei_to_ether(wei): def wei_to_ether(wei):
''' """
Convert wei to ether Convert wei to ether
''' """
return 1.0 * wei / 10**18 return 1.0 * wei / 10**18
def ether_to_wei(ether): def ether_to_wei(ether):
''' """
Convert ether to wei Convert ether to wei
''' """
return ether * 10**18 return ether * 10**18

@ -37,7 +37,7 @@ class DynLoader:
m = re.match(r'^(0x[0-9a-fA-F]{40})$', dependency_address) m = re.match(r'^(0x[0-9a-fA-F]{40})$', dependency_address)
if (m): if m:
dependency_address = m.group(1) dependency_address = m.group(1)
else: else:
@ -47,7 +47,7 @@ class DynLoader:
code = self.eth.eth_getCode(dependency_address) code = self.eth.eth_getCode(dependency_address)
if (code == "0x"): if code == "0x":
return None return None
else: else:
return Disassembly(code) return Disassembly(code)

@ -1,3 +1,4 @@
coloredlogs>=10.0
configparser>=3.5.0 configparser>=3.5.0
coverage coverage
eth_abi>=1.0.0 eth_abi>=1.0.0

@ -82,6 +82,7 @@ setup(
packages=find_packages(exclude=['contrib', 'docs', 'tests']), packages=find_packages(exclude=['contrib', 'docs', 'tests']),
install_requires=[ install_requires=[
'coloredlogs>=10.0',
'ethereum>=2.3.2', 'ethereum>=2.3.2',
'z3-solver>=4.5', 'z3-solver>=4.5',
'requests', 'requests',

@ -6,13 +6,13 @@ from mythril.laser.ethereum import svm
from tests import * from tests import *
SHA256_TEST = [ (0,False) for i in range(6)] SHA256_TEST = [(0, False) for _ in range(6)]
RIPEMD160_TEST = [ (0,False) for i in range(6)] RIPEMD160_TEST = [(0, False) for _ in range(6)]
ECRECOVER_TEST = [ (0,False) for i in range(9)] ECRECOVER_TEST = [(0, False) for _ in range(9)]
IDENTITY_TEST = [ (0, False) for i in range(4)] IDENTITY_TEST = [(0, False) for _ in range(4)]
SHA256_TEST[0] = (5555555555555555, True) #These are Random numbers to check whether the 'if condition' is entered or not(True means entered) SHA256_TEST[0] = (5555555555555555, True) #These are Random numbers to check whether the 'if condition' is entered or not(True means entered)
SHA256_TEST[1] = (323232325445454546, True) SHA256_TEST[1] = (323232325445454546, True)
@ -98,9 +98,9 @@ def _test_natives(laser_info, test_list, test_name):
assert(success == len(test_list)) assert(success == len(test_list))
class NativeTests(BaseTestCase): class NativeTests(BaseTestCase):
def runTest(self): @staticmethod
def runTest():
disassembly = SolidityContract('./tests/native_tests.sol').disassembly disassembly = SolidityContract('./tests/native_tests.sol').disassembly
account = Account("0x0000000000000000000000000000000000000000", disassembly) account = Account("0x0000000000000000000000000000000000000000", disassembly)
accounts = {account.address: account} accounts = {account.address: account}

Loading…
Cancel
Save