(self, function, data_graph)
If an argument passed from the stack (i.e. dword ptr [ebp+4h]) is saved to a local variable on the stack at the
        beginning of the function, and this local variable was never modified anywhere in this function, and no pointer
        of any stack variable is saved in any register, then we can replace all references to this local variable to
        that argument instead.

        :param function:
        :param networkx.MultiDiGraph data_graph:
        :return:
If an argument passed from the stack (i.e. dword ptr [ebp+4h]) is saved to a local variable on the stack at the
        beginning of the function, and this local variable was never modified anywhere in this function, and no pointer
        of any stack variable is saved in any register, then we can replace all references to this local variable to
        that argument instead.
If an argument passed from the stack ( i . e . dword ptr [ ebp + 4h ] ) is saved to a local variable on the stack at the beginning of the function and this local variable was never modified anywhere in this function and no pointer of any stack variable is saved in any register then we can replace all references to this local variable to that argument instead .
def _redundant_stack_variable_removal(self, function, data_graph):
        """
        If an argument passed from the stack (i.e. dword ptr [ebp+4h]) is saved to a local variable on the stack at the
        beginning of the function, and this local variable was never modified anywhere in this function, and no pointer
        of any stack variable is saved in any register, then we can replace all references to this local variable to
        that argument instead.

        :param function:
        :param networkx.MultiDiGraph data_graph:
        :return:
        """

        # check if there is any stack pointer being stored into any register other than esp
        # basically check all consumers of stack pointers
        stack_ptrs = [ ]
        sp_offset = self.project.arch.registers['esp'][0]
        bp_offset = self.project.arch.registers['ebp'][0]
        for n in data_graph.nodes():
            if isinstance(n.variable, SimRegisterVariable) and n.variable.reg in (sp_offset, bp_offset):
                stack_ptrs.append(n)

        # for each stack pointer variable, make sure none of its consumers is a general purpose register
        for stack_ptr in stack_ptrs:
            out_edges = data_graph.out_edges(stack_ptr, data=True)
            for _, dst, data in out_edges:
                if 'type' in data and data['type'] == 'kill':
                    # we don't care about killing edges
                    continue
                if isinstance(dst.variable, SimRegisterVariable) and dst.variable.reg < 40 and \
                        dst.variable.reg not in (sp_offset, bp_offset):
                    # oops
                    l.debug('Function %s does not satisfy requirements of redundant stack variable removal.',
                            repr(function)
                            )
                    return

        argument_variables = [ ]

        for n in data_graph.nodes():
            if isinstance(n.variable, SimStackVariable) and n.variable.base == 'bp' and n.variable.offset >= 0:
                argument_variables.append(n)

        if not argument_variables:
            return

        #print function
        #print argument_variables

        argument_to_local = { }
        argument_register_as_retval = set()

        # for each argument, find its correspondence on the local stack frame

        for argument_variable in argument_variables:
            # is it copied to the stack?
            successors0 = list(data_graph.successors(argument_variable))

            if not successors0:
                continue

            if len(successors0) != 1:
                continue

            if isinstance(successors0[0].variable, SimRegisterVariable):
                # argument -> register -> stack
                out_edges = data_graph.out_edges(successors0[0], data=True)
                successors1 = [ s for _, s, data in out_edges if 'type' not in data or data['type'] != 'kill' ]
                if len(successors1) == 1:
                    successor1 = successors1[0]
                    if isinstance(successor1.variable, SimStackVariable):
                        if (successor1.variable.base == 'sp' and successor1.variable.offset > 0) or \
                                (successor1.variable.base == 'bp' and successor1.variable.offset < 0):
                            # yes it's copied onto the stack!
                            argument_to_local[argument_variable] = successor1

                # if the register is eax, and it's not killed later, it might be the return value of this function
                # in that case, we cannot eliminate the instruction that moves stack argument to that register
                if successors0[0].variable.reg == self.project.arch.registers['eax'][0]:
                    killers = [ s for _, s, data in out_edges if 'type' in data and data['type'] == 'kill']
                    if not killers:
                        # it might be the return value
                        argument_register_as_retval.add(argument_variable)

            else:
                # TODO:
                import ipdb; ipdb.set_trace()

        #import pprint
        #pprint.pprint(argument_to_local, width=160)

        # find local correspondence that are not modified throughout this function
        redundant_stack_variables = [ ]

        for argument, local_var in argument_to_local.items():
            # local_var cannot be killed anywhere
            out_edges = data_graph.out_edges(local_var, data=True)

            consuming_locs = [ ]

            for _, consumer, data in out_edges:
                consuming_locs.append(consumer.location)
                if 'type' in data and data['type'] == 'kill':
                    break
            else:
                # no killing edges. the value is not changed!
                rsv = RedundantStackVariable(argument, local_var, consuming_locs)
                if argument in argument_register_as_retval:
                    rsv.argument_register_as_retval = True
                redundant_stack_variables.append(rsv)

        self.redundant_stack_variables.extend(redundant_stack_variables)
