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]
Example #2
0
    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
Example #3
0
    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)
Example #5
0
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)
Example #6
0
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())
Example #7
0
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
Example #8
0
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())