示例#1
0
 def emul_variable_instr(self, instr, state):
     if instr.name in ['get_local', 'get_global']:
         instr.ssa = SSA(new_assignement=self.ssa_counter,
                         method_name=instr.name)
         state.ssa_stack.append(instr)
         self.ssa_counter += 1
     elif instr.name in ['set_local', 'set_global']:
         state.ssa_stack.pop()
         instr.ssa = SSA(method_name=instr.name)
     elif instr.name == 'tee_local':
         state.ssa_stack.append(state.ssa_stack[-1])
         self.ssa_counter += 1
     return False
示例#2
0
 def emul_parametric_instr(self, instr, state):
     if instr.name == 'drop':
         state.ssa_stack.pop()
         instr.ssa = SSA(method_name=instr.name)
     else:  # select
         arg = [
             state.ssa_stack.pop(),
             state.ssa_stack.pop(),
             state.ssa_stack.pop()
         ]
         instr.ssa = SSA(new_assignement=self.ssa_counter,
                         method_name=instr.name,
                         args=arg)
         state.ssa_stack.append(instr)
         self.ssa_counter += 1
     return False
示例#3
0
    def ssa_system_instruction(self, instr, state):

        halt = False

        if instr.name == 'CREATE':
            args = [
                state.ssa_stack.pop(),
                state.ssa_stack.pop(),
                state.ssa_stack.pop()
            ]
            instr.ssa = SSA(new_assignement=self.ssa_counter,
                            method_name=instr.name,
                            args=args)
            state.ssa_stack.append(instr)
            self.ssa_counter += 1

        elif instr.name in ('CALL', 'CALLCODE', 'DELEGATECALL', 'STATICCALL'):

            if instr.name in ('CALL', 'CALLCODE'):
                gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \
                    state.ssa_stack.pop(), state.ssa_stack.pop(), state.ssa_stack.pop(), state.ssa_stack.pop(), state.ssa_stack.pop(), state.ssa_stack.pop(), state.ssa_stack.pop()
                args = [
                    gas, to, value, meminstart, meminsz, memoutstart, memoutsz
                ]

            else:
                gas, to, meminstart, meminsz, memoutstart, memoutsz = \
                    state.ssa_stack.pop(), state.ssa_stack.pop(), state.ssa_stack.pop(), state.ssa_stack.pop(), state.ssa_stack.pop(), state.ssa_stack.pop()
                args = [gas, to, meminstart, meminsz, memoutstart, memoutsz]

            instr.ssa = SSA(new_assignement=self.ssa_counter,
                            method_name=instr.name,
                            args=args)
            state.ssa_stack.append(instr)
            self.ssa_counter += 1

        elif instr.name in ['RETURN', 'REVERT']:
            offset, length = state.ssa_stack.pop(), state.ssa_stack.pop()
            instr.ssa = SSA(method_name=instr.name, args=[offset, length])
            halt = True

        elif instr.name in ['INVALID', 'SELFDESTRUCT']:
            # SSA STACK
            instr.ssa = SSA(method_name=instr.name)
            halt = True

        return halt
示例#4
0
    def emul_sha3_instruction(self, instr, state):
        '''Symbolic execution of SHA3 group of opcode'''

        # SSA STACK
        s0, s1 = state.ssa_stack.pop(), state.ssa_stack.pop()
        instr.ssa = SSA(self.ssa_counter, instr.name, args=[s0, s1])
        state.ssa_stack.append(instr)
        self.ssa_counter += 1
示例#5
0
 def emul_constant_instr(self, instr, state):
     op = int.from_bytes(instr.operand, byteorder='big')
     instr.ssa = SSA(self.ssa_counter,
                     instr.name,
                     op,
                     instr_type=SSA_TYPE_CONSTANT)
     state.ssa_stack.append(instr)
     self.ssa_counter += 1
     return False
示例#6
0
    def emul_conversion_instr(self, instr, state):
        arg = [state.ssa_stack.pop()]
        instr.ssa = SSA(new_assignement=self.ssa_counter,
                        method_name=instr.name,
                        args=arg)
        state.ssa_stack.append(instr)
        self.ssa_counter += 1

        return False
