Merge branch 'develop' into features/smt-abs

pull/788/head
JoranHonig 6 years ago committed by GitHub
commit 780e089987
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      mythril/disassembler/disassembly.py
  2. 4
      mythril/ethereum/evmcontract.py
  3. 10
      mythril/interfaces/cli.py
  4. 12
      mythril/mythril.py
  5. 55
      mythril/support/signatures.py
  6. 10
      tests/__init__.py
  7. 4
      tests/mythril_dir/config.ini
  8. BIN
      tests/mythril_dir/signatures.db.example

@ -73,7 +73,7 @@ def get_function_info(
function_names = signature_database.get(function_hash) function_names = signature_database.get(function_hash)
if len(function_names) > 1: if len(function_names) > 1:
# In this case there was an ambiguous result # In this case there was an ambiguous result
function_name = "**ambiguous** {}".format(function_names[0]) function_name = "[{}] (ambiguous)".format(", ".join(function_names))
elif len(function_names) == 1: elif len(function_names) == 1:
function_name = function_names[0] function_name = function_names[0]
else: else:

@ -37,6 +37,10 @@ class EVMContract(persistent.Persistent):
return self.disassembly.get_easm() return self.disassembly.get_easm()
def get_creation_easm(self):
return self.creation_disassembly.get_easm()
def matches_expression(self, expression): def matches_expression(self, expression):
str_eval = "" str_eval = ""

@ -364,10 +364,12 @@ def main():
print(storage) print(storage)
elif args.disassemble: elif args.disassemble:
easm_text = mythril.contracts[ # or mythril.disassemble(mythril.contracts[0])
0
].get_easm() # or mythril.disassemble(mythril.contracts[0]) if mythril.contracts[0].code:
sys.stdout.write(easm_text) print("Runtime Disassembly: \n" + mythril.contracts[0].get_easm())
if mythril.contracts[0].creation_code:
print("Disassembly: \n" + mythril.contracts[0].get_creation_easm())
elif args.graph or args.fire_lasers: elif args.graph or args.fire_lasers:
if not mythril.contracts: if not mythril.contracts:

@ -17,6 +17,7 @@ from solc.exceptions import SolcError
import solc import solc
from configparser import ConfigParser from configparser import ConfigParser
import platform import platform
from shutil import copyfile
from mythril.ethereum import util from mythril.ethereum import util
from mythril.ethereum.evmcontract import EVMContract from mythril.ethereum.evmcontract import EVMContract
@ -116,11 +117,18 @@ class Mythril(object):
except KeyError: except KeyError:
mythril_dir = os.path.join(os.path.expanduser("~"), ".mythril") mythril_dir = os.path.join(os.path.expanduser("~"), ".mythril")
# Initialize data directory and signature database
if not os.path.exists(mythril_dir): if not os.path.exists(mythril_dir):
# Initialize data directory
logging.info("Creating mythril data directory") logging.info("Creating mythril data directory")
os.mkdir(mythril_dir) os.mkdir(mythril_dir)
db_path = str(Path(mythril_dir) / "signatures.db")
if not os.path.exists(db_path):
# if the default mythril dir doesn't contain a signature DB
# initialize it with the default one from the project root
parent_dir = Path(__file__).parent.parent
copyfile(str(parent_dir / "signatures.db"), db_path)
return mythril_dir return mythril_dir
def _init_config(self): def _init_config(self):

@ -6,12 +6,42 @@ import os
import time import time
import logging import logging
import sqlite3 import sqlite3
import multiprocessing
import functools
from typing import List from typing import List
from collections import defaultdict
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
from mythril.exceptions import CompilerError from mythril.exceptions import CompilerError
lock = multiprocessing.Lock()
def synchronized(sync_lock):
""" Synchronization decorator """
def wrapper(f):
@functools.wraps(f)
def inner_wrapper(*args, **kw):
with sync_lock:
return f(*args, **kw)
return inner_wrapper
return wrapper
class Singleton(type):
_instances = {}
@synchronized(lock)
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
try: try:
# load if available but do not fail # load if available but do not fail
import ethereum_input_decoder import ethereum_input_decoder
@ -24,7 +54,7 @@ except ImportError:
class SQLiteDB(object): class SQLiteDB(object):
""" """
Simple CM for sqlite3 databases. Commits everything at exit. Simple context manager for sqlite3 databases. Commits everything at exit.
""" """
def __init__(self, path): def __init__(self, path):
@ -45,11 +75,15 @@ class SQLiteDB(object):
return "<SQLiteDB path={}>".format(self.path) return "<SQLiteDB path={}>".format(self.path)
class SignatureDB(object): class SignatureDB(object, metaclass=Singleton):
def __init__(self, enable_online_lookup: bool = False, path: str = None) -> None: def __init__(self, enable_online_lookup: bool = False, path: str = None) -> None:
self.enable_online_lookup = enable_online_lookup self.enable_online_lookup = enable_online_lookup
self.online_lookup_miss = set() self.online_lookup_miss = set()
self.online_lookup_timeout = 0 self.online_lookup_timeout = 0
# if we're analysing a Solidity file, store its hashes
# here to prevent unnecessary lookups
self.solidity_sigs = defaultdict(list)
if path is None: if path is None:
self.path = os.environ.get("MYTHRIL_DIR") or os.path.join( self.path = os.environ.get("MYTHRIL_DIR") or os.path.join(
os.path.expanduser("~"), ".mythril" os.path.expanduser("~"), ".mythril"
@ -116,6 +150,12 @@ class SignatureDB(object):
""" """
byte_sig = self._normalize_byte_sig(byte_sig) byte_sig = self._normalize_byte_sig(byte_sig)
# check if we have any Solidity signatures to look up
text_sigs = self.solidity_sigs.get(byte_sig)
if text_sigs is not None:
return text_sigs
# try lookup in the local DB # try lookup in the local DB
with SQLiteDB(self.path) as cur: with SQLiteDB(self.path) as cur:
cur.execute("SELECT text_sig FROM signatures WHERE byte_sig=?", (byte_sig,)) cur.execute("SELECT text_sig FROM signatures WHERE byte_sig=?", (byte_sig,))
@ -156,7 +196,6 @@ class SignatureDB(object):
:param file_path: solidity source code file path :param file_path: solidity source code file path
:return: :return:
""" """
sigs = {}
cmd = [solc_binary, "--hashes", file_path] cmd = [solc_binary, "--hashes", file_path]
if solc_args: if solc_args:
cmd.extend(solc_args.split()) cmd.extend(solc_args.split())
@ -184,12 +223,16 @@ class SignatureDB(object):
for line in stdout: for line in stdout:
# the ':' need not be checked but just to be sure # the ':' need not be checked but just to be sure
if all(map(lambda x: x in line, ["(", ")", ":"])): if all(map(lambda x: x in line, ["(", ")", ":"])):
sigs["0x" + line.split(":")[0]] = [line.split(":")[1].strip()] solc_bytes = "0x" + line.split(":")[0]
solc_text = line.split(":")[1].strip()
self.solidity_sigs[solc_bytes].append(solc_text)
logging.debug("Signatures: found %d signatures after parsing" % len(sigs)) logging.debug(
"Signatures: found %d signatures after parsing" % len(self.solidity_sigs)
)
# update DB with what we've found # update DB with what we've found
for byte_sig, text_sigs in sigs.items(): for byte_sig, text_sigs in self.solidity_sigs.items():
for text_sig in text_sigs: for text_sig in text_sigs:
self.add(byte_sig, text_sig) self.add(byte_sig, text_sig)

@ -18,16 +18,6 @@ MYTHRIL_DIR = TESTS_DIR / "mythril_dir"
class BaseTestCase(TestCase): class BaseTestCase(TestCase):
def setUp(self): def setUp(self):
self.changed_files = [] self.changed_files = []
self.ori_mythril_dir = os.environ.get("MYTHRIL_DIR", "")
os.environ["MYTHRIL_DIR"] = str(MYTHRIL_DIR)
shutil.copyfile(
str(MYTHRIL_DIR / "signatures.db.example"),
str(MYTHRIL_DIR / "signatures.db"),
)
def tearDown(self):
os.environ["MYTHRIL_DIR"] = self.ori_mythril_dir
os.remove(str(MYTHRIL_DIR / "signatures.db"))
def compare_files_error_message(self): def compare_files_error_message(self):
message = "Following output files are changed, compare them manually to see differences: \n" message = "Following output files are changed, compare them manually to see differences: \n"

@ -1,4 +0,0 @@
[defaults]
leveldb_dir = /Users/bernhardmueller/Library/Ethereum/geth/chaindata
dynamic_loading = infura
Loading…
Cancel
Save