diff --git a/mythril/interfaces/cli.py b/mythril/interfaces/cli.py index 36880aea..242e3f2f 100644 --- a/mythril/interfaces/cli.py +++ b/mythril/interfaces/cli.py @@ -90,6 +90,11 @@ def main(): action="store_true", help="auto-load dependencies from the blockchain", ) + inputs.add_argument( + "--no-onchain-storage-access", + action="store_true", + help="turns off getting the data from onchain contracts", + ) outputs = parser.add_argument_group("output formats") outputs.add_argument( @@ -268,10 +273,11 @@ def main(): mythril = Mythril( solv=args.solv, dynld=args.dynld, + onchain_storage_access=(not args.no_onchain_storage_access), solc_args=args.solc_args, enable_online_lookup=args.query_signature, ) - if args.dynld and not (args.rpc or args.i): + if args.dynld or not args.no_onchain_storage_access and not (args.rpc or args.i): mythril.set_api_from_config_path() if args.address: @@ -280,7 +286,7 @@ def main(): mythril.set_api_rpc_infura() elif args.rpc: mythril.set_api_rpc(rpc=args.rpc, rpctls=args.rpctls) - elif not args.dynld: + elif not (args.dynld or not args.no_onchain_storage_access): mythril.set_api_rpc_localhost() elif args.search or args.contract_hash_to_address: # Open LevelDB if necessary diff --git a/mythril/laser/ethereum/call.py b/mythril/laser/ethereum/call.py index c6311d51..b64b5ed6 100644 --- a/mythril/laser/ethereum/call.py +++ b/mythril/laser/ethereum/call.py @@ -122,9 +122,9 @@ def get_callee_account(global_state, callee_address, dynamic_loader): try: code = dynamic_loader.dynld(environment.active_account.address, callee_address) - except Exception: - logging.debug("Unable to execute dynamic loader.") - raise ValueError() + except ValueError as error: + logging.debug("Unable to execute dynamic loader because: {}".format(error.message)) + raise ValueError(error.message) if code is None: logging.debug("No code returned, not a contract account?") raise ValueError() diff --git a/mythril/laser/ethereum/instructions.py b/mythril/laser/ethereum/instructions.py index c31d5f46..75dfada0 100644 --- a/mythril/laser/ethereum/instructions.py +++ b/mythril/laser/ethereum/instructions.py @@ -701,8 +701,8 @@ class Instruction: try: code = self.dynamic_loader.dynld(environment.active_account.address, addr) - except Exception as e: - logging.info("error accessing contract storage due to: " + str(e)) + except ValueError as e: + logging.info("error accessing contract storage due to: " + str(e.message)) state.stack.append(global_state.new_bitvec("extcodesize_" + str(addr), 256)) return [global_state] diff --git a/mythril/laser/ethereum/state.py b/mythril/laser/ethereum/state.py index 20c17c62..a02b6fe6 100644 --- a/mythril/laser/ethereum/state.py +++ b/mythril/laser/ethereum/state.py @@ -133,7 +133,7 @@ class Storage: try: return self._storage[item] except KeyError: - if self.address and int(self.address[2:], 16) != 0 and self.dynld: + if self.address and int(self.address[2:], 16) != 0 and (self.dynld and self.dynld.storage_loading): try: self._storage[item] = int( self.dynld.read_storage( diff --git a/mythril/mythril.py b/mythril/mythril.py index d5dbd76f..deb2261e 100644 --- a/mythril/mythril.py +++ b/mythril/mythril.py @@ -78,12 +78,13 @@ class Mythril(object): """ def __init__( - self, solv=None, solc_args=None, dynld=False, enable_online_lookup=False + self, solv=None, solc_args=None, dynld=False, enable_online_lookup=False, onchain_storage_access=True ): self.solv = solv self.solc_args = solc_args self.dynld = dynld + self.onchain_storage_access=onchain_storage_access self.enable_online_lookup = enable_online_lookup self.mythril_dir = self._init_mythril_dir() @@ -411,7 +412,7 @@ class Mythril(object): contract, address, strategy, - dynloader=DynLoader(self.eth) if self.dynld else None, + dynloader=DynLoader(self.eth, storage_loading=self.onchain_storage_access, contract_loading=self.dynld), max_depth=max_depth, execution_timeout=execution_timeout, create_timeout=create_timeout, @@ -434,7 +435,7 @@ class Mythril(object): contract, address, strategy, - dynloader=DynLoader(self.eth) if self.dynld else None, + dynloader=DynLoader(self.eth, storage_loading=self.onchain_storage_access, contract_loading=self.dynld), max_depth=max_depth, execution_timeout=execution_timeout, create_timeout=create_timeout, @@ -460,7 +461,7 @@ class Mythril(object): contract, address, strategy, - dynloader=DynLoader(self.eth) if self.dynld else None, + dynloader=DynLoader(self.eth, storage_loading=self.onchain_storage_access, contract_loading=self.dynld), max_depth=max_depth, execution_timeout=execution_timeout, create_timeout=create_timeout, diff --git a/mythril/support/loader.py b/mythril/support/loader.py index ac5a5d2e..c35e0374 100644 --- a/mythril/support/loader.py +++ b/mythril/support/loader.py @@ -4,12 +4,17 @@ import re class DynLoader: - def __init__(self, eth): + def __init__(self, eth, contract_loading=True, storage_loading=True): self.eth = eth self.storage_cache = {} + self.contract_loading = contract_loading + self.storage_loading = storage_loading def read_storage(self, contract_address, index): + if not self.storage_loading: + raise Exception('Cannot load from the storage when the storage_loading flag is false') + try: contract_ref = self.storage_cache[contract_address] data = contract_ref[index] @@ -36,6 +41,9 @@ class DynLoader: def dynld(self, contract_address, dependency_address): + if not self.contract_loading: + raise ValueError('Cannot load contract when contract_loading flag is false') + logging.info( "Dynld at contract " + contract_address + ": " + dependency_address )