示例#7
0
    def ssa_block_instruction(self, instr, state):

        if instr.name == 'BLOCKHASH':
            # SSA STACK
            blocknumber = state.ssa_stack.pop()
            instr.ssa = SSA(new_assignement=self.ssa_counter,
                            method_name=instr.name,
                            args=[blocknumber])
            state.ssa_stack.append(instr)
            self.ssa_counter += 1

        elif instr.name in [
                'COINBASE', 'TIMESTAMP', 'NUMBER', 'DIFFICULTY', 'GASLIMIT'
        ]:
            instr.ssa = SSA(new_assignement=self.ssa_counter,
                            method_name=instr.name)
            state.ssa_stack.append(instr)
            self.ssa_counter += 1
示例#8
0
    def emul_logical_i64_instr(self, instr, state):
        if instr.name == 'i64.eqz':
            arg = [state.ssa_stack.pop()]
        else:
            arg = [state.ssa_stack.pop(), state.ssa_stack.pop()]
        instr.ssa = SSA(new_assignement=self.ssa_counter,
                        method_name=instr.name,
                        args=arg)
        state.ssa_stack.append(instr)
        self.ssa_counter += 1

        return False
示例#9
0
    def emul_arithmetic_f64_instr(self, instr, state):
        if instr.name in ['f64.abs', 'f64.neg']:
            arg = [state.ssa_stack.pop()]
        else:
            arg = [state.ssa_stack.pop(), state.ssa_stack.pop()]
        instr.ssa = SSA(new_assignement=self.ssa_counter,
                        method_name=instr.name,
                        args=arg)
        state.ssa_stack.append(instr)
        self.ssa_counter += 1

        return False
示例#10
0
    def emul_arithmetic_i32_instr(self, instr, state):
        if instr.name in ['i32.clz', 'i32.ctz', 'i32.popcnt']:
            arg = [state.ssa_stack.pop()]
        else:
            arg = [state.ssa_stack.pop(), state.ssa_stack.pop()]
        instr.ssa = SSA(new_assignement=self.ssa_counter,
                        method_name=instr.name,
                        args=arg)
        state.ssa_stack.append(instr)
        self.ssa_counter += 1

        return False
示例#11
0
    def emul_memory_instr(self, instr, state):
        # load
        if 'load' in instr.name:
            arg = [state.ssa_stack.pop()]
            instr.ssa = SSA(new_assignement=self.ssa_counter,
                            method_name=instr.name,
                            args=arg)
            state.ssa_stack.append(instr)
            self.ssa_counter += 1

        elif 'store' in instr.name:
            arg = [state.ssa_stack.pop(), state.ssa_stack.pop()]
            instr.ssa = SSA(method_name=instr.name, args=arg)

        elif instr.name == 'current_memory':
            instr.ssa = SSA(new_assignement=self.ssa_counter,
                            method_name=instr.name)
            state.ssa_stack.append(instr)
            self.ssa_counter += 1
        else:
            instr.ssa = SSA(method_name=instr.name)
        return False
示例#12
0
    def ssa_environmental_instruction(self, instr, state):

        if instr.name in [
                'ADDRESS', 'ORIGIN', 'CALLER', 'CALLVALUE', 'CALLDATASIZE',
                'CODESIZE', 'RETURNDATASIZE', 'GASPRICE'
        ]:
            # SSA STACK
            instr.ssa = SSA(new_assignement=self.ssa_counter,
                            method_name=instr.name)
            state.ssa_stack.append(instr)
            self.ssa_counter += 1

        elif instr.name in ['BALANCE', 'CALLDATALOAD', 'EXTCODESIZE']:
            # SSA STACK
            s0 = state.ssa_stack.pop()
            instr.ssa = SSA(new_assignement=self.ssa_counter,
                            method_name=instr.name,
                            args=[s0])
            state.ssa_stack.append(instr)
            self.ssa_counter += 1

        elif instr.name in ['CALLDATACOPY', 'CODECOPY', 'RETURNDATACOPY']:
            op0, op1, op2 = state.ssa_stack.pop(), state.ssa_stack.pop(
            ), state.ssa_stack.pop()
            # SSA STACK
            instr.ssa = SSA(method_name=instr.name, args=[op0, op1, op2])

        elif instr.name == 'EXTCODECOPY':
            addr = state.ssa_stack.pop()
            start, s2, size = state.ssa_stack.pop(), state.ssa_stack.pop(
            ), state.ssa_stack.pop()
            # SSA STACK
            instr.ssa = SSA(new_assignement=self.ssa_counter,
                            method_name=instr.name,
                            args=[addr, start, s2, size])
            state.ssa_stack.append(instr)
            self.ssa_counter += 1
