angr/analyses/girlscout.py
def _determinebase_address(self):
        """
        The basic idea is simple: start from a specific point, try to construct
        functions as much as we can, and maintain a function distribution graph
        and a call graph simultaneously. Repeat searching until we come to the
        end that there is no new function to be found.
        A function should start with:
            # some addresses that a call exit leads to, or
            # certain instructions. They are recoreded in SimArch.

        For a better performance, instead of blindly scanning the entire process
        space, we first try to search for instruction patterns that a function
        may start with, and start scanning at those positions. Then we try to
        decode anything that is left.
        """

        traced_address = set()
        self.functions = set()
        self.call_map = networkx.DiGraph()
        self.cfg = networkx.DiGraph()
        initial_state = self.project.factory.blank_state(mode="fastpath")
        initial_options = initial_state.options - { o.TRACK_CONSTRAINTS } - o.refs
        initial_options |= { o.SUPER_FASTPATH }
        # initial_options.remove(o.COW_STATES)
        initial_state.options = initial_options
        # Sadly, not all calls to functions are explicitly made by call
        # instruction - they could be a jmp or b, or something else. So we
        # should record all exits from a single function, and then add
        # necessary calling edges in our call map during the post-processing
        # phase.
        function_exits = defaultdict(set)

        dump_file_prefix = self.project.filename

        if self._pickle_intermediate_results and \
                os.path.exists(dump_file_prefix + "_indirect_jumps.angr"):
            l.debug("Loading existing intermediate results.")
            self._indirect_jumps = pickle.load(open(dump_file_prefix + "_indirect_jumps.angr", "rb"))
            self.cfg = pickle.load(open(dump_file_prefix + "_coercecfg.angr", "rb"))
            self._unassured_functions = pickle.load(open(dump_file_prefix + "_unassured_functions.angr", "rb"))
        else:
            # Performance boost :-)
            # Scan for existing function prologues
            self._scan_function_prologues(traced_address, function_exits, initial_state)

            if self._pickle_intermediate_results:
                l.debug("Dumping intermediate results.")
                pickle.dump(self._indirect_jumps, open(dump_file_prefix + "_indirect_jumps.angr", "wb"), -1)
                pickle.dump(self.cfg, open(dump_file_prefix + "_coercecfg.angr", "wb"), -1)
                pickle.dump(self._unassured_functions, open(dump_file_prefix + "_unassured_functions.angr", "wb"), -1)

        if len(self._indirect_jumps):
            # We got some indirect jumps!
            # Gotta execute each basic block and see where it wants to jump to
            function_starts = self._process_indirect_jumps()

            self.base_address = self._solve_forbase_address(function_starts, self._unassured_functions)

            l.info("Base address should be 0x%x", self.base_address)

        else:
            l.debug("No indirect jumps are found. We switch to the slowpath mode.")
            # TODO: Slowpath mode...
            while True:
                next_addr = self._get_next_code_addr(initial_state)
                percentage = self._seg_list.occupied_size * 100.0 / (self._valid_memory_region_size)
                l.info("Analyzing %xh, progress %0.04f%%", next_addr, percentage)
                if next_addr is None:
                    break

                self.call_map.add_node(next_addr)

                self._scan_code(traced_address, function_exits, initial_state, next_addr)

        # Post-processing: Map those calls that are not made by call/blr
        # instructions to their targets in our map
        for src, s in function_exits.items():
            if src in self.call_map:
                for target in s:
                    if target in self.call_map:
                        self.call_map.add_edge(src, target)

        nodes = sorted(self.call_map.nodes())
        for i in range(len(nodes) - 1):
            if nodes[i] >= nodes[i + 1] - 4:
                for dst in self.call_map.successors(nodes[i + 1]):
                    self.call_map.add_edge(nodes[i], dst)
                for src in self.call_map.predecessors(nodes[i + 1]):
                    self.call_map.add_edge(src, nodes[i])
                self.call_map.remove_node(nodes[i + 1])

        l.debug("Construction finished.")

        The basic idea is simple: start from a specific point, try to construct
        functions as much as we can, and maintain a function distribution graph
        and a call graph simultaneously. Repeat searching until we come to the
        end that there is no new function to be found.
        A function should start with:
            # some addresses that a call exit leads to, or
            # certain instructions. They are recoreded in SimArch.

        For a better performance, instead of blindly scanning the entire process
        space, we first try to search for instruction patterns that a function
        may start with, and start scanning at those positions. Then we try to
        decode anything that is left.
        
