Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Develop HotFix 0.1.6 #10

Merged
merged 3 commits into from
Jan 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

> Ithildin was a type of specially crafted Mithril that only the most experienced craftsmen of the Noldor could learn how to make and pass on to others.

Ithilding is a semantic analysis tool for EVM bytecode based on [Mythril](https://github.com/ConsenSys/mythril).
Ithildin is a semantic analysis tool for EVM bytecode based on [Mythril](https://github.com/ConsenSys/mythril).
By using symbolic execution and taint analysis, it aims at detecting functions that are restricted by authentication patterns, and to extract administrator addresses whenever possible.

Check out the wiki for a list of currently working patterns and some that are planned to be implemented soon.
Expand Down
2 changes: 1 addition & 1 deletion ithildin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from mythril.mythril.mythril_config import MythrilConfig

__version__ = '0.1.5'
__version__ = '0.1.6'

formatter = logging.Formatter('[%(levelname)s\t] %(asctime)s - %(name)s %(message)s')

Expand Down
7 changes: 3 additions & 4 deletions ithildin/report/benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,17 @@
from typing import List, Optional, Text

from ithildin import __version__
from mythril.disassembler.disassembly import Disassembly


class Result:

def __init__(self,
disassembly: Disassembly,
function_hashes: List[Text],
contract_address: Text,
contract_index: int,
detected_functions: List[Text],
compiler_version: Optional[Text] = None) -> None:
self.disassembly = disassembly
self.function_hashes = function_hashes
self.contract_address = contract_address
self.contract_index = contract_index
self.detected_functions = detected_functions
Expand All @@ -36,7 +35,7 @@ def total_functions_count(self):

def to_dict(self):
return {
'bytecode': self.disassembly.bytecode,
'functionHashes': self.function_hashes,
'contractAddress': self.contract_address,
'contractIndex': self.contract_index,
'detectedFunctions': self.detected_functions,
Expand Down
26 changes: 15 additions & 11 deletions ithildin/tools/benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from functools import lru_cache
from typing import Optional, Set, Text, Tuple

from mythril.disassembler.disassembly import Disassembly
from mythril.support.signatures import SignatureDB
from mythril.mythril import MythrilDisassembler

Expand Down Expand Up @@ -56,6 +55,7 @@ def lookup_signature(signature_hash: Text) -> Text:


def save_benchmark_state(report: Report, positive_sample: Set[int], negative_sample: Set[int]) -> None:
log.info('Saving benchmark state to filesystem...')
benchmark_state = {
'report': report.to_dict(),
'positiveSample': list(positive_sample),
Expand Down Expand Up @@ -83,7 +83,9 @@ def load_benchmark_state(path=benchmark_state_path) -> Tuple[Report, Set[int], S
start_time=benchmark_state['report'].get('startTime', None),
end_time=benchmark_state['report'].get('endTime', None))
for result in benchmark_state['report']['results']:
report.add_result(Result(Disassembly(result['bytecode']),
if len(result['functionHashes']) == 0:
continue
report.add_result(Result(result['functionHashes'],
result['contractAddress'],
result['contractIndex'],
result['detectedFunctions'],
Expand All @@ -105,6 +107,8 @@ def check_for_existing_flags(result: Result, contract: Contract, strategy: Text,
if func_entity is None:
continue
flagged_funcion = flagged_function_repository.get(func_entity, strategy)
if flagged_funcion is None:
continue
if flagged_funcion.flag == Flag.VALID:
if func_hash in marked_func_hashes:
result.true_positives += 1
Expand All @@ -124,21 +128,21 @@ def check_for_existing_flags(result: Result, contract: Contract, strategy: Text,

def ask_for_marked_functions(result: Result, contract: Contract, strategy: Text, existing_hashes: Set[Text]):
for signature, sig_hash in [(sig, signature_hash(sig)) for sig in result.detected_functions if signature_hash(sig) not in existing_hashes]:
func_entity = function_repository.save(contract, signature, sig_hash)
print('! Has the function "{}" been correctly identified?'.format(signature))
answer = get_binary_answer()
if answer:
result.true_positives += 1
else:
result.false_positives += 1
func_entity = function_repository.save(contract, signature, sig_hash)
flagged_function_repository.set_flag(func_entity, strategy, Flag.VALID if answer else Flag.INVALID)


def ask_for_missing_functions(result: Result, contract: Contract, strategy: Text, valid_func_hashes: Set[Text]) -> Set[Text]:
if len(valid_func_hashes) == 0:
return set()
missing_functions = set()
print('! Enter signatures of missed functions one by one (type \'c\' if none are left)')
print('! One by one, enter all function signatures that were missed by the strategy (type \'c\' if none are left)')
signature = input('> Signature (type \'c\' to continue): ')
while signature != 'c':
sig_hash = signature_hash(signature)
Expand All @@ -163,7 +167,7 @@ def start_verification(report: Report, verification_sample: Set[int]) -> None:
if result.contract_index not in verification_sample:
continue
contract = contract_repository.save(result.contract_address, result.compiler_version)
func_hashes = set(result.disassembly.func_hashes)
func_hashes = set(result.function_hashes)
marked_hashes = {signature_hash(sig) for sig in result.detected_functions}
print('! Verifying contract at address %s' % result.contract_address)
print('! Total functions in contract: %d' % len(func_hashes))
Expand All @@ -177,9 +181,9 @@ def start_verification(report: Report, verification_sample: Set[int]) -> None:
missing_hashes = {signature_hash(signature) for signature in missing_functions}
# Next, mark all remaining functions as INVALID
for func_hash in func_hashes - marked_hashes - existing_hashes - missing_hashes:
signature = lookup_signature(func_hash)
func_entity = function_repository.get(contract, signature_hash=func_hash)
if func_entity is None:
signature = lookup_signature(func_hash)
func_entity = function_repository.save(contract, signature, func_hash)
flagged_function_repository.set_flag(func_entity, strategy, Flag.INVALID)
result.true_negatives += 1
Expand Down Expand Up @@ -244,12 +248,12 @@ def new_benchmark(args) -> None:
positive_instances.add(i)
else:
log.info('Nothing found for contract %d/%d at address %s', i + 1, instance_count, target_address)
function_names = [result.function_name
for report_item in analysis_report.reports if len(report_item.results) > 0
for result in report_item.results]
detected_functions = [result.function_name
for report_item in analysis_report.reports if len(report_item.results) > 0
for result in report_item.results]
compiler_version = row[args.version_column] if args.version_column is not None else None
benchmark_report.add_result(Result(contract_loader.disassembly(), target_address, i, function_names,
compiler_version=compiler_version))
function_hashes = contract_loader.disassembly().func_hashes if contract_loader.disassembly() else []
benchmark_report.add_result(Result(function_hashes, target_address, i, detected_functions, compiler_version=compiler_version))
strategy_loader.reset_strategies()
benchmark_report.end_time = time.strftime(TIME_FORMAT)
negative_instances = contract_sample - positive_instances
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pathlib
from setuptools import find_packages, setup

VERSION = '0.1.5'
VERSION = '0.1.6'
CURRENT_DIR = pathlib.Path(__file__).parent
README = (CURRENT_DIR / 'README.md').read_text()
PYTHON_REQUIREMENT = '>=3.6.0'
Expand Down