Example #1
0
 def read_line(self, asmline, linenr=None):
     ''' 
     Read a line of the asm file and process the information
     to keep track of the registers and stack.
     
     Parameters
     ----------
     asmline: string
         a line of assembler
     linenr: int, optional
         the current line number in the file. 
         If not given, it will not be used in the output.
     
     Raises
     ------
     CpuException
         if any underlying exception has been raised 
      '''
     log.debug('reading line: %s', asmline)
     if linenr is not None:
         self.line_number = linenr
     self.address = ParseUtil.get_address_from_asmline(asmline)
     try:
         self._read_basic_block(asmline)
         self._read_meth_impl(asmline)
         self._read_assignment(asmline)
         self._read_call_line(asmline)
     except (ArgumentsException,
             VarAssignmentIvarRegisterWrongTypeException) as e:
         raise CpuException(self, e), None, sys.exc_info()[2]
Example #2
0
 def get_argument(self, register=None, objc_msgSend_stret=False):
     ''' Get an argument from the specified `Register`.
     If the `Cpu` fetches its arguments from stack, pop it from where.
     
     Parameters
     ----------
     objc_msgSend_stret: bool, optional (default is False)
         indicate an objc_msgSend_stret
         
     Parameters
     ----------
     register: Register
         from which `Register` to get_from_idx the argument
     '''
     res = None
     if self.cpu.fetches_all_arguments_from_stack():
         res = self.stack.pop()
         # pop address for structure return from stack
         if objc_msgSend_stret:
             log.debug(
                 'popped structure return address from stack due to objc_msgSend_stret(). Popped: %s',
                 res)
             res = self.stack.pop()
     else:
         res = self.get_registers().get_value_for_register(register)
     return res
Example #3
0
 def store_self_cmd_for_stack_fetching_cpu(self, objc_class, selector):
     ''' Store self and _cmd in the appropriate place '''
     # self and _cmd are the first arguments on the stack -> push it
     cpu = self.cpu
     cpu.memory.registers.set_value_for_register_ann_method_ead(
         StackVar('arg_0'), objc_class)
     cpu.memory.registers.set_value_for_register_ann_method_ead(
         StackVar('arg_4'), selector)
     log.debug('added self and _cmd as StackVar: %s', cpu.memory.registers)
Example #4
0
    def read_assignment(self, asmline):
        '''
        Read an assignment and try to split it.
        
        Parameters
        ----------
        asmline : string
            line of assembler
        '''
        self.reset()
        sp_sub_val = self.parse_util.parse_stack_pointer_sub(asmline)
        # do not read e.g. "sub sp, #0x14"
        if sp_sub_val is None:
            op, val1, val2 = self.parse_util.parse_assignment_split(asmline)
            
            if None not in (op, val1, val2):
                
                # switch sides if "str" command
                # "str r0, [r4, r5]" -> "str [r4, r5], r0"
                # "str r0, [r4, r5]" means r4.r5 = r0
                if op.find('str') != -1:
                    val1, val2 = val2, val1
                
                self._log_assignment_split(val1, val2)

                # stack push and fetch                
                self._process_stack_push_via_stm(asmline)
                self._process_stack_push_via_str(val1)
                self._process_stack_fetch(val2)
                
                var_assign1 = self.parse_util.parse_var_assignment_without_ivar_ref_from_asmline(val1)
                
                pu = self.parse_util
                val2_remaining_types = [pu.parse_ivar, pu.parse_objc_class,
                                       pu.parse_selector, pu.parse_string,
                                       pu.parse_imp, pu.parse_var_assignment_without_ivar_ref_from_asmline]
                
                lobject = self._get_val1_value(val1, var_assign1)
                self.lobject = lobject
                robject = None
                if not self.is_stack_fetch:
                    robject = self._get_val2_value(val2, val2_remaining_types)
                else:
                    robject = self.get_current_stackframe_for_fetch()[self.stack_address]
                    log.debug('fetched %s from stack at address: %s', robject, hex(self.stack_address))
                self.robject = robject
                
                # save register in args_since_last_call to determine number of register arguments needed for next call
                if self.cpu.c_func_heuristic:
                    self.add_reg_arg_since_last_call(lobject, robject)    
                
                # set pushed objects
                if self.stack_push_via_str:
                    # pushed object is right object
                    # consider e.g. "str        r5, [sp]", val2 = r5 and val1 = sp (due to reordering) 
                    self.stack_push_objects = [self.robject]
Example #5
0
 def _read_call_line(self, asmline):
     ''' Read a call line like e.g. "call qword [ds:objc_msg_alloc] ; @selector(alloc)" 
     ,split to what is called and interpret the called stuff.
     '''
     called = self.parse_util.parse_call_instruction(asmline)
     if called is not None:
         if self.c_func_heuristic:
             log.debug(
                 'args since last call: \n%s', ', '.join(
                     str(x) for x in self.assignment_matching_system.
                     get_usage_since_last_call()))
         self._read_called(asmline, called)
         self.assignment_matching_system.clear_usage_since_last_call()