示例#13
0
    def emul_comparaison_logic_instruction(self, instr, state):

        if instr.name in [
                'LT', 'GT', 'SLT', 'SGT', 'EQ', 'AND', 'OR', 'XOR', 'BYTE'
        ]:
            args = [state.ssa_stack.pop(), state.ssa_stack.pop()]
        elif instr.name in ['ISZERO', 'NOT']:
            args = [state.ssa_stack.pop()]

        # SSA emulation
        instr.ssa = SSA(self.ssa_counter, instr.name, args=args)
        state.ssa_stack.append(instr)
        self.ssa_counter += 1

        # Symbolic Execution emulation
        if self.symbolic_exec:
            result = self.simplify_ssa.symbolic_dispatcher(instr.name, args)
            state.stack.append(result)
示例#14
0
    def emul_arithmetic_instruction(self, instr, state):

        if instr.name in [
                'ADD', 'SUB', 'MUL', 'DIV', 'MOD', 'SDIV', 'SMOD', 'EXP',
                'SIGNEXTEND'
        ]:
            args = [state.ssa_stack.pop(), state.ssa_stack.pop()]
        elif instr.name in ['ADDMOD', 'MULMOD']:
            args = [
                state.ssa_stack.pop(),
                state.ssa_stack.pop(),
                state.ssa_stack.pop()
            ]

        # SSA emulation
        instr.ssa = SSA(self.ssa_counter, instr.name, args=args)
        state.ssa_stack.append(instr)
        self.ssa_counter += 1

        # Symbolic Execution emulation
        if self.symbolic_exec:
            result = self.simplify_ssa.symbolic_dispatcher(instr.name, args)
            state.stack.append(result)
