mirror of https://github.com/crytic/slither
commit
28d9981726
@ -0,0 +1,9 @@ |
||||
pragma solidity >=0.4.24 <0.5.4; |
||||
|
||||
contract Test { |
||||
|
||||
function foo() public returns (address) { |
||||
address from = msg.sender; |
||||
return(from); |
||||
} |
||||
} |
@ -0,0 +1,39 @@ |
||||
import sys |
||||
from slither.slither import Slither |
||||
from slither.evm.convert import SourceToEVM |
||||
|
||||
if len(sys.argv) != 2: |
||||
print('python3 function_called.py functions_called.sol') |
||||
exit(-1) |
||||
|
||||
# Init slither |
||||
slither = Slither(sys.argv[1]) |
||||
|
||||
# Get the contract evm instructions |
||||
contract = slither.get_contract_from_name('Test') |
||||
contract_ins = SourceToEVM.get_evm_instructions(contract) |
||||
print("## Contract evm instructions: {} ##".format(contract.name)) |
||||
for ins in contract_ins: |
||||
print(str(ins)) |
||||
|
||||
# Get the constructor evm instructions |
||||
constructor = contract.constructor |
||||
print("## Function evm instructions: {} ##".format(constructor.name)) |
||||
constructor_ins = SourceToEVM.get_evm_instructions(constructor) |
||||
for ins in constructor_ins: |
||||
print(str(ins)) |
||||
|
||||
# Get the function evm instructions |
||||
function = contract.get_function_from_signature('foo()') |
||||
print("## Function evm instructions: {} ##".format(function.name)) |
||||
function_ins = SourceToEVM.get_evm_instructions(function) |
||||
for ins in function_ins: |
||||
print(str(ins)) |
||||
|
||||
# Get the node evm instructions |
||||
nodes = function.nodes |
||||
for node in nodes: |
||||
node_ins = SourceToEVM.get_evm_instructions(node) |
||||
print("Node evm instructions: {}".format(str(node))) |
||||
for ins in node_ins: |
||||
print(str(ins)) |
@ -0,0 +1,445 @@ |
||||
## Contract evm instructions: Test ## |
||||
PUSH1 0x80 |
||||
PUSH1 0x40 |
||||
MSTORE |
||||
CALLVALUE |
||||
DUP1 |
||||
ISZERO |
||||
PUSH2 0x10 |
||||
JUMPI |
||||
PUSH1 0x0 |
||||
DUP1 |
||||
REVERT |
||||
JUMPDEST |
||||
POP |
||||
CALLER |
||||
PUSH1 0x0 |
||||
DUP1 |
||||
PUSH2 0x100 |
||||
EXP |
||||
DUP2 |
||||
SLOAD |
||||
DUP2 |
||||
PUSH20 0xffffffffffffffffffffffffffffffffffffffff |
||||
MUL |
||||
NOT |
||||
AND |
||||
SWAP1 |
||||
DUP4 |
||||
PUSH20 0xffffffffffffffffffffffffffffffffffffffff |
||||
AND |
||||
MUL |
||||
OR |
||||
SWAP1 |
||||
SSTORE |
||||
POP |
||||
PUSH1 0xfa |
||||
DUP1 |
||||
PUSH2 0x5f |
||||
PUSH1 0x0 |
||||
CODECOPY |
||||
PUSH1 0x0 |
||||
RETURN |
||||
STOP |
||||
PUSH1 0x80 |
||||
PUSH1 0x40 |
||||
MSTORE |
||||
PUSH1 0x4 |
||||
CALLDATASIZE |
||||
LT |
||||
PUSH1 0x49 |
||||
JUMPI |
||||
PUSH1 0x0 |
||||
CALLDATALOAD |
||||
PUSH29 0x100000000000000000000000000000000000000000000000000000000 |
||||
SWAP1 |
||||
DIV |
||||
PUSH4 0xffffffff |
||||
AND |
||||
DUP1 |
||||
PUSH4 0x14ba3f12 |
||||
EQ |
||||
PUSH1 0x4e |
||||
JUMPI |
||||
DUP1 |
||||
PUSH4 0xc2985578 |
||||
EQ |
||||
PUSH1 0x8c |
||||
JUMPI |
||||
JUMPDEST |
||||
PUSH1 0x0 |
||||
DUP1 |
||||
REVERT |
||||
JUMPDEST |
||||
CALLVALUE |
||||
DUP1 |
||||
ISZERO |
||||
PUSH1 0x59 |
||||
JUMPI |
||||
PUSH1 0x0 |
||||
DUP1 |
||||
REVERT |
||||
JUMPDEST |
||||
POP |
||||
PUSH1 0x76 |
||||
PUSH1 0x4 |
||||
DUP1 |
||||
CALLDATASIZE |
||||
SUB |
||||
DUP2 |
||||
ADD |
||||
SWAP1 |
||||
DUP1 |
||||
DUP1 |
||||
CALLDATALOAD |
||||
SWAP1 |
||||
PUSH1 0x20 |
||||
ADD |
||||
SWAP1 |
||||
SWAP3 |
||||
SWAP2 |
||||
SWAP1 |
||||
POP |
||||
POP |
||||
POP |
||||
PUSH1 0xb4 |
||||
JUMP |
||||
JUMPDEST |
||||
PUSH1 0x40 |
||||
MLOAD |
||||
DUP1 |
||||
DUP3 |
||||
DUP2 |
||||
MSTORE |
||||
PUSH1 0x20 |
||||
ADD |
||||
SWAP2 |
||||
POP |
||||
POP |
||||
PUSH1 0x40 |
||||
MLOAD |
||||
DUP1 |
||||
SWAP2 |
||||
SUB |
||||
SWAP1 |
||||
RETURN |
||||
JUMPDEST |
||||
CALLVALUE |
||||
DUP1 |
||||
ISZERO |
||||
PUSH1 0x97 |
||||
JUMPI |
||||
PUSH1 0x0 |
||||
DUP1 |
||||
REVERT |
||||
JUMPDEST |
||||
POP |
||||
PUSH1 0x9e |
||||
PUSH1 0xc1 |
||||
JUMP |
||||
JUMPDEST |
||||
PUSH1 0x40 |
||||
MLOAD |
||||
DUP1 |
||||
DUP3 |
||||
DUP2 |
||||
MSTORE |
||||
PUSH1 0x20 |
||||
ADD |
||||
SWAP2 |
||||
POP |
||||
POP |
||||
PUSH1 0x40 |
||||
MLOAD |
||||
DUP1 |
||||
SWAP2 |
||||
SUB |
||||
SWAP1 |
||||
RETURN |
||||
JUMPDEST |
||||
PUSH1 0x0 |
||||
PUSH1 0xa |
||||
DUP3 |
||||
ADD |
||||
SWAP1 |
||||
POP |
||||
SWAP2 |
||||
SWAP1 |
||||
POP |
||||
JUMP |
||||
JUMPDEST |
||||
PUSH1 0x0 |
||||
DUP1 |
||||
PUSH1 0xa |
||||
DUP2 |
||||
ADD |
||||
SWAP2 |
||||
POP |
||||
POP |
||||
SWAP1 |
||||
JUMP |
||||
STOP |
||||
PUSH1 0x80 |
||||
PUSH1 0x40 |
||||
MSTORE |
||||
PUSH1 0x4 |
||||
CALLDATASIZE |
||||
LT |
||||
PUSH1 0x49 |
||||
JUMPI |
||||
PUSH1 0x0 |
||||
CALLDATALOAD |
||||
PUSH29 0x100000000000000000000000000000000000000000000000000000000 |
||||
SWAP1 |
||||
DIV |
||||
PUSH4 0xffffffff |
||||
AND |
||||
DUP1 |
||||
PUSH4 0x14ba3f12 |
||||
EQ |
||||
PUSH1 0x4e |
||||
JUMPI |
||||
DUP1 |
||||
PUSH4 0xc2985578 |
||||
EQ |
||||
PUSH1 0x8c |
||||
JUMPI |
||||
JUMPDEST |
||||
PUSH1 0x0 |
||||
DUP1 |
||||
REVERT |
||||
JUMPDEST |
||||
CALLVALUE |
||||
DUP1 |
||||
ISZERO |
||||
PUSH1 0x59 |
||||
JUMPI |
||||
PUSH1 0x0 |
||||
DUP1 |
||||
REVERT |
||||
JUMPDEST |
||||
POP |
||||
PUSH1 0x76 |
||||
PUSH1 0x4 |
||||
DUP1 |
||||
CALLDATASIZE |
||||
SUB |
||||
DUP2 |
||||
ADD |
||||
SWAP1 |
||||
DUP1 |
||||
DUP1 |
||||
CALLDATALOAD |
||||
SWAP1 |
||||
PUSH1 0x20 |
||||
ADD |
||||
SWAP1 |
||||
SWAP3 |
||||
SWAP2 |
||||
SWAP1 |
||||
POP |
||||
POP |
||||
POP |
||||
PUSH1 0xb4 |
||||
JUMP |
||||
JUMPDEST |
||||
PUSH1 0x40 |
||||
MLOAD |
||||
DUP1 |
||||
DUP3 |
||||
DUP2 |
||||
MSTORE |
||||
PUSH1 0x20 |
||||
ADD |
||||
SWAP2 |
||||
POP |
||||
POP |
||||
PUSH1 0x40 |
||||
MLOAD |
||||
DUP1 |
||||
SWAP2 |
||||
SUB |
||||
SWAP1 |
||||
RETURN |
||||
JUMPDEST |
||||
CALLVALUE |
||||
DUP1 |
||||
ISZERO |
||||
PUSH1 0x97 |
||||
JUMPI |
||||
PUSH1 0x0 |
||||
DUP1 |
||||
REVERT |
||||
JUMPDEST |
||||
POP |
||||
PUSH1 0x9e |
||||
PUSH1 0xc1 |
||||
JUMP |
||||
JUMPDEST |
||||
PUSH1 0x40 |
||||
MLOAD |
||||
DUP1 |
||||
DUP3 |
||||
DUP2 |
||||
MSTORE |
||||
PUSH1 0x20 |
||||
ADD |
||||
SWAP2 |
||||
POP |
||||
POP |
||||
PUSH1 0x40 |
||||
MLOAD |
||||
DUP1 |
||||
SWAP2 |
||||
SUB |
||||
SWAP1 |
||||
RETURN |
||||
JUMPDEST |
||||
PUSH1 0x0 |
||||
PUSH1 0xa |
||||
DUP3 |
||||
ADD |
||||
SWAP1 |
||||
POP |
||||
SWAP2 |
||||
SWAP1 |
||||
POP |
||||
JUMP |
||||
JUMPDEST |
||||
PUSH1 0x0 |
||||
DUP1 |
||||
PUSH1 0xa |
||||
DUP2 |
||||
ADD |
||||
SWAP2 |
||||
POP |
||||
POP |
||||
SWAP1 |
||||
JUMP |
||||
STOP |
||||
## Function evm instructions: constructor ## |
||||
PUSH1 0x80 |
||||
PUSH1 0x40 |
||||
MSTORE |
||||
CALLVALUE |
||||
DUP1 |
||||
ISZERO |
||||
PUSH2 0x10 |
||||
JUMPI |
||||
PUSH1 0x0 |
||||
DUP1 |
||||
REVERT |
||||
JUMPDEST |
||||
POP |
||||
CALLER |
||||
PUSH1 0x0 |
||||
DUP1 |
||||
PUSH2 0x100 |
||||
EXP |
||||
DUP2 |
||||
SLOAD |
||||
DUP2 |
||||
PUSH20 0xffffffffffffffffffffffffffffffffffffffff |
||||
MUL |
||||
NOT |
||||
AND |
||||
SWAP1 |
||||
DUP4 |
||||
PUSH20 0xffffffffffffffffffffffffffffffffffffffff |
||||
AND |
||||
MUL |
||||
OR |
||||
SWAP1 |
||||
SSTORE |
||||
POP |
||||
PUSH1 0xfa |
||||
DUP1 |
||||
PUSH2 0x5f |
||||
PUSH1 0x0 |
||||
CODECOPY |
||||
PUSH1 0x0 |
||||
RETURN |
||||
## Function evm instructions: foo ## |
||||
JUMPDEST |
||||
CALLVALUE |
||||
DUP1 |
||||
ISZERO |
||||
PUSH1 0x97 |
||||
JUMPI |
||||
PUSH1 0x0 |
||||
DUP1 |
||||
REVERT |
||||
JUMPDEST |
||||
POP |
||||
PUSH1 0x9e |
||||
PUSH1 0xc1 |
||||
JUMP |
||||
JUMPDEST |
||||
PUSH1 0x40 |
||||
MLOAD |
||||
DUP1 |
||||
DUP3 |
||||
DUP2 |
||||
MSTORE |
||||
PUSH1 0x20 |
||||
ADD |
||||
SWAP2 |
||||
POP |
||||
POP |
||||
PUSH1 0x40 |
||||
MLOAD |
||||
DUP1 |
||||
SWAP2 |
||||
SUB |
||||
SWAP1 |
||||
RETURN |
||||
JUMPDEST |
||||
PUSH1 0x0 |
||||
DUP1 |
||||
PUSH1 0xa |
||||
DUP2 |
||||
ADD |
||||
SWAP2 |
||||
POP |
||||
POP |
||||
SWAP1 |
||||
JUMP |
||||
Node evm instructions: ENTRY_POINT None |
||||
JUMPDEST |
||||
CALLVALUE |
||||
POP |
||||
PUSH1 0x9e |
||||
PUSH1 0xc1 |
||||
JUMP |
||||
JUMPDEST |
||||
PUSH1 0x40 |
||||
MLOAD |
||||
DUP1 |
||||
DUP3 |
||||
DUP2 |
||||
MSTORE |
||||
PUSH1 0x20 |
||||
ADD |
||||
SWAP2 |
||||
POP |
||||
POP |
||||
PUSH1 0x40 |
||||
MLOAD |
||||
DUP1 |
||||
SWAP2 |
||||
SUB |
||||
SWAP1 |
||||
RETURN |
||||
JUMPDEST |
||||
PUSH1 0x0 |
||||
POP |
||||
SWAP1 |
||||
JUMP |
||||
Node evm instructions: NEW VARIABLE None |
||||
DUP1 |
||||
Node evm instructions: RETURN (i + 10) |
||||
PUSH1 0xa |
||||
DUP2 |
||||
ADD |
||||
SWAP2 |
||||
POP |
@ -0,0 +1,19 @@ |
||||
pragma solidity >=0.4.24 <0.5.4; |
||||
|
||||
contract Test { |
||||
|
||||
address owner; |
||||
|
||||
constructor () public { |
||||
owner = msg.sender; |
||||
} |
||||
|
||||
function foo() public returns (uint) { |
||||
uint i; |
||||
return(i+10); |
||||
} |
||||
|
||||
function foobar(uint i) public returns (uint) { |
||||
return(i+10); |
||||
} |
||||
} |
@ -0,0 +1,2 @@ |
||||
from .convert import generate_source_to_evm_ins_mapping |
||||
from .evm_cfg_builder import load_evm_cfg_builder |
@ -0,0 +1,191 @@ |
||||
import logging |
||||
from slither.core.declarations import (Contract, Function) |
||||
from slither.core.cfg.node import Node |
||||
from slither.utils.function import get_function_id |
||||
from slither.exceptions import SlitherError |
||||
from .evm_cfg_builder import load_evm_cfg_builder |
||||
|
||||
logger = logging.getLogger('ConvertToEVM') |
||||
|
||||
KEY_EVM_INS = "EVM_INSTRUCTIONS" |
||||
|
||||
|
||||
def get_evm_instructions(obj): |
||||
assert isinstance(obj, (Function, Contract, Node)) |
||||
|
||||
if KEY_EVM_INS not in obj.context: |
||||
|
||||
CFG = load_evm_cfg_builder() |
||||
|
||||
slither = obj.slither |
||||
|
||||
if not slither.crytic_compile: |
||||
raise SlitherError('EVM features require to compile with crytic-compile') |
||||
|
||||
contract_info = {} |
||||
function_info = {} |
||||
node_info = {} |
||||
|
||||
if isinstance(obj, Node): |
||||
contract_info['contract'] = obj.function.contract |
||||
elif isinstance(obj, Function): |
||||
contract_info['contract'] = obj.contract |
||||
else: |
||||
contract_info['contract'] = obj |
||||
|
||||
# Get contract runtime bytecode, srcmap and cfg |
||||
contract_info['bytecode_runtime'] = slither.crytic_compile.bytecode_runtime( |
||||
contract_info['contract'].name) |
||||
contract_info['srcmap_runtime'] = slither.crytic_compile.srcmap_runtime( |
||||
contract_info['contract'].name) |
||||
contract_info['cfg'] = CFG(contract_info['bytecode_runtime']) |
||||
|
||||
# Get contract init bytecode, srcmap and cfg |
||||
contract_info['bytecode_init'] = slither.crytic_compile.bytecode_init(contract_info['contract'].name) |
||||
contract_info['srcmap_init'] = slither.crytic_compile.srcmap_init(contract_info['contract'].name) |
||||
contract_info['cfg_init'] = CFG(contract_info['bytecode_init']) |
||||
|
||||
# Get evm instructions |
||||
if isinstance(obj, Contract): |
||||
# Get evm instructions for contract |
||||
obj.context[KEY_EVM_INS] = _get_evm_instructions_contract(contract_info) |
||||
|
||||
elif isinstance(obj, Function): |
||||
# Get evm instructions for function |
||||
function_info['function'] = obj |
||||
function_info['contract_info'] = contract_info |
||||
obj.context[KEY_EVM_INS] = _get_evm_instructions_function(function_info) |
||||
|
||||
else: |
||||
# Get evm instructions for node |
||||
node_info['node'] = obj |
||||
|
||||
# CFG and srcmap depend on function being constructor or not |
||||
if node_info['node'].function.is_constructor: |
||||
cfg = contract_info['cfg_init'] |
||||
srcmap = contract_info['srcmap_init'] |
||||
else: |
||||
cfg = contract_info['cfg'] |
||||
srcmap = contract_info['srcmap_runtime'] |
||||
|
||||
node_info['cfg'] = cfg |
||||
node_info['srcmap'] = srcmap |
||||
node_info['contract'] = contract_info['contract'] |
||||
node_info['slither'] = slither |
||||
|
||||
obj.context[KEY_EVM_INS] = _get_evm_instructions_node(node_info) |
||||
|
||||
return obj.context.get(KEY_EVM_INS, []) |
||||
|
||||
|
||||
def _get_evm_instructions_contract(contract_info): |
||||
# Combine the instructions of constructor and the rest of the contract |
||||
return contract_info['cfg_init'].instructions + contract_info['cfg'].instructions |
||||
|
||||
|
||||
def _get_evm_instructions_function(function_info): |
||||
function = function_info['function'] |
||||
|
||||
# CFG depends on function being constructor or not |
||||
if function.is_constructor: |
||||
cfg = function_info['contract_info']['cfg_init'] |
||||
# _dispatcher is the only function recognised by evm-cfg-builder in bytecode_init. |
||||
# _dispatcher serves the role of the constructor in init code, |
||||
# given that there are no other functions. |
||||
# Todo: Could rename it appropriately in evm-cfg-builder |
||||
# by detecting that init bytecode is being parsed. |
||||
name = "_dispatcher" |
||||
hash = "" |
||||
else: |
||||
cfg = function_info['contract_info']['cfg'] |
||||
name = function.name |
||||
# Get first four bytes of function singature's keccak-256 hash used as function selector |
||||
hash = str(hex(get_function_id(function.full_name))) |
||||
|
||||
function_evm = _get_function_evm(cfg, name, hash) |
||||
if function_evm is None: |
||||
logger.error("Function " + function.name + " not found in the EVM code") |
||||
raise SlitherError("Function " + function.name + " not found in the EVM code") |
||||
|
||||
function_ins = [] |
||||
for basic_block in sorted(function_evm.basic_blocks, key=lambda x: x.start.pc): |
||||
for ins in basic_block.instructions: |
||||
function_ins.append(ins) |
||||
|
||||
return function_ins |
||||
|
||||
|
||||
def _get_evm_instructions_node(node_info): |
||||
# Get evm instructions for node's contract |
||||
contract_pcs = generate_source_to_evm_ins_mapping(node_info['cfg'].instructions, |
||||
node_info['srcmap'], |
||||
node_info['slither'], |
||||
node_info['contract'].source_mapping['filename_absolute']) |
||||
contract_file = node_info['slither'].source_code[node_info['contract'].source_mapping['filename_absolute']].encode( |
||||
'utf-8') |
||||
|
||||
# Get evm instructions corresponding to node's source line number |
||||
node_source_line = contract_file[0:node_info['node'].source_mapping['start']].count("\n".encode("utf-8")) \ |
||||
+ 1 |
||||
node_pcs = contract_pcs.get(node_source_line, []) |
||||
node_ins = [] |
||||
for pc in node_pcs: |
||||
node_ins.append(node_info['cfg'].get_instruction_at(pc)) |
||||
|
||||
return node_ins |
||||
|
||||
|
||||
def _get_function_evm(cfg, function_name, function_hash): |
||||
for function_evm in cfg.functions: |
||||
# Match function hash |
||||
if function_evm.name[:2] == "0x" and function_evm.name == function_hash: |
||||
return function_evm |
||||
# Match function name |
||||
elif function_evm.name[:2] != "0x" and function_evm.name.split('(')[0] == function_name: |
||||
return function_evm |
||||
return None |
||||
|
||||
|
||||
def generate_source_to_evm_ins_mapping(evm_instructions, srcmap_runtime, slither, filename): |
||||
""" |
||||
Generate Solidity source to EVM instruction mapping using evm_cfg_builder:cfg.instructions |
||||
and solc:srcmap_runtime |
||||
|
||||
Returns: Solidity source to EVM instruction mapping |
||||
""" |
||||
|
||||
source_to_evm_mapping = {} |
||||
file_source = slither.source_code[filename].encode('utf-8') |
||||
prev_mapping = [] |
||||
|
||||
for idx, mapping in enumerate(srcmap_runtime): |
||||
# Parse srcmap_runtime according to its format |
||||
# See https://solidity.readthedocs.io/en/v0.5.9/miscellaneous.html#source-mappings |
||||
# In order to compress these source mappings especially for bytecode, the following rules are used: |
||||
# If a field is empty, the value of the preceding element is used. |
||||
# If a : is missing, all following fields are considered empty. |
||||
|
||||
mapping_item = mapping.split(':') |
||||
mapping_item += prev_mapping[len(mapping_item):] |
||||
|
||||
for i in range(len(mapping_item)): |
||||
if mapping_item[i] == '': |
||||
mapping_item[i] = int(prev_mapping[i]) |
||||
|
||||
offset, length, file_id, _ = mapping_item |
||||
prev_mapping = mapping_item |
||||
|
||||
if file_id == '-1': |
||||
# Internal compiler-generated code snippets to be ignored |
||||
# See https://github.com/ethereum/solidity/issues/6119#issuecomment-467797635 |
||||
continue |
||||
|
||||
offset = int(offset) |
||||
line_number = file_source[0:offset].count("\n".encode("utf-8")) + 1 |
||||
|
||||
# Append evm instructions to the corresponding source line number |
||||
# Note: Some evm instructions in mapping are not necessarily in program execution order |
||||
# Note: The order depends on how solc creates the srcmap_runtime |
||||
source_to_evm_mapping.setdefault(line_number, []).append(evm_instructions[idx].pc) |
||||
|
||||
return source_to_evm_mapping |
@ -0,0 +1,15 @@ |
||||
import logging |
||||
from slither.exceptions import SlitherError |
||||
|
||||
logger = logging.getLogger('ConvertToEVM') |
||||
|
||||
def load_evm_cfg_builder(): |
||||
try: |
||||
# Avoiding the addition of evm_cfg_builder as permanent dependency |
||||
from evm_cfg_builder.cfg import CFG |
||||
return CFG |
||||
except ImportError: |
||||
logger.error("To use evm features, you need to install evm-cfg-builder") |
||||
logger.error("Documentation: https://github.com/crytic/evm_cfg_builder") |
||||
logger.error("Installation: pip install evm-cfg-builder") |
||||
raise SlitherError("evm-cfg-builder not installed.") |
@ -0,0 +1,109 @@ |
||||
""" |
||||
Module printing evm mapping of the contract |
||||
""" |
||||
from slither.printers.abstract_printer import AbstractPrinter |
||||
from slither.analyses.evm import generate_source_to_evm_ins_mapping, load_evm_cfg_builder |
||||
from slither.utils.colors import blue, green, magenta, red |
||||
|
||||
|
||||
class PrinterEVM(AbstractPrinter): |
||||
ARGUMENT = 'evm' |
||||
HELP = 'Print the evm instructions of nodes in functions' |
||||
|
||||
WIKI = 'https://github.com/trailofbits/slither/wiki/Printer-documentation#evm' |
||||
|
||||
def output(self, _filename): |
||||
""" |
||||
_filename is not used |
||||
Args: |
||||
_filename(string) |
||||
""" |
||||
|
||||
txt = "" |
||||
|
||||
if not self.slither.crytic_compile: |
||||
txt = 'The EVM printer requires to compile with crytic-compile' |
||||
self.info(red(txt)) |
||||
res = self.generate_output(txt) |
||||
return res |
||||
evm_info = self._extract_evm_info(self.slither) |
||||
|
||||
for contract in self.slither.contracts_derived: |
||||
txt += blue('Contract {}\n'.format(contract.name)) |
||||
|
||||
contract_file = self.slither.source_code[contract.source_mapping['filename_absolute']].encode('utf-8') |
||||
contract_file_lines = open(contract.source_mapping['filename_absolute'], 'r').readlines() |
||||
|
||||
contract_pcs = {} |
||||
contract_cfg = {} |
||||
|
||||
for function in contract.functions: |
||||
txt += blue(f'\tFunction {function.canonical_name}\n') |
||||
|
||||
# CFG and source mapping depend on function being constructor or not |
||||
if function.is_constructor: |
||||
contract_cfg = evm_info['cfg_init', contract.name] |
||||
contract_pcs = evm_info['mapping_init', contract.name] |
||||
else: |
||||
contract_cfg = evm_info['cfg', contract.name] |
||||
contract_pcs = evm_info['mapping', contract.name] |
||||
|
||||
for node in function.nodes: |
||||
txt += green("\t\tNode: " + str(node) + "\n") |
||||
node_source_line = contract_file[0:node.source_mapping['start']].count("\n".encode("utf-8")) + 1 |
||||
txt += green('\t\tSource line {}: {}\n'.format(node_source_line, |
||||
contract_file_lines[node_source_line - 1].rstrip())) |
||||
txt += magenta('\t\tEVM Instructions:\n') |
||||
node_pcs = contract_pcs.get(node_source_line, []) |
||||
for pc in node_pcs: |
||||
txt += magenta('\t\t\t0x{:x}: {}\n'.format(int(pc), contract_cfg.get_instruction_at(pc))) |
||||
|
||||
for modifier in contract.modifiers: |
||||
txt += blue(f'\tModifier {modifier.canonical_name}\n') |
||||
for node in modifier.nodes: |
||||
txt += green("\t\tNode: " + str(node) + "\n") |
||||
node_source_line = contract_file[0:node.source_mapping['start']].count("\n".encode("utf-8")) + 1 |
||||
txt += green('\t\tSource line {}: {}\n'.format(node_source_line, |
||||
contract_file_lines[node_source_line - 1].rstrip())) |
||||
txt += magenta('\t\tEVM Instructions:\n') |
||||
node_pcs = contract_pcs.get(node_source_line, []) |
||||
for pc in node_pcs: |
||||
txt += magenta('\t\t\t0x{:x}: {}\n'.format(int(pc), contract_cfg.get_instruction_at(pc))) |
||||
|
||||
self.info(txt) |
||||
res = self.generate_output(txt) |
||||
return res |
||||
|
||||
def _extract_evm_info(self, slither): |
||||
""" |
||||
Extract evm information for all derived contracts using evm_cfg_builder |
||||
|
||||
Returns: evm CFG and Solidity source to Program Counter (pc) mapping |
||||
""" |
||||
|
||||
evm_info = {} |
||||
|
||||
CFG = load_evm_cfg_builder() |
||||
|
||||
for contract in slither.contracts_derived: |
||||
contract_bytecode_runtime = slither.crytic_compile.bytecode_runtime(contract.name) |
||||
contract_srcmap_runtime = slither.crytic_compile.srcmap_runtime(contract.name) |
||||
cfg = CFG(contract_bytecode_runtime) |
||||
evm_info['cfg', contract.name] = cfg |
||||
evm_info['mapping', contract.name] = generate_source_to_evm_ins_mapping( |
||||
cfg.instructions, |
||||
contract_srcmap_runtime, |
||||
slither, |
||||
contract.source_mapping['filename_absolute']) |
||||
|
||||
contract_bytecode_init = slither.crytic_compile.bytecode_init(contract.name) |
||||
contract_srcmap_init = slither.crytic_compile.srcmap_init(contract.name) |
||||
cfg_init = CFG(contract_bytecode_init) |
||||
|
||||
evm_info['cfg_init', contract.name] = cfg_init |
||||
evm_info['mapping_init', contract.name] = generate_source_to_evm_ins_mapping( |
||||
cfg_init.instructions, |
||||
contract_srcmap_init, slither, |
||||
contract.source_mapping['filename_absolute']) |
||||
|
||||
return evm_info |
Loading…
Reference in new issue