Example #6
0
 def read_return_important_objc_call(self, imp, destination,
                                     return_register):
     ''' Read a line which seems to return something and store it in the return register. 
     
     Returns
     -------
     is return important
     '''
     is_return_important = imp.is_return_important()
     if is_return_important:
         log.debug('%s = %s', return_register, destination)
         self.cpu.memory.registers.set_value_for_register(
             return_register, destination)
     return is_return_important
Example #7
0
 def add(self, value, address):
     '''
     Add an object to the `StackFrame`.
     The stack grows to higher addresses, 
     meaning that the top of the stack is always the value with the lowest address.
     The order in which the elements are added does not play any role.
     The order is given by the `address`.
     
     Parameters
     ----------
     address: int
     '''
     msg = 'stack frame at %s' % hex(address)
     hopanno.annotate_assignment(msg, value, self.cpu.address)
     log.debug(msg)
     self.stack[address] = value
 def process_stack_push(self):
     ''' Push the objects onto the stack '''
     if self.get_is_stack_push():
         cpu = self.cpu
         pushed_objects = self.get_stack_push_objects()
         address = self.get_stack_address()
         try:
             for obj in pushed_objects:
                 # resolve register value
                 if isinstance(obj, Register):
                     obj = cpu.memory.registers.resolve_current_register_value(obj)
                 log.debug('pushing %s on StackFrame at address: %s' % (obj, address))
                 cpu.memory.stack.add(obj, address)        
                 address += self.get_cpu().pointer_size()
             self.stack_address = address
         except TypeError:
             pass
Example #9
0
 def create_and_store_remaining_method_selector_arguments(
         self, start_addr, method_selector):
     '''
     Create and store the remaining arguments for the `method_selector`.
     There will be as many arguments created as the `Selector` still needs
     and stored as `StackVar`.
     
     Parameters
     ----------
     start_addr: int
         the suffix for the first `StackVar` argument (e.g. arg_4) 
     method_selector: Selector
         the `Selector` describing the method that is currently being read by the `Cpu`
     
     Raises
     ------
     SelectorOverloadedException
         if selector has more arguments than it needs
     '''
     cpu = self.cpu
     if method_selector is not None:
         STACKVAR_PREFIX = 'arg'
         addr = start_addr
         start = method_selector.cnt_has_arguments() + 1
         for i in range(start, method_selector.cnt_needs_arguments() + 1):
             method_sel_arg = ObjectiveCRuntime.create_method_selector_arg(
                 i)
             # cut off the 0x prefix and use upper case for rest of address
             stackvar_name = '%s_%s' % (
                 STACKVAR_PREFIX, Util.hex_string_without_0x(addr).upper())
             # save argument as StackVar
             stackvar = StackVar(stackvar_name)
             cpu.memory.registers.set_value_for_register_ann_method_ead(
                 stackvar, method_sel_arg)
             method_selector.add_argument(method_sel_arg)
             log.debug('method_selector arg %s = %s', method_sel_arg,
                       stackvar)
             addr += cpu.pointer_size()
Example #10
0
    def _process_var_assignment(self, var_assign):
        '''
        Process the `VarAssignment` and keep track of the instance variable cache
        as well as setting the superclass if available.
        
        Parameters
        ----------
        var_assign: VarAssignment
        '''
        if var_assign is not None:
            # set entry in IVarLookup if VarAssignment contains IVar
            ivar = var_assign.get_ivar_value()
            ivar_ref = ivar.get_ivar_ref()

            self.objc_runtime.set_superclass(ivar)

            if ivar is not None:
                if ivar.create_ivar_lookup_entry(
                        self.objc_runtime.ivar_lookup):
                    msg = '%s -> %s' % (ivar.get_ivar_class(), ivar_ref)
                    log.debug(msg)
                    hopanno.annotate_other_assignment(
                        'added ivar lookup entry: ' + msg, self.address)
                    hopanno.annotate_assignment_method_head(msg)
Example #11
0
 def __init__(self, file_path):
     self.__abs_file_path = abspath(file_path)
     # set None to use the `reopen_file` method
     self.__file_obj = None
     log.debug('file path: %s' % (self.get_abs_file_path()))
     self.reopen_file()