示例#15
0
    def emul_control_instr(self, instr, state, depth):
        halt = False
        if instr.name == 'unreachable':
            instr.ssa = SSA(method_name=instr.name)
            halt = True
        elif instr.name in ['nop', 'block', 'loop', 'else']:
            instr.ssa = SSA(method_name=instr.name)
        elif instr.name == 'if':
            arg = [state.ssa_stack.pop()]
            instr.ssa = SSA(method_name=instr.name, args=arg)
            # TODO branch if
            # inst + 1 == true block
            # need to find offset false block using edges or basicblocks list
            logging.warning('SSA: branch if not yet supported')
        elif instr.name == 'end':
            instr.ssa = SSA(method_name=instr.name)
            # check if it's the last instructions of the function
            if instr.offset == self.current_f_instructions[-1].offset:
                logging.warning("[X] break %s" % instr.name)
                halt = True
        elif instr.name == 'br':
            instr.ssa = SSA(method_name=instr.name)
            jump_addr = instr.xref

            # get instruction with this value as offset
            target = next(
                filter(lambda element: element.offset == jump_addr[0],
                       self.current_f_instructions))

            if target.offset not in state.instructions_visited:
                # condition are True
                logging.warning('[X] follow br branch offset 0x%x' %
                                (target.offset))
                new_state = copy.deepcopy(state)
                new_state.pc = self.current_f_instructions.index(target)
                # follow the br
                self.emulate(new_state, depth=depth + 1)
            else:
                logging.warning('[X] Loop detected, skipping br 0x%x' %
                                jump_addr[0])
                halt = True
            halt = True

        elif instr.name == 'br_if':
            arg = [state.ssa_stack.pop()]
            instr.ssa = SSA(method_name=instr.name, args=arg)

            logging.warning('[X] follow br_if default branch offset 0x%x' %
                            (instr.offset_end + 1))
            new_state = copy.deepcopy(state)

            self.emulate(new_state, depth=depth + 1)
            # after we return from emul - restore current_basicblock
            self.current_basicblock = self.basicblock_per_instr[instr.offset]

            jump_addr = instr.xref
            # get instruction with this value as offset
            target = next(
                filter(lambda element: element.offset == jump_addr[0],
                       self.current_f_instructions))

            if target.offset not in state.instructions_visited:
                # condition are True
                logging.warning('[X] follow br_if branch offset 0x%x' %
                                (target.offset))
                new_state = copy.deepcopy(state)
                new_state.pc = self.current_f_instructions.index(target)

                # follow the br_if
                self.emulate(new_state, depth=depth + 1)

            else:
                logging.warning('[X] Loop detected, skipping br_if 0x%x' %
                                jump_addr[0])
                halt = True
            halt = True
        elif instr.name == 'br_table':
            arg = [state.ssa_stack.pop()]
            instr.ssa = SSA(method_name=instr.name, args=arg)
            # TODO branch br_table
            logging.warning('SSA: branch br_table not yet supported')
        elif instr.name == 'return':
            arg = [state.ssa_stack.pop()]
            instr.ssa = SSA(method_name=instr.name, args=arg)
            halt = True
        elif instr.name == 'call':
            f_offset = int.from_bytes(instr.operand, 'big')
            target_func = self.ana.func_prototypes[f_offset]
            name, param_str, return_str, f_type = target_func
            # format_func_name()
            instr.ssa = SSA(method_name=instr.name + '_to_' + name)
            if param_str:
                num_arg = len(param_str.split(' '))
                #print(hex(state.ssa_stack[-1].offset))
                arg = [state.ssa_stack.pop() for x in range(1, num_arg + 1)]
                instr.ssa.args = arg
            if return_str:
                instr.ssa.new_assignement = self.ssa_counter
                state.ssa_stack.append(instr)
                self.ssa_counter += 1
        elif instr.name == 'call_indirect':
            arg = [state.ssa_stack.pop()]
            ''' # issue when table is imported
            # arg is constant
            if arg[0].ssa.instr_type == SSA_TYPE_CONSTANT:
                f_offset = int.from_bytes(instr.operand, 'big')
                target_func = self.ana.func_prototypes[f_offset]

                name, param_str, return_str, f_type = target_func
                # format_func_name()
                instr.ssa = SSA(method_name=instr.name + '_to_' + name)
                if param_str:
                    num_arg = len(param_str.split(' '))
                    print(hex(state.ssa_stack[-1].offset))
                    arg = [state.ssa_stack.pop() for x in range(1, num_arg+1)]
                    instr.ssa.args = arg
                if return_str:
                    instr.ssa.new_assignement = self.ssa_counter
                    state.ssa_stack.append(instr)
                    self.ssa_counter += 1
            else:
            '''
            instr.ssa = SSA(method_name=instr.name, args=arg)
            # test if arg is constant --> do like call
            # else - stay like that
        return halt