def _redundant_stack_variable_removal ( self , function , data_graph ) : # check if there is any stack pointer being stored into any register other than esp # basically check all consumers of stack pointers stack_ptrs = [ ] sp_offset = self . project . arch . registers [ 'esp' ] [ 0 ] bp_offset = self . project . arch . registers [ 'ebp' ] [ 0 ] for n in data_graph . nodes ( ) : if isinstance ( n . variable , SimRegisterVariable ) and n . variable . reg in ( sp_offset , bp_offset ) : stack_ptrs . append ( n ) # for each stack pointer variable, make sure none of its consumers is a general purpose register for stack_ptr in stack_ptrs : out_edges = data_graph . out_edges ( stack_ptr , data = True ) for _ , dst , data in out_edges : if 'type' in data and data [ 'type' ] == 'kill' : # we don't care about killing edges continue if isinstance ( dst . variable , SimRegisterVariable ) and dst . variable . reg < 40 and dst . variable . reg not in ( sp_offset , bp_offset ) : # oops l . debug ( 'Function %s does not satisfy requirements of redundant stack variable removal.' , repr ( function ) ) return argument_variables = [ ] for n in data_graph . nodes ( ) : if isinstance ( n . variable , SimStackVariable ) and n . variable . base == 'bp' and n . variable . offset >= 0 : argument_variables . append ( n ) if not argument_variables : return #print function #print argument_variables argument_to_local = { } argument_register_as_retval = set ( ) # for each argument, find its correspondence on the local stack frame for argument_variable in argument_variables : # is it copied to the stack? successors0 = list ( data_graph . successors ( argument_variable ) ) if not successors0 : continue if len ( successors0 ) != 1 : continue if isinstance ( successors0 [ 0 ] . variable , SimRegisterVariable ) : # argument -> register -> stack out_edges = data_graph . out_edges ( successors0 [ 0 ] , data = True ) successors1 = [ s for _ , s , data in out_edges if 'type' not in data or data [ 'type' ] != 'kill' ] if len ( successors1 ) == 1 : successor1 = successors1 [ 0 ] if isinstance ( successor1 . variable , SimStackVariable ) : if ( successor1 . variable . base == 'sp' and successor1 . variable . offset > 0 ) or ( successor1 . variable . base == 'bp' and successor1 . variable . offset < 0 ) : # yes it's copied onto the stack! argument_to_local [ argument_variable ] = successor1 # if the register is eax, and it's not killed later, it might be the return value of this function # in that case, we cannot eliminate the instruction that moves stack argument to that register if successors0 [ 0 ] . variable . reg == self . project . arch . registers [ 'eax' ] [ 0 ] : killers = [ s for _ , s , data in out_edges if 'type' in data and data [ 'type' ] == 'kill' ] if not killers : # it might be the return value argument_register_as_retval . add ( argument_variable ) else : # TODO: import ipdb ipdb . set_trace ( ) #import pprint #pprint.pprint(argument_to_local, width=160) # find local correspondence that are not modified throughout this function redundant_stack_variables = [ ] for argument , local_var in argument_to_local . items ( ) : # local_var cannot be killed anywhere out_edges = data_graph . out_edges ( local_var , data = True ) consuming_locs = [ ] for _ , consumer , data in out_edges : consuming_locs . append ( consumer . location ) if 'type' in data and data [ 'type' ] == 'kill' : break else : # no killing edges. the value is not changed! rsv = RedundantStackVariable ( argument , local_var , consuming_locs ) if argument in argument_register_as_retval : rsv . argument_register_as_retval = True redundant_stack_variables . append ( rsv ) self . redundant_stack_variables . extend ( redundant_stack_variables )