(self, func, callsites)
:param func:
        :param callsites:
        :return:
def _refine_function_arguments(self, func, callsites):
        """

        :param func:
        :param callsites:
        :return:
        """

        for i, c in enumerate(callsites):
            # Execute one block ahead of the callsite, and execute one basic block after the callsite
            # In this process, the following tasks are performed:
            # - Record registers/stack variables that are modified
            # - Record the change of the stack pointer
            # - Check if the return value is used immediately
            # We assume that the stack is balanced before and after the call (you can have caller clean-up of course).
            # Any abnormal behaviors will be logged.
            # Hopefully this approach will allow us to have a better understanding of parameters of those function
            # stubs and function proxies.

            if c.simprocedure_name is not None:
                # Skip all SimProcedures
                continue

            l.debug("Refining %s at 0x%x (%d/%d).", repr(func), c.addr, i, len(callsites))

            # Get a basic block ahead of the callsite
            blocks_ahead = [c]

            # the block after
            blocks_after = []
            successors = self.get_successors_and_jumpkind(c, excluding_fakeret=False)
            for s, jk in successors:
                if jk == 'Ijk_FakeRet':
                    blocks_after = [s]
                    break

            regs_overwritten = set()
            stack_overwritten = set()
            regs_read = set()
            regs_written = set()

            try:
                # Execute the predecessor
                path = self.project.factory.path(
                    self.project.factory.blank_state(mode="fastpath", addr=blocks_ahead[0].addr))
                path.step()
                all_successors = path.next_run.successors + path.next_run.unsat_successors
                if not all_successors:
                    continue

                suc = all_successors[0]
                se = suc.solver
                # Examine the path log
                actions = suc.history.recent_actions
                sp = se.eval_one(suc.regs.sp, default=0) + self.project.arch.call_sp_fix
                for ac in actions:
                    if ac.type == "reg" and ac.action == "write":
                        regs_overwritten.add(ac.offset)
                    elif ac.type == "mem" and ac.action == "write":
                        addr = se.eval_one(ac.addr.ast, default=0)
                        if (self.project.arch.call_pushes_ret and addr >= sp + self.project.arch.bytes) or \
                                (not self.project.arch.call_pushes_ret and addr >= sp):
                            offset = addr - sp
                            stack_overwritten.add(offset)

                func.prepared_registers.add(tuple(regs_overwritten))
                func.prepared_stack_variables.add(tuple(stack_overwritten))

            except (SimError, AngrError):
                pass

            try:
                if blocks_after:
                    path = self.project.factory.path(
                        self.project.factory.blank_state(mode="fastpath", addr=blocks_after[0].addr))
                    path.step()
                    all_successors = path.next_run.successors + path.next_run.unsat_successors
                    if not all_successors:
                        continue

                    suc = all_successors[0]
                    actions = suc.history.recent_actions
                    for ac in actions:
                        if ac.type == "reg" and ac.action == "read" and ac.offset not in regs_written:
                            regs_read.add(ac.offset)
                        elif ac.type == "reg" and ac.action == "write":
                            regs_written.add(ac.offset)

                    # Filter registers, remove unnecessary registers from the set
                    # regs_overwritten = self.project.arch.argument_registers.intersection(regs_overwritten)
                    regs_read = self.project.arch.argument_registers.intersection(regs_read)

                    func.registers_read_afterwards.add(tuple(regs_read))

            except (SimError, AngrError):
                pass
def _refine_function_arguments ( self , func , callsites ) : for i , c in enumerate ( callsites ) : # Execute one block ahead of the callsite, and execute one basic block after the callsite # In this process, the following tasks are performed: # - Record registers/stack variables that are modified # - Record the change of the stack pointer # - Check if the return value is used immediately # We assume that the stack is balanced before and after the call (you can have caller clean-up of course). # Any abnormal behaviors will be logged. # Hopefully this approach will allow us to have a better understanding of parameters of those function # stubs and function proxies. if c . simprocedure_name is not None : # Skip all SimProcedures continue l . debug ( "Refining %s at 0x%x (%d/%d)." , repr ( func ) , c . addr , i , len ( callsites ) ) # Get a basic block ahead of the callsite blocks_ahead = [ c ] # the block after blocks_after = [ ] successors = self . get_successors_and_jumpkind ( c , excluding_fakeret = False ) for s , jk in successors : if jk == 'Ijk_FakeRet' : blocks_after = [ s ] break regs_overwritten = set ( ) stack_overwritten = set ( ) regs_read = set ( ) regs_written = set ( ) try : # Execute the predecessor path = self . project . factory . path ( self . project . factory . blank_state ( mode = "fastpath" , addr = blocks_ahead [ 0 ] . addr ) ) path . step ( ) all_successors = path . next_run . successors + path . next_run . unsat_successors if not all_successors : continue suc = all_successors [ 0 ] se = suc . solver # Examine the path log actions = suc . history . recent_actions sp = se . eval_one ( suc . regs . sp , default = 0 ) + self . project . arch . call_sp_fix for ac in actions : if ac . type == "reg" and ac . action == "write" : regs_overwritten . add ( ac . offset ) elif ac . type == "mem" and ac . action == "write" : addr = se . eval_one ( ac . addr . ast , default = 0 ) if ( self . project . arch . call_pushes_ret and addr >= sp + self . project . arch . bytes ) or ( not self . project . arch . call_pushes_ret and addr >= sp ) : offset = addr - sp stack_overwritten . add ( offset ) func . prepared_registers . add ( tuple ( regs_overwritten ) ) func . prepared_stack_variables . add ( tuple ( stack_overwritten ) ) except ( SimError , AngrError ) : pass try : if blocks_after : path = self . project . factory . path ( self . project . factory . blank_state ( mode = "fastpath" , addr = blocks_after [ 0 ] . addr ) ) path . step ( ) all_successors = path . next_run . successors + path . next_run . unsat_successors if not all_successors : continue suc = all_successors [ 0 ] actions = suc . history . recent_actions for ac in actions : if ac . type == "reg" and ac . action == "read" and ac . offset not in regs_written : regs_read . add ( ac . offset ) elif ac . type == "reg" and ac . action == "write" : regs_written . add ( ac . offset ) # Filter registers, remove unnecessary registers from the set # regs_overwritten = self.project.arch.argument_registers.intersection(regs_overwritten) regs_read = self . project . arch . argument_registers . intersection ( regs_read ) func . registers_read_afterwards . add ( tuple ( regs_read ) ) except ( SimError , AngrError ) : pass