示例#16
0
    def ssa_stack_memory_storage_flow_instruction(self, instr, state, depth):

        halt = False
        op = instr.name

        if op == 'POP':
            # SSA STACK
            s0 = state.ssa_stack.pop()
            instr.ssa = SSA(method_name=instr.name)

        elif op in ['MLOAD', 'SLOAD']:
            # SSA STACK
            s0 = state.ssa_stack.pop()
            instr.ssa = SSA(new_assignement=self.ssa_counter,
                            method_name=instr.name,
                            args=[s0])
            state.ssa_stack.append(instr)
            self.ssa_counter += 1

        elif op in ['MSTORE', 'MSTORE8', 'SSTORE']:
            # SSA STACK
            s0, s1 = state.ssa_stack.pop(), state.ssa_stack.pop()
            instr.ssa = SSA(method_name=instr.name, args=[s0, s1])

        elif op == 'JUMP':
            # SSA STACK
            push_instr = state.ssa_stack.pop()
            instr.ssa = SSA(method_name=instr.name, args=[push_instr])

            # get instruction with this value as offset
            if push_instr.ssa.is_constant:
                #jump_addr = int.from_bytes(push_instr.operand, byteorder='big')
                jump_addr = push_instr.operand_interpretation
                # get instruction with this value as offset
                target = next(
                    filter(lambda element: element.offset == jump_addr,
                           self.instructions))
            else:
                # try to resolve the SSA repr
                jump_addr = self.simplify_ssa.resolve_instr_ssa(push_instr)
                target = next(
                    filter(lambda element: element.offset == jump_addr,
                           self.instructions))
                if not jump_addr:
                    logging.warning('JUMP DYNAMIC')
                    logging.warning('[X] push_instr %x: %s ' %
                                    (push_instr.offset, push_instr.name))
                    logging.warning('[X] push_instr.ssa %s' %
                                    push_instr.ssa.format())
                    list_args = [
                        arg.ssa.format() for arg in push_instr.ssa.args
                    ]
                    logging.warning('[X] push_instr.ssa %s' % list_args)
                    return True

            # depth of 1 - prevent looping
            #if (depth < self.max_depth):
            if target.name != "JUMPDEST":
                logging.info('[X] Bad JUMP to 0x%x' % jump_addr)
                return True

            if target.offset not in state.instructions_visited:
                logging.info('[X] follow JUMP branch offset 0x%x' %
                             target.offset)
                new_state = copy.deepcopy(state)
                new_state.pc = self.instructions.index(target)
                #state.pc = self.instructions.index(target)

                # follow the JUMP
                self.edges.append(
                    Edge(self.current_basicblock.name,
                         'block_%x' % target.offset, EDGE_UNCONDITIONAL))
                self.emulate(new_state, depth=depth + 1)

                halt = True

            else:
                #logging.info('[X] Max depth reached, skipping JUMP 0x%x' % jump_addr)
                self.edges.append(
                    Edge(self.current_basicblock.name,
                         'block_%x' % target.offset, EDGE_UNCONDITIONAL))
                logging.info('[X] Loop detected, skipping JUMP 0x%x' %
                             jump_addr)
                halt = True

            self.current_basicblock = self.basicblock_per_instr[instr.offset]

        elif op == 'JUMPI':
            # SSA STACK
            push_instr, condition = state.ssa_stack.pop(), state.ssa_stack.pop(
            )
            instr.ssa = SSA(method_name=instr.name,
                            args=[push_instr, condition])

            logging.info('[X] follow JUMPI default branch offset 0x%x' %
                         (instr.offset_end + 1))
            new_state = copy.deepcopy(state)
            self.edges.append(
                Edge(self.current_basicblock.name,
                     'block_%x' % (instr.offset_end + 1),
                     EDGE_CONDITIONAL_FALSE))
            self.emulate(new_state, depth=depth + 1)
            self.current_basicblock = self.basicblock_per_instr[instr.offset]

            # get instruction with this value as offset
            if push_instr.ssa.is_constant:
                #jump_addr = int.from_bytes(push_instr.operand, byteorder='big')
                jump_addr = push_instr.operand_interpretation
                # get instruction with this value as offset
                target = next(
                    filter(lambda element: element.offset == jump_addr,
                           self.instructions))
            else:
                # try to resolve the SSA repr
                jump_addr = self.simplify_ssa.resolve_instr_ssa(push_instr)
                target = next(
                    filter(lambda element: element.offset == jump_addr,
                           self.instructions))
                if not jump_addr:
                    logging.warning('JUMP DYNAMIC')
                    logging.warning('[X] push_instr %x: %s ' %
                                    (push_instr.offset, push_instr.name))
                    logging.warning('[X] push_instr.ssa %s' %
                                    push_instr.ssa.format())
                    list_args = [
                        arg.ssa.format() for arg in push_instr.ssa.args
                    ]
                    logging.warning('[X] push_instr.ssa %s' % list_args)
                    return True

            if target.name != "JUMPDEST":
                logging.info('[X] Bad JUMP to 0x%x' % jump_addr)
                return True

            if target.offset not in state.instructions_visited:
                # condition are True
                logging.info('[X] follow JUMPI branch offset 0x%x' %
                             (target.offset))
                new_state = copy.deepcopy(state)
                new_state.pc = self.instructions.index(target)

                # follow the JUMPI
                self.edges.append(
                    Edge(self.current_basicblock.name,
                         'block_%x' % target.offset, EDGE_CONDITIONAL_TRUE))
                self.emulate(new_state, depth=depth + 1)

            else:
                self.edges.append(
                    Edge(self.current_basicblock.name,
                         'block_%x' % target.offset, EDGE_CONDITIONAL_TRUE))
                logging.warning('[X] Loop detected, skipping JUMPI 0x%x' %
                                jump_addr)
                logging.warning('[X] push_instr.ssa %s' %
                                push_instr.ssa.format())
                halt = True
            halt = True

        elif op in ['PC', 'MSIZE', 'GAS']:
            # SSA STACK
            instr.ssa = SSA(new_assignement=self.ssa_counter,
                            method_name=instr.name)
            state.ssa_stack.append(instr)
            self.ssa_counter += 1

        elif op == 'JUMPDEST':
            # SSA STACK
            instr.ssa = SSA(method_name=instr.name)

        return halt