The basic idea is simple: start from a specific point, try to construct
functions as much as we can, and maintain a function distribution graph
and a call graph simultaneously. Repeat searching until we come to the
end that there is no new function to be found.
A function should start with:
some addresses that a call exit leads to, or
certain instructions. They are recoreded in SimArch.

For a better performance, instead of blindly scanning the entire process
space, we first try to search for instruction patterns that a function
may start with, and start scanning at those positions. Then we try to
decode anything that is left.
The basic idea is simple : start from a specific point try to construct functions as much as we can and maintain a function distribution graph and a call graph simultaneously . Repeat searching until we come to the end that there is no new function to be found . A function should start with : some addresses that a call exit leads to or certain instructions . They are recoreded in SimArch . For a better performance instead of blindly scanning the entire process space we first try to search for instruction patterns that a function may start with and start scanning at those positions . Then we try to decode anything that is left .
def _determinebase_address(self):
        traced_address = set()
        self.functions = set()
        self.call_map = networkx.DiGraph()
        self.cfg = networkx.DiGraph()
        initial_state = self.project.factory.blank_state(mode="fastpath")
        initial_options = initial_state.options - { o.TRACK_CONSTRAINTS } - o.refs
        initial_options |= { o.SUPER_FASTPATH }
        initial_state.options = initial_options
        function_exits = defaultdict(set)
        dump_file_prefix = self.project.filename
        if self._pickle_intermediate_results and \
                os.path.exists(dump_file_prefix + "_indirect_jumps.angr"):
            l.debug("Loading existing intermediate results.")
            self._indirect_jumps = pickle.load(open(dump_file_prefix + "_indirect_jumps.angr", "rb"))
            self.cfg = pickle.load(open(dump_file_prefix + "_coercecfg.angr", "rb"))
            self._unassured_functions = pickle.load(open(dump_file_prefix + "_unassured_functions.angr", "rb"))
        else:
            self._scan_function_prologues(traced_address, function_exits, initial_state)
            if self._pickle_intermediate_results:
                l.debug("Dumping intermediate results.")
                pickle.dump(self._indirect_jumps, open(dump_file_prefix + "_indirect_jumps.angr", "wb"), -1)
                pickle.dump(self.cfg, open(dump_file_prefix + "_coercecfg.angr", "wb"), -1)
                pickle.dump(self._unassured_functions, open(dump_file_prefix + "_unassured_functions.angr", "wb"), -1)
        if len(self._indirect_jumps):
            function_starts = self._process_indirect_jumps()
            self.base_address = self._solve_forbase_address(function_starts, self._unassured_functions)
            l.info("Base address should be 0x%x", self.base_address)
        else:
            l.debug("No indirect jumps are found. We switch to the slowpath mode.")
            while True:
                next_addr = self._get_next_code_addr(initial_state)
                percentage = self._seg_list.occupied_size * 100.0 / (self._valid_memory_region_size)
                l.info("Analyzing %xh, progress %0.04f%%", next_addr, percentage)
                if next_addr is None:
                    break
                self.call_map.add_node(next_addr)
                self._scan_code(traced_address, function_exits, initial_state, next_addr)
        for src, s in function_exits.items():
            if src in self.call_map:
                for target in s:
                    if target in self.call_map:
                        self.call_map.add_edge(src, target)
        nodes = sorted(self.call_map.nodes())
        for i in range(len(nodes) - 1):
            if nodes[i] >= nodes[i + 1] - 4:
                for dst in self.call_map.successors(nodes[i + 1]):
                    self.call_map.add_edge(nodes[i], dst)
                for src in self.call_map.predecessors(nodes[i + 1]):
                    self.call_map.add_edge(src, nodes[i])
                self.call_map.remove_node(nodes[i + 1])
        l.debug("Construction finished.")
