pull/561/head
p0n1 6 years ago
commit e72c56e670
  1. 8
      mythril/analysis/symbolic.py
  2. 2
      mythril/analysis/traceexplore.py
  3. 4
      mythril/ether/asm.py
  4. 4
      mythril/ether/ethcontract.py
  5. 8
      mythril/ether/evm.py
  6. 2
      mythril/ether/util.py
  7. 10
      mythril/laser/ethereum/instructions.py
  8. 3
      mythril/laser/ethereum/taint_analysis.py
  9. 4
      mythril/laser/ethereum/util.py
  10. 24
      mythril/leveldb/accountindexing.py
  11. 104
      mythril/leveldb/client.py
  12. 18
      mythril/leveldb/eth_db.py
  13. 36
      mythril/leveldb/state.py
  14. 9
      mythril/mythril.py
  15. 28
      mythril/rpc/base_client.py
  16. 4
      mythril/rpc/client.py
  17. 16
      mythril/rpc/utils.py
  18. 4
      mythril/support/loader.py
  19. 12
      tests/native_test.py

@ -9,9 +9,9 @@ from mythril.laser.ethereum.strategy.basic import DepthFirstSearchStrategy, Brea
class SymExecWrapper:
'''
"""
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,
execution_timeout=None, create_timeout=None):
@ -67,7 +67,7 @@ class SymExecWrapper:
# ignore prebuilts
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]))
else:
self.calls.append(Call(self.nodes[key], state, state_index, op, to, gas, value))
@ -105,7 +105,7 @@ class SymExecWrapper:
taint = True
for constraint in s.node.constraints:
if ("caller" in str(constraint)):
if "caller" in str(constraint):
taint = False
break

@ -81,7 +81,7 @@ def get_serializable_statespace(statespace):
for edge in statespace.edges:
if (edge.condition is None):
if edge.condition is None:
label = ""
else:

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

@ -50,7 +50,7 @@ class ETHContract(persistent.Persistent):
m = re.match(r'^code#([a-zA-Z0-9\s,\[\]]+)#', token)
if (m):
if m:
if easm_code is None:
easm_code = self.get_easm()
@ -60,7 +60,7 @@ class ETHContract(persistent.Persistent):
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()

@ -47,13 +47,13 @@ def trace(code, calldata = ""):
m = re.match(r'.*stack=(\[.*?\])', line)
if (m):
if m:
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):
stack += hex(int(stackitems[i])) + ", "
@ -65,7 +65,7 @@ def trace(code, calldata = ""):
else:
stack = "[]"
if (re.match(r'^PUSH.*', op)):
if re.match(r'^PUSH.*', op):
val = re.search(r'pushvalue=(\d+)', line).group(1)
pushvalue = hex(int(val))
trace.append({'pc': pc, 'op': op, 'stack': stack, 'pushvalue': pushvalue})

@ -10,7 +10,7 @@ import json
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:])
else:
return bytes.fromhex(hex_encoded_string)

@ -779,7 +779,8 @@ class Instruction:
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:
data = global_state.environment.active_account.storage[index]
except KeyError:
@ -792,8 +793,8 @@ class Instruction:
global_state.mstate.stack.append(data)
return [global_state]
def _get_constraints(self, keccak_keys, this_key, argument):
@staticmethod
def _get_constraints(keccak_keys, this_key, argument):
global keccak_function_manager
for keccak_key in keccak_keys:
if keccak_key == this_key:
@ -843,7 +844,8 @@ class Instruction:
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:
global_state.environment.active_account = deepcopy(global_state.environment.active_account)
global_state.accounts[

@ -109,7 +109,8 @@ class TaintRunner:
records = TaintRunner.execute_node(node, record, index)
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)
for child in children:
current_nodes.append((child, records[-1], 0))

@ -16,7 +16,7 @@ def sha3(seed):
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:])
else:
return bytes.fromhex(hex_encoded_string)
@ -99,7 +99,7 @@ def concrete_int_to_bytes(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 (simplify(val).as_long()).to_bytes(32, byteorder='big')

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

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

@ -32,9 +32,9 @@ STATE_DEFAULTS = {
class Account(rlp.Serializable):
'''
"""
adjusted account from ethereum.state
'''
"""
fields = [
('nonce', big_endian_int),
@ -57,15 +57,15 @@ class Account(rlp.Serializable):
@property
def code(self):
'''
"""
code rlp data
'''
"""
return self.db.get(self.code_hash)
def get_storage_data(self, key):
'''
"""
get storage data
'''
"""
if key not in self.storage_cache:
v = self.storage_trie.get(utils.encode_int32(key))
self.storage_cache[key] = utils.big_endian_to_int(
@ -74,24 +74,24 @@ class Account(rlp.Serializable):
@classmethod
def blank_account(cls, db, address, initial_nonce=0):
'''
"""
creates a blank account
'''
"""
db.put(BLANK_HASH, b'')
o = cls(initial_nonce, 0, trie.BLANK_ROOT, BLANK_HASH, db, address)
o.existent_at_start = False
return o
def is_blank(self):
'''
"""
checks if is a blank account
'''
"""
return self.nonce == 0 and self.balance == 0 and self.code_hash == BLANK_HASH
class State():
'''
class State:
"""
adjusted state from ethereum.state
'''
"""
def __init__(self, db, root):
self.db = db
@ -101,9 +101,9 @@ class State():
self.cache = {}
def get_and_cache_account(self, address):
'''
"""
gets and caches an account for an addres, creates blank if not found
'''
"""
if address in self.cache:
return self.cache[address]
rlpdata = self.secure_trie.get(address)
@ -120,9 +120,9 @@ class State():
return o
def get_all_accounts(self):
'''
"""
iterates through trie to and yields non-blank leafs as accounts
'''
"""
for address_hash, rlpdata in self.secure_trie.trie.iter_branch():
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
def _init_mythril_dir(self):
@staticmethod
def _init_mythril_dir():
try:
mythril_dir = os.environ['MYTHRIL_DIR']
except KeyError:
@ -179,7 +180,8 @@ class Mythril(object):
def analyze_truffle_project(self, *args, **kwargs):
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
# Only proper versions are supported. No nightlies, commits etc (such as available in remix)
@ -435,7 +437,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.")
return '\n'.join(outtxt)
def disassemble(self, contract):
@staticmethod
def disassemble(contract):
return contract.get_easm()
@staticmethod

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

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

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

@ -6,13 +6,13 @@ from mythril.laser.ethereum import svm
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[1] = (323232325445454546, True)
@ -98,9 +98,9 @@ def _test_natives(laser_info, test_list, test_name):
assert(success == len(test_list))
class NativeTests(BaseTestCase):
def runTest(self):
@staticmethod
def runTest():
disassembly = SolidityContract('./tests/native_tests.sol').disassembly
account = Account("0x0000000000000000000000000000000000000000", disassembly)
accounts = {account.address: account}

Loading…
Cancel
Save