示例#17
0
    def emulate_one_instruction(self, instr, state, depth):

        halt = False

        #
        #  0s: Stop and Arithmetic Operations
        #
        if instr.name == 'STOP':
            if self.ssa:
                instr.ssa = SSA(method_name=instr.name)
            halt = True
        elif instr.is_arithmetic:
            self.emul_arithmetic_instruction(instr, state)
        #
        #  10s: Comparison & Bitwise Logic Operations
        #
        elif instr.is_comparaison_logic:
            self.emul_comparaison_logic_instruction(instr, state)
        #
        #  20s: SHA3
        #
        elif instr.is_sha3:
            self.emul_sha3_instruction(instr, state)
        #
        #  30s: Environment Information
        #
        elif instr.is_environmental:
            self.ssa_environmental_instruction(instr, state)
        #
        #  40s: Block Information
        #
        elif instr.uses_block_info:
            self.ssa_block_instruction(instr, state)
        #
        #  50s: Stack, Memory, Storage, and Flow Information
        #
        elif instr.uses_stack_block_storage_info:
            halt = self.ssa_stack_memory_storage_flow_instruction(
                instr, state, depth)
        #
        #  60s & 70s: Push Operations
        #
        elif instr.name.startswith("PUSH"):
            #value = int.from_bytes(instr.operand, byteorder='big')
            instr.ssa = SSA(self.ssa_counter,
                            instr.name,
                            instr.operand_interpretation,
                            instr_type=SSA_TYPE_CONSTANT)
            state.ssa_stack.append(instr)
            self.ssa_counter += 1
        #
        #  80s: Duplication Operations
        #
        elif instr.name.startswith('DUP'):
            # DUPn (eg. DUP1: a b c -> a b c c, DUP3: a b c -> a b c a)
            position = instr.pops  # == XX from DUPXX
            try:
                # SSA STACK
                instr.ssa = SSA(new_assignement=self.ssa_counter,
                                method_name=instr.name,
                                args=[state.ssa_stack[-position]])
                state.ssa_stack.append(state.ssa_stack[-position])
                self.ssa_counter += 1
                halt = False
            except:
                logging.info('[-] STACK underflow')
                halt = True
        #
        #  90s: Swap Operations
        #
        elif instr.name.startswith('SWAP'):
            # SWAPn (eg. SWAP1: a b c d -> a b d c, SWAP3: a b c d -> d b c a)
            position = instr.pops - 1  # == XX from SWAPXX
            try:
                temp = state.ssa_stack[-position - 1]
                state.ssa_stack[-position - 1] = state.ssa_stack[-1]
                state.ssa_stack[-1] = temp

                instr.ssa = SSA(method_name=instr.name, args=[temp])

                halt = False
            except:
                logging.warning('[-] STACK underflow')
                halt = True
                #raise ValueError('STACK underflow')
        #
        #  a0s: Logging Operations
        #
        elif instr.name.startswith('LOG'):
            # only stack operations emulated
            arg = [state.ssa_stack.pop() for x in range(instr.pops)]
            instr.ssa = SSA(method_name=instr.name, args=arg)
            #state.ssa_stack.append(instr)
        #
        #  f0s: System Operations
        #
        elif instr.is_system:
            halt = self.ssa_system_instruction(instr, state)
            #ssa.append(instr.name)

        # UNKNOWN INSTRUCTION
        else:
            logging.warning('UNKNOWN = ' + instr.name)

        return halt