def _determinebase_address ( self ) : traced_address = set ( ) self . functions = set ( ) self . call_map = networkx . DiGraph ( ) self . cfg = networkx . DiGraph ( ) initial_state = self . project . factory . blank_state ( mode = "fastpath" ) initial_options = initial_state . options - { o . TRACK_CONSTRAINTS } - o . refs initial_options |= { o . SUPER_FASTPATH } initial_state . options = initial_options function_exits = defaultdict ( set ) dump_file_prefix = self . project . filename if self . _pickle_intermediate_results and os . path . exists ( dump_file_prefix + "_indirect_jumps.angr" ) : l . debug ( "Loading existing intermediate results." ) self . _indirect_jumps = pickle . load ( open ( dump_file_prefix + "_indirect_jumps.angr" , "rb" ) ) self . cfg = pickle . load ( open ( dump_file_prefix + "_coercecfg.angr" , "rb" ) ) self . _unassured_functions = pickle . load ( open ( dump_file_prefix + "_unassured_functions.angr" , "rb" ) ) else : self . _scan_function_prologues ( traced_address , function_exits , initial_state ) if self . _pickle_intermediate_results : l . debug ( "Dumping intermediate results." ) pickle . dump ( self . _indirect_jumps , open ( dump_file_prefix + "_indirect_jumps.angr" , "wb" ) , - 1 ) pickle . dump ( self . cfg , open ( dump_file_prefix + "_coercecfg.angr" , "wb" ) , - 1 ) pickle . dump ( self . _unassured_functions , open ( dump_file_prefix + "_unassured_functions.angr" , "wb" ) , - 1 ) if len ( self . _indirect_jumps ) : function_starts = self . _process_indirect_jumps ( ) self . base_address = self . _solve_forbase_address ( function_starts , self . _unassured_functions ) l . info ( "Base address should be 0x%x" , self . base_address ) else : l . debug ( "No indirect jumps are found. We switch to the slowpath mode." ) while True : next_addr = self . _get_next_code_addr ( initial_state ) percentage = self . _seg_list . occupied_size * 100.0 / ( self . _valid_memory_region_size ) l . info ( "Analyzing %xh, progress %0.04f%%" , next_addr , percentage ) if next_addr is None : break self . call_map . add_node ( next_addr ) self . _scan_code ( traced_address , function_exits , initial_state , next_addr ) for src , s in function_exits . items ( ) : if src in self . call_map : for target in s : if target in self . call_map : self . call_map . add_edge ( src , target ) nodes = sorted ( self . call_map . nodes ( ) ) for i in range ( len ( nodes ) - 1 ) : if nodes [ i ] >= nodes [ i + 1 ] - 4 : for dst in self . call_map . successors ( nodes [ i + 1 ] ) : self . call_map . add_edge ( nodes [ i ] , dst ) for src in self . call_map . predecessors ( nodes [ i + 1 ] ) : self . call_map . add_edge ( src , nodes [ i ] ) self . call_map . remove_node ( nodes [ i + 1 ] ) l . debug ( "Construction finished." )
The basic idea is simple: start from a specific point, try to construct
functions as much as we can, and maintain a function distribution graph
and a call graph simultaneously.
The basic idea is simple : start from a specific point try to construct functions as much as we can and maintain a function distribution graph and a call graph simultaneously .
"""
        The basic idea is simple: start from a specific point, try to construct
        functions as much as we can, and maintain a function distribution graph
        and a call graph simultaneously. Repeat searching until we come to the
        end that there is no new function to be found.
        A function should start with:
            # some addresses that a call exit leads to, or
            # certain instructions. They are recoreded in SimArch.

        For a better performance, instead of blindly scanning the entire process
        space, we first try to search for instruction patterns that a function
        may start with, and start scanning at those positions. Then we try to
        decode anything that is left.
        """ # initial_options.remove(o.COW_STATES) # Sadly, not all calls to functions are explicitly made by call # instruction - they could be a jmp or b, or something else. So we # should record all exits from a single function, and then add # necessary calling edges in our call map during the post-processing # phase. # Performance boost :-) # Scan for existing function prologues # We got some indirect jumps! # Gotta execute each basic block and see where it wants to jump to # TODO: Slowpath mode... # Post-processing: Map those calls that are not made by call/blr # instructions to their targets in our map
self