def setUp(self): code = codecs.decode( "6003600302600f56601b60006000a15b6101a5600060" "00a160019003801515600f57600660006000a1", "hex", ) env = Env(code) self.sm = sm.SymbolicMachine(env)
def runTest(self): logger.info("Compiling contract %s" % self.filename) p = subprocess.run( [ "solc", "--optimize", "--combined-json=bin-runtime", self.filename ], capture_output=True, text=True, ) self.assertEqual(p.returncode, 0, "solc compilation failed:\n%s" % p.stderr) output = json.loads(p.stdout) assert "contracts" in output identifier, properties = list(output["contracts"].items())[0] bin_runtime = properties["bin-runtime"] logger.info("Runtime bytecode: %s", bin_runtime) bin_runtime = codecs.decode(bin_runtime, "hex") logger.info("Compiled. Symbolic execution.") e = env.Env( bin_runtime, address=utils.bvv(int(ADDRESS, 16)), caller=utils.DEFAULT_CALLER, origin=utils.DEFAULT_CALLER, balance=utils.bvv(BALANCE), ) s = sm.SymbolicMachine(e) s.execute(timeout_sec=EXEC_TIMEOUT) self.assertTrue(s.outcomes) ra = recursive_analyzer.RecursiveAnalyzer( max_wei_to_send=MAX_TO_SEND, min_wei_to_receive=MIN_TO_RECEIVE, block="invalid", ) # Never contact the blockchain, instead all the storage are 0 ra.actual_storage = {} bug = ra.check_states(s.outcomes, timeout=ANALYSIS_TIMEOUT, max_depth=MAX_TRANSACTION_DEPTH) self.assertTrue(bug, self.filename)
def run_code(self, code, env={}): code = bytes(code) self.sm = sm.SymbolicMachine(Env(code, **env)) self.state = self.sm.branch_queue[0][1] return self.sm.exec_branch(self.state)
def outcomes(self, code, env={}): code = bytes(code) self.sm = sm.SymbolicMachine(Env(code, **env)) self.sm.execute(timeout_sec=10) return self.sm.outcomes
def main(): args = parser.parse_args() if args.v.isnumeric(): coloredlogs.install(level=int(args.v)) elif hasattr(logging, args.v.upper()): coloredlogs.install(level=getattr(logging, args.v.upper())) else: err_exit("Logging should be DEBUG/INFO/WARNING/ERROR.") try: logging.debug("Node working. Block %i ", w3.eth.blockNumber) except web3.exceptions.CannotHandleRequest: err_exit( "Seems like Web3.py can't connect to your Ethereum node.\n" "If you don't have one and you want to use Infura, you can set " "WEB3_PROVIDER_URI as follows:\n" "$ export WEB3_PROVIDER_URI='https://mainnet.infura.io'") if args.contract_addr == "-": # Let's read the runtime bytecode from stdin code = sys.stdin.read().strip("\n") if not code.isalnum(): err_exit( "Runtime bytecode read from stdin needs to be hexadecimal.") code = codecs.decode(code, "hex") # Dummy address, dummy balance args.contract_addr = "0xDEADBEEF00000000000000000000000000000000" if not args.force_balance: args.force_balance = Web3.toWei(1.337, "ether") else: code = w3.eth.getCode(args.contract_addr, block_identifier=args.block) balance = args.force_balance or w3.eth.getBalance( args.contract_addr, block_identifier=args.block) print("Analyzing contract at %s with balance %f ether." % (args.contract_addr, Web3.fromWei(balance, "ether"))) if balance < args.min_to_receive: err_exit("Balance is smaller than --min-to-receive: " "the analyzer will never find anything.") if args.summarize: logging.info( "Summarizer enabled, we won't constrain the caller/origin " "so more of the contract can get explored. " "It may be slower.") e = env.Env( code, address=utils.bvv(int(args.contract_addr, 16)), balance=utils.bvv(balance), ) else: e = env.Env( code, address=utils.bvv(int(args.contract_addr, 16)), caller=utils.DEFAULT_CALLER, origin=utils.DEFAULT_CALLER, balance=utils.bvv(balance), ) print("Starting symbolic execution step...") s = sm.SymbolicMachine(e, fuzz=not args.disable_fuzzing) s.execute(timeout_sec=args.exec_timeout) print("Symbolic execution finished with coverage %i%%." % int(s.get_coverage() * 100)) print("Outcomes: %i interesting. %i total and %i unfinished paths." % ( sum(int(o.is_interesting()) for o in s.outcomes), len(s.outcomes), len(s.partial_outcomes), )) if args.summarize: print() print("Methods from the summarizer:") summary.HumanSummarizer(s).print_methods() print() print("Starting analysis step...") ra = recursive_analyzer.RecursiveAnalyzer( max_wei_to_send=args.max_to_send, min_wei_to_receive=args.min_to_receive, block=args.block, ) bug = ra.check_states(s.outcomes, timeout=args.analysis_timeout, max_depth=args.max_transaction_depth) if bug: solver = bug[2] if logging.getLogger().isEnabledFor(logging.DEBUG): print("Composite state:") print(bug[0].debug_string()) print() print() print("Path:") for i, state in enumerate(bug[1]): print() print("Transaction %i, symbolic state:" % (i + 1)) print(state.debug_string()) print() print("Transaction %i, example solution:" % (i + 1)) print(state.env.solution_string(solver)) print() print() print("======> Bug found! Need %i transactions. <======" % len(bug[1])) else: print("Nothing to report.")