├── requirements.txt ├── time_measurement.py ├── constant.py ├── test ├── __init__.py └── test_data_structures.py ├── fee_schedule.py ├── exceptions.py ├── LICENSE ├── ra.py ├── .gitignore ├── README.md ├── utils.py ├── vulnerability_verifier.py ├── control_flow_manager.py ├── data_structures.py └── vm.py /requirements.txt: -------------------------------------------------------------------------------- 1 | pysha3 2 | z3-solver -------------------------------------------------------------------------------- /time_measurement.py: -------------------------------------------------------------------------------- 1 | exec_time = {} 2 | solving_time = {} -------------------------------------------------------------------------------- /constant.py: -------------------------------------------------------------------------------- 1 | RUNNING = 'running' 2 | TERMINATED = 'terminated' 3 | RETURNED = 'returned' 4 | CALLABLE = 'callable' -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from sys import path 3 | path.append(str(Path(__file__).parent.parent)) 4 | -------------------------------------------------------------------------------- /fee_schedule.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from data_structures import WorldState, MachineState, ExecutionEnvironment 3 | W_ZERO = 'W_ZERO' 4 | 5 | 6 | 7 | G_ZERO = 'G_ZERO' 8 | 9 | mnemonic_to_instruction_subset = { 10 | 'STOP': W_ZERO 11 | } 12 | 13 | instruction_subset_to_fee_name = { 14 | W_ZERO: G_ZERO 15 | } 16 | 17 | schedule = { 18 | G_ZERO:0 19 | } 20 | 21 | def mnemonic_to_fee(mnemonic:str): 22 | pass 23 | 24 | # CCALL, CSELFDESTRUCT and CSSTORE 25 | def c_call(): 26 | pass 27 | 28 | def c_selfdestruct(): 29 | pass 30 | 31 | def c_sstore(): 32 | pass 33 | 34 | # the memory cost function 35 | def c_mem(): 36 | pass 37 | 38 | # The general gas cost function 39 | def c(world_state, machine_state, execution_environment): 40 | return 1 41 | -------------------------------------------------------------------------------- /test/test_data_structures.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from data_structures import Bytecode 3 | from exceptions import EVMbytecodeError 4 | 5 | 6 | class TestBytecode: 7 | 8 | def test_init(self): 9 | Bytecode("0123456789abcdef") 10 | 11 | with pytest.raises(EVMbytecodeError): 12 | Bytecode("0123456789abcdef0") 13 | 14 | with pytest.raises(EVMbytecodeError): 15 | Bytecode("0g") 16 | 17 | def test_append(self): 18 | bytecode = Bytecode() 19 | bytecode.append("0b") 20 | 21 | with pytest.raises(EVMbytecodeError): 22 | bytecode.append("0g") 23 | 24 | with pytest.raises(EVMbytecodeError): 25 | bytecode.append("0") 26 | 27 | def test_slice(self): 28 | bytecode = Bytecode("0123456789abcdef") 29 | assert bytecode[0] == "01" 30 | assert bytecode[7] == "ef" 31 | assert bytecode[1:7] == ["23", "45", "67", "89", "ab", "cd"] 32 | -------------------------------------------------------------------------------- /exceptions.py: -------------------------------------------------------------------------------- 1 | class RuntimeErorr(Exception): 2 | __str__ = lambda self: 'Runtime error.' 3 | 4 | class DevelopmentErorr(Exception): 5 | def __init__(self, msg=''): 6 | if len(msg) != 0: 7 | msg += ' ' 8 | self.msg = msg 9 | __str__ = lambda self: 'Implementation or design error. ' + self.msg 10 | 11 | class SettingError(Exception): 12 | def __init__(self, msg=''): 13 | if len(msg) != 0: 14 | msg += ' ' 15 | self.msg = msg 16 | __str__ = lambda self: 'Setting error. ' + self.msg 17 | 18 | 19 | class NotBitVecRef256Erorr(DevelopmentErorr): 20 | __str__ = lambda self: super().__str__() + 'The given object is not 256bit-BitVector.' 21 | 22 | class NotBitVecNumRef256Erorr(DevelopmentErorr): 23 | __str__ = lambda self: super().__str__() + 'The given object is not concrete value of 256bit-BitVector.' 24 | 25 | class EVMbytecodeError(DevelopmentErorr): 26 | __str__ = lambda self: super().__str__() + 'Or, the given EVM byte code may be incorrect.' 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 wanidon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ra.py: -------------------------------------------------------------------------------- 1 | from vm import VM 2 | from data_structures import WorldState, Bytecode 3 | from utils import reset_time 4 | reset_time() 5 | 6 | # get contract code 7 | bytecode = '45600557fe44600a57fe4300' 8 | bytecode = '608060405234801561001057600080fd5b506004361061002b5760003560e01c80633ccfd60b14610030575b600080fd5b61003861003a565b005b60003373ffffffffffffffffffffffffffffffffffffffff166000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205460405180600001905060006040518083038185875af1925050503d80600081146100d8576040519150601f19603f3d011682016040523d82523d6000602084013e6100dd565b606091505b50509050801561012c5760008060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b5056fea265627a7a72305820300e906c61c5f68e15c6ee302e607456ecebe0d50832807f306741ec3e07d2c764736f6c63430005090032' 9 | 10 | bytecode = input() 11 | 12 | 13 | primary_contracts = [bytecode] 14 | secondary_contract = None 15 | tertiary_contracts = [] 16 | 17 | world_state = WorldState() 18 | vm = VM(world_state) 19 | for b in primary_contracts: 20 | vm.add_primary_contract(Bytecode(b)) 21 | # vm.run_all() 22 | vm.verify_full_state() -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | playground/ 107 | .png 108 | .dot 109 | TODO.md 110 | private/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RA 2 | Re-entrancy Analyzer, driven by symbolic execution. 3 | This tool simulates calling function which may cause re-enctancy attack, and detect it. This can also verify construction of new contract as external calling. 4 | ## Requirement 5 | - `cmake` command (used for building z3 solver) 6 | - for Mac: `brew install cmake` 7 | - Python3 (3.7 or higher is confirmed to run) 8 | - Python modules 9 | - z3-solver (this installation may take about 10 minutes) 10 | - pysha3 (used for convenience to represent sha3 output) 11 | - Graphviz (optional; used to draw the execution path) 12 | 13 | ## Usage 14 | 1. Get some EVM bytecode to be verified. If you want to verify your Solidity code, you can get its bytecode as follows: 15 | `solc -o {OUTPUT_DIR} --bin-runtime {YOUR_SOLIDITY_FILE} ` 16 | 1. Run ra.py. 17 | `python3 ra.py` 18 | 1. Give the bytecode to standard input. For example, the following code has a simple re-entrancy. 19 | `606060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633ccfd60b14610046575b600080fd5b341561005157600080fd5b61005961005b565b005b3373ffffffffffffffffffffffffffffffffffffffff166000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205460405180602001905060006040518083038185875af192505050156101155760008060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b5600a165627a7a72305820e598d1a576b5047521cba20260ee9579fee29311bfbb5623191a49ca74a1e4380029` 20 | 3. If the given code has re-entrancy, RA teach you the combination of functions which may cause re-entrancy as tuple of function IDs. 21 | `('0x3ccfd60b', '0x3ccfd60b', True, 6.267011556017678)` 22 | In this case, The first `'0x3ccfd60b'` is function ID of the first called function, and the second `'0x3ccfd60b'` is function ID of the second called (called by malicious contract) function. 23 | ## Limitation 24 | - ~~RA cannnot create new contract whose code will be determine dynamically(such part will be symbol variable). It is due to the data type which represents contract. Ra just uses a string as EVM bytecode. Thus, if it is replaced with python list, or temporally fixes such dynamic code, you can analyse such contracts.~~ 25 | Now you can verify code constructing new contract. 26 | - Some EVM opcodes are not implemented in RA's VM. 27 | 28 | ## License 29 | This program is released under the MIT license. 30 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from z3 import * 3 | from z3 import BitVecRef, simplify 4 | 5 | from exceptions import NotBitVecRef256Erorr, NotBitVecNumRef256Erorr 6 | from math import copysign 7 | from sys import stderr 8 | import time_measurement 9 | from time import perf_counter 10 | import os 11 | 12 | def convert_to_bitvec8(x: str | BitVecRef) -> BitVecRef: 13 | return BitVecVal(int(x, 16), 8) if isinstance(x, str) else x 14 | 15 | def BitVec256(name) -> BitVecRef: 16 | return BitVec(name,256) 17 | 18 | def BitVecVal256(val) -> BitVecRef: 19 | return BitVecVal(val, 256) 20 | 21 | def zero8bit()-> BitVecRef: 22 | return BitVecVal(0,8) 23 | 24 | def checkBitVecRef256(word): 25 | if isinstance(word, BitVecRef) and word.size() == 256: 26 | return word 27 | else: 28 | raise NotBitVecRef256Erorr() 29 | 30 | def checkBitVecNumRef256(word): 31 | if isinstance(word, BitVecNumRef) and word.size() == 256: 32 | return word 33 | else: 34 | raise NotBitVecNumRef256Erorr() 35 | 36 | def sign(x): 37 | checkBitVecNumRef256(x) 38 | return copysign(1, x.as_signed_long()) 39 | 40 | def test_checkBitVecRef256(): 41 | from z3 import BitVec, BitVecVal 42 | print(checkBitVecRef256(BitVecVal(111,256))) 43 | print(checkBitVecRef256(BitVec('x',256))) 44 | #print(isBitVecRef256(34))a 45 | print(checkBitVecRef256(BitVec('y',128))) 46 | print('hoge') 47 | 48 | def BitVecOne256(): 49 | return BitVecVal256(1) 50 | 51 | def BitVecZero256(): 52 | return BitVecVal256(0) 53 | 54 | DEBUG = True 55 | def pdbg(*somthing): 56 | if DEBUG: print(*somthing) 57 | 58 | 59 | def bv_to_signed_int(x): 60 | return simplify(If(x < 0, BV2Int(x) - 2 ** x.size(), BV2Int(x))) 61 | 62 | def dbgredmsg(*something): 63 | stderr.write(' '.join([str(d) for d in something])+'\n') 64 | 65 | def reset_time(): 66 | time_measurement.exec_time[os.getpid()] = perf_counter() 67 | time_measurement.solving_time[os.getpid()] = 0 68 | 69 | def get_time(): 70 | return perf_counter() - time_measurement.exec_time[os.getpid()], time_measurement.solving_time[os.getpid()] 71 | 72 | def solve_and_time(condition, solver=None): 73 | s = Solver() if solver is None else solver 74 | s.add(condition) 75 | t = perf_counter() 76 | r = s.check() 77 | print(time_measurement.solving_time) 78 | time_measurement.solving_time[os.getpid()] += perf_counter() - t 79 | # 80 | return r == sat 81 | 82 | def get_model_and_time(condition, solver=None): 83 | s = Solver() if solver is None else solver 84 | s.add(condition) 85 | t = perf_counter() 86 | r = s.check() 87 | time_measurement.solving_time[os.getpid()] += perf_counter() - t 88 | return s.model() if r == sat else False 89 | 90 | if __name__ == '__main__': 91 | test_checkBitVecRef256() 92 | -------------------------------------------------------------------------------- /vulnerability_verifier.py: -------------------------------------------------------------------------------- 1 | from z3 import Solver, sat, BitVecVal, BitVec, unsat, BitVecNumRef,And 2 | from data_structures import Storage 3 | from collections import defaultdict 4 | from utils import * 5 | import time_measurement 6 | from time import time 7 | from constant import * 8 | 9 | class VulnerabilityVerifier: 10 | def __init__(self): 11 | self.independently_executed_state = dict() 12 | self.ies_path_condition = True 13 | self.cross_called_executed_state = dict() 14 | self.cces_path_condition = True 15 | self.caller_state = dict() 16 | self.ies_depth = 0 17 | self.cces_depth = 0 18 | self.caller_state_depth = 0 19 | 20 | 21 | 22 | self.function_ids = set() 23 | self.callable_function_ids = set() 24 | self.extracting_fid = False 25 | self.executing_caller = False 26 | self.executing_callee = False 27 | self.executing_cross_function = False 28 | 29 | self.first_call = False 30 | self.second_call = False 31 | self.third_call = False 32 | self.x = None 33 | self.y = None 34 | 35 | def init_states(self): 36 | self.independently_executed_state = dict() 37 | self.cross_called_executed_state = dict() 38 | self.caller_state = dict() 39 | self.ies_depth = 0 40 | self.cces_depth = 0 41 | self.caller_state_depth = 0 42 | 43 | def add_state_to_ies(self, storage: Storage, balance, condition): 44 | dict = storage.get_data() 45 | 46 | dict['BALANCE'] = balance 47 | dict['PATH_CONDITION'] = condition 48 | 49 | 50 | 51 | self.independently_executed_state[condition] = dict 52 | 53 | 54 | 55 | def add_state_to_cces(self, storage: Storage, balance, condition): 56 | dict = storage.get_data() 57 | 58 | dict['BALANCE'] = balance 59 | dict['PATH_CONDITION'] = condition 60 | self.cross_called_executed_state[condition] = dict 61 | 62 | def add_caller_state(self, storage: Storage, balance, condition): 63 | 64 | dict = storage.get_data() 65 | dict['BALANCE'] = balance 66 | dict['PATH_CONDITION'] = condition 67 | if self.caller_state_depth > depth: 68 | dict.update(self.caller_state) 69 | self.caller_state = dict 70 | else: 71 | self.caller_state.update(dict) 72 | self.caller_state_depth = depth 73 | 74 | def get_caller_state(self): 75 | return Storage(storage_data=self.caller_state) 76 | 77 | def get_function_ids(self) -> set: 78 | return self.function_ids 79 | 80 | def get_callable_function_ids(self) -> set: 81 | return self.callable_function_ids 82 | 83 | def diff_states(self): 84 | C1 = list(self.cross_called_executed_state.keys()) 85 | C2 = list(self.independently_executed_state.keys()) 86 | for i,c1 in enumerate(C1): 87 | vulnerable = True 88 | pair = None 89 | for j,c2 in enumerate(C2): 90 | m = get_model_and_time(And(c1!=c2)) 91 | print(i, j, m) 92 | if not m: 93 | pair = c2 94 | vulnerable = False 95 | break 96 | 97 | if vulnerable: 98 | return True 99 | else: 100 | C2.remove(pair) 101 | 102 | return False 103 | 104 | 105 | 106 | 107 | def extract_function_id(self, condition, block_state): 108 | if self.extracting_fid: 109 | m = get_model_and_time(condition) 110 | if m: 111 | fid = m[BitVec('function_id', 32)] 112 | # the case contract has only one function 113 | if fid is None: 114 | self.function_ids.add(BitVecVal(0, 32)) 115 | if 'callable' in block_state: 116 | self.callable_function_ids.add(BitVecVal(0, 32)) 117 | 118 | else: 119 | self.function_ids.add(fid) 120 | if 'callable' in block_state: 121 | self.callable_function_ids.add(fid) 122 | return True 123 | 124 | return False 125 | 126 | def set_extracting_fid(self): 127 | self.extracting_fid = True 128 | self.executing_caller = False 129 | self.executing_callee = False 130 | self.executing_cross_function = False 131 | 132 | def set_executing_caller(self): 133 | self.extracting_fid = False 134 | self.executing_caller = True 135 | self.executing_callee = False 136 | self.executing_cross_function = False 137 | 138 | def set_executing_callee(self): 139 | self.extracting_fid = False 140 | self.executing_caller = False 141 | self.executing_callee = True 142 | self.executing_cross_function = False 143 | 144 | def set_executing_cross_function(self): 145 | self.extracting_fid = False 146 | self.executing_caller = False 147 | self.executing_callee = False 148 | self.executing_cross_function = True 149 | 150 | def extract_data(self, condition, block_state, storage, balance): 151 | if self.extracting_fid: 152 | self.extract_function_id(condition, block_state) 153 | elif self.executing_callee: 154 | self.add_state_to_ies(storage, balance, condition) 155 | elif self.executing_cross_function and CALLABLE in block_state: 156 | self.add_state_to_cces(storage, balance, condition) 157 | 158 | def is_first_call(self): 159 | if self.first_call: 160 | self.first_call = False 161 | return True 162 | else: 163 | return False 164 | 165 | def is_second_call(self): 166 | if not self.first_call and self.second_call: 167 | self.second_call = False 168 | return True 169 | else: 170 | return False 171 | 172 | def is_third_call(self): 173 | if not self.second_call and self.third_call: 174 | self.third_call = False 175 | return True 176 | else: 177 | return False 178 | 179 | def set_first_call(self, b): 180 | self.first_call = b 181 | 182 | def set_second_call(self, b): 183 | self.second_call = b 184 | 185 | def set_third_call(self, b): 186 | self.third_call = b 187 | 188 | def set_x(self, x): 189 | self.x = x 190 | 191 | def set_y(self, y): 192 | self.y = y 193 | 194 | def get_x(self): 195 | return self.x 196 | 197 | def get_y(self): 198 | return self.y 199 | 200 | def extract_data_before_call(self, condition, block_state, storage, balance, depth, ): 201 | pass 202 | 203 | def extract_data_with_callback(self, condition, block_state, storage, balance, depth, ): 204 | pass 205 | 206 | 207 | 208 | 209 | class VulenerabilityVerifierBeforeCall(VulnerabilityVerifier): 210 | def extract_data(self, condition, block_state, storage, balance, depth, ): 211 | if self.extracting_fid: 212 | self.extract_function_id(condition, block_state) 213 | elif self.executing_caller: 214 | self.add_caller_state(storage, balance, depth, condition) 215 | 216 | def extract_data_before_call(self, condition, block_state, storage, balance, depth): 217 | if self.executing_callee and self.is_first_call(): 218 | self.add_state_to_ies(storage, balance, depth, condition) 219 | elif self.executing_cross_function and self.is_third_call(): 220 | self.add_state_to_cces(storage, balance, depth, condition) 221 | 222 | 223 | class VulnerabilityVerifierAfterCall(VulnerabilityVerifier): 224 | def extract_data(self, condition, block_state, storage, balance, depth, ): 225 | if self.extracting_fid: 226 | self.extract_function_id(condition, block_state) 227 | elif self.executing_caller: 228 | self.add_caller_state(storage, balance, depth, condition) 229 | elif self.executing_callee: 230 | self.add_state_to_ies(storage, balance, depth, condition) 231 | # elif self.executing_cross_function and self.is_third_call(): 232 | # self.add_state_to_cces(storage, balance, depth, condition) 233 | 234 | def extract_data_before_call(self, condition, block_state, storage, balance, depth,): 235 | pass 236 | 237 | 238 | def extract_data_with_callback(self, condition, block_state, storage, balance, depth, ): 239 | if self.executing_cross_function and self.is_third_call(): 240 | self.add_state_to_cces(storage, balance, depth, condition) 241 | 242 | -------------------------------------------------------------------------------- /control_flow_manager.py: -------------------------------------------------------------------------------- 1 | from data_structures import BasicBlock, ExecutionEnvironment, Memory, Stack 2 | from exceptions import DevelopmentErorr 3 | from z3 import Solver, unsat, Or, Not, BitVecRef, BoolRef, simplify,sat 4 | from collections import defaultdict 5 | from utils import dbgredmsg, solve_and_time 6 | import time_measurement 7 | from copy import deepcopy 8 | from vulnerability_verifier import VulnerabilityVerifierAfterCall 9 | 10 | class ControlFlowManager: 11 | def __init__(self): 12 | self.__cfmanager_id = id(self) 13 | self.basic_blocks = [] 14 | self.__edges = defaultdict(list) 15 | self.__CFG_name = "CFG_{}".format(self.__cfmanager_id) 16 | # self.__call_stack = [] 17 | self.__dfs_stack = [] 18 | # exec_id -> index of bytecode -> bool 19 | self.visited_address = defaultdict(lambda: defaultdict(lambda : False)) 20 | self.processing_block: BasicBlock = None 21 | 22 | # def convert_bytecode_to_account(self, bytecode) -> Account: 23 | # Account() 24 | 25 | def get_cfmanager_id(self) -> int: 26 | return self.__cfmanager_id 27 | 28 | def get_exec_id(self): 29 | return self.processing_block.get_exec_env().get_exec_env_id() 30 | 31 | def push_to_call_stack(self, block: BasicBlock): 32 | self.processing_block.push_call_stack(block) 33 | 34 | def pop_from_call_stack(self) -> BasicBlock: 35 | return self.processing_block.pop_call_stack() 36 | 37 | def push_to_dfs_stack(self, block: BasicBlock): 38 | self.__dfs_stack.append(block) 39 | 40 | def pop_from_dfs_stack(self) -> BasicBlock: 41 | return self.__dfs_stack.pop() 42 | 43 | def is_dfs_stack_empty(self): 44 | return len(self.__dfs_stack) == 0 45 | 46 | def is_call_stack_empty(self): 47 | return self.processing_block.get_call_stack_size() == 0 48 | 49 | 50 | 51 | def add_mnemonic(self, mnemonic: str): 52 | self.processing_block.mnemonics.append([self.processing_block.get_pc(), mnemonic]) 53 | 54 | # def set_jump_dest(self, dest): 55 | # self.processing_block.set_jumpdest(dest) 56 | def add_immediatevalue(self, iv: str): 57 | self.processing_block.mnemonics[-1][1] += ' 0x' + iv 58 | 59 | def get_num_basicblocks(self): 60 | return len(self.basic_blocks) 61 | 62 | def set_procesisng_block(self, block:BasicBlock): 63 | self.processing_block = block 64 | self.basic_blocks.append(block) 65 | 66 | def get_processing_block(self) -> BasicBlock: 67 | return self.processing_block 68 | 69 | def integrate_path_condition(self, constraint: BoolRef, integrated_block: BasicBlock): 70 | integrated_block.set_path_condition( 71 | simplify(Or(integrated_block.get_path_condition, constraint)) 72 | ) 73 | for dest in self.get_dest_blocks(integrated_block): 74 | self.integrate_path_condition(constraint, dest) 75 | 76 | def inherit_from_processing_block(self, continuable: bool, jumpable: bool, condition): 77 | # s = Solver() 78 | # s.push() 79 | exec_id = self.get_exec_id() 80 | depth_of_call = self.get_processing_block().get_exec_env().depth_of_call 81 | #print('inherit',depth_of_call) 82 | 83 | pc = self.get_processing_block().get_pc() 84 | if continuable: 85 | # 先にcontinuation_blockを生成することでブロック番号を若くする 86 | # Generate a continuation_block first to make the block number younger 87 | continuation_block = self.processing_block.inherit(len(self.basic_blocks), False) 88 | continuation_block.add_constraint_to_path_condition(Not(condition)) 89 | continuation_block.add_path(exec_id, depth_of_call, pc, False) 90 | 91 | # 到達不能の場合 または 到達済みの場合何もしない 92 | # nothing to do when unreachable or reached 93 | # s.add(continuation_block.get_path_condition()) 94 | # st = time() 95 | # ck = s.check() 96 | # time_measurement.solving_time += time() - st 97 | if not self.visited_address[continuation_block.get_path()][continuation_block.get_pc()]: 98 | # print('visited', 99 | # ck, 100 | # self.visited_address[self.get_exec_id()][continuation_block.get_pc()], 101 | # self.get_processing_block().get_pc()) 102 | self.basic_blocks.append(continuation_block) 103 | self.__edges[self.processing_block].append(continuation_block) 104 | else: 105 | continuable = False 106 | 107 | # s.pop() 108 | 109 | 110 | 111 | if jumpable: 112 | jump_block = self.processing_block.inherit( 113 | new_block_number=len(self.basic_blocks), 114 | jflag=True) 115 | jump_block.add_constraint_to_path_condition(condition) 116 | jump_block.add_path(exec_id, depth_of_call, pc, True) 117 | 118 | 119 | 120 | if not self.visited_address[jump_block.get_path()][jump_block.get_pc()]: 121 | # print('visited,jumpable?', 122 | # ck, 123 | # self.visited_address[self.get_exec_id()][jump_block.get_pc()], 124 | # self.get_processing_block().get_pc()) 125 | # print('exec id=',self.get_exec_id()) 126 | self.basic_blocks.append(jump_block) 127 | self.__edges[self.processing_block].append(jump_block) 128 | else: 129 | print('visited!') 130 | print(hex(jump_block.get_pc()),jump_block.get_path(),) 131 | jumpable = False 132 | 133 | 134 | 135 | if continuable: 136 | self.processing_block = continuation_block 137 | if jumpable: 138 | # print('push to dfs stack') 139 | self.push_to_dfs_stack(jump_block) 140 | 141 | elif jumpable: 142 | self.processing_block = jump_block 143 | else: 144 | # dbgredmsg(condition) 145 | return False 146 | pass 147 | 148 | return self.processing_block 149 | 150 | # # ジャンプのみでブロック更新がある場合 151 | # def jump_from_processing_block(self): 152 | # jump_block = self.processing_block.inherit(len(self.basic_blocks), True) 153 | # self.basic_blocks.append(jump_block) 154 | # self.__edges[self.processing_block].append(jump_block) 155 | 156 | # switch to block beginning with JUMPDEST 157 | def search_existing_block(self, 158 | exec_id: int, 159 | pc: int): 160 | for block in self.basic_blocks: 161 | if block.get_machine_state().get_pc() == pc \ 162 | and block.get_exec_env().get_exec_env_id() == exec_id\ 163 | and block != self.processing_block: 164 | return block 165 | return None 166 | 167 | 168 | def switch_block(self, 169 | exec_id: int, 170 | pc: int): 171 | block = self.get_processing_block().inherit( 172 | new_block_number=self.get_num_basicblocks() 173 | ) 174 | self.add_edge(self.get_processing_block(), block) 175 | block.set_pc(block.get_pc()-1) 176 | self.set_procesisng_block(block) 177 | 178 | #廃止 179 | def switch_existing_block(self, 180 | exec_id: int, 181 | pc: int): 182 | block = self.search_existing_block(exec_id,pc) 183 | if block is not None: 184 | dbgredmsg('pre dfs stack size=',self.get_processing_block().get_dfs_stack_size()) 185 | dbgredmsg(self.processing_block, block) 186 | self.add_edge(self.processing_block, block) 187 | self.set_procesisng_block(block) 188 | dbgredmsg('next dfs stack size=',self.get_processing_block().get_dfs_stack_size()) 189 | return 190 | else: 191 | dbgredmsg('not found') 192 | 193 | # next_block = self.get_processing_block().inherit( 194 | # new_block_number=self.get_num_basicblocks() 195 | # ) 196 | # 197 | # self.add_basic_block(next_block) 198 | # self.add_edge(self.get_processing_block(), next_block) 199 | # # next_block.set_pc(next_block.get_pc()) 200 | # self.set_procesisng_block(next_block) 201 | 202 | return 203 | 204 | def external_call(self, 205 | # account_number:int, 206 | exec_env:ExecutionEnvironment, 207 | condition=True 208 | #machine_state:MachineState=None 209 | ): 210 | # print('external call') 211 | # print('prev storage:') 212 | # print(self.get_processing_block().get_machine_state().get_storage().get_data()) 213 | continuation_block = self.processing_block.inherit(len(self.basic_blocks), False) 214 | self.add_basic_block(continuation_block) 215 | # self.push_to_call_stack(continuation_block) 216 | 217 | 218 | new_block_number = len(self.basic_blocks) 219 | external_block = self.processing_block.duplicate( 220 | # account_number=account_number, 221 | new_block_number=new_block_number, 222 | exec_env=exec_env, 223 | # dfs_stack=[] 224 | ) 225 | # print('external call') 226 | # print('prev call stack:') 227 | # print(id(self.processing_block.call_stack), self.processing_block.call_stack) 228 | # print('next call stack:') 229 | # print(id(external_block.call_stack), external_block.call_stack) 230 | external_block.set_pc(0) 231 | external_block.get_machine_state().set_stack(Stack(block_number=new_block_number)) 232 | external_block.get_machine_state().set_memory(Memory(block_number=new_block_number)) 233 | external_block.add_constraint_to_path_condition(condition) 234 | external_block.push_call_stack(continuation_block) 235 | 236 | # # TODO manage visited address 237 | # if not self.visited_address[exec_env.get_exec_env_id()][external_block.get_pc()]: 238 | 239 | 240 | self.basic_blocks.append(external_block) 241 | self.__edges[self.processing_block].append(external_block) 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | self.processing_block = external_block 250 | # print('next storage:') 251 | # print(self.get_processing_block().get_machine_state().get_storage().get_data()) 252 | return self.processing_block 253 | 254 | 255 | 256 | 257 | def rollback_from_dfs_stack(self): 258 | while not self.is_dfs_stack_empty(): 259 | next_block = self.pop_from_dfs_stack() 260 | next_pc = next_block.get_pc() 261 | if self.visited_address[next_block.get_path()][next_pc]: 262 | # print('visited!') 263 | # print(next_block.get_path(),next_pc) 264 | # for edge in self.__edges.values(): 265 | # while next_block in edge: 266 | # edge.remove(next_block) 267 | # print('deleted') 268 | continue 269 | else: 270 | self.set_procesisng_block(next_block) 271 | return next_block 272 | 273 | return False 274 | 275 | 276 | 277 | 278 | def rollback_from_call_stack(self, verifier:VulnerabilityVerifierAfterCall = None) -> BasicBlock: 279 | if self.is_call_stack_empty(): 280 | return False 281 | else: 282 | 283 | # print('rollback from call stack') 284 | 285 | verifier.extract_data_with_callback(self.processing_block.get_path_condition(), 286 | self.processing_block.get_block_state(), 287 | self.processing_block.get_machine_state().get_storage(), 288 | self.processing_block.get_machine_state().get_balance(), 289 | self.processing_block.get_depth(), 290 | ) 291 | return_block = self.pop_from_call_stack() 292 | 293 | self.add_edge(self.processing_block, return_block) 294 | return_block.add_constraint_to_path_condition(self.processing_block.get_path_condition()) 295 | new_block_num = return_block.get_block_number() 296 | # return_block.get_machine_state().set_memory( 297 | # self.processing_block.get_machine_state().get_memory().duplicate(new_block_num)) 298 | # return_block.get_machine_state().set_stack( 299 | # self.processing_block.get_machine_state().get_stack().duplicate(new_block_num)) 300 | # print('storage:',id(self.processing_block.get_machine_state().get_storage())) 301 | # print(self.processing_block.get_machine_state().get_storage().get_data()) 302 | # print('dup:') 303 | # print(self.processing_block.get_machine_state().get_storage().duplicate(new_block_num).get_data()) 304 | return_block.get_machine_state().set_storage( 305 | self.processing_block.get_machine_state().get_storage().duplicate(new_block_num)) 306 | return_block.get_machine_state().set_balance( 307 | deepcopy(self.processing_block.get_machine_state().get_balance()) 308 | ) 309 | return_block.unite_path(self.get_processing_block()) 310 | return_block.block_state |= self.processing_block.block_state 311 | # print('balance prev') 312 | # print(self.processing_block.get_machine_state().get_balance()) 313 | # print('balance next') 314 | # print(return_block.get_machine_state().get_balance()) 315 | 316 | # print(hex(self.get_processing_block().get_pc()),return_block) 317 | # print(self.get_processing_block().get_path()) 318 | self.processing_block = return_block 319 | 320 | return return_block 321 | 322 | 323 | def add_basic_block(self, basic_block: BasicBlock): 324 | self.basic_blocks.append(basic_block) 325 | 326 | def add_visited_block(self, basic_block: BasicBlock): 327 | self.__visited_blocks.append(basic_block) 328 | 329 | def get_basic_blocks(self): 330 | return self.basic_blocks 331 | 332 | def get_visited_blocks(self): 333 | return self.__visited_blocks 334 | 335 | def add_edge(self, origin: BasicBlock, dest: BasicBlock): 336 | dest.depth = origin.depth + 1 337 | self.__edges[origin].append(dest) 338 | 339 | def get_dest_blocks(self, origin: BasicBlock): 340 | return self.__edges[origin] 341 | 342 | def get_CFG_name(self): 343 | return self.__CFG_name 344 | 345 | def extract_mnemonics(self, block: BasicBlock): 346 | # node = block.get_block_number() 347 | node = id(block) 348 | nodes = set() 349 | nodes.add(node) 350 | mnemonics = {node:str(block.get_mnemonic_as_str())} 351 | jumpdests = {node:block.get_jumpdest()} 352 | edges = '' 353 | dests = self.get_dest_blocks(block) 354 | for d in dests: 355 | if d==block: 356 | continue 357 | if len(d.get_mnemonic_as_str())==0: 358 | continue 359 | # dn = d.get_block_number() 360 | dn = id(d) 361 | nodes.add(dn) 362 | edges += 'block' + str(node) + ' -> ' + 'block' + str(dn) + '\n' 363 | 364 | m, e, n, j = self.extract_mnemonics(d) 365 | 366 | mnemonics.update(m) 367 | edges += e 368 | nodes |= n 369 | jumpdests.update(j) 370 | 371 | return (mnemonics,edges,nodes,jumpdests) 372 | 373 | def gen_CFG(self): 374 | root = self.basic_blocks[0] 375 | mnemonics, edges, nodes, jumpdests = self.extract_mnemonics(root) 376 | nodedefine = '' 377 | for n in nodes: 378 | nodedefine += 'block' + str(n) + '[shape=box,label = "' 379 | nodedefine += mnemonics[n] 380 | if jumpdests[n] != -1: 381 | nodedefine += 'jumpdest: {0:04x}\n'.format(jumpdests[n]) 382 | 383 | nodedefine += '"' 384 | if ' CALL\l' == mnemonics[n][-7:] or ' CREATE\l' == mnemonics[n][-9:]: 385 | nodedefine += ',color = red' 386 | elif (' STOP\l' in mnemonics[n] 387 | or ' RETURN\l' in mnemonics[n] 388 | or ' REVERT\l' in mnemonics[n]) and 'block' + str(n) + ' ->' in edges: 389 | nodedefine += ',color = green' 390 | nodedefine += '];\n' 391 | 392 | cfg = 'digraph ' + self.get_CFG_name() + ' {\n' 393 | cfg += nodedefine 394 | cfg += edges 395 | cfg += '}' 396 | 397 | return (self.get_CFG_name(), cfg) 398 | 399 | def show_all(self): 400 | print('----CFG name and number--') 401 | print(self.get_CFG_name(),self.get_cfmanager_id()) 402 | print('----basic blocks-') 403 | print(self.basic_blocks) 404 | print('----visited address-') 405 | print(self.visited_address) 406 | print('----edges----') 407 | print(self.__edges) 408 | 409 | -------------------------------------------------------------------------------- /data_structures.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from collections import deque 3 | from copy import deepcopy, copy 4 | from z3 import And, Or, Not, BitVecRef, BitVecNumRef, BoolRef, BitVec, BitVecVal, Concat, Extract, simplify, Int, Solver, unsat, sat, ZeroExt, ExprRef 5 | 6 | from utils import BitVec256, BitVecVal256, dbgredmsg, zero8bit, checkBitVecRef256 7 | from exceptions import DevelopmentErorr, SettingError, EVMbytecodeError 8 | from constant import RUNNING, TERMINATED, RETURNED, CALLABLE 9 | from collections import defaultdict 10 | from random import random 11 | 12 | WORDBITSIZE = 256 13 | WORDBYTESIZE = WORDBITSIZE // 8 14 | 15 | 16 | class Bytecode(list): 17 | 18 | def __init__(self, string_code: str = None) -> None: 19 | data = [] 20 | if string_code: 21 | # Two characters represent one byte. 22 | prev = '' 23 | for c in string_code: 24 | if prev: 25 | one_byte = prev + c 26 | self.check_valid_char(one_byte) 27 | data.append(one_byte) 28 | prev = '' 29 | else: 30 | prev = c 31 | if prev: 32 | raise EVMbytecodeError('The length of the given code is an odd number.') 33 | super().__init__(data) 34 | 35 | def append(self, x: str | BitVecRef): 36 | if not isinstance(x, BitVecRef): 37 | self.check_valid_char(x) 38 | super().append(x) 39 | 40 | def check_valid_char(self, one_byte: str) -> bool: 41 | if len(one_byte) != 2: 42 | raise EVMbytecodeError('Cannot represent a single byte because the given number of characters is not two.') 43 | 44 | for c in one_byte: 45 | if c not in '0123456789abcdef': 46 | raise EVMbytecodeError('A non-hexadecimal character is given.') 47 | 48 | return True 49 | 50 | 51 | class Stack: 52 | 53 | def __init__(self, block_number=0, stdata=None, num_stack_var=0): 54 | # blockNumber will be VM object's member 55 | self.__blockNumber = block_number 56 | self.__stackdata = deque() if stdata is None else stdata 57 | self.__numStackVar = num_stack_var 58 | 59 | def duplicate(self, new_block_number:int): 60 | return Stack(new_block_number, deepcopy(self.__stackdata), self.__numStackVar) 61 | 62 | def generateStackVar(self) -> BitVecRef: 63 | self.__numStackVar += 1 64 | return BitVec256('stackVar{}-{}'.format(self.__blockNumber, self.__numStackVar)) 65 | 66 | def push(self, w:BitVecRef): 67 | if len(self.__stackdata) < 1024: 68 | self.__stackdata.append(checkBitVecRef256(w)) 69 | # self.__stackdata.append(w) 70 | else: 71 | # TODO stack limit reached 1024 72 | pass 73 | 74 | def pop(self) -> BitVecRef: 75 | if len(self.__stackdata) >= 1: 76 | return checkBitVecRef256(self.__stackdata.pop()) 77 | else: 78 | # generate a symbolic variable 79 | # TODO this may cause stack underflow 80 | return self.generateStackVar() 81 | 82 | def get_stack_size(self) -> int: 83 | return len(self.__stackdata) 84 | 85 | # def swapx(self, x:int): 86 | # if x < 1 or 16 < x: 87 | # raise DevelopmentErorr() 88 | # 89 | # if x + 1 > self.__size(): 90 | # for _ in range(x + 1 - self.__size()): 91 | # self.__stackdata.appendleft(self.generateStackVar()) 92 | # 93 | # a = self.__stackdata[self.__size() - 1] 94 | # self.__stackdata[self.__size() - 1] = self.__stackdata[self.__size() - 1 - x] 95 | # self.__stackdata[self.__size() - 1 - x] = a 96 | 97 | # def dupx(self, x:int): 98 | # if x < 1 or 16 < x: 99 | # raise DevelopmentErorr() 100 | # 101 | # if x > self.__size(): 102 | # for _ in range(x - self.__size()): 103 | # self.__stackdata.appendleft(self.generateStackVar()) 104 | # 105 | # self.__stackdata.append(deepcopy(self.__stackdata[self.__size() - x])) 106 | 107 | def show_data(self): 108 | print('stack_id',id(self),len(self.__stackdata)) 109 | for i in range(len(self.__stackdata))[::-1]: 110 | # print("{}:{}".format(i, self.__stackdata[i])) 111 | d = simplify(self.__stackdata[i]) 112 | print(i,':',format(d.as_long(), '02x') if isinstance(d,BitVecNumRef) else d) 113 | 114 | 115 | class Memory: 116 | # big-endian 117 | def __init__(self, block_number=0, immediate_data=None, num_memory_var=0): 118 | # blockNumber will be VM object's member 119 | self.__blockNumber = block_number 120 | self.__immediate_data = [] if immediate_data is None else immediate_data 121 | self.__numMemoryVar = num_memory_var 122 | 123 | def duplicate(self, new_block_number: int=None): 124 | if new_block_number is None: 125 | new_block_number = self.__blockNumber 126 | return Memory(new_block_number, deepcopy(self.__immediate_data), self.__numMemoryVar) 127 | 128 | def __generateMemoryVar(self): 129 | self.__numMemoryVar += 1 130 | return BitVec256('memoryVar{}-{}'.format(self.__blockNumber, self.__numMemoryVar)) 131 | 132 | def mstore(self, offset: BitVecNumRef, value: BitVecRef): 133 | if not isinstance(offset, BitVecNumRef) and not isinstance(offset, int): 134 | raise DevelopmentErorr('Does not support memory operations indexed by symbol variables.') 135 | 136 | offset = offset.as_long() if isinstance(offset, BitVecNumRef) else offset 137 | checkBitVecRef256(value) 138 | 139 | if offset + WORDBYTESIZE > len(self.__immediate_data): 140 | d = offset + WORDBYTESIZE - len(self.__immediate_data) 141 | self.__immediate_data.extend([zero8bit() for _ in range(d)]) 142 | 143 | # for dict 144 | # 145 | # for i in range(self.__size(), offset + WORDBYTESIZE): 146 | # self.__memdata[str(i)] = zero8bit() 147 | # 148 | for i in range(WORDBYTESIZE): 149 | self.__immediate_data[offset + (WORDBYTESIZE - 1 - i)] = Extract(i * 8 + 7, i * 8, value) 150 | 151 | def mstore8(self, offset: BitVecNumRef, value:BitVecRef): 152 | if isinstance(offset, BitVecNumRef): 153 | offset = checkBitVecRef256(offset).as_long() 154 | elif not isinstance(offset, int): 155 | raise DevelopmentErorr('Does not support memory operations indexed by symbol variables.') 156 | 157 | 158 | #checkBitVecRef256(value) 159 | 160 | if offset >= len(self.__immediate_data): 161 | d = offset - len(self.__immediate_data) + 1 162 | self.__immediate_data.extend([zero8bit() for _ in range(d)]) 163 | self.__immediate_data[offset] = simplify(Extract(7, 0, value)) 164 | 165 | def mload(self, offset: BitVecNumRef): 166 | if isinstance(offset, BitVecNumRef): 167 | offset = offset.as_long() 168 | elif not isinstance(offset, int): 169 | raise DevelopmentErorr('Does not support memory operations indexed by symbol variables.') 170 | 171 | if offset + WORDBYTESIZE > len(self.__immediate_data): 172 | # ~ index out of bounds ~ 173 | # generate a symblolic variable 174 | newmemvar = self.__generateMemoryVar() 175 | d = offset + WORDBYTESIZE - len(self.__immediate_data) 176 | if d < WORDBYTESIZE: 177 | for i in range(d): 178 | self.__immediate_data.append(Extract((d - i - 1) * 8 + 7, (d - i - 1) * 8, newmemvar)) 179 | return simplify(Concat(self.__immediate_data[offset: WORDBYTESIZE+offset])) 180 | else: 181 | self.mstore(BitVecVal256(offset), newmemvar) 182 | return newmemvar 183 | 184 | else: 185 | return simplify( 186 | Concat(self.__immediate_data[offset: WORDBYTESIZE+offset])) 187 | 188 | 189 | def size(self): 190 | return len(self.__immediate_data) 191 | 192 | def get_one_byte(self, i: int): 193 | return self.__immediate_data[i] if i < len(self.__immediate_data) else BitVecVal(0, 8) 194 | 195 | def show_data(self): 196 | print(self.__immediate_data) 197 | 198 | 199 | class MsgData(Memory): 200 | # use exec_env_num instead of block number 201 | def __generateMemoryVar(self): 202 | self.__numMemoryVar += 1 203 | return BitVec256('MsgData{}-{}'.format(self.__blockNumber, self.__numMemoryVar)) 204 | 205 | def set_function_id(self, fid:BitVecRef = None): 206 | if isinstance(fid, str) and len(fid) == 8: 207 | fid = BitVecVal(int(fid, 16), 32) 208 | elif isinstance(fid, int): 209 | fid = BitVecVal(fid, 32) 210 | elif isinstance(fid, BitVecRef) and fid.size() == 32: 211 | pass 212 | elif fid is None: 213 | fid = BitVec('function_id', 32) 214 | else: 215 | raise SettingError('illegal function id given') 216 | 217 | for i in range(4): 218 | fragment = Extract(i*8+7, i*8, fid) 219 | self.mstore8(3-i, fragment) 220 | 221 | def set_arguments(self, num): 222 | offset = self.size() 223 | for i in range(num): 224 | self.mstore(offset+i*32, BitVec256('msg_data_{}'.format(i))) 225 | 226 | def set_concrete_arguments(self, value: BitVecNumRef): 227 | offset = self.size() 228 | r = value.size()//8 229 | for i in range(r): 230 | self.mstore8(offset + i, Extract((r-i-1)*8+7, (r-i-1)*8, value)) 231 | 232 | 233 | class Storage: 234 | def __init__(self, block_number=0, storage_data=None, num_storage_var=0): 235 | self.__block_number = block_number 236 | self.__storage_data = {} if storage_data is None else storage_data 237 | self.__num_storage_var = num_storage_var 238 | 239 | def __generate_storage_var(self, key): 240 | # self.__num_storage_var += 1 241 | # for create-based 242 | # return BitVecVal256(0xff) 243 | return BitVec256('default_storage_'+str(key)) 244 | 245 | def sload(self, key: BitVecRef) -> BitVecRef: 246 | key = str(checkBitVecRef256(key)) 247 | if key in self.__storage_data.keys(): 248 | return self.__storage_data[key] 249 | else: 250 | newvar = self.__generate_storage_var(key) 251 | self.__storage_data[key] = newvar 252 | return newvar 253 | 254 | def sstore(self, key: BitVecRef, value: BitVecRef): 255 | checkBitVecRef256(key) 256 | checkBitVecRef256(value) 257 | # # concrete value 258 | # if type(key) == BitVecNumRef: 259 | # key = key.as_long() 260 | # # symbolic variable 261 | # else: 262 | # key = str(key) 263 | self.__storage_data[str(key)] = value 264 | 265 | def duplicate(self, new_block_number): 266 | return Storage(new_block_number, deepcopy(self.__storage_data), self.__num_storage_var) 267 | 268 | def show_data(self): 269 | for k,v in self.__storage_data.items(): 270 | print('key={}, value={}'.format(k, v)) 271 | 272 | def get_data(self): 273 | return self.__storage_data 274 | 275 | 276 | 277 | class Returndata(Memory): 278 | pass 279 | 280 | 281 | 282 | # TODO call data 283 | class Calldata: 284 | pass 285 | 286 | 287 | class Account: 288 | def __init__(self, bytecode: Bytecode, 289 | account_num: int, 290 | balance: BitVecRef = None, 291 | nonce=0): 292 | self.bytecode = bytecode 293 | self.account_num = account_num 294 | self.__balance = BitVec256('account_balance_{}'.format(self.account_num)) if balance is None else balance 295 | self.nonce = nonce 296 | self.I = None 297 | 298 | def get_account_num(self) -> int: 299 | return self.account_num 300 | 301 | def get_balance(self) -> BitVecRef: 302 | return self.__balance 303 | 304 | def get_bytecode(self) -> Bytecode: 305 | return self.bytecode 306 | 307 | def codesize(self) -> int: 308 | return len(self.bytecode) 309 | 310 | 311 | class WorldState: 312 | def __init__(self): 313 | self.execution_environments = [] 314 | self.block_hashes = {} 315 | self.accounts = {} 316 | 317 | def add_account(self, bytecode: Bytecode, addr: BitVecRef = None) -> BitVecRef: 318 | # generate account number and account address 319 | new_num = len(self.accounts) 320 | new_addr = ZeroExt(96, BitVec('address{}'.format(new_num), 160)) if addr is None else addr 321 | # generate Account object 322 | account = Account(bytecode, new_num) 323 | # アドレスとAccountインスタンスの対応をaccountsに保存 324 | self.accounts[str(new_addr)] = account 325 | return new_addr 326 | 327 | def get_account(self, addr: BitVecRef) -> Account: 328 | return self.accounts[str(addr)] 329 | 330 | def get_account_num(self, addr:BitVecRef) -> int: 331 | return self.accounts[addr].get_account_num() 332 | 333 | # def generate_execution_environment(self): 334 | # pass 335 | ''' 336 | IH= { 337 | 'coinbase': BitVec('coinbase_{}'.format(eenum), 256), 338 | 'timestamp': BitVec('timestamp_{}'.format(eenum), 256), 339 | 'number': BitVec('blocknumber_{}'.format(eenum), 256), 340 | 'difficulty': BitVec('difficulty_{}'.format(eenum), 256), 341 | 'gaslimit': BitVec('_{}'.format(eenum), 256) 342 | } 343 | ''' 344 | 345 | 346 | class BlockHeader: 347 | def __init__(self, 348 | Hi:BitVecRef = None, 349 | Hp:BitVecRef = None, 350 | Hc:BitVecRef = None, 351 | Hs:BitVecRef = None, 352 | Hd:BitVecRef = None, 353 | Hl:BitVecRef = None): 354 | # self.__block_id = 'tmp{}'.format(int(random()*10**6)%10**6) if Hi is None else str(Hi) 355 | self.__block_number = BitVec256('blocknumber_current_block') if Hi is None else Hi 356 | self.__parent_hash = BitVec256('parent_hash_'+str(self.get_block_number())) if Hp is None else Hp 357 | self.__beneficiary = BitVec256('beneficiary_'+str(self.get_block_number())) if Hc is None else Hc 358 | self.__timestamp = BitVec256('timestamp_'+str(self.get_block_number())) if Hs is None else Hs 359 | self.__difficulty = BitVec256('difficulty_'+str(self.get_block_number())) if Hd is None else Hd 360 | self.__gaslimit = BitVec256('gaslimit_'+str(self.get_block_number())) if Hl is None else Hl 361 | 362 | def get_parent_hash(self) -> BitVecRef: 363 | return self.__parent_hash 364 | 365 | def get_block_number(self) -> BitVecRef: 366 | return self.__block_number 367 | 368 | def get_beneficiary(self) -> BitVecRef: 369 | return self.__beneficiary 370 | 371 | def get_timestamp(self) -> BitVecRef: 372 | return self.__timestamp 373 | 374 | def get_difficulty(self) -> BitVecRef: 375 | return self.__difficulty 376 | 377 | def get_gaslimit(self) -> BitVecRef: 378 | return self.__gaslimit 379 | 380 | 381 | 382 | class ExecutionEnvironment: 383 | def __init__(self, 384 | exec_env_id=None, 385 | Ib: str='', 386 | Ia:BitVecRef = None, 387 | Io:BitVecRef = None, 388 | Ip:BitVecRef = None, 389 | Id:BitVecRef = None, 390 | Is:BitVecRef = None, 391 | Iv:BitVecRef = None, 392 | IH:dict = None, 393 | Ie:int = None, 394 | Iw:bool = True): 395 | self.__exec_env_id = id(self) if exec_env_id is None else exec_env_id 396 | self.this_address = BitVec256('Ia_{}'.format(self.__exec_env_id)) if Ia is None else Ia 397 | self.tx_originator = BitVec256('Io_{}'.format(self.__exec_env_id)) if Io is None else Io 398 | self.gasprice = BitVec256('Ip_{}'.format(self.__exec_env_id)) if Ip is None else Ip 399 | self.msg_data = MsgData(self.__exec_env_id) if Id is None else Id 400 | self.msg_sender = BitVec256('Is_{}'.format(self.__exec_env_id)) if Is is None else Is 401 | self.msg_value = BitVec256('Iv') if Iv is None else Iv 402 | # TODO 403 | self.this_code = BitVec256('Ib_{}'.format(self.__exec_env_id)) if Ib is None else Ib 404 | self.block_header = BlockHeader() if IH is None else IH # TODO: blockheader IH 405 | self.depth_of_call = 0 if Ie is None else Ie 406 | self.permission_to_change_state = Iw 407 | #self.accounts = [] 408 | # about Iw: https://ethereum.stackexchange.com/questions/49210/execution-environment-variables-iw-and-ie 409 | # def add_account(self,code: str): 410 | # self.accounts.append(Account()) 411 | 412 | def get_exec_env_id(self) -> int: 413 | return self.__exec_env_id 414 | def set_exec_env_id(self, id:int): 415 | self.__exec_env_id = id 416 | 417 | 418 | def get_block_header(self) -> BlockHeader: 419 | return self.block_header 420 | 421 | def get_code(self) -> list: 422 | return self.this_code 423 | 424 | def get_msg_data(self) -> MsgData: 425 | return self.msg_data 426 | 427 | def get_this_address(self) -> BitVecRef: 428 | return self.this_address 429 | 430 | def set_this_address(self, address): 431 | self.this_address = address 432 | 433 | def show_all(self): 434 | print(self.this_address,self.tx_originator,self.gasprice, 435 | self.msg_data,self.msg_sender,self.msg_value,self.this_code,self.block_header, 436 | self.depth_of_call,self.permission_to_change_state) 437 | 438 | 439 | class MachineState: 440 | def __init__(self, 441 | pc=0, 442 | memory=None, 443 | stack=None, 444 | storage=None, 445 | gas = None, 446 | retOffset = -1, 447 | retLength = -1, 448 | balance = None, 449 | returndata=None 450 | # calldata=Calldata() 451 | ): 452 | self.pc = pc 453 | self.memory = Memory() if memory is None else memory 454 | self.stack = Stack() if stack is None else stack 455 | self.storage = Storage() if storage is None else storage 456 | self.gas = BitVec256('gas_available{}'.format(id(self))) if gas is None else gas 457 | self.retOffset = retOffset 458 | self.retLength = retLength 459 | self.balance = BitVec256('default_balance') if balance is None else balance 460 | self.returndata = Returndata() if returndata is None else returndata 461 | 462 | # self.returndata = returndata 463 | # self.calldata = calldata 464 | 465 | def duplicate(self, new_block_number): 466 | return MachineState( 467 | self.pc, 468 | self.memory.duplicate(new_block_number), 469 | self.stack.duplicate(new_block_number), 470 | self.storage.duplicate(new_block_number), 471 | retOffset=self.retOffset, 472 | retLength=self.retLength, 473 | balance=self.balance, 474 | returndata=self.returndata.duplicate(new_block_number) 475 | ) 476 | 477 | def get_pc(self): 478 | return self.pc 479 | 480 | def set_pc(self, pc: int): 481 | self.pc = pc 482 | 483 | def get_memory(self) -> Memory: 484 | return self.memory 485 | 486 | def get_stack(self) -> Stack: 487 | return self.stack 488 | 489 | def set_memory(self, memory:Memory): 490 | self.memory = memory 491 | 492 | def set_stack(self, stack:Stack): 493 | self.stack = stack 494 | 495 | def set_storage(self, storage:Storage): 496 | self.storage = storage 497 | 498 | def get_storage(self): 499 | return self.storage 500 | 501 | def get_gas(self) -> BitVecRef: 502 | return self.gas 503 | 504 | def set_gas(self, _gas): 505 | self.gas = _gas 506 | 507 | def set_retOffset(self, retOffset): 508 | self.retOffset = retOffset 509 | 510 | def get_retOffset(self) -> int: 511 | return self.retOffset 512 | 513 | def set_retLength(self, retLength): 514 | self.retLength = retLength 515 | 516 | def get_retLength(self) -> int: 517 | return self.retLength 518 | 519 | def get_return_data(self) -> Returndata: 520 | return self.returndata 521 | 522 | def set_return_data(self, returndate: Returndata): 523 | self.returndata = returndate 524 | 525 | def get_balance(self): 526 | return self.balance 527 | 528 | def set_balance(self, balance): 529 | self.balance = simplify(balance) 530 | 531 | def show_all(self): 532 | print('----stack----') 533 | self.stack.show_data() 534 | print('----memory---') 535 | self.memory.show_data() 536 | print('----storage--') 537 | self.storage.show_data() 538 | print('----pc-------') 539 | print(self.get_pc()) 540 | print('----gas------') 541 | print(self.gas) 542 | 543 | # for type annotation in methods of BasicBlock 544 | class BasicBlock: 545 | pass 546 | 547 | class BasicBlock: 548 | def __init__(self, 549 | #account_number: int, # どのコントラクトのブロックか 550 | block_number: int, 551 | machine_state: MachineState = None, 552 | exec_env: ExecutionEnvironment = None, 553 | mnemonics: list = None, 554 | path_condition = True, 555 | cond_exp_for_JUMP = False, 556 | # dfs_stack=None, 557 | call_stack=None, 558 | jumpdest=-1, 559 | #path_location: list = None, 560 | block_state: set = None, 561 | depth: int = 0, 562 | path = None, 563 | ): 564 | 565 | #self.account_number = account_number 566 | self.block_number = block_number 567 | self.__machine_state = MachineState() if machine_state is None else machine_state 568 | # TODO 569 | #self.__exec_env = ExecutionEnvironment(account_number) if exec_env is None else exec_env 570 | self.__exec_env = ExecutionEnvironment() if exec_env is None else exec_env 571 | self.mnemonics = [] if mnemonics is None else mnemonics 572 | self.path_condition = path_condition 573 | self.cond_exp_for_JUMP = cond_exp_for_JUMP 574 | # self.dfs_stack = [] if dfs_stack is None else dfs_stack 575 | self.call_stack = [] if call_stack is None else call_stack 576 | self.__jumpdest = jumpdest 577 | self.jumpflag = False 578 | #self.path_location = None if path_location is None else path_location 579 | self.block_state = {RUNNING} if block_state is None else block_state 580 | self.depth = depth 581 | self.path = set() if path is None else path 582 | 583 | 584 | def get_block_number(self): 585 | return self.block_number 586 | 587 | def add_mnemonic(self, numaddedbyte: int, mnemonic: str): 588 | self.mnemonics.append((self.__machine_state.get_pc(), mnemonic)) 589 | 590 | def get_mnemonic_as_str(self): 591 | retstr = '' 592 | for i,op in self.mnemonics: 593 | retstr += '0x{0:04x} '.format(i) + op + '\\l' 594 | # if op == 'STOP' or op == 'INVALID' or op == 'RETURN' or op == 'REVERT': 595 | # retstr += 'path condition: ' + str( 596 | # simplify(self.path_condition) if isinstance(self.path_condition, ExprRef) 597 | # else self.path_condition 598 | # ) + '\\n' 599 | # if op == 'STOP' or op == 'INVALID' or op == 'RETURN' or op == 'REVERT': 600 | # 601 | # self.get_machine_state().get_memory().mload() 602 | # retstr += 'function id: ' + str() + '\\n' 603 | 604 | return retstr 605 | 606 | def duplicate(self, 607 | #account_number: int, 608 | new_block_number: int, 609 | machine_state:MachineState = None, 610 | exec_env:ExecutionEnvironment = None, 611 | # dfs_stack = None, 612 | depth = None) -> BasicBlock: 613 | return BasicBlock(block_number=new_block_number, 614 | machine_state=self.__machine_state.duplicate(new_block_number) if machine_state is None else machine_state, 615 | # TODO 不変なのでコピーする必要はないはず 616 | exec_env=self.__exec_env if exec_env is None else exec_env, 617 | # deepcopy(self.mnemonics), 618 | path_condition=deepcopy(self.path_condition), 619 | cond_exp_for_JUMP=deepcopy(self.cond_exp_for_JUMP), 620 | # TODO check 1 621 | # dfs_stack=self.dfs_stack if dfs_stack is None else dfs_stack, 622 | call_stack=deepcopy(self.call_stack), 623 | #path_location=self.path_condition, 624 | block_state=copy(self.block_state), 625 | depth=self.depth if depth is None else depth, 626 | path=copy(self.path) 627 | ) 628 | 629 | def inherit(self, 630 | new_block_number: int, 631 | jflag: bool = False, 632 | depth=None) -> BasicBlock: 633 | block = self.duplicate(new_block_number=new_block_number,depth=self.depth+1) 634 | # When a new block is reached by jump 635 | 636 | if jflag: 637 | block.set_pc(self.get_jumpdest()) 638 | block.jumpflag = True 639 | else: 640 | block.set_pc(block.get_pc()+1) 641 | #block.clean_mnemonics() 642 | return block 643 | 644 | def clean_mnemonics(self): 645 | self.mnemonics = [] 646 | self.__jumpdest = -1 647 | 648 | # def push_dfs_stack(self, block:BasicBlock): 649 | # self.dfs_stack.append(block) 650 | # 651 | # def pop_dfs_stack(self) -> BasicBlock: 652 | # b = self.dfs_stack.pop() 653 | # return b 654 | 655 | # def get_dfs_stack_size(self) -> int: 656 | # return len(self.dfs_stack) 657 | 658 | # def show_dfs_stack(self): 659 | # print(self.dfs_stack) 660 | 661 | def push_call_stack(self, block:BasicBlock): 662 | self.call_stack.append(block) 663 | 664 | def pop_call_stack(self) -> BasicBlock: 665 | return self.call_stack.pop() 666 | 667 | def get_call_stack_size(self) -> int: 668 | return len(self.call_stack) 669 | 670 | def show_call_stack(self): 671 | print(self.call_stack) 672 | 673 | def set_pc(self, pc:int): 674 | self.__machine_state.set_pc(pc) 675 | 676 | def get_pc(self): 677 | return self.__machine_state.get_pc() 678 | 679 | def get_gas(self): 680 | return self.__machine_state.get_gas() 681 | 682 | def get_machine_state(self) -> MachineState: 683 | return self.__machine_state 684 | 685 | def get_exec_env(self) -> ExecutionEnvironment: 686 | return self.__exec_env 687 | 688 | def set_jumpdest(self, dest): 689 | if isinstance(dest,str): 690 | self.__jumpdest = int(dest, 16) 691 | elif isinstance(dest,BitVecNumRef): 692 | self.__jumpdest = dest.as_long() 693 | elif isinstance(dest,int): 694 | self.__jumpdest = dest 695 | else: 696 | raise DevelopmentErorr 697 | 698 | def get_jumpdest(self): 699 | return self.__jumpdest 700 | 701 | # VMが持つデータを取り出す(pc以外は参照として) 702 | # def extract_data(self): 703 | # return self.machine_state.get_memory(),\ 704 | # self.machine_state.get_stack(),\ 705 | # self.__storage,\ 706 | # self.machine_state.get_pc() 707 | 708 | def add_constraint_to_path_condition(self, constraint: BoolRef): 709 | self.path_condition = simplify(And(self.path_condition, constraint)) 710 | 711 | def get_path_condition(self) -> BoolRef: 712 | return self.path_condition 713 | 714 | def set_path_condition(self, condition: BoolRef): 715 | self.path_condition = condition 716 | 717 | def set_cond_exp_for_JUMP(self, constraint): 718 | self.cond_exp_for_JUMP = constraint 719 | 720 | def get_cond_exp_for_JUMP(self): 721 | return self.cond_exp_for_JUMP 722 | 723 | def get_exec_env(self) -> ExecutionEnvironment: 724 | return self.__exec_env 725 | 726 | def get_machine_state(self) -> MachineState: 727 | return self.__machine_state 728 | 729 | def add_block_state(self, state: str): 730 | self.block_state.add(state) 731 | 732 | def get_block_state(self) -> list: 733 | return self.block_state 734 | 735 | def get_depth(self): 736 | return self.depth 737 | 738 | def get_path(self) -> str: 739 | return str(self.path) 740 | 741 | def add_path(self, exec_env_id, depth_of_call, pc: int, jumped: bool): 742 | self.path.add((str(exec_env_id), str(depth_of_call), pc, jumped)) 743 | # print('path added,',(str(exec_env_id), str(depth_of_call), pc, jumped)) 744 | # print(self.get_path()) 745 | 746 | def unite_path(self, block:BasicBlock): 747 | self.path |= block.path 748 | 749 | 750 | 751 | 752 | -------------------------------------------------------------------------------- /vm.py: -------------------------------------------------------------------------------- 1 | from data_structures import Bytecode, Stack, Memory, MsgData, Storage, Returndata, ExecutionEnvironment, WorldState, MachineState, BasicBlock, Account 2 | from control_flow_manager import ControlFlowManager 3 | from z3 import BV2Int, Int2BV, And, Or, Xor, Not, If, BitVecRef, BitVecNumRef, BitVecVal, BitVec, Concat, Extract, simplify, Solver, sat, unsat, UDiv, URem, Concat, AstRef, LShR, ZeroExt 4 | from utils import * 5 | from utils import convert_to_bitvec8 6 | from collections import defaultdict 7 | from fee_schedule import c 8 | from exceptions import DevelopmentErorr 9 | from vulnerability_verifier import VulnerabilityVerifier 10 | from constant import RUNNING, TERMINATED, RETURNED, CALLABLE 11 | import time_measurement 12 | 13 | from time import time 14 | from copy import deepcopy, copy 15 | from sha3 import keccak_256 # this needs pysha3 16 | import subprocess 17 | import sys 18 | from multiprocessing import Pool, Process, cpu_count 19 | 20 | from os import environ 21 | dbg = (lambda *s: sys.stderr.write("\033[92m{}\033[0m".format(' '.join(str(x) for x in s)+'\n'))) if 'TERM_PROGRAM' in environ else lambda *s: 0 22 | 23 | 24 | WORDBITSIZE = 256 25 | WORDBYTESIZE = WORDBITSIZE // 8 26 | 27 | 28 | 29 | 30 | 31 | def v(arg): 32 | reset_time() 33 | x, y, contract = arg 34 | x = BitVecVal(x, 32) 35 | y = BitVecVal(y, 32) 36 | vm = VM(WorldState()) 37 | contract = vm.add_primary_contract(contract) 38 | secondary_contract = Bytecode('600080600481803362fffffff100') 39 | vm.add_secondary_contract(secondary_contract) 40 | vm.cfmanager = ControlFlowManager() 41 | # self.σ.accounts = copy(preserved_accounts) 42 | vm.vulnerability_verifier.init_states() 43 | block_y = vm.init_state(addr=contract, exec_env_id=y, 44 | msg_sender=ZeroExt(96, BitVec('address1', 160))) 45 | vm.get_exec_env().get_msg_data().set_function_id(y) 46 | vm.get_exec_env().get_msg_data().set_arguments(1000) 47 | vm.cfmanager.basic_blocks.pop() 48 | block_x = vm.init_state(addr=contract, exec_env_id=x, depth_of_call=2, 49 | msg_sender=ZeroExt(96, BitVec('address1', 160)) 50 | ) 51 | block_x.block_number = 1 52 | vm.cfmanager.basic_blocks.append(block_y) 53 | vm.get_exec_env().get_msg_data().set_function_id(x) 54 | # self.get_exec_env().get_msg_data().set_concrete_arguments(BitVecVal(0xff, 256)) 55 | vm.get_exec_env().get_msg_data().set_arguments(1000) 56 | vm.get_processing_block().call_stack.append(block_y) 57 | 58 | vm.vulnerability_verifier.set_executing_callee() 59 | # self.get_machine_state().set_balance(BitVecVal(0xff, 256)) 60 | 61 | vm.vulnerability_verifier.set_first_call(False) 62 | vm.vulnerability_verifier.set_second_call(False) 63 | vm.vulnerability_verifier.set_third_call(False) 64 | vm.run() 65 | 66 | name, cfg = vm.cfmanager.gen_CFG() 67 | name = 'CFG-{}-{}-independent'.format(x, y) 68 | with open(name + '.txt', 'w') as f: 69 | f.write(str(vm.vulnerability_verifier.independently_executed_state) + '\n' + str( 70 | vm.vulnerability_verifier.cross_called_executed_state)) 71 | with open(name + '.dot', 'w') as f: 72 | f.write(cfg) 73 | try: 74 | subprocess.call(['dot', '-T', 'png', name + '.dot', '-o', name + '.png']) 75 | except: 76 | pass 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | vm.vulnerability_verifier.set_x(x) 85 | vm.vulnerability_verifier.set_y(y) 86 | vm.vulnerability_verifier.set_first_call(True) 87 | vm.vulnerability_verifier.set_second_call(True) 88 | vm.vulnerability_verifier.set_third_call(True) 89 | vm.vulnerability_verifier.set_executing_cross_function() 90 | vm.cfmanager = ControlFlowManager() 91 | vm.init_state(addr=contract, exec_env_id=x, 92 | msg_sender=ZeroExt(96, BitVec('address1', 160)) 93 | ) 94 | vm.get_exec_env().get_msg_data().set_function_id(x) 95 | vm.get_exec_env().get_msg_data().set_arguments(1000) 96 | vm.run() 97 | 98 | name, cfg = vm.cfmanager.gen_CFG() 99 | name = 'CFG-{}-{}'.format(x, y) 100 | with open(name + '-state.txt', 'w') as f: 101 | f.write(str(vm.vulnerability_verifier.independently_executed_state) + '\n' + str( 102 | vm.vulnerability_verifier.cross_called_executed_state)) 103 | with open(name + '.dot', 'w') as f: 104 | f.write(cfg) 105 | try: 106 | subprocess.call(['dot', '-T', 'png', name + '.dot', '-o', name + '.png']) 107 | except: 108 | pass 109 | e,s = get_time() 110 | 111 | return (hex(x.as_long()), hex(y.as_long()), vm.vulnerability_verifier.diff_states(), s) 112 | 113 | 114 | 115 | def v2(arg): 116 | 117 | reset_time() 118 | x, y, contract = arg 119 | x = BitVecVal(x,32) 120 | y = BitVecVal(y,32) 121 | vm = VM(WorldState()) 122 | initial_addr = BitVecVal(0x1111111111111111111111111111111111111111, 256) 123 | addr = BitVecVal(0x2222222222222222222222222222222222222222, 256) 124 | contract = vm.add_primary_contract(contract,addr) 125 | 126 | 127 | secondary_contract = Bytecode('600080600481803362fffffff100') 128 | vm.add_secondary_contract(secondary_contract,initial_addr) 129 | 130 | preserved_accounts = copy(vm.σ.accounts) 131 | 132 | 133 | 134 | 135 | vm.cfmanager = ControlFlowManager() 136 | # self.σ.accounts = copy(preserved_accounts) 137 | vm.vulnerability_verifier.init_states() 138 | msg_sender = BitVecVal(0x1111111111111111111111111111111111111111, 256) 139 | block_y = vm.init_state(addr=contract, exec_env_id=y, 140 | msg_sender=msg_sender) 141 | vm.get_exec_env().get_msg_data().set_function_id(y) 142 | # vm.get_exec_env().get_msg_data().set_arguments(1000) 143 | vm.get_exec_env().get_msg_data().set_concrete_arguments(BitVecVal(0xff, 256)) 144 | vm.cfmanager.basic_blocks.pop() 145 | block_x = vm.init_state(addr=contract, exec_env_id=x, depth_of_call=2, 146 | msg_sender=msg_sender 147 | ) 148 | block_x.block_number = 1 149 | vm.cfmanager.basic_blocks.append(block_y) 150 | vm.get_exec_env().get_msg_data().set_function_id(x) 151 | vm.get_exec_env().get_msg_data().set_concrete_arguments(BitVecVal(0xff, 256)) 152 | #vm.get_exec_env().get_msg_data().set_arguments(1000) 153 | vm.get_processing_block().call_stack.append(block_y) 154 | 155 | vm.vulnerability_verifier.set_executing_callee() 156 | # self.get_machine_state().set_balance(BitVecVal(0xff, 256)) 157 | 158 | vm.vulnerability_verifier.set_first_call(False) 159 | vm.vulnerability_verifier.set_second_call(False) 160 | vm.vulnerability_verifier.set_third_call(False) 161 | vm.run() 162 | 163 | name, cfg = vm.cfmanager.gen_CFG() 164 | name = 'CFG-{}-{}-independent'.format(x, y) 165 | with open(name + '.txt', 'w') as f: 166 | f.write(str(vm.vulnerability_verifier.independently_executed_state) + '\n' + str( 167 | vm.vulnerability_verifier.cross_called_executed_state)) 168 | with open(name + '.dot', 'w') as f: 169 | f.write(cfg) 170 | try: 171 | subprocess.call(['dot', '-T', 'png', name + '.dot', '-o', name + '.png']) 172 | except: 173 | pass 174 | 175 | 176 | 177 | 178 | 179 | vm.σ.accounts = preserved_accounts 180 | 181 | vm.vulnerability_verifier.set_x(x) 182 | vm.vulnerability_verifier.set_y(y) 183 | vm.vulnerability_verifier.set_first_call(True) 184 | vm.vulnerability_verifier.set_second_call(True) 185 | vm.vulnerability_verifier.set_third_call(True) 186 | vm.vulnerability_verifier.set_executing_cross_function() 187 | vm.cfmanager = ControlFlowManager() 188 | vm.init_state(addr=contract, exec_env_id=x, 189 | msg_sender=msg_sender 190 | ) 191 | vm.get_exec_env().get_msg_data().set_function_id(x) 192 | # vm.get_exec_env().get_msg_data().set_arguments(1000) 193 | vm.get_exec_env().get_msg_data().set_concrete_arguments(BitVecVal(0xff, 256)) 194 | vm.run() 195 | 196 | name, cfg = vm.cfmanager.gen_CFG() 197 | name = 'CFG-{}-{}'.format(x, y) 198 | with open(name + '-state.txt', 'w') as f: 199 | f.write(str(vm.vulnerability_verifier.independently_executed_state) + '\n' + str( 200 | vm.vulnerability_verifier.cross_called_executed_state)) 201 | with open(name + '.dot', 'w') as f: 202 | f.write(cfg) 203 | try: 204 | subprocess.call(['dot', '-T', 'png', name + '.dot', '-o', name + '.png']) 205 | except: 206 | pass 207 | e,s = get_time() 208 | 209 | return (hex(x.as_long()), hex(y.as_long()), vm.vulnerability_verifier.diff_states(), s) 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | class VM(): 222 | def __init__(self, world_state: WorldState): 223 | 224 | self.σ = world_state 225 | self.primary_contracts = [] 226 | self.primary_contract_index = 0 227 | self.secondary_contract = None 228 | self.tertiary_contracts = [] 229 | self.vulnerability_verifier = VulnerabilityVerifier() 230 | # self.exec_env_num = 0 231 | 232 | def add_primary_contract(self, bytecode: Bytecode, addr: BitVecNumRef = None) -> BitVecRef: 233 | contract_account_addr = self.σ.add_account(bytecode, addr) 234 | self.primary_contracts.append(contract_account_addr) 235 | return contract_account_addr 236 | 237 | def add_secondary_contract(self, bytecode: Bytecode, addr: BitVecNumRef = None) -> None: 238 | self.secondary_contract = self.σ.add_account(bytecode, addr) 239 | 240 | def add_tertiary_contract(self, bytecodes: Bytecode): 241 | for b in bytecodes: 242 | self.tertiary_contracts.append(self.σ.add_account(b)) 243 | 244 | def init_state(self, addr: BitVecRef, 245 | value: BitVecRef = None, 246 | storage = None, 247 | balance = None, 248 | exec_env_id = None, 249 | msg_sender=None, 250 | depth_of_call=0): 251 | 252 | 253 | account = self.σ.accounts[str(addr)] 254 | 255 | µ = MachineState(storage=storage,balance=balance) 256 | I = ExecutionEnvironment(exec_env_id=exec_env_id, 257 | Ia=addr, 258 | Ib=account.bytecode, 259 | Iv=value, 260 | Is=msg_sender, 261 | Ie=depth_of_call) 262 | 263 | block = BasicBlock(block_number=0, machine_state=µ, exec_env=I) 264 | self.cfmanager.set_procesisng_block(block) 265 | return block 266 | 267 | def verify_full_state(self): 268 | sum_solvingtime = 0 269 | while self.primary_contract_index < 1: 270 | self.cfmanager = ControlFlowManager() 271 | self.vulnerability_verifier.set_extracting_fid() 272 | 273 | contract = self.primary_contracts[self.primary_contract_index] 274 | 275 | 276 | #msg_sender = BitVecVal(0x1111111111111111111111111111111111111111, 256) 277 | self.init_state(contract, 278 | # msg_sender=msg_sender, 279 | ) 280 | # self.get_machine_state().set_balance(BitVecVal(0xff, 256)) 281 | 282 | self.get_exec_env().get_msg_data().set_function_id() 283 | #self.get_exec_env().get_msg_data().set_concrete_arguments(BitVecVal(0xff, 256)) 284 | self.get_exec_env().get_msg_data().set_arguments(1000) 285 | self.run() 286 | print('callable fids=', self.vulnerability_verifier.get_callable_function_ids()) 287 | print('fids=', self.vulnerability_verifier.get_function_ids()) 288 | print('num process:',cpu_count() - 2) 289 | def multi(num_process, function, X, Y, contract): 290 | p = Pool(num_process) # 最大プロセス数 291 | return p.map(function, [(x, y, contract) for x in X for y in Y]) 292 | 293 | # result = multi(cpu_count() - 2, 294 | # v, 295 | # [id.as_long() for id in self.vulnerability_verifier.callable_function_ids], 296 | # [id.as_long() for id in self.vulnerability_verifier.function_ids], 297 | # str(self.get_exec_env().this_code),) 298 | def single(function,X,Y,contract): 299 | return map(function,[(x, y, contract) for x in X for y in Y]) 300 | # result = single( 301 | result = multi(cpu_count() - 2, 302 | v, 303 | [id.as_long() for id in self.vulnerability_verifier.callable_function_ids], 304 | [id.as_long() for id in self.vulnerability_verifier.function_ids], 305 | self.get_code(),) 306 | 307 | for r in result: 308 | print('--------------') 309 | print(r) 310 | print('--------------') 311 | sum_solvingtime +=r[3] 312 | 313 | self.primary_contract_index += 1 314 | return sum_solvingtime 315 | 316 | 317 | def verify_full_state_create(self): 318 | sum_solvingtime = 0 319 | while self.primary_contract_index < 1: 320 | self.cfmanager = ControlFlowManager() 321 | self.vulnerability_verifier.set_extracting_fid() 322 | 323 | contract = self.primary_contracts[self.primary_contract_index] 324 | 325 | 326 | msg_sender = BitVecVal(0x1111111111111111111111111111111111111111, 256) 327 | self.init_state(contract, 328 | msg_sender=msg_sender, 329 | ) 330 | # self.get_machine_state().set_balance(BitVecVal(0xff, 256)) 331 | 332 | self.get_exec_env().get_msg_data().set_function_id() 333 | self.get_exec_env().get_msg_data().set_concrete_arguments(BitVecVal(0xff, 256)) 334 | #self.get_exec_env().get_msg_data().set_arguments(1000) 335 | self.run() 336 | print('callable fids=', self.vulnerability_verifier.get_callable_function_ids()) 337 | print('fids=', self.vulnerability_verifier.get_function_ids()) 338 | 339 | def multi(num_process, function, X, Y, contract): 340 | p = Pool(num_process) # 最大プロセス数 341 | return p.map(function, [(x, y, contract) for x in X for y in Y]) 342 | 343 | # result = multi(cpu_count() - 2, 344 | # v, 345 | # [id.as_long() for id in self.vulnerability_verifier.callable_function_ids], 346 | # [id.as_long() for id in self.vulnerability_verifier.function_ids], 347 | # str(self.get_exec_env().this_code),) 348 | def single(function,X,Y,contract): 349 | return map(function,[(x, y, contract) for x in X for y in Y]) 350 | print('number of proccess:', cpu_count() - 2) 351 | result = single( 352 | # result = multi(cpu_count() - 2, 353 | v2, 354 | [id.as_long() for id in self.vulnerability_verifier.callable_function_ids], 355 | [id.as_long() for id in self.vulnerability_verifier.function_ids], 356 | self.get_code(),) 357 | 358 | for r in result: 359 | print('--------------') 360 | print(r) 361 | print('--------------') 362 | sum_solvingtime +=r[3] 363 | 364 | self.primary_contract_index += 1 365 | return sum_solvingtime 366 | 367 | 368 | 369 | 370 | 371 | def verify_create_based_re_entrancy(self, verifier = None): 372 | 373 | if verifier is not None: 374 | self.vulnerability_verifier = verifier 375 | #while self.primary_contract_index < len(self.primary_contracts): 376 | while self.primary_contract_index < 1: 377 | 378 | preserved_accounts = copy(self.σ.accounts) 379 | dbgredmsg(self.primary_contract_index) 380 | self.vulnerability_verifier.set_extracting_fid() 381 | self.cfmanager = ControlFlowManager() 382 | contract = self.primary_contracts[self.primary_contract_index] 383 | # msg_sender = BitVecVal(0x11, 8) 384 | # for i in range(19): 385 | # msg_sender = Concat(msg_sender,BitVecVal(0x11, 8)) 386 | # for i in range(12): 387 | # msg_sender = Concat(BitVecVal(0, 8), msg_sender) 388 | 389 | msg_sender = BitVecVal(0x1111111111111111111111111111111111111111,256) 390 | self.init_state(contract, 391 | msg_sender=msg_sender, 392 | ) 393 | #self.get_machine_state().set_balance(BitVecVal(0xff, 256)) 394 | 395 | self.get_exec_env().get_msg_data().set_function_id() 396 | self.get_exec_env().get_msg_data().set_concrete_arguments(BitVecVal(0xff, 256)) 397 | self.run() 398 | 399 | name, cfg = self.cfmanager.gen_CFG() 400 | name = 'CFG-no-specified-fid' 401 | with open(name + '.dot', 'w') as f: 402 | f.write(cfg) 403 | try: 404 | subprocess.call(['dot', '-T', 'png', name + '.dot', '-o', name + '.png']) 405 | except: 406 | pass 407 | print('callable fids=', self.vulnerability_verifier.get_callable_function_ids()) 408 | print('fids=', self.vulnerability_verifier.get_function_ids()) 409 | 410 | 411 | for x in self.vulnerability_verifier.callable_function_ids: 412 | # sys.stderr.write('independent execution: x\n') 413 | self.σ.accounts = copy(preserved_accounts) 414 | self.vulnerability_verifier.set_executing_caller() 415 | self.cfmanager = ControlFlowManager() 416 | self.init_state(addr=contract, exec_env_id=x.as_long(), msg_sender=msg_sender) 417 | self.get_exec_env().get_msg_data().set_function_id(x) 418 | self.get_exec_env().get_msg_data().set_concrete_arguments(BitVecVal(0xff, 256)) 419 | #self.get_machine_state().set_balance(BitVecVal(0xff, 256)) 420 | self.run() 421 | 422 | caller_state = deepcopy(self.vulnerability_verifier.get_caller_state()) 423 | balance = caller_state.get_data()['BALANCE'] 424 | condition = deepcopy(caller_state.get_data()['PATH_CONDITION']) 425 | 426 | #for y in self.vulnerability_verifier.callable_function_ids: 427 | for y in self.vulnerability_verifier.function_ids: 428 | 429 | #self.σ.accounts = copy(preserved_accounts) 430 | self.vulnerability_verifier.init_states() 431 | # dbgredmsg('independent execution: y') 432 | 433 | self.vulnerability_verifier.set_executing_callee() 434 | self.cfmanager = ControlFlowManager() 435 | self.init_state(addr=contract, exec_env_id=y.as_long(), 436 | storage=caller_state, balance=balance, msg_sender=msg_sender) 437 | self.get_processing_block().set_path_condition(condition) 438 | self.get_exec_env().get_msg_data().set_function_id(y) 439 | self.vulnerability_verifier.set_first_call(True) 440 | self.get_exec_env().get_msg_data().set_concrete_arguments(BitVecVal(0xff, 256)) 441 | self.run() 442 | name, cfg = self.cfmanager.gen_CFG() 443 | name = 'CFG-{}-{}-independent'.format(x, y) 444 | with open(name + '.txt', 'w') as f: 445 | f.write(str(self.vulnerability_verifier.independently_executed_state) + '\n' + str( 446 | self.vulnerability_verifier.cross_called_executed_state)) 447 | with open(name + '.dot', 'w') as f: 448 | f.write(cfg) 449 | try: 450 | subprocess.call(['dot', '-T', 'png', name + '.dot', '-o', name + '.png']) 451 | except: 452 | pass 453 | # dbgredmsg('vulnerability_verifier.independently_executed_state') 454 | 455 | # ここからcross-function 456 | 457 | dbgredmsg('start cross-function execution') 458 | self.vulnerability_verifier.set_x(x) 459 | self.vulnerability_verifier.set_y(y) 460 | self.vulnerability_verifier.set_first_call(True) 461 | self.vulnerability_verifier.set_second_call(True) 462 | self.vulnerability_verifier.set_third_call(True) 463 | self.vulnerability_verifier.set_executing_cross_function() 464 | self.cfmanager = ControlFlowManager() 465 | self.init_state(addr=contract, exec_env_id=x.as_long(), msg_sender=ZeroExt(96, BitVec('address1', 160))) 466 | self.get_exec_env().get_msg_data().set_function_id(x) 467 | self.get_exec_env().get_msg_data().set_concrete_arguments(BitVecVal(0xff, 256)) 468 | #self.get_machine_state().set_balance(BitVecVal(0xff, 256)) 469 | self.σ.accounts = copy(preserved_accounts) 470 | self.run() 471 | # dbgredmsg('vulnerability_verifier.cross_called_executed_state') 472 | dbgredmsg('x=', x, 'y=', y) 473 | dbgredmsg('vulnerable:', self.vulnerability_verifier.diff_states()) 474 | 475 | name, cfg = self.cfmanager.gen_CFG() 476 | name = 'CFG-{}-{}'.format(x, y) 477 | with open(name + '-state.txt', 'w') as f: 478 | f.write(str(self.vulnerability_verifier.independently_executed_state) + '\n' + str( 479 | self.vulnerability_verifier.cross_called_executed_state)) 480 | with open(name + '.dot', 'w') as f: 481 | f.write(cfg) 482 | try: 483 | subprocess.call(['dot', '-T', 'png', name + '.dot', '-o', name + '.png']) 484 | except: 485 | pass 486 | 487 | 488 | self.primary_contract_index += 1 489 | 490 | 491 | 492 | def verify_cross_function_re_entrancy(self, verifier = None): 493 | 494 | if verifier is not None: 495 | self.vulnerability_verifier = verifier 496 | while self.primary_contract_index < len(self.primary_contracts): 497 | self.vulnerability_verifier.set_extracting_fid() 498 | 499 | # sys.stderr.write('extracting a function id\n') 500 | self.cfmanager = ControlFlowManager() 501 | contract = self.primary_contracts[self.primary_contract_index] 502 | self.init_state(contract) 503 | self.get_exec_env().get_msg_data().set_function_id() 504 | self.get_exec_env().get_msg_data().set_arguments(1000) 505 | self.run() 506 | 507 | name, cfg = self.cfmanager.gen_CFG() 508 | name = 'CFG-no-specified-fid' 509 | with open(name + '.dot', 'w') as f: 510 | f.write(cfg) 511 | try: 512 | subprocess.call(['dot', '-T', 'png', name + '.dot', '-o', name + '.png']) 513 | except: 514 | pass 515 | print('callable fids=',self.vulnerability_verifier.get_callable_function_ids()) 516 | print('fids=',self.vulnerability_verifier.get_function_ids()) 517 | 518 | 519 | if len(self.vulnerability_verifier.callable_function_ids) == 0: 520 | dbgredmsg('vulnerable:',False) 521 | 522 | 523 | 524 | states=defaultdict(dict) 525 | 526 | 527 | for x in self.vulnerability_verifier.callable_function_ids: 528 | # sys.stderr.write('independent execution: x\n') 529 | self.vulnerability_verifier.set_executing_caller() 530 | self.cfmanager = ControlFlowManager() 531 | self.init_state(addr=contract,exec_env_id=x.as_long(),msg_sender=ZeroExt(96,BitVec('address1', 160))) 532 | self.get_exec_env().get_msg_data().set_function_id(x) 533 | self.get_exec_env().get_msg_data().set_arguments(1024) 534 | self.run() 535 | 536 | caller_state = deepcopy(self.vulnerability_verifier.get_caller_state()) 537 | balance = caller_state.get_data()['BALANCE'] 538 | condition = deepcopy(caller_state.get_data()['PATH_CONDITION']) 539 | 540 | # for y in self.vulnerability_verifier.callable_function_ids: 541 | for y in self.vulnerability_verifier.function_ids: 542 | self.vulnerability_verifier.init_states() 543 | # dbgredmsg('independent execution: y') 544 | 545 | self.vulnerability_verifier.set_executing_callee() 546 | self.cfmanager = ControlFlowManager() 547 | self.init_state(addr=contract,exec_env_id=y.as_long(), 548 | storage=caller_state,balance=balance,msg_sender=ZeroExt(96,BitVec('address1', 160))) 549 | self.get_processing_block().set_path_condition(condition) 550 | self.get_exec_env().get_msg_data().set_function_id(y) 551 | # self.vulnerability_verifier.set_first_call(True) 552 | self.get_exec_env().get_msg_data().set_arguments(1024) 553 | self.run() 554 | name, cfg = self.cfmanager.gen_CFG() 555 | name = 'CFG-{}-{}-independent'.format(x, y) 556 | with open(name + '-independent-state.txt', 'w') as f: 557 | f.write(str(self.vulnerability_verifier.independently_executed_state) + '\n' + str( 558 | self.vulnerability_verifier.cross_called_executed_state)) 559 | with open(name + '.dot', 'w') as f: 560 | f.write(cfg) 561 | try: 562 | subprocess.call(['dot', '-T', 'png', name + '.dot', '-o', name + '.png']) 563 | except: 564 | pass 565 | # dbgredmsg('vulnerability_verifier.independently_executed_state') 566 | 567 | 568 | 569 | #ここからcross-function 570 | 571 | dbgredmsg('start cross-function execution') 572 | self.vulnerability_verifier.set_x(x) 573 | self.vulnerability_verifier.set_y(y) 574 | self.vulnerability_verifier.set_first_call(True) 575 | self.vulnerability_verifier.set_second_call(True) 576 | self.vulnerability_verifier.set_third_call(True) 577 | self.vulnerability_verifier.set_executing_cross_function() 578 | self.cfmanager = ControlFlowManager() 579 | self.init_state(addr=contract, exec_env_id=x.as_long(), msg_sender=ZeroExt(96, BitVec('address1', 160))) 580 | self.get_exec_env().get_msg_data().set_function_id(x) 581 | self.get_exec_env().get_msg_data().set_arguments(1024) 582 | self.run() 583 | # dbgredmsg('vulnerability_verifier.cross_called_executed_state') 584 | dbgredmsg('x=', x, 'y=', y) 585 | dbgredmsg('vulnerable:', self.vulnerability_verifier.diff_states()) 586 | 587 | 588 | name, cfg = self.cfmanager.gen_CFG() 589 | name = 'CFG-{}-{}'.format(x, y) 590 | with open(name+'-state.txt','w') as f: 591 | f.write(str(self.vulnerability_verifier.independently_executed_state)+'\n'+str(self.vulnerability_verifier.cross_called_executed_state)) 592 | with open(name + '.dot','w') as f: 593 | f.write(cfg) 594 | try: 595 | subprocess.call(['dot', '-T', 'png', name + '.dot', '-o', name + '.png']) 596 | except: 597 | pass 598 | 599 | 600 | 601 | 602 | 603 | self.primary_contract_index += 1 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | def run_all(self): 620 | 621 | while self.primary_contract_index < len(self.primary_contracts): 622 | sys.stderr.write('run a contract¥n') 623 | self.cfmanager = ControlFlowManager() 624 | a = self.primary_contracts[self.primary_contract_index] 625 | self.init_state(a) 626 | self.get_exec_env().get_msg_data().set_function_id() 627 | self.get_exec_env().get_msg_data().set_arguments(1024) 628 | self.run() 629 | name, cfg = self.cfmanager.gen_CFG() 630 | 631 | 632 | print(name) 633 | with open(name + '.dot', 'w') as f: 634 | f.write(cfg) 635 | self.primary_contract_index += 1 636 | #self.show_vm_state() 637 | try: 638 | subprocess.call(['dot', '-T', 'png', name + '.dot', '-o', name + '.png']) 639 | except: 640 | pass 641 | 642 | 643 | def show_vm_state(self): 644 | # I 645 | print('----I--------') 646 | self.get_exec_env().show_all() 647 | # µ 648 | print('----µ--------') 649 | self.get_machine_state().show_all() 650 | # CFG 651 | print('----cfmanager------') 652 | self.cfmanager.show_all() 653 | 654 | # getter 655 | def get_processing_block(self) -> BasicBlock: 656 | return self.cfmanager.processing_block 657 | 658 | def get_exec_env(self) -> ExecutionEnvironment: 659 | return self.get_processing_block().get_exec_env() 660 | 661 | def get_machine_state(self) -> MachineState: 662 | return self.get_processing_block().get_machine_state() 663 | 664 | def get_address(self) -> BitVecRef: 665 | return self.get_exec_env().this_address 666 | 667 | def get_account_num(self) -> int: 668 | return self.σ.get_account(self.get_address()).get_account_num() 669 | 670 | def get_balance(self) -> BitVecRef: 671 | return self.get_machine_state().get_balance() 672 | 673 | def set_balance(self,balance): 674 | self.get_machine_state().set_balance(balance) 675 | 676 | def get_origin(self) -> BitVecRef: 677 | return self.get_exec_env().tx_originator 678 | 679 | def get_caller(self) -> BitVecRef: 680 | return self.get_exec_env().msg_sender 681 | 682 | def get_value(self) -> BitVecRef: 683 | return self.get_exec_env().msg_value 684 | 685 | def get_data(self) -> MsgData: 686 | return self.get_exec_env().msg_data 687 | 688 | def get_code(self) -> list: 689 | return self.get_exec_env().this_code 690 | 691 | def get_gasprice(self) -> BitVecRef: 692 | return self.get_exec_env().gasprice 693 | 694 | 695 | # for manipulating components 696 | def get_pc(self): 697 | return self.get_machine_state().pc 698 | 699 | def set_pc(self, pc): 700 | self.cfmanager.processing_block.machine_state.pc = pc 701 | 702 | def push_to_stack(self, value): 703 | self.get_machine_state().get_stack().push(value) 704 | 705 | def pop_from_stack(self): 706 | return self.get_machine_state().stack.pop() 707 | 708 | def dup_on_stack(self, x): 709 | self.cfmanager.processing_block.machine_state.stack.op_dupx(x) 710 | 711 | def jumped(self): 712 | return self.cfmanager.processing_block.jumpflag 713 | 714 | def reach_jumpdest(self): 715 | self.cfmanager.processing_block.jumpflag = False 716 | # def swapx_on_stack(self,x): 717 | # self.CfgManager.processing_block.machine_state.get_stack().swapx(x) 718 | # 719 | # def dupx_on_stack(self, x): 720 | # self.CfgManager.processing_block.machine_state.get_stack().dupx(x) 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | # for utility 734 | def increment_pc(self): 735 | self.get_machine_state().set_pc(self.get_machine_state().get_pc() + 1) 736 | 737 | def get_byte_from_bytecode(self): 738 | return ''.join(self.get_code()[self.get_pc()]) 739 | 740 | 741 | # 現状ではrun中にjumpdestかを確認 742 | def check_jumpdest(self, dest:BitVecNumRef): 743 | d = dest.as_long() 744 | return True if ''.join(self.get_code()[d]) == '5b' else False 745 | 746 | def convert_to_expression(self, condition): 747 | if isinstance(condition, BitVecNumRef): 748 | if condition.as_long() == 0: 749 | return False 750 | else: 751 | return True 752 | else: 753 | return condition 754 | 755 | def branch(self, continuable, jumpable, condition): 756 | return self.cfmanager.inherit_from_processing_block(continuable, jumpable, condition) 757 | 758 | def external_call(self, 759 | exec_env:ExecutionEnvironment 760 | ): 761 | self.cfmanager.external_call(exec_env) 762 | 763 | def set_jumpdest(self, dest): 764 | self.cfmanager.processing_block.set_jumpdest(dest) 765 | 766 | def get_jumpdest(self): 767 | return self.cfmanager.processing_block.get_jumpdest() 768 | 769 | def add_immediatevalue(self, iv): 770 | self.cfmanager.add_immediatevalue(iv) 771 | 772 | def run(self): 773 | while True: 774 | 775 | # if self.cfmanager.visited_address[ 776 | # self.get_exec_env().get_exec_env_id()][self.get_pc()]: 777 | # 778 | # 779 | # block = self.cfmanager.search_existing_block( 780 | # self.get_exec_env().get_exec_env_id(), self.get_pc()) 781 | # dbgredmsg(block,' was visited') 782 | # self.cfmanager.integrate_path_condition( 783 | # constraint=self.get_processing_block().get_path_condition(), 784 | # integrated_block=block) 785 | # if self.op_stop() == False: 786 | # break 787 | # # # TODO: 終了系の命令でrollbackを実行 788 | # # if self.cfmanager.is_dfs_stack_empty(): 789 | # # return 790 | # # else: 791 | # # self.cfmanager.rollback_from_dfs_stack() 792 | # # continue 793 | # else: 794 | # self.cfmanager.visited_address[self.get_exec_env().get_exec_env_id()][self.get_pc()] = True 795 | 796 | self.cfmanager.visited_address[self.get_processing_block().get_path()][self.get_pc()] = True 797 | 798 | 799 | 800 | 801 | 802 | if self.get_machine_state().get_pc() >= len(self.get_code()): 803 | raise DevelopmentErorr 804 | 805 | hex_opcode = self.get_byte_from_bytecode() 806 | mnemonic = self.hex_to_mnemonic(hex_opcode) 807 | #print('pc=', '0x{:04x}'.format(self.get_pc()), mnemonic,self.get_exec_env().get_exec_env_id()) 808 | 809 | 810 | if self.jumped(): 811 | if mnemonic != 'JUMPDEST': 812 | # TODO: exception 813 | print('jumped but not jumpdest') 814 | else: 815 | self.reach_jumpdest() 816 | 817 | elif mnemonic == 'JUMPDEST': 818 | # self.cfmanager.switch_existing_block( 819 | # self.get_exec_env().get_exec_env_id(), 820 | # self.get_pc() 821 | # ) 822 | # self.cfmanager.visited_address[self.get_processing_block().get_path()][self.get_pc()] = True 823 | pass 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | self.cfmanager.add_mnemonic(mnemonic) 833 | func, num_inputs, num_output, *funcarg = self.mnemonic_to_func(mnemonic) 834 | s = [] 835 | self.cfmanager.processing_block.get_machine_state().set_gas( simplify( 836 | self.cfmanager.processing_block.get_gas() - 837 | c( 838 | self.σ, 839 | self.cfmanager.processing_block.get_machine_state(), 840 | self.cfmanager.processing_block.get_exec_env() 841 | ))) 842 | # self.µ.gas = self.µ.gas - BitVecVal256(c(self.σ, self.µ, self.I)) 843 | for i in range(num_inputs): 844 | s.append(deepcopy(self.pop_from_stack())) 845 | ret = func(s, *funcarg) 846 | if ret is False: 847 | break 848 | 849 | def terminate(self): 850 | # print('terminate') 851 | # print('pc=',self.get_pc()) 852 | # print(self.get_processing_block().get_path()) 853 | # print('storage id:',id(self.get_machine_state().get_storage())) 854 | # print('depth:',self.get_processing_block().get_depth()) 855 | self.get_processing_block().add_constraint_to_path_condition(self.get_balance() >= 0) 856 | self.vulnerability_verifier.extract_data( 857 | self.get_processing_block().get_path_condition(), 858 | self.get_processing_block().get_block_state(), 859 | self.get_machine_state().get_storage(), 860 | self.get_balance(), 861 | ) 862 | return self.cfmanager.rollback_from_dfs_stack() 863 | 864 | def hex_to_mnemonic(self, hex: str): 865 | d = defaultdict(lambda: 'INVALID') 866 | d['00'] = 'STOP' 867 | d['01'] = 'ADD' 868 | d['02'] = 'MUL' 869 | d['03'] = 'SUB' 870 | d['04'] = 'DIV' 871 | d['05'] = 'SDIV' 872 | d['06'] = 'MOD' 873 | d['07'] = 'SMOD' 874 | d['08'] = 'ADDMOD' 875 | d['09'] = 'MULMOD' 876 | d['0a'] = 'EXP' 877 | d['0b'] = 'SIGNEXTEND' 878 | 879 | d['10'] = 'LT' 880 | d['11'] = 'GT' 881 | d['12'] = 'SLT' 882 | d['13'] = 'SGT' 883 | d['14'] = 'EQ' 884 | d['15'] = 'ISZERO' 885 | d['16'] = 'AND' 886 | d['17'] = 'OR' 887 | d['18'] = 'XOR' 888 | d['19'] = 'NOT' 889 | d['1a'] = 'BYTE' 890 | d['1b'] = 'SHL' 891 | d['1c'] = 'SHR' 892 | d['1d'] = 'SAR' 893 | 894 | d['20'] = 'SHA3' 895 | 896 | d['30'] = 'ADDRESS' 897 | d['31'] = 'BALANCE' 898 | d['32'] = 'ORIGIN' 899 | d['33'] = 'CALLER' 900 | d['34'] = 'CALLVALUE' 901 | d['35'] = 'CALLDATALOAD' 902 | d['36'] = 'CALLDATASIZE' 903 | d['37'] = 'CODEDATACOPY' 904 | d['38'] = 'CODESIZE' 905 | d['39'] = 'CODECOPY' 906 | d['3a'] = 'GASPRICE' 907 | d['3b'] = 'EXTCODESIZE' 908 | d['3c'] = 'EXTCODECOPY' 909 | d['3d'] = 'RETURNDATASIZE' 910 | d['3e'] = 'RETURNDATACOPY' 911 | 912 | d['40'] = 'BLOCKHASH' 913 | d['41'] = 'COINBASE' 914 | d['42'] = 'TIMESTAMP' 915 | d['43'] = 'NUMBER' 916 | d['44'] = 'DIFFICULTY' 917 | d['45'] = 'GASLIMIT' 918 | 919 | d['50'] = 'POP' 920 | d['51'] = 'MLOAD' 921 | d['52'] = 'MSTORE' 922 | d['53'] = 'MSTORE8' 923 | d['54'] = 'SLOAD' 924 | d['55'] = 'SSTORE' 925 | d['56'] = 'JUMP' 926 | d['57'] = 'JUMPI' 927 | d['58'] = 'PC' 928 | d['59'] = 'MSIZE' 929 | d['5a'] = 'GAS' 930 | d['5b'] = 'JUMPDEST' 931 | 932 | d['60'] = 'PUSH1' 933 | d['61'] = 'PUSH2' 934 | d['62'] = 'PUSH3' 935 | d['63'] = 'PUSH4' 936 | d['64'] = 'PUSH5' 937 | d['65'] = 'PUSH6' 938 | d['66'] = 'PUSH7' 939 | d['67'] = 'PUSH8' 940 | d['68'] = 'PUSH9' 941 | d['69'] = 'PUSH10' 942 | d['6a'] = 'PUSH11' 943 | d['6b'] = 'PUSH12' 944 | d['6c'] = 'PUSH13' 945 | d['6d'] = 'PUSH14' 946 | d['6e'] = 'PUSH15' 947 | d['6f'] = 'PUSH16' 948 | d['70'] = 'PUSH17' 949 | d['71'] = 'PUSH18' 950 | d['72'] = 'PUSH19' 951 | d['73'] = 'PUSH20' 952 | d['74'] = 'PUSH21' 953 | d['75'] = 'PUSH22' 954 | d['76'] = 'PUSH23' 955 | d['77'] = 'PUSH24' 956 | d['78'] = 'PUSH25' 957 | d['79'] = 'PUSH26' 958 | d['7a'] = 'PUSH27' 959 | d['7b'] = 'PUSH28' 960 | d['7c'] = 'PUSH29' 961 | d['7d'] = 'PUSH30' 962 | d['7e'] = 'PUSH31' 963 | d['7f'] = 'PUSH32' 964 | 965 | d['80'] = 'DUP1' 966 | d['81'] = 'DUP2' 967 | d['82'] = 'DUP3' 968 | d['83'] = 'DUP4' 969 | d['84'] = 'DUP5' 970 | d['85'] = 'DUP6' 971 | d['86'] = 'DUP7' 972 | d['87'] = 'DUP8' 973 | d['88'] = 'DUP9' 974 | d['89'] = 'DUP10' 975 | d['8a'] = 'DUP11' 976 | d['8b'] = 'DUP12' 977 | d['8c'] = 'DUP13' 978 | d['8d'] = 'DUP14' 979 | d['8e'] = 'DUP15' 980 | d['8f'] = 'DUP16' 981 | 982 | d['90'] = 'SWAP1' 983 | d['91'] = 'SWAP2' 984 | d['92'] = 'SWAP3' 985 | d['93'] = 'SWAP4' 986 | d['94'] = 'SWAP5' 987 | d['95'] = 'SWAP6' 988 | d['96'] = 'SWAP7' 989 | d['97'] = 'SWAP8' 990 | d['98'] = 'SWAP9' 991 | d['99'] = 'SWAP10' 992 | d['9a'] = 'SWAP11' 993 | d['9b'] = 'SWAP12' 994 | d['9c'] = 'SWAP13' 995 | d['9d'] = 'SWAP14' 996 | d['9e'] = 'SWAP15' 997 | d['9f'] = 'SWAP16' 998 | 999 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | d['f0'] = 'CREATE' 1006 | d['f1'] = 'CALL' 1007 | d['f2'] = 'CALLCODE' 1008 | d['f3'] = 'RETURN' 1009 | d['f4'] = 'DELEGATECALL' 1010 | d['fa'] = 'STATICCALL' 1011 | d['fd'] = 'REVERT' 1012 | 1013 | 1014 | 1015 | 1016 | 1017 | 1018 | d['fe'] = 'INVALID' 1019 | d['ff'] = 'SELFDESTRUCT' 1020 | return d[hex] 1021 | 1022 | def mnemonic_to_func(self, mnemonic: str): 1023 | 1024 | d = { 1025 | # 0s 1026 | 'STOP': (self.op_stop, 0, 0), 1027 | 'ADD': (self.op_add, 2, 1), 1028 | 'MUL': (self.op_mul, 2, 1), 1029 | 'SUB': (self.op_sub, 2, 1), 1030 | 'DIV': (self.op_div, 2, 1), 1031 | 'SDIV': (self.op_sdiv, 2, 1), 1032 | 'MOD': (self.op_mod, 2, 1), 1033 | 'SMOD': (self.op_smod, 2, 1), 1034 | 'ADDMOD': (self.op_addmod, 3, 1), 1035 | 'MULMOD': (self.op_mulmod, 3, 1), 1036 | 'EXP': (self.op_exp, 2, 1), 1037 | 'SIGNEXTEND': (self.op_signextend, 2, 1), 1038 | 1039 | # 10s 1040 | 'LT': (self.op_lt, 2, 1), 1041 | 'GT': (self.op_gt, 2, 1), 1042 | 'SLT': (self.op_slt, 2, 1), 1043 | 'SGT': (self.op_sgt, 2, 1), 1044 | 'EQ': (self.op_eq, 2, 1), 1045 | 'ISZERO': (self.op_iszero, 1, 1), 1046 | 'AND': (self.op_and, 2, 1), 1047 | 'OR': (self.op_or, 2, 1), 1048 | 'XOR': (self.op_xor, 2, 1), 1049 | 'NOT': (self.op_not, 1, 1), 1050 | 'BYTE': (self.op_byte, 2, 1), 1051 | 'SHL': (self.op_shl, 2, 1), 1052 | 'SHR': (self.op_shr, 2, 1), 1053 | 'SAR': (self.op_sar, 2, 1), 1054 | 1055 | # 20s 1056 | 'SHA3': (self.op_sha3, 2, 1), 1057 | 1058 | # 30s 1059 | 'ADDRESS': (self.op_address, 0, 1), 1060 | 'BALANCE': (self.op_balance, 1, 1), 1061 | 'ORIGIN' : (self.op_origin, 0, 1), 1062 | 'CALLER' : (self.op_caller, 0, 1), 1063 | 'CALLVALUE' : (self.op_callvalue, 0, 1), 1064 | 'CALLDATALOAD' : (self.op_calldataload, 1, 1), 1065 | 'CALLDATASIZE' : (self.op_calldatasize, 0, 1), 1066 | 'CALLDATACOPY' : (self.op_calldatacopy, 3, 0), 1067 | 'CODESIZE' : (self.op_codesize, 0, 1), 1068 | 'CODECOPY' : (self.op_codecopy, 3, 0), 1069 | 'GASPRICE' : (self.op_gasprice, 0, 1), 1070 | 'EXTCODESIZE': (self.op_extcodesize, 1, 1), 1071 | 'EXTCODECOPY': (self.op_extcodecopy, 3, 0), 1072 | 'RETURNDATASIZE': (self.op_returndatasize, 0, 1), 1073 | 'RETURNDATACOPY': (self.op_returndatacopy, 3, 0), 1074 | 1075 | # 40s 1076 | 'BLOCKHASH': (self.op_blockhash, 1, 1), 1077 | 'COINBASE': (self.op_coinbase, 0, 1), 1078 | 'TIMESTAMP': (self.op_timestamp, 0, 1), 1079 | 'NUMBER': (self.op_number, 0, 1), 1080 | 'DIFFICULTY': (self.op_difficulty, 0, 1), 1081 | 'GASLIMIT': (self.op_gaslimit, 0, 1), 1082 | 1083 | # 50s 1084 | 'POP': (self.op_pop, 1, 0), 1085 | 'MLOAD': (self.op_mload, 1, 1), 1086 | 'MSTORE': (self.op_mstore, 2, 0), 1087 | 'MSTORE8': (self.op_mstore8, 2, 0), 1088 | 'SLOAD': (self.op_sload, 1, 1), 1089 | 'SSTORE': (self.op_sstore, 2, 0), 1090 | 'JUMP': (self.op_jump, 1, 0), 1091 | 'JUMPI': (self.op_jumpi, 2, 0), 1092 | 'PC': (self.op_pc, 0, 1), 1093 | 'MSIZE': (self.op_msize, 0, 1), 1094 | 'GAS': (self.op_gas, 0, 1), 1095 | 'JUMPDEST': (self.op_jumpdest, 0, 0), 1096 | 1097 | 1098 | # 60s & 70s: Push Operations 1099 | 'PUSH1': (self.op_pushx, 0, 1, 1), 1100 | 'PUSH2': (self.op_pushx, 0, 1, 2), 1101 | 'PUSH3': (self.op_pushx, 0, 1, 3), 1102 | 'PUSH4': (self.op_pushx, 0, 1, 4), 1103 | 'PUSH5': (self.op_pushx, 0, 1, 5), 1104 | 'PUSH6': (self.op_pushx, 0, 1, 6), 1105 | 'PUSH7': (self.op_pushx, 0, 1, 7), 1106 | 'PUSH8': (self.op_pushx, 0, 1, 8), 1107 | 'PUSH9': (self.op_pushx, 0, 1, 9), 1108 | 'PUSH10': (self.op_pushx, 0, 1, 10), 1109 | 'PUSH11': (self.op_pushx, 0, 1, 11), 1110 | 'PUSH12': (self.op_pushx, 0, 1, 12), 1111 | 'PUSH13': (self.op_pushx, 0, 1, 13), 1112 | 'PUSH14': (self.op_pushx, 0, 1, 14), 1113 | 'PUSH15': (self.op_pushx, 0, 1, 15), 1114 | 'PUSH16': (self.op_pushx, 0, 1, 16), 1115 | 'PUSH17': (self.op_pushx, 0, 1, 17), 1116 | 'PUSH18': (self.op_pushx, 0, 1, 18), 1117 | 'PUSH19': (self.op_pushx, 0, 1, 19), 1118 | 'PUSH20': (self.op_pushx, 0, 1, 20), 1119 | 'PUSH21': (self.op_pushx, 0, 1, 21), 1120 | 'PUSH22': (self.op_pushx, 0, 1, 22), 1121 | 'PUSH23': (self.op_pushx, 0, 1, 23), 1122 | 'PUSH24': (self.op_pushx, 0, 1, 24), 1123 | 'PUSH25': (self.op_pushx, 0, 1, 25), 1124 | 'PUSH26': (self.op_pushx, 0, 1, 26), 1125 | 'PUSH27': (self.op_pushx, 0, 1, 27), 1126 | 'PUSH28': (self.op_pushx, 0, 1, 28), 1127 | 'PUSH29': (self.op_pushx, 0, 1, 29), 1128 | 'PUSH30': (self.op_pushx, 0, 1, 30), 1129 | 'PUSH31': (self.op_pushx, 0, 1, 31), 1130 | 'PUSH32': (self.op_pushx, 0, 1, 32), 1131 | 1132 | # 80s: Duplication Operations 1133 | 'DUP1': (self.op_dupx, 1, 2), 1134 | 'DUP2': (self.op_dupx, 2, 3), 1135 | 'DUP3': (self.op_dupx, 3, 4), 1136 | 'DUP4': (self.op_dupx, 4, 5), 1137 | 'DUP5': (self.op_dupx, 5, 6), 1138 | 'DUP6': (self.op_dupx, 6, 7), 1139 | 'DUP7': (self.op_dupx, 7, 8), 1140 | 'DUP8': (self.op_dupx, 8, 9), 1141 | 'DUP9': (self.op_dupx, 9, 10), 1142 | 'DUP10': (self.op_dupx, 10, 11), 1143 | 'DUP11': (self.op_dupx, 11, 12), 1144 | 'DUP12': (self.op_dupx, 12, 13), 1145 | 'DUP13': (self.op_dupx, 13, 14), 1146 | 'DUP14': (self.op_dupx, 14, 15), 1147 | 'DUP15': (self.op_dupx, 15, 16), 1148 | 'DUP16': (self.op_dupx, 16, 17), 1149 | 1150 | # 90s: Exchange Operations 1151 | 'SWAP1': (self.op_swapx, 2, 2), 1152 | 'SWAP2': (self.op_swapx, 3, 3), 1153 | 'SWAP3': (self.op_swapx, 4, 4), 1154 | 'SWAP4': (self.op_swapx, 5, 5), 1155 | 'SWAP5': (self.op_swapx, 6, 6), 1156 | 'SWAP6': (self.op_swapx, 7, 7), 1157 | 'SWAP7': (self.op_swapx, 8, 8), 1158 | 'SWAP8': (self.op_swapx, 9, 9), 1159 | 'SWAP9': (self.op_swapx, 10, 10), 1160 | 'SWAP10': (self.op_swapx, 11, 11), 1161 | 'SWAP11': (self.op_swapx, 12, 12), 1162 | 'SWAP12': (self.op_swapx, 13, 13), 1163 | 'SWAP13': (self.op_swapx, 14, 14), 1164 | 'SWAP14': (self.op_swapx, 15, 15), 1165 | 'SWAP15': (self.op_swapx, 16, 16), 1166 | 'SWAP16': (self.op_swapx, 17, 17), 1167 | 1168 | # a0s: Logging Operations 1169 | # f0s: System operations 1170 | 'CREATE': (self.op_create, 3, 1), 1171 | 'CALL': (self.op_call, 7, 1), 1172 | 'CALLCODE': (self.op_callcode, 7, 1), 1173 | 'RETURN': (self.op_return, 2, 0), 1174 | 'DELEGATECALL': (self.op_delegatecall, 6, 1), 1175 | 1176 | 1177 | 1178 | 1179 | 1180 | 'REVERT': (self.op_revert, 2, 0), 1181 | 1182 | 1183 | 1184 | 1185 | 1186 | 1187 | 'INVALID': (self.op_invalid, 0, 0), # TODO implement exceptional halting 1188 | 'SELFDESTRUCT': (self.op_selfdestruct, 1, 0) 1189 | 1190 | } 1191 | return d[mnemonic] 1192 | 1193 | # 0s: Stop and Arithmetic Operations 1194 | 1195 | def op_stop(self, s): 1196 | 1197 | if self.cfmanager.rollback_from_call_stack(self.vulnerability_verifier): 1198 | # when the external call was caused by CREATE 1199 | prevpc = self.get_processing_block().get_pc() - 1 1200 | if ''.join(self.get_processing_block().get_exec_env().get_code()[prevpc]) == 'f0': 1201 | self.push_to_stack(BitVecZero256()) 1202 | elif ''.join(self.get_processing_block().get_exec_env().get_code()[prevpc]) == 'f1': 1203 | self.push_to_stack( 1204 | # BitVec256('call_succeeds_{}_{}'.format(self.get_exec_env().get_exec_env_id(), prevpc * 2)) 1205 | # BitVec256('call_succeeds_{}_{}'.format(self.get_exec_env().depth_of_call, prevpc * 2)) 1206 | # BitVec256('call_succeeds_{}'.format( self.get_pc() * 2)) 1207 | # BitVec256('call_succeeds') 1208 | BitVecOne256() 1209 | ) 1210 | 1211 | 1212 | else: 1213 | return self.terminate() 1214 | 1215 | def op_add(self, s): 1216 | self.push_to_stack(simplify(s[0] + s[1])) 1217 | self.increment_pc() 1218 | 1219 | def op_mul(self, s): 1220 | self.push_to_stack(simplify(s[0] * s[1])) 1221 | self.increment_pc() 1222 | 1223 | def op_sub(self, s): 1224 | self.push_to_stack(simplify(s[0] - s[1])) 1225 | self.increment_pc() 1226 | 1227 | def op_div(self, s): 1228 | if type(s[1]) == BitVecNumRef and s[1].as_long() == 0: 1229 | self.push_to_stack(BitVecVal256(0)) 1230 | else: 1231 | self.push_to_stack(simplify(s[0] / s[1])) 1232 | self.increment_pc() 1233 | 1234 | def op_sdiv(self, s): 1235 | if isinstance(s[1], BitVecNumRef) and s[1].as_singed_long() == 0: 1236 | self.push_to_stack(BitVecVal256(0)) 1237 | elif isinstance(s[0], BitVecNumRef) and isinstance(s[1], BitVecNumRef): 1238 | if s[0].as_signed_long() == -2**255 and s[1].as_signed_long() == -1: 1239 | self.push_to_stack(BitVecVal256(-2**255)) 1240 | else: 1241 | self.push_to_stack(BitVecVal256(s[0].as_signed_long() // s[1].as_singed_long())) 1242 | else: 1243 | # sgn(μs[0] ÷ μs[1])⌊|μs[0] ÷ μs[1]|⌋ otherwise 1244 | # TODO: when s[0] or s[1] is symbol variable 1245 | # TODO: use z3 singed div 1246 | self.push_to_stack(simplify(s[0]/s[1])) 1247 | 1248 | self.increment_pc() 1249 | 1250 | def op_mod(self, s): 1251 | if isinstance(s[1], BitVecNumRef) and s[1].as_long() == 0: 1252 | self.push_to_stack(BitVecVal256(0)) 1253 | else: 1254 | self.push_to_stack(simplify(URem(s[0], s[1]))) 1255 | self.increment_pc() 1256 | 1257 | def op_smod(self, s): 1258 | if isinstance(s[1], BitVecNumRef) and s[1].as_signed_long() == 0: 1259 | self.push_to_stack(BitVecVal256(0)) 1260 | else: 1261 | self.push_to_stack(simplify(s[0] % s[1])) 1262 | self.increment_pc() 1263 | 1264 | def op_addmod(self, s): 1265 | if isinstance(s[2], BitVecNumRef) and s[2].as_long() == 0: 1266 | self.push_to_stack(BitVecVal256(0)) 1267 | else: 1268 | s0 = s[0].as_long() if isinstance(s[0], BitVecNumRef) else s[0] 1269 | s1 = s[1].as_long() if isinstance(s[1], BitVecNumRef) else s[1] 1270 | self.push_to_stack(simplify(URem((s0 + s1), s[2]))) 1271 | self.increment_pc() 1272 | 1273 | def op_mulmod(self, s): 1274 | if isinstance(s[2], BitVecNumRef) and s[2].as_long() == 0: 1275 | self.push_to_stack(BitVecVal256(0)) 1276 | # TODO check modulo 1277 | else: 1278 | s0 = s[0].as_long() if isinstance(s[0], BitVecNumRef) else s[0] 1279 | s1 = s[1].as_long() if isinstance(s[1], BitVecNumRef) else s[1] 1280 | self.push_to_stack(URem((s0 * s1), s[2])) 1281 | self.increment_pc() 1282 | 1283 | def op_exp(self, s): 1284 | a = s[0] 1285 | b = s[1] 1286 | self.push_to_stack(simplify(Int2BV(BV2Int(a) ** BV2Int(b),256))) 1287 | self.increment_pc() 1288 | 1289 | # TODO 1290 | def op_signextend(self, s): 1291 | # TODO: if s[0] is symbol variable 1292 | # TODO: test 1293 | if isinstance(s[0], BitVecVal256): 1294 | t = 256 - 8*(s[0].as_long() + 1) 1295 | sign = Extract(t, t, s[1]) 1296 | ret = s[0] 1297 | for i in range(t+1): 1298 | ret = simplify(Concat(sign, ret)) 1299 | self.push_to_stack(ret) 1300 | self.increment_pc() 1301 | 1302 | # 10s: Comparison & Bitwise Logic Operations 1303 | 1304 | def op_lt(self, s): 1305 | self.push_to_stack(If(BV2Int(s[0]) < BV2Int(s[1]), BitVecOne256(), BitVecZero256())) 1306 | self.increment_pc() 1307 | 1308 | def op_gt(self, s): 1309 | self.push_to_stack(If(BV2Int(s[0]) > BV2Int(s[1]), BitVecOne256(), BitVecZero256())) 1310 | self.increment_pc() 1311 | 1312 | def op_slt(self, s): 1313 | self.push_to_stack(If(bv_to_signed_int(s[0]) < bv_to_signed_int(s[1]), BitVecOne256(), BitVecZero256())) 1314 | self.increment_pc() 1315 | 1316 | def op_sgt(self, s): 1317 | self.push_to_stack(If(bv_to_signed_int(s[0]) > bv_to_signed_int(s[1]), BitVecOne256(), BitVecZero256())) 1318 | self.increment_pc() 1319 | 1320 | def op_eq(self, s): 1321 | self.push_to_stack(If(BV2Int(s[0]) == BV2Int(s[1]), BitVecOne256(), BitVecZero256())) 1322 | self.increment_pc() 1323 | 1324 | def op_iszero(self, s): 1325 | self.push_to_stack(If(BV2Int(s[0]) == 0, BitVecOne256(), BitVecZero256())) 1326 | self.increment_pc() 1327 | 1328 | def op_and(self, s): 1329 | self.push_to_stack(simplify(s[0] & s[1])) 1330 | self.increment_pc() 1331 | 1332 | def op_or(self, s): 1333 | self.push_to_stack(simplify(s[0] | s[1])) 1334 | self.increment_pc() 1335 | 1336 | def op_xor(self, s): 1337 | self.push_to_stack(simplify(s[0] ^ s[1])) 1338 | self.increment_pc() 1339 | 1340 | def op_not(self, s): 1341 | self.push_to_stack(~s[0]) 1342 | self.increment_pc() 1343 | 1344 | def op_byte(self, s): 1345 | mask = Int2BV((2 ** 8 - 1) * 2 ** (BV2Int(s[0]) * 8), 256) 1346 | # extract a byte 1347 | a = mask & s[1] 1348 | # s[0] bit shift 1349 | b = Int2BV(2 ** (BV2Int(s[0]) * 8), 256) 1350 | self.push_to_stack(simplify(UDiv(a, b))) 1351 | self.increment_pc() 1352 | 1353 | def op_shl(self, s): 1354 | shift = s[0] 1355 | value = s[1] 1356 | if isinstance(shift, BitVecNumRef): 1357 | shift = shift.as_long() 1358 | self.push_to_stack(simplify(value << shift)) 1359 | else: 1360 | # too slow 1361 | self.push_to_stack(simplify(value * Int2BV(2 ** BV2Int(shift), 256))) 1362 | self.increment_pc() 1363 | 1364 | def op_shr(self, s): 1365 | shift = s[0] 1366 | value = s[1] 1367 | if isinstance(shift, BitVecNumRef): 1368 | shift = shift.as_long() 1369 | self.push_to_stack(simplify(LShR(value, shift))) 1370 | else: 1371 | self.push_to_stack(simplify(UDiv(value, Int2BV(2 ** BV2Int(shift), 256)))) 1372 | self.increment_pc() 1373 | 1374 | def op_sar(self, s): 1375 | shift = s[0] 1376 | value = s[1] 1377 | if isinstance(shift, BitVecNumRef): 1378 | shift = shift.as_long() 1379 | self.push_to_stack(simplify(value >> shift)) 1380 | else: 1381 | # TODO 1382 | # self.push_to_stack(simplify(UDiv(value, Int2BV(2 ** BV2Int(shift), 256)) 1383 | # or , 256))) 1384 | return False 1385 | self.increment_pc() 1386 | 1387 | 1388 | # 20s: SHA3 1389 | def op_sha3(self, s): 1390 | 1391 | 1392 | #TODO 1393 | if isinstance(s[0],BitVecNumRef) and isinstance(s[1],BitVecNumRef): 1394 | offset = s[0].as_long() 1395 | length = s[1].as_long() 1396 | data = self.get_machine_state().get_memory().get_one_byte(offset) 1397 | for i in range(offset+1, offset+length): 1398 | # data += bytes(self.get_machine_state().get_memory().get_one_byte(i)) 1399 | data = Concat(data,self.get_machine_state().get_memory().get_one_byte(i)) 1400 | k = keccak_256() 1401 | data = str(simplify(data)) 1402 | k.update(data.encode()) 1403 | hash = k.hexdigest() 1404 | # print('sha3,data=',data) 1405 | # print(hash[:16]) 1406 | self.push_to_stack(BitVec256('SHA3_' + hash[:16] )) 1407 | #self.push_to_stack(BitVec256('SHA3_{}'.format(self.get_pc()))) 1408 | self.increment_pc() 1409 | # 1410 | # pass 1411 | # offset = self.stack.pop() 1412 | # length = self.stack.pop() 1413 | 1414 | # 30s: Environmental Information 1415 | def op_address(self, s): 1416 | self.push_to_stack(self.get_address()) 1417 | self.increment_pc() 1418 | 1419 | def op_balance(self, s): 1420 | self.push_to_stack(self.get_balance()) 1421 | self.increment_pc() 1422 | 1423 | def op_origin(self, s): 1424 | self.push_to_stack(self.get_origin()) 1425 | self.increment_pc() 1426 | 1427 | def op_caller(self, s): 1428 | self.push_to_stack(self.get_caller()) 1429 | self.increment_pc() 1430 | 1431 | def op_callvalue(self, s): 1432 | self.push_to_stack(self.get_value()) 1433 | self.increment_pc() 1434 | 1435 | def op_calldataload(self, s): 1436 | msg_data = self.get_data().duplicate() 1437 | if not isinstance(s[0],BitVecNumRef): 1438 | raise DevelopmentErorr() 1439 | memsize = msg_data.size() 1440 | 1441 | for i in range(memsize, s[0].as_long()+WORDBYTESIZE): 1442 | msg_data.mstore8(i, BitVecZero256()) 1443 | self.push_to_stack(msg_data.mload(s[0])) 1444 | self.increment_pc() 1445 | 1446 | def op_calldatasize(self, s): 1447 | self.push_to_stack(BitVecVal256(self.get_data().size())) 1448 | self.increment_pc() 1449 | 1450 | def op_calldatacopy(self,s): 1451 | if not (isinstance(s[0], BitVecNumRef) and isinstance(s[1], BitVecNumRef) and isinstance(s[2], BitVecNumRef)): 1452 | raise DevelopmentErorr() 1453 | 1454 | memoffset = s[0].as_long() 1455 | dataoffset = s[1].as_long() 1456 | copysize = s[2].as_long() 1457 | 1458 | data = self.get_data() 1459 | tmpdata = [] 1460 | 1461 | for i in range(dataoffset, dataoffset+copysize): 1462 | tmpdata.append(data.get_one_byte(i)) 1463 | 1464 | memory = self.get_machine_state().get_memory() 1465 | for i in range(memoffset, copysize): 1466 | memory.mstore8(i, tmpdata[i]) 1467 | 1468 | self.increment_pc() 1469 | 1470 | def op_codesize(self, s): 1471 | self.push_to_stack(BitVecVal256(len(self.get_code()))) 1472 | self.increment_pc() 1473 | 1474 | def op_codecopy(self, s): 1475 | memoffset = s[0].as_long() 1476 | codeoffset = s[1].as_long() 1477 | copysize = s[2].as_long() 1478 | 1479 | codesize = len(self.get_code()) 1480 | tmpcode = self.get_code()[codeoffset:codeoffset+copysize] + ['00'] * (codeoffset + copysize - codesize) 1481 | 1482 | memory = self.get_machine_state().get_memory() 1483 | 1484 | for i in range(copysize): 1485 | memory.mstore8(memoffset+i, convert_to_bitvec8(tmpcode[i])) 1486 | 1487 | self.increment_pc() 1488 | 1489 | def op_gasprice(self, s): 1490 | self.push_to_stack(self.get_gasprice()) 1491 | self.increment_pc() 1492 | 1493 | def op_extcodesize(self, s): 1494 | #addr = str(simplify(s[0] % 2**160)) 1495 | addr = str(simplify(s[0])) 1496 | try: 1497 | self.push_to_stack(BitVecVal256(len(self.σ.get_account(addr).get_bytecode()))) 1498 | except: 1499 | # TODO 1500 | self.push_to_stack(BitVec256('EXT_CODE_{}'.format(self.get_pc()))) 1501 | self.increment_pc() 1502 | 1503 | def op_extcodecopy(self, s): 1504 | addr = s[0] % 2**160 1505 | memoffset = s[1].as_long() 1506 | codeoffset = s[2].as_long() 1507 | copysize = s[3].as_long() 1508 | code = self.σ.get_account(addr).get_bytecode() 1509 | codesize = len(code) 1510 | tmpcode = code[codeoffset:copysize] + '00' * (codeoffset + copysize - codesize) 1511 | memory = self.get_machine_state().get_memory() 1512 | for i in range(memoffset, copysize): 1513 | memory.mstore8(BitVecVal256(i), BitVecVal256(tmpcode[i])) 1514 | self.increment_pc() 1515 | 1516 | def op_returndatasize(self, s): 1517 | self.push_to_stack(BitVecVal256(self.get_machine_state().get_return_data().size())) 1518 | self.increment_pc() 1519 | 1520 | 1521 | def op_returndatacopy(self, s): 1522 | destOffset = s[0].as_long() 1523 | offset = s[1].as_long() 1524 | length = s[2].as_long() 1525 | 1526 | for i in range(length): 1527 | self.get_machine_state().get_memory().mstore8( 1528 | destOffset+i, 1529 | self.get_machine_state().get_return_data().mload(offset+i) 1530 | ) 1531 | self.increment_pc() 1532 | 1533 | 1534 | 1535 | 1536 | 1537 | 1538 | 1539 | 1540 | 1541 | 1542 | # 40s: Block Information 1543 | def op_blockhash(self, s): 1544 | n = BV2Int(s[0]) 1545 | current_block_number = BV2Int(self.get_exec_env().get_block_header().get_block_number()) 1546 | parent_hash = self.get_exec_env().get_block_header().get_parent_hash() 1547 | a = current_block_number - n 1548 | self.push_to_stack(If(Or(a <= 0, 256 <= a), BitVecZero256(), 1549 | If(a == 1, parent_hash, BitVec256('block_hash_' + str(n))))) 1550 | self.increment_pc() 1551 | 1552 | def op_coinbase(self, s): 1553 | self.push_to_stack(self.get_exec_env().get_block_header().get_beneficiary()) 1554 | self.increment_pc() 1555 | 1556 | def op_timestamp(self, s): 1557 | self.push_to_stack(self.get_exec_env().get_block_header().get_timestamp()) 1558 | self.increment_pc() 1559 | 1560 | def op_number(self, s): 1561 | self.push_to_stack(self.get_exec_env().get_block_header().get_block_number()) 1562 | self.increment_pc() 1563 | 1564 | def op_difficulty(self, s): 1565 | self.push_to_stack((self.get_exec_env().get_block_header().get_difficulty())) 1566 | self.increment_pc() 1567 | 1568 | def op_gaslimit(self, s): 1569 | self.push_to_stack(self.get_exec_env().get_block_header().get_gaslimit()) 1570 | self.increment_pc() 1571 | 1572 | 1573 | # 50s: Stack, Memory, Storage and Flow Operations 1574 | def op_pop(self, s): 1575 | self.increment_pc() 1576 | 1577 | def op_mload(self, s): 1578 | self.push_to_stack(self.get_machine_state().get_memory().mload(s[0])) 1579 | self.increment_pc() 1580 | 1581 | def op_mstore(self, s): 1582 | self.get_machine_state().get_memory().mstore(s[0], s[1]) 1583 | self.increment_pc() 1584 | 1585 | def op_mstore8(self, s): 1586 | self.get_machine_state().get_memory().mstore8(s[0],s[1]) 1587 | self.increment_pc() 1588 | 1589 | def op_sload(self, s): 1590 | # print('-----------op_sload------------:', id(self.get_machine_state().get_storage())) 1591 | # print(s[0]) 1592 | # print(self.get_machine_state().get_storage().sload(s[0])) 1593 | # print('---------------------------------') 1594 | self.push_to_stack(self.get_machine_state().get_storage().sload(s[0])) 1595 | self.increment_pc() 1596 | 1597 | def op_sstore(self, s): 1598 | # print('-----------op_sstore------------:',id(self.get_machine_state().get_storage())) 1599 | # print(s[1]) 1600 | # print(s[0]) 1601 | # print('---------------------------------') 1602 | self.get_machine_state().get_storage().sstore(s[0], s[1]) 1603 | self.increment_pc() 1604 | 1605 | def op_jump(self, s): 1606 | if not isinstance(s[0], BitVecNumRef): 1607 | raise DevelopmentErorr('cannot to jump to address expressed with symbol variable') 1608 | # 現状ではrun中にjumpdestかを確認 1609 | # if not self.check_jumpdest(s[0]): 1610 | # raise 1611 | jumpdest = s[0] 1612 | self.set_jumpdest(jumpdest) 1613 | if self.branch(continuable=False, jumpable=True, condition=True) == False: 1614 | return self.op_stop(s) 1615 | 1616 | 1617 | 1618 | 1619 | 1620 | def op_jumpi(self, s): 1621 | jumpdest = s[0] 1622 | condition = self.convert_to_expression(s[1]) 1623 | 1624 | self.set_jumpdest(jumpdest) 1625 | # 式でないただのシンボル変数なら,0でないかを判定 1626 | if type(condition) == BitVecRef: 1627 | condition = condition != 0 1628 | 1629 | # ジャンプ可能か 1630 | jumpable = solve_and_time(And(condition,self.get_processing_block().get_path_condition())) 1631 | # 継続可能か 1632 | continuable = solve_and_time(And(Not(condition), self.get_processing_block().get_path_condition())) 1633 | # print('op_jumpi') 1634 | # print(hex(self.get_pc()),self.get_exec_env().depth_of_call,self.get_processing_block().get_path()) 1635 | # print(continuable,jumpable) 1636 | # print() 1637 | if not self.branch(continuable, jumpable, condition): 1638 | return self.op_stop(s) 1639 | 1640 | def op_pc(self, s): 1641 | self.push_to_stack(self.get_machine_state().get_pc()) 1642 | self.increment_pc() 1643 | 1644 | def op_msize(self, s): 1645 | self.push_to_stack(self.get_machine_state().get_memory().size()) 1646 | self.increment_pc() 1647 | 1648 | def op_gas(self, s): 1649 | self.push_to_stack(self.get_machine_state().get_gas()) 1650 | self.increment_pc() 1651 | 1652 | def op_jumpdest(self, s): 1653 | self.increment_pc() 1654 | 1655 | 1656 | # 60s & 70s: Push Operations 1657 | def op_pushx(self, s, x): 1658 | # extract value 1659 | v = '' 1660 | for i in range(x): 1661 | self.increment_pc() 1662 | v += self.get_byte_from_bytecode() 1663 | self.add_immediatevalue(v) 1664 | v = BitVecVal256(int(v, 16)) 1665 | self.push_to_stack(v) 1666 | self.increment_pc() 1667 | 1668 | 1669 | 1670 | 1671 | # 80s: Duplication Operations 1672 | def op_dupx(self, s): 1673 | v = deepcopy(s[-1]) 1674 | for i in range(len(s)-1,-1,-1): 1675 | self.push_to_stack(s[i]) 1676 | self.push_to_stack(v) 1677 | self.increment_pc() 1678 | 1679 | # 90s: Exchange Operations 1680 | def op_swapx(self, s): 1681 | t = deepcopy(s[0]) 1682 | b = deepcopy(s[-1]) 1683 | self.push_to_stack(t) 1684 | for i in range(len(s)-2,0,-1): 1685 | self.push_to_stack(s[i]) 1686 | self.push_to_stack(b) 1687 | self.increment_pc() 1688 | 1689 | def op_create(self, s): 1690 | 1691 | if not (isinstance(s[1], BitVecNumRef) and isinstance(s[2], BitVecNumRef)): 1692 | 1693 | raise DevelopmentErorr('illegal parameter given to CREATE '+str(s[1:3])) 1694 | 1695 | offset = s[1].as_long() 1696 | length = s[2].as_long() 1697 | new_code = Bytecode() 1698 | import sys 1699 | for i in range(length): 1700 | op = self.get_machine_state().get_memory().get_one_byte(offset+i) 1701 | 1702 | 1703 | if isinstance(simplify(op), BitVecNumRef): 1704 | new_code.append(format(simplify(op).as_long(), '02x')) 1705 | else: 1706 | new_code.append(op) 1707 | 1708 | 1709 | 1710 | self.set_balance(self.get_balance() - s[0]) 1711 | # generate new execution environment 1712 | exec_env = ExecutionEnvironment( 1713 | Ia=BitVecZero256(), 1714 | Ib=new_code, 1715 | Is=self.get_address(), 1716 | Iv=s[0]) 1717 | # call initialization code 1718 | self.external_call(#self.get_account_num(), 1719 | 1720 | exec_env=exec_env) 1721 | 1722 | 1723 | 1724 | 1725 | 1726 | 1727 | def op_call(self, s): 1728 | 1729 | if not (isinstance(s[3], BitVecNumRef) 1730 | and isinstance(s[4], BitVecNumRef) 1731 | and isinstance(s[5], BitVecNumRef) 1732 | and isinstance(s[6], BitVecNumRef)): 1733 | print(s,self.get_pc()) 1734 | raise DevelopmentErorr('some parameter given to CALL must be concrete value') 1735 | 1736 | self.get_processing_block().add_block_state(CALLABLE) 1737 | 1738 | 1739 | gas = s[0] 1740 | 1741 | value = s[2] 1742 | argsOffset = s[3].as_long() 1743 | argsLength = s[4].as_long() 1744 | retOffset = s[5].as_long() 1745 | retLength = s[6].as_long() 1746 | 1747 | msg_data = MsgData() 1748 | for i in range(argsLength): 1749 | op = self.get_machine_state().get_memory().get_one_byte(argsOffset + i) 1750 | op = BitVecVal(int(op, 16),8) if isinstance(op, str) else op 1751 | msg_data.mstore8(i, op) 1752 | 1753 | self.get_machine_state().set_retLength(retLength) 1754 | self.get_machine_state().set_retOffset(retOffset) 1755 | 1756 | # print('op_call') 1757 | # print('pc=',self.get_pc()) 1758 | # print('balance prev') 1759 | # print(self.get_balance()) 1760 | self.set_balance(self.get_balance() - value) 1761 | #print(self.get_processing_block().get_path_condition()) 1762 | # self.get_processing_block().add_constraint_to_path_condition(self.get_balance() >= 0) 1763 | #print(self.get_processing_block().get_path_condition()) 1764 | # print('value:') 1765 | # print(value) 1766 | # print('balance next') 1767 | # print(self.get_balance()) 1768 | 1769 | 1770 | # print(self.get_exec_env().msg_sender) 1771 | # print(self.get_address()) 1772 | # print(self.get_balance()) 1773 | 1774 | 1775 | 1776 | 1777 | self.vulnerability_verifier.extract_data_before_call( 1778 | self.get_processing_block().get_path_condition(), 1779 | self.get_processing_block().get_block_state(), 1780 | self.get_machine_state().get_storage(), 1781 | self.get_machine_state().get_balance(), 1782 | self.get_processing_block().get_depth(), 1783 | ) 1784 | 1785 | 1786 | if self.vulnerability_verifier.is_first_call(): 1787 | addr = self.secondary_contract 1788 | fid = BitVecVal(0, 32) 1789 | print('FIRST CALL') 1790 | #TODO:returndata 1791 | elif self.vulnerability_verifier.is_second_call(): 1792 | addr = self.primary_contracts[self.primary_contract_index] 1793 | fid = self.vulnerability_verifier.get_y() 1794 | msg_data.set_function_id(fid) 1795 | #for cross-function 1796 | msg_data.set_arguments(1024) 1797 | #for create-based 1798 | #msg_data.set_concrete_arguments(BitVecVal(0xff,256)) 1799 | print('SECOND CALL') 1800 | else: 1801 | self.push_to_stack( 1802 | # BitVec256('call_succeeds_{}_{}'.format(self.get_exec_env().get_exec_env_id(), self.get_pc() * 2))) 1803 | # BitVec256('call_succeeds_{}_{}'.format(self.get_exec_env().depth_of_call, self.get_pc() * 2))) 1804 | # BitVec256('call_succeeds_{}'.format(self.get_pc() * 2)) 1805 | # BitVec256('call_succeeds') 1806 | BitVecOne256() 1807 | ) 1808 | self.increment_pc() 1809 | 1810 | returndata = [BitVec('Return_data{}-{}_{}'.format(self.get_exec_env().get_exec_env_id(), 1811 | self.get_pc(), 1812 | i),8) for i in range(retLength)] 1813 | for i in range(retLength): 1814 | self.get_machine_state().get_memory().mstore8(i+retOffset,returndata[i]) 1815 | self.get_machine_state().set_return_data(Returndata(immediate_data=returndata)) 1816 | 1817 | return 1818 | 1819 | 1820 | if addr is not None : 1821 | 1822 | exec_env = ExecutionEnvironment( 1823 | exec_env_id=fid.as_long(), 1824 | Ia=addr, 1825 | Ib=self.σ.get_account(str(addr)).get_bytecode(), 1826 | Is=self.get_address(), 1827 | Iv=None if fid.as_long() == self.vulnerability_verifier.get_y().as_long() else value,#fallbackの想定 1828 | Id=msg_data, 1829 | Ie=self.get_exec_env().depth_of_call + 1 1830 | 1831 | 1832 | ) 1833 | 1834 | if fid.as_long() == self.vulnerability_verifier.get_y().as_long(): 1835 | exec_env.set_exec_env_id(id(exec_env)) 1836 | self.external_call(exec_env) 1837 | 1838 | 1839 | 1840 | 1841 | 1842 | 1843 | 1844 | 1845 | 1846 | 1847 | def op_callcode(self, s): 1848 | pass 1849 | 1850 | def op_return(self, s): 1851 | 1852 | 1853 | 1854 | 1855 | # extract return data 1856 | if (not(isinstance(s[0],BitVecNumRef)) or (not isinstance(s[1],BitVecNumRef))): 1857 | raise DevelopmentErorr 1858 | 1859 | offset = s[0].as_long() 1860 | length = s[1].as_long() 1861 | 1862 | retOffset = self.get_machine_state().get_retOffset() 1863 | retLength = self.get_machine_state().get_retLength() 1864 | self.get_machine_state().set_retOffset(-1) 1865 | self.get_machine_state().set_retLength(-1) 1866 | 1867 | returndata = [] 1868 | for i in range(offset, offset+length): 1869 | returndata.append( 1870 | self.get_machine_state().get_memory().get_one_byte(i)) 1871 | 1872 | next_block = self.cfmanager.rollback_from_call_stack(self.vulnerability_verifier) 1873 | if next_block: 1874 | 1875 | self.get_machine_state().set_return_data( 1876 | Returndata(block_number=next_block.get_block_number(), 1877 | immediate_data=returndata)) 1878 | for i in range(retLength): 1879 | next_block.get_machine_state().get_memory().mstore8(retOffset+i,returndata[i]) 1880 | 1881 | 1882 | # when the external call was caused by CREATE 1883 | prevpc = self.get_processing_block().get_pc() - 1 1884 | if ''.join(self.get_processing_block().get_exec_env().get_code()[prevpc]) == 'f0': 1885 | 1886 | addr = self.add_primary_contract(''.join([format(v.as_long(),'02x') for v in returndata])) 1887 | 1888 | # TODO gas calculation 1889 | condition = And(s[0] <= self.get_balance(), 1890 | self.get_machine_state().get_stack().get_stack_size() < 1024) 1891 | self.push_to_stack(simplify(If(condition, 1892 | addr, 1893 | BitVecZero256() 1894 | ))) 1895 | elif ''.join(self.get_processing_block().get_exec_env().get_code()[prevpc]) == 'f1': 1896 | #self.push_to_stack(BitVec256('call_succeeds_{}_{}'.format(self.get_exec_env().get_exec_env_id(), prevpc * 2))) 1897 | 1898 | # self.push_to_stack(BitVec256('call_succeeds_{}_{}'.format(self.get_exec_env().depth_of_call, prevpc* 2))) 1899 | # self.push_to_stack( 1900 | # BitVec256('call_succeeds_{}'.format(prevpc * 2))) 1901 | self.push_to_stack( 1902 | # BitVec256('call_succeeds') 1903 | BitVecOne256() 1904 | ) 1905 | 1906 | 1907 | 1908 | else: 1909 | return self.terminate() 1910 | 1911 | 1912 | def op_delegatecall(self, s): 1913 | pass 1914 | def op_revert(self, s): 1915 | # 他に正常終了するルートがあるという前提でcallstackよりdfsstackからのロールバック優先 1916 | 1917 | if not self.cfmanager.rollback_from_dfs_stack(): 1918 | # TODO call stackからのロールバック時にデータの継承を行わない 1919 | return self.cfmanager.rollback_from_call_stack(self.vulnerability_verifier) 1920 | 1921 | # if self.cfmanager.get_processing_block().get_call_stack_size() > 0: 1922 | # return_block = self.cfmanager.pop_from_call_stack() 1923 | # # self.cfmanager.add_edge(self.get_processing_block(),return_block) 1924 | # self.cfmanager.set_procesisng_block(return_block) 1925 | # else: 1926 | # if self.cfmanager.is_dfs_stack_empty(): 1927 | # return False 1928 | # else: 1929 | # self.cfmanager.rollback_from_dfs_stack() 1930 | 1931 | def op_invalid(self,s): 1932 | # TODO 異常終了 1933 | print('op=',self.get_code()[self.get_pc()], "code=", self.get_code(), self.get_pc()) 1934 | return False 1935 | 1936 | def op_selfdestruct(self, s): 1937 | print(s) 1938 | return False 1939 | 1940 | --------------------------------------------------------------------------------