def jump_(self, global_state): state = global_state.mstate disassembly = global_state.environment.code try: jump_addr = util.get_concrete_int(state.stack.pop()) except AttributeError: logging.debug("Invalid jump argument (symbolic address)") return [] except IndexError: # Stack Underflow return [] index = util.get_instruction_index(disassembly.instruction_list, jump_addr) if index is None: logging.debug("JUMP to invalid address") return [] op_code = disassembly.instruction_list[index]['opcode'] if op_code != "JUMPDEST": logging.debug("Skipping JUMP to invalid destination (not JUMPDEST): " + str(jump_addr)) return [] new_state = copy(global_state) new_state.mstate.pc = index new_state.mstate.depth += 1 return [new_state]
def jumpi_(self, global_state): state = global_state.mstate disassembly = global_state.environment.code states = [] op0, condition = state.stack.pop(), state.stack.pop() try: jump_addr = util.get_concrete_int(op0) # FIXME: to broad exception handler except: logging.debug("Skipping JUMPI to invalid destination.") return [global_state] index = util.get_instruction_index(disassembly.instruction_list, jump_addr) if not index: logging.debug("Invalid jump destination: " + str(jump_addr)) return [global_state] instr = disassembly.instruction_list[index] # True case condi = simplify(condition) if type( condition) == BoolRef else condition != 0 if instr['opcode'] == "JUMPDEST": if (type(condi) == bool and condi) or (type(condi) == BoolRef and not is_false(condi)): new_state = copy(global_state) new_state.mstate.pc = index new_state.mstate.depth += 1 new_state.mstate.constraints.append(condi if type(condi) == bool else simplify(condi)) states.append(new_state) else: logging.debug("True, Pruned unreachable states. at %s" % (state.pc)) # False case negated = simplify( Not(condition)) if type(condition) == BoolRef else condition == 0 if (type(negated) == bool and negated) or (type(negated) == BoolRef and not is_false(negated)): new_state = copy(global_state) new_state.mstate.depth += 1 new_state.mstate.constraints.append(negated if type(negated) == bool else simplify(negated)) states.append(new_state) else: logging.debug("False, Pruned unreachable states. at %s" % (state.pc)) return states
def get_source_info(self, address): index = helper.get_instruction_index(self.disassembly.instruction_list, address) solidity_file = self.solidity_files[ self.mappings[index].solidity_file_idx] filename = solidity_file.filename offset = self.mappings[index].offset length = self.mappings[index].length code = solidity_file.data[offset:offset + length] lineno = self.mappings[index].lineno return SourceCodeInfo(filename, lineno, code)
def get_source_info(self, address, constructor=False): disassembly = self.creation_disassembly if constructor else self.disassembly mappings = self.constructor_mappings if constructor else self.mappings index = helper.get_instruction_index(disassembly.instruction_list, address) solidity_file = self.solidity_files[mappings[index].solidity_file_idx] filename = solidity_file.filename offset = mappings[index].offset length = mappings[index].length code = solidity_file.data.encode("utf-8")[offset:offset + length].decode( "utf-8", errors="ignore") lineno = mappings[index].lineno return SourceCodeInfo(filename, lineno, code)
def get_source_information(contract, instruction_list, mappings, address): index = helper.get_instruction_index(instruction_list, address) if index >= len(mappings): return None solidity_file = contract.solidity_files[mappings[index].solidity_file_idx] filename = solidity_file.filename offset = mappings[index].offset length = mappings[index].length code = solidity_file.data[offset:offset + length] lineno = mappings[index].lineno return SourceCodeInfo(filename, lineno, code)
def analyze_truffle_project(args): project_root = os.getcwd() build_dir = os.path.join(project_root, "build", "contracts") files = os.listdir(build_dir) for filename in files: if re.match(r'.*\.json$', filename) and filename != "Migrations.json": with open(os.path.join(build_dir, filename)) as cf: contractdata = json.load(cf) try: name = contractdata['contractName'] bytecode = contractdata['deployedBytecode'] except: print( "Unable to parse contract data. Please use Truffle 4 to compile your project." ) sys.exit() if (len(bytecode) < 4): continue ethcontract = ETHContract(bytecode, name=name) address = util.get_indexed_address(0) sym = SymExecWrapper(ethcontract, address, args.strategy, max_depth=args.max_depth) issues = fire_lasers(sym) if not len(issues): if (args.outform == 'text' or args.outform == 'markdown'): print("# Analysis result for " + name + "\n\nNo issues found.") else: result = { 'contract': name, 'result': { 'success': True, 'error': None, 'issues': [] } } print(json.dumps(result)) else: report = Report() # augment with source code disassembly = ethcontract.disassembly source = contractdata['source'] deployedSourceMap = contractdata['deployedSourceMap'].split( ";") mappings = [] for item in deployedSourceMap: mapping = item.split(":") if len(mapping) > 0 and len(mapping[0]) > 0: offset = int(mapping[0]) if len(mapping) > 1 and len(mapping[1]) > 0: length = int(mapping[1]) if len(mapping) > 2 and len(mapping[2]) > 0: idx = int(mapping[2]) lineno = source[0:offset].count('\n') + 1 mappings.append(SourceMapping(idx, offset, length, lineno)) for issue in issues: index = get_instruction_index(disassembly.instruction_list, issue.address) if index: try: offset = mappings[index].offset length = mappings[index].length issue.filename = filename issue.code = source[offset:offset + length] issue.lineno = mappings[index].lineno except IndexError: logging.debug("No code mapping at index %d", index) report.append_issue(issue) if (args.outform == 'json'): result = { 'contract': name, 'result': { 'success': True, 'error': None, 'issues': list(map(lambda x: x.as_dict, issues)) } } print(json.dumps(result)) else: if (args.outform == 'text'): print("# Analysis result for " + name + ":\n\n" + report.as_text()) elif (args.outform == 'markdown'): print(report.as_markdown())
def get_sourcecode_and_mapping(address, instr_list, mappings): index = helper.get_instruction_index(instr_list, address) if index is not None and len(mappings) > index: return mappings[index] else: return None
def analyze_truffle_project(sigs, args): project_root = os.getcwd() build_dir = os.path.join(project_root, "build", "contracts") files = os.listdir(build_dir) for filename in files: if re.match(r".*\.json$", filename) and filename != "Migrations.json": with open(os.path.join(build_dir, filename)) as cf: contractdata = json.load(cf) try: name = contractdata["contractName"] bytecode = contractdata["deployedBytecode"] filename = PurePath(contractdata["sourcePath"]).name except KeyError: print( "Unable to parse contract data. Please use Truffle 4 to compile your project." ) sys.exit() if len(bytecode) < 4: continue get_sigs_from_truffle(sigs, contractdata) ethcontract = ETHContract(bytecode, name=name) address = util.get_indexed_address(0) sym = SymExecWrapper( ethcontract, address, args.strategy, max_depth=args.max_depth, create_timeout=args.create_timeout, execution_timeout=args.execution_timeout, max_transaction_count=args.max_transaction_count, ) issues = fire_lasers(sym) if not len(issues): if args.outform == "text" or args.outform == "markdown": print("# Analysis result for " + name + "\n\nNo issues found.") else: result = { "contract": name, "result": { "success": True, "error": None, "issues": [] }, } print(json.dumps(result)) else: report = Report() # augment with source code deployed_disassembly = ethcontract.disassembly constructor_disassembly = ethcontract.creation_disassembly source = contractdata["source"] deployed_source_map = contractdata["deployedSourceMap"].split( ";") source_map = contractdata["sourceMap"].split(";") deployed_mappings = get_mappings(source, deployed_source_map) constructor_mappings = get_mappings(source, source_map) for issue in issues: if issue.function == "constructor": mappings = constructor_mappings disassembly = constructor_disassembly else: mappings = deployed_mappings disassembly = deployed_disassembly index = get_instruction_index(disassembly.instruction_list, issue.address) if index: try: offset = mappings[index].offset length = mappings[index].length issue.filename = filename issue.code = source.encode("utf-8")[offset:offset + length].decode( "utf-8") issue.lineno = mappings[index].lineno except IndexError: logging.debug("No code mapping at index %d", index) report.append_issue(issue) if args.outform == "json": result = { "contract": name, "result": { "success": True, "error": None, "issues": list(map(lambda x: x.as_dict, issues)), }, } print(json.dumps(result)) else: if args.outform == "text": print("# Analysis result for " + name + ":\n\n" + report.as_text()) elif args.outform == "markdown": print(report.as_markdown())