示例#1
0
 def load_from_bytecode(
         self,
         code: str,
         bin_runtime: bool = False,
         address: Optional[str] = None) -> Tuple[str, EVMContract]:
     """
     Returns the address and the contract class for the given bytecode
     :param code: Bytecode
     :param bin_runtime: Whether the code is runtime code or creation code
     :param address: address of contract
     :return: tuple(address, Contract class)
     """
     if address is None:
         address = util.get_indexed_address(0)
     if bin_runtime:
         self.contracts.append(
             EVMContract(
                 code=code,
                 name="MAIN",
                 enable_online_lookup=self.enable_online_lookup,
             ))
     else:
         self.contracts.append(
             EVMContract(
                 creation_code=code,
                 name="MAIN",
                 enable_online_lookup=self.enable_online_lookup,
             ))
     return address, self.contracts[
         -1]  # return address and contract object
示例#2
0
    def test_generate_graph(self):
        for input_file in TESTDATA_INPUTS.iterdir():
            output_expected = TESTDATA_OUTPUTS_EXPECTED / (
                input_file.name + ".graph.html"
            )
            output_current = TESTDATA_OUTPUTS_CURRENT / (
                input_file.name + ".graph.html"
            )

            contract = EVMContract(input_file.read_text())

            sym = SymExecWrapper(
                contract,
                address=(util.get_indexed_address(0)),
                strategy="dfs",
                transaction_count=1,
                execution_timeout=5,
            )

            html = generate_graph(sym)
            output_current.write_text(html)

            lines_expected = re.findall(
                r"'label': '.*'", str(output_current.read_text())
            )
            lines_found = re.findall(r"'label': '.*'", str(output_current.read_text()))
            if not (lines_expected == lines_found):
                self.found_changed_files(input_file, output_expected, output_current)

        self.assert_and_show_changed_files()
示例#3
0
    def load_from_bytecode(self, code, bin_runtime=False, address=None):
        """

        :param code:
        :param bin_runtime:
        :param address:
        :return:
        """
        if address is None:
            address = util.get_indexed_address(0)
        if bin_runtime:
            self.contracts.append(
                EVMContract(
                    code=code,
                    name="MAIN",
                    enable_online_lookup=self.enable_online_lookup,
                ))
        else:
            self.contracts.append(
                EVMContract(
                    creation_code=code,
                    name="MAIN",
                    enable_online_lookup=self.enable_online_lookup,
                ))
        return address, self.contracts[
            -1]  # return address and contract object
示例#4
0
    def test_generate_graph(self):
        for input_file in TESTDATA_INPUTS.iterdir():
            output_expected = TESTDATA_OUTPUTS_EXPECTED / (input_file.name +
                                                           ".graph.html")
            output_current = TESTDATA_OUTPUTS_CURRENT / (input_file.name +
                                                         ".graph.html")

            contract = EVMContract(input_file.read_text())
            disassembler = MythrilDisassembler()
            disassembler.contracts.append(contract)
            analyzer = MythrilAnalyzer(
                disassembler=disassembler,
                strategy="dfs",
                execution_timeout=5,
                max_depth=30,
                address=(util.get_indexed_address(0)),
            )

            html = analyzer.graph_html(transaction_count=1)
            output_current.write_text(html)

            lines_expected = re.findall(r"'label': '.*'",
                                        str(output_current.read_text()))
            lines_found = re.findall(r"'label': '.*'",
                                     str(output_current.read_text()))
            if not (lines_expected == lines_found):
                self.found_changed_files(input_file, output_expected,
                                         output_current)

        self.assert_and_show_changed_files()
def test_sym_exec():
    contract = SolidityContract(
        str(tests.TESTDATA_INPUTS_CONTRACTS / "calls.sol"),
        solc_binary=Mythril._init_solc_binary("0.5.0"),
    )

    sym = SymExecWrapper(contract,
                         address=(util.get_indexed_address(0)),
                         strategy="dfs")
    issues = fire_lasers(sym)

    assert len(issues) != 0
示例#6
0
def test_sym_exec():
    contract = SolidityContract(
        str(tests.TESTDATA_INPUTS_CONTRACTS / "calls.sol"))

    sym = SymExecWrapper(
        contract,
        address=(util.get_indexed_address(0)),
        strategy="dfs",
        execution_timeout=25,
    )
    issues = fire_lasers(sym)

    assert len(issues) != 0