Example #12
0
    def _read_called(self, asmline, called):
        ''' Read a called line like e.g. "qword [ds:objc_msg_alloc] ; @selector(alloc)"
        and hand it on to the appropriate read method.
        
        Parameters
        ----------
        asmline: string
        called: ImpStub, Selector or string
        
        Raises
        ------
        IvarRefWrongTypeException
            if the ivar_ref has the wrong type
        CpuException
            if the value of the destination register could not be resolved
        CpuCouldNotReadLogFuncException
            if the NSLog could not be read
        CpuCouldNotReadMsgSendEception
            if the objc_msgsend() could not be read
        '''
        # called does not have to be a string due to the fact that read_register()
        # will recall this method with register resolved and passed as called
        imp = selector = destination = None
        if isinstance(called, str):
            imp = self.parse_util.parse_imp(called)
            # called can be e.g. _foo or sub_foo
            if not imp:
                called_method = self.parse_util.parse_called_method_name(
                    called)
                if called_method is not None:
                    called = called_method
                else:
                    if self.ignore_hex_addr_call(
                    ) and self.parse_util.parse_hex(called):
                        return

        # called can be Selector or Imp
        else:
            if isinstance(called, Imp):
                imp = called
            elif isinstance(called, Selector):
                selector = called
        try:
            # only pop from stack if really needed for a MsgSend
            if imp is not None and imp.is_any_msg_send(
            ) and self.fetches_all_arguments_from_stack(
            ) or not self.fetches_all_arguments_from_stack():
                is_msg_send_stret = False
                if imp is not None:
                    is_msg_send_stret = imp.is_any_msg_send_stret()
                destination = self.get_current_destination(is_msg_send_stret)

            return_register = self.return_register()

            # if selector is not None, it's already resolved
            if selector is None:
                selector = self.parse_util.parse_selector(asmline)

            register = None
            if isinstance(called, str):
                # same as above, called can be anything else than a string
                register = self.parse_util.parse_register(called)

            log.debug('destination is %s', destination)

            # destination is VarAssignment
            if isinstance(destination, VarAssignment):
                # get ivar value from VarAssignment
                destination = destination.get_ivar_value()

            if imp is not None:
                msgSend = self.objc_runtime.read_msg_send(imp, destination)
                if msgSend is None:
                    if self.objc_runtime.read_return_important_objc_call(
                            imp, destination, return_register):
                        return
                    if self._read_formatstring_log(imp):
                        return
                    else:
                        self._read_remaining_stub(imp)
            else:
                if self._read_c_func(called):
                    return
                else:
                    self._read_register(register, asmline)
                if not self.objc_runtime.read_selector(selector, destination):
                    # TODO: HOW TO HANDLE (fp)([NSURLRequest class], selx, YES, host); ???
                    # called can be something else due to recalling of `_read_register`
                    if isinstance(called, str):
                        own_c_method_call = ParseUtil.parse_own_c_method_called(
                            called)
                        if own_c_method_call is not None:
                            self._read_own_c_func_call(own_c_method_call)

        except CpuCouldNotGetDestination as e:
            raise CpuException(self, e), None, sys.exc_info()[2]
Example #13
0
    def _read_meth_impl(self, asmline):
        ''' Read a line like e.g. "methImpl_AppDelegate_applicationDidFinishLaunching_"
        and detect the current class (AppDelegate) as well as the current method (applicationDidFinishLaunching_)
        
        If the `Cpu` does not fetch all of its arguments from stack, 
        self and _cmd will be set to the `Register`s defined by `CallingConventionsInterface`.
        
        Otherwise implement `store_self_cmd_for_stack_fetching_cpu` in your `Cpu`.
        
        Raises
        ------
        CpuCouldNotGetSelfref
            raised if the self reference could not be detected
        SelectorOverloadedException
            raised if the selector has more arguments than it needs
        SelectorUnderloadedException
            raised if the selector has less arguments than it needs 
            '''
        method_implementation, match = self.parse_util.parse_any_method_implementation(
            asmline)
        is_meth_impl, is_own_c_method_def, is_c_method, is_category = method_implementation

        method_impl = None
        if any([is_meth_impl, is_category]):
            method_impl = match
            selector = match.fst_selector()
            objc_class = match.msg_receiver

            if not self.fetches_all_arguments_from_stack():
                selfref_reg = self.destination_register()
                if selfref_reg is not None:
                    # store class for self reference
                    self.set_selfref_reg_value(objc_class)
                else:
                    raise CpuCouldNotGetSelfref(
                        self, selfref_reg, '%s.%s' % (objc_class, selector))
            else:
                # set self and _cmd for cpu fetching its args from stack
                self.objc_runtime.store_self_cmd_for_stack_fetching_cpu(
                    objc_class, selector)

            self.objc_runtime.create_and_store_method_selector_arguments(
                selector)
            # set method to use it in _try_log_reading_meth_impl
            self.method = method_impl

            # log method
            self._try_log_reading_meth_impl(selector)

            self.set_method_selector_reg_value(selector)

        elif is_c_method or is_own_c_method_def:
            log.debug('reading method: %s', match)
            method_impl = match
            self.create_and_store_c_func_arguments(method_impl)

        if any(method_implementation):
            clilog.info('reading method: %s', method_impl)
            # set method and sender for `MethodCall`
            self.method = method_impl
            self.method_call.sender = method_impl
 def _log_assignment_split(self, val1, val2):
     ''' Use to log the assignment after splitting to its operands. Mostly values after comma '''
     log.debug('assignment split: (%s, %s)', val1, val2)