Add source mapping to myth cmdline tool

pull/72/head
Bernhard Mueller 7 years ago
parent 449f343da4
commit 522754092b
  1. 46
      myth
  2. 2
      mythril/analysis/modules/tx_origin.py
  3. 6
      mythril/analysis/report.py
  4. 65
      mythril/ether/soliditycontract.py
  5. 1
      mythril/ether/util.py
  6. 3
      mythril/exceptions.py

46
myth

@ -7,6 +7,7 @@
from mythril.ether import evm, util
from mythril.ether.contractstorage import get_persistent_storage
from mythril.ether.ethcontract import ETHContract
from mythril.ether.soliditycontract import SolidityContract
from mythril.ether.util import compile_solidity
from mythril.rpc.client import EthJsonRpc
from mythril.ipc.client import EthIpc
@ -14,11 +15,12 @@ from mythril.rpc.exceptions import ConnectionError
from mythril.support import signatures
from mythril.support.truffle import analyze_truffle_project
from mythril.support.loader import DynLoader
from mythril.exceptions import CompilerError
from mythril.exceptions import CompilerError, NoContractFoundError
from mythril.analysis.symbolic import StateSpace
from mythril.analysis.callgraph import generate_graph
from mythril.analysis.security import fire_lasers
from mythril.analysis.report import Report
from laser.ethereum import helper
from web3 import Web3
from ethereum import utils
from pathlib import Path
@ -242,6 +244,9 @@ contracts = []
if (args.code):
contracts.append(ETHContract(args.code, name="MAIN", address = util.get_indexed_address(0)))
# Get bytecode from a contract address
elif (args.address):
if not re.match(r'0x[a-fA-F0-9]{40}', args.address):
@ -259,10 +264,13 @@ elif (args.address):
exitWithError("Could not connect to RPC server. Make sure that your node is running and that RPC parameters are set correctly.")
contracts.append(ETHContract(code, name=args.address, address = args.address))
# Compile Solidity source file(s)
elif (len(args.solidity_file)):
if(args.graph and len(args.solidity_file) > 1):
exitWithError("Cannot generate call graphs for multiple input files. Please do it one at a time.")
exitWithError("Cannot generate call graphs from multiple input files. Please do it one at a time.")
for file in args.solidity_file:
@ -271,26 +279,18 @@ elif (len(args.solidity_file)):
signatures.add_signatures_from_file(file, sigs) # Parse file for new function signatures
try:
name, bytecode = compile_solidity(file, solc_binary)
contract = SolidityContract(file)
contracts.append(contract)
except CompilerError as e:
exitWithError(e)
# Max. 16 input files supported!
contract = ETHContract(bytecode, name = name)
contracts.append(contract)
except NoContractFoundError:
print("The file " + file + " does not contain a compilable contract.")
# Save updated function signatures
with open(signatures_file, 'w') as f:
json.dump(sigs, f)
# Analyse contracts one-by-one
else:
exitWithError("No input bytecode. Please provide EVM code via -c BYTECODE, -a ADDRESS, or -i SOLIDITY_FILES")
@ -380,6 +380,22 @@ elif (args.graph) or (args.fire_lasers):
if (len(issues)):
report = Report()
disassembly = contract.get_disassembly()
if (type(contract) == SolidityContract):
for issue in issues:
if (issue.pc):
index = helper.get_instruction_index(disassembly.instruction_list, issue.pc)
solidity_file = contract.solidity_files[contract.mappings[index].solidity_file_idx]
issue.filename = solidity_file.filename
offset = contract.mappings[index].offset
length = contract.mappings[index].length
issue.code = solidity_file.data[offset:offset+length]
for i in range(0, len(issues)):
report.append_issue(issues[i])

@ -22,7 +22,7 @@ def execute(statespace):
if(instruction['opcode'] == "ORIGIN"):
issue = Issue(node.module_name, node.function_name, None, "Use of tx.origin", "Warning", \
issue = Issue(node.module_name, node.function_name, instruction['address'], "Use of tx.origin", "Warning", \
"Function " + node.function_name + " retrieves the transaction origin (tx.origin) using the ORIGIN opcode. Use tx.sender instead.\nSee also: https://solidity.readthedocs.io/en/develop/security-considerations.html#tx-origin"
)

@ -12,6 +12,7 @@ class Issue:
self.description = description
self.type = _type
self.debug = debug
self.filename = None
self.code = None
@ -49,8 +50,11 @@ class Report:
text += issue.description + "\n--------------------\n"
if issue.filename:
text += "In file: " + issue.filename + "\n"
if issue.code:
text += "Affected code:\n\n" + issue.code + "\n--------------------\n"
text += "\n\n" + issue.code + "\n\n--------------------\n"
if len(issue.debug):
text += "++++ Debugging info ++++\n" + issue.debug + "\n"

@ -0,0 +1,65 @@
from mythril.ether.ethcontract import ETHContract
from mythril.ether.util import *
from mythril.exceptions import NoContractFoundError
class SourceMapping:
def __init__(self, solidity_file_idx, offset, length):
self.solidity_file_idx = solidity_file_idx
self.offset = offset
self.length = length
class SolidityFile:
def __init__(self, filename, data):
self.filename = filename
self.data = data
class SolidityContract(ETHContract):
def __init__(self, input_file, contract_name = None):
data = get_solc_json(input_file)
self.solidity_files = []
for filename in data['sourceList']:
with open(filename, 'r') as file:
code = file.read()
self.solidity_files.append(SolidityFile(filename, code))
for key, contract in data['contracts'].items():
filename, name = key.split(":")
has_contract = False
if filename == input_file and len(contract['bin-runtime']):
self.name = name
self.code = contract['bin-runtime']
self.creation_code = contract['bin']
has_contract = True
break
if not has_contract:
raise NoContractFoundError
self.mappings = []
srcmap = contract['srcmap-runtime'].split(";")
for item in srcmap:
m = re.search(r"^(\d+):(\d+)", item)
if (m):
offset = int(m.group(1))
length = int(m.group(2))
m = re.search(r"^\d+:\d+:(\d+)", item)
if (m):
idx = int(m.group(1))
self.mappings.append(SourceMapping(idx, offset, length))
super().__init__(self.code, self.creation_code, name)

@ -6,6 +6,7 @@ from subprocess import Popen, PIPE
import binascii
import os
import re
import json
def safe_decode(hex_encoded_string):

@ -2,4 +2,7 @@ class CompilerError(Exception):
pass
class UnsatError(Exception):
pass
class NoContractFoundError(Exception):
pass
Loading…
Cancel
Save