示例#7
0
    def load_from_solidity(self, solidity_files):
        """

        :param solidity_files:
        :return:
        """
        address = util.get_indexed_address(0)
        contracts = []
        for file in solidity_files:
            if ":" in file:
                file, contract_name = file.split(":")
            else:
                contract_name = None

            file = os.path.expanduser(file)

            try:
                # import signatures from solidity source
                self.sigs.import_solidity_file(file,
                                               solc_binary=self.solc_binary,
                                               solc_args=self.solc_args)
                if contract_name is not None:
                    contract = SolidityContract(
                        input_file=file,
                        name=contract_name,
                        solc_args=self.solc_args,
                        solc_binary=self.solc_binary,
                    )
                    self.contracts.append(contract)
                    contracts.append(contract)
                else:
                    for contract in get_contracts_from_file(
                            input_file=file,
                            solc_args=self.solc_args,
                            solc_binary=self.solc_binary,
                    ):
                        self.contracts.append(contract)
                        contracts.append(contract)

            except FileNotFoundError:
                raise CriticalError("Input file not found: " + file)
            except CompilerError as e:
                raise CriticalError(e)
            except NoContractFoundError:
                log.error("The file " + file +
                          " does not contain a compilable contract.")

        return address, contracts
示例#8
0
def _generate_report(input_file):
    contract = EVMContract(input_file.read_text(), enable_online_lookup=False)
    sym = SymExecWrapper(
        contract,
        address=(util.get_indexed_address(0)),
        strategy="dfs",
        execution_timeout=30,
        transaction_count=1,
    )
    issues = fire_lasers(sym)

    report = Report()
    for issue in issues:
        issue.filename = "test-filename.sol"
        report.append_issue(issue)
    return report, input_file
示例#9
0
    def load_from_solidity(
            self,
            solidity_files: List[str]) -> Tuple[str, List[SolidityContract]]:
        """

        :param solidity_files: List of solidity_files
        :return: tuple of address, contract class list
        """
        address = util.get_indexed_address(0)
        contracts = []
        for file in solidity_files:
            if ":" in file:
                file, contract_name = file.split(":")
            else:
                contract_name = None

            file = os.path.expanduser(file)

            try:
                # import signatures from solidity source
                self.sigs.import_solidity_file(
                    file,
                    solc_binary=self.solc_binary,
                    solc_settings_json=self.solc_settings_json,
                )
                if contract_name is not None:
                    contract = SolidityContract(
                        input_file=file,
                        name=contract_name,
                        solc_settings_json=self.solc_settings_json,
                        solc_binary=self.solc_binary,
                    )
                    self.contracts.append(contract)
                    contracts.append(contract)
                else:
                    for contract in get_contracts_from_file(
                            input_file=file,
                            solc_settings_json=self.solc_settings_json,
                            solc_binary=self.solc_binary,
                    ):
                        self.contracts.append(contract)
                        contracts.append(contract)

            except FileNotFoundError:
                raise CriticalError("Input file not found: " + file)
            except CompilerError as e:
                error_msg = str(e)
                # Check if error is related to solidity version mismatch
                if ("Error: Source file requires different compiler version"
                        in error_msg):
                    # Grab relevant line "pragma solidity <solv>...", excluding any comments
                    solv_pragma_line = error_msg.split("\n")[-3].split("//")[0]
                    # Grab solidity version from relevant line
                    solv_match = re.findall(r"[0-9]+\.[0-9]+\.[0-9]+",
                                            solv_pragma_line)
                    error_suggestion = ("<version_number>"
                                        if len(solv_match) != 1 else
                                        solv_match[0])
                    error_msg = (
                        error_msg +
                        '\nSolidityVersionMismatch: Try adding the option "--solv '
                        + error_suggestion + '"\n')

                raise CriticalError(error_msg)
            except NoContractFoundError:
                log.error("The file " + file +
                          " does not contain a compilable contract.")

        return address, contracts
示例#10
0
def analyze_truffle_project(sigs, args):
    """

    :param sigs:
    :param 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 = EVMContract(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,
                transaction_count=args.transaction_count,
                modules=args.modules or [],
                compulsory_statespace=False,
            )
            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:
                            log.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())