From e0565455a0e5f2e325ea7193f1c1f0512332b560 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Sun, 29 Jul 2018 16:14:20 +0530 Subject: [PATCH 1/2] Store signatures for truffle --- mythril/mythril.py | 2 +- mythril/support/signatures.py | 12 ++++++++-- mythril/support/truffle.py | 44 +++++++++++++++++++++++++++-------- 3 files changed, 45 insertions(+), 13 deletions(-) diff --git a/mythril/mythril.py b/mythril/mythril.py index e687e480..519b557c 100644 --- a/mythril/mythril.py +++ b/mythril/mythril.py @@ -180,7 +180,7 @@ class Mythril(object): config.set('defaults', 'dynamic_loading', 'infura') def analyze_truffle_project(self, *args, **kwargs): - return analyze_truffle_project(*args, **kwargs) # just passthru for now + return analyze_truffle_project(self.sigs, *args, **kwargs) # just passthru by passing signatures for now def _init_solc_binary(self, version): # Figure out solc binary and version diff --git a/mythril/support/signatures.py b/mythril/support/signatures.py index ac62a2e1..b1e91122 100644 --- a/mythril/support/signatures.py +++ b/mythril/support/signatures.py @@ -216,9 +216,17 @@ class SignatureDb(object): :param code: solidity source code :return: dictionary {sighash: function_signature} """ - sigs = {} funcs = re.findall(r'function[\s]+(.*?\))', code, re.DOTALL) + return SignatureDb.get_sigs_from_functions(funcs) + + @staticmethod + def get_sigs_from_functions(funcs): + """ + :param funcs: accepts a list of functions + :return: their signature mappings + """ + sigs = {} for f in funcs: f = re.sub(r'[\n]', '', f) m = re.search(r'^([A-Za-z0-9_]+)', f) @@ -242,5 +250,5 @@ class SignatureDb(object): signature = re.sub(r'\s', '', signature) sigs["0x" + utils.sha3(signature)[:4].hex()] = signature - logging.debug("Signatures: parse soldiity found %d signatures" % len(sigs)) + logging.debug("Signatures: found %d signatures after parsing" % len(sigs)) return sigs diff --git a/mythril/support/truffle.py b/mythril/support/truffle.py index d59879ab..9c86ebc7 100644 --- a/mythril/support/truffle.py +++ b/mythril/support/truffle.py @@ -6,6 +6,7 @@ import json import logging from mythril.ether.ethcontract import ETHContract from mythril.ether.soliditycontract import SourceMapping +from mythril.exceptions import CriticalError from mythril.analysis.security import fire_lasers from mythril.analysis.symbolic import SymExecWrapper from mythril.analysis.report import Report @@ -14,7 +15,7 @@ from mythril.ether import util from mythril.laser.ethereum.util import get_instruction_index -def analyze_truffle_project(args): +def analyze_truffle_project(sigs, args): project_root = os.getcwd() @@ -22,6 +23,15 @@ def analyze_truffle_project(args): files = os.listdir(build_dir) + try: + sigs.open() + except FileNotFoundError as fnfe: + logging.info( + "No signature database found. Creating database if sigs are loaded in: " + sigs.signatures_file + "\n" + + "Consider replacing it with the pre-initialized database at https://raw.githubusercontent.com/ConsenSys/mythril/master/signatures.json") + except json.JSONDecodeError as jde: + raise CriticalError("Invalid JSON in signatures file " + sigs.signatures_file + "\n" + str(jde)) + for filename in files: if re.match(r'.*\.json$', filename) and filename != "Migrations.json": @@ -33,13 +43,17 @@ def analyze_truffle_project(args): name = contractdata['contractName'] bytecode = contractdata['deployedBytecode'] filename = PurePath(contractdata['sourcePath']).name - except: + abi = contractdata['abi'] + except KeyError: print("Unable to parse contract data. Please use Truffle 4 to compile your project.") sys.exit() - - if (len(bytecode) < 4): + if len(bytecode) < 4: continue + list_of_functions = parse_abi_for_functions(abi) + sigs.signatures.update(sigs.get_sigs_from_functions(list_of_functions)) + sigs.write() + ethcontract = ETHContract(bytecode, name=name) address = util.get_indexed_address(0) @@ -47,7 +61,7 @@ def analyze_truffle_project(args): issues = fire_lasers(sym) if not len(issues): - if (args.outform == 'text' or args.outform == 'markdown'): + 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': []}} @@ -60,11 +74,11 @@ def analyze_truffle_project(args): disassembly = ethcontract.disassembly source = contractdata['source'] - deployedSourceMap = contractdata['deployedSourceMap'].split(";") + deployed_source_map = contractdata['deployedSourceMap'].split(";") mappings = [] - for item in deployedSourceMap: + for item in deployed_source_map: mapping = item.split(":") if len(mapping) > 0 and len(mapping[0]) > 0: @@ -97,13 +111,23 @@ def analyze_truffle_project(args): report.append_issue(issue) - if (args.outform == 'json'): + 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'): + if args.outform == 'text': print("# Analysis result for " + name + ":\n\n" + report.as_text()) - elif (args.outform == 'markdown'): + elif args.outform == 'markdown': print(report.as_markdown()) + + +def parse_abi_for_functions(abi): + funcs = [] + for data in abi: + if data['type'] != 'function': + continue + args = '('+','.join([input['type'] for input in data['inputs']])+')' + funcs.append(data['name']+args) + return funcs From ca0900e92074dc740d8f9e9bb56a0ed97d391620 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Sun, 29 Jul 2018 18:36:33 +0530 Subject: [PATCH 2/2] Remove reduandant open --- mythril/support/truffle.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/mythril/support/truffle.py b/mythril/support/truffle.py index 9c86ebc7..93138cdc 100644 --- a/mythril/support/truffle.py +++ b/mythril/support/truffle.py @@ -23,15 +23,6 @@ def analyze_truffle_project(sigs, args): files = os.listdir(build_dir) - try: - sigs.open() - except FileNotFoundError as fnfe: - logging.info( - "No signature database found. Creating database if sigs are loaded in: " + sigs.signatures_file + "\n" + - "Consider replacing it with the pre-initialized database at https://raw.githubusercontent.com/ConsenSys/mythril/master/signatures.json") - except json.JSONDecodeError as jde: - raise CriticalError("Invalid JSON in signatures file " + sigs.signatures_file + "\n" + str(jde)) - for filename in files: if re.match(r'.*\.json$', filename) and filename != "Migrations.json":