(self, job)
Before processing a CFGJob.
        Right now each block is traced at most once. If it is traced more than once, we will mark it as "should_skip"
        before tracing it.
        An AngrForwardAnalysisSkipJob exception is raised in order to skip analyzing the job.

        :param CFGJob job: The CFG job object.
        :param dict _locals: A bunch of local variables that will be kept around when handling this job and its
                        corresponding successors.
        :return: None
Before processing a CFGJob.
        Right now each block is traced at most once. If it is traced more than once, we will mark it as "should_skip"
        before tracing it.
        An AngrForwardAnalysisSkipJob exception is raised in order to skip analyzing the job.
Before processing a CFGJob . Right now each block is traced at most once . If it is traced more than once we will mark it as should_skip before tracing it . An AngrForwardAnalysisSkipJob exception is raised in order to skip analyzing the job .
def _pre_job_handling(self, job):  # pylint:disable=arguments-differ
        """
        Before processing a CFGJob.
        Right now each block is traced at most once. If it is traced more than once, we will mark it as "should_skip"
        before tracing it.
        An AngrForwardAnalysisSkipJob exception is raised in order to skip analyzing the job.

        :param CFGJob job: The CFG job object.
        :param dict _locals: A bunch of local variables that will be kept around when handling this job and its
                        corresponding successors.
        :return: None
        """

        # Extract initial info the CFGJob
        job.call_stack_suffix = job.get_call_stack_suffix()
        job.current_function = self.kb.functions.function(job.func_addr, create=True,
                                                          syscall=job.jumpkind.startswith("Ijk_Sys"))
        src_block_id = job.src_block_id
        src_exit_stmt_idx = job.src_exit_stmt_idx
        src_ins_addr = job.src_ins_addr
        addr = job.addr

        # Log this address
        if l.level == logging.DEBUG:
            self._analyzed_addrs.add(addr)

        if addr == job.func_addr:
            # Store the input state of this function
            self._function_input_states[job.func_addr] = job.state

        # deregister this job
        self._deregister_analysis_job(job.func_addr, job)

        # Generate a unique key for this job
        block_id = job.block_id

        # Should we skip tracing this block?
        should_skip = False
        if self._traced_addrs[job.call_stack_suffix][addr] >= self._max_iterations:
            should_skip = True
        elif self._is_call_jumpkind(job.jumpkind) and \
             self._call_depth is not None and \
             len(job.call_stack) > self._call_depth and \
             (self._call_tracing_filter is None or self._call_tracing_filter(job.state, job.jumpkind)):
            should_skip = True

        # SimInspect breakpoints support
        job.state._inspect('cfg_handle_job', BP_BEFORE)

        # Get a SimSuccessors out of current job
        sim_successors, exception_info, _ = self._get_simsuccessors(addr, job, current_function_addr=job.func_addr)

        # determine the depth of this basic block
        if self._max_steps is None:
            # it's unnecessary to track depth when we are not limiting max_steps
            depth = None
        else:
            if src_block_id is None:
                # oh this is the very first basic block on this path
                depth = 0
            else:
                src_cfgnode = self._nodes[src_block_id]
                depth = src_cfgnode.depth + 1
                # the depth will not be updated later on even if this block has a greater depth on another path.
                # consequently, the `max_steps` limit is not veyr precise - I didn't see a need to make it precise
                # though.

        if block_id not in self._nodes:
            # Create the CFGNode object
            cfg_node = self._create_cfgnode(sim_successors, job.call_stack, job.func_addr, block_id=block_id,
                                            depth=depth
                                            )

            self._model.add_node(block_id, cfg_node)

        else:
            # each block_id should only correspond to one CFGNode
            # use the existing CFGNode object
            # note that since we reuse existing CFGNodes, we may miss the following cases:
            #
            # mov eax, label_0       mov ebx, label_1
            #     jmp xxx                 jmp xxx
            #         |                      |
            #         |                      |
            #         *----------------------*
            #                     |
            #                 call eax
            #
            # Since the basic block "call eax" will only be traced once, either label_0 or label_1 will be missed in
            # this case. Indirect jump resolution might be able to get it, but that's another story. Ideally,
            # "call eax" should be traced twice with *different* sim_successors keys, which requires block ID being flow
            # sensitive, but it is way too expensive.

            cfg_node = self._nodes[block_id]

        # Increment tracing count for this block
        self._traced_addrs[job.call_stack_suffix][addr] += 1

        if self._keep_state:
            # TODO: if we are reusing an existing CFGNode, we will be overwriting the original input state here. we
            # TODO: should save them all, which, unfortunately, requires some redesigning :-(
            cfg_node.input_state = sim_successors.initial_state

        # See if this job cancels another FakeRet
        # This should be done regardless of whether this job should be skipped or not, otherwise edges will go missing
        # in the CFG or function transition graphs.
        if job.jumpkind == 'Ijk_FakeRet' or \
                (job.jumpkind == 'Ijk_Ret' and block_id in self._pending_jobs):
            # The fake ret is confirmed (since we are returning from the function it calls). Create an edge for it
            # in the graph.

            the_jobs = [ ]
            if block_id in self._pending_jobs:
                the_jobs = self._pending_jobs.pop(block_id)
                for the_job in the_jobs:  # type: Pendingjob
                    self._deregister_analysis_job(the_job.caller_func_addr, the_job)
            else:
                the_jobs = [job]

            for the_job in the_jobs:
                self._graph_add_edge(the_job.src_block_id, block_id,
                                     jumpkind='Ijk_FakeRet',
                                     stmt_idx=the_job.src_exit_stmt_idx,
                                     ins_addr=src_ins_addr
                                     )
                self._update_function_transition_graph(the_job.src_block_id, block_id,
                                                       jumpkind='Ijk_FakeRet',
                                                       ins_addr=src_ins_addr,
                                                       stmt_idx=the_job.src_exit_stmt_idx,
                                                       confirmed=True
                                                       )

        if sim_successors is None or should_skip:
            # We cannot retrieve the block, or we should skip the analysis of this node
            # But we create the edge anyway. If the sim_successors does not exist, it will be an edge from the previous
            # node to a PathTerminator
            self._graph_add_edge(src_block_id, block_id,
                                 jumpkind=job.jumpkind,
                                 stmt_idx=src_exit_stmt_idx,
                                 ins_addr=src_ins_addr
                                 )
            self._update_function_transition_graph(src_block_id, block_id,
                                                   jumpkind=job.jumpkind,
                                                   ins_addr=src_ins_addr,
                                                   stmt_idx=src_exit_stmt_idx)

            # We are good. Raise the exception and leave
            raise AngrSkipJobNotice()

        self._update_thumb_addrs(sim_successors, job.state)

        # We store the function hints first. Function hints will be checked at the end of the analysis to avoid
        # any duplication with existing jumping targets
        if self._enable_function_hints:
            if sim_successors.sort == 'IRSB' and sim_successors.all_successors:
                function_hints = self._search_for_function_hints(sim_successors.all_successors[0])
                for f in function_hints:
                    self._pending_function_hints.add(f)

        self._graph_add_edge(src_block_id, block_id, jumpkind=job.jumpkind, stmt_idx=src_exit_stmt_idx,
                             ins_addr=src_ins_addr)
        self._update_function_transition_graph(src_block_id, block_id, jumpkind=job.jumpkind,
                                               ins_addr=src_ins_addr, stmt_idx=src_exit_stmt_idx)

        if block_id in self._pending_edges:
            # there are some edges waiting to be created. do it here.
            for src_key, dst_key, data in self._pending_edges[block_id]:
                self._graph_add_edge(src_key, dst_key, **data)
            del self._pending_edges[block_id]

        block_info = {reg: sim_successors.initial_state.registers.load(reg) for reg in self.project.arch.persistent_regs}
        self._block_artifacts[addr] = block_info

        job.cfg_node = cfg_node
        job.sim_successors = sim_successors
        job.exception_info = exception_info

        # For debugging purposes!
        job.successor_status = {}
def _pre_job_handling ( self , job ) : # pylint:disable=arguments-differ # Extract initial info the CFGJob job . call_stack_suffix = job . get_call_stack_suffix ( ) job . current_function = self . kb . functions . function ( job . func_addr , create = True , syscall = job . jumpkind . startswith ( "Ijk_Sys" ) ) src_block_id = job . src_block_id src_exit_stmt_idx = job . src_exit_stmt_idx src_ins_addr = job . src_ins_addr addr = job . addr # Log this address if l . level == logging . DEBUG : self . _analyzed_addrs . add ( addr ) if addr == job . func_addr : # Store the input state of this function self . _function_input_states [ job . func_addr ] = job . state # deregister this job self . _deregister_analysis_job ( job . func_addr , job ) # Generate a unique key for this job block_id = job . block_id # Should we skip tracing this block? should_skip = False if self . _traced_addrs [ job . call_stack_suffix ] [ addr ] >= self . _max_iterations : should_skip = True elif self . _is_call_jumpkind ( job . jumpkind ) and self . _call_depth is not None and len ( job . call_stack ) > self . _call_depth and ( self . _call_tracing_filter is None or self . _call_tracing_filter ( job . state , job . jumpkind ) ) : should_skip = True # SimInspect breakpoints support job . state . _inspect ( 'cfg_handle_job' , BP_BEFORE ) # Get a SimSuccessors out of current job sim_successors , exception_info , _ = self . _get_simsuccessors ( addr , job , current_function_addr = job . func_addr ) # determine the depth of this basic block if self . _max_steps is None : # it's unnecessary to track depth when we are not limiting max_steps depth = None else : if src_block_id is None : # oh this is the very first basic block on this path depth = 0 else : src_cfgnode = self . _nodes [ src_block_id ] depth = src_cfgnode . depth + 1 # the depth will not be updated later on even if this block has a greater depth on another path. # consequently, the `max_steps` limit is not veyr precise - I didn't see a need to make it precise # though. if block_id not in self . _nodes : # Create the CFGNode object cfg_node = self . _create_cfgnode ( sim_successors , job . call_stack , job . func_addr , block_id = block_id , depth = depth ) self . _model . add_node ( block_id , cfg_node ) else : # each block_id should only correspond to one CFGNode # use the existing CFGNode object # note that since we reuse existing CFGNodes, we may miss the following cases: # # mov eax, label_0       mov ebx, label_1 #     jmp xxx                 jmp xxx #         |                      | #         |                      | #         *----------------------* #                     | #                 call eax # # Since the basic block "call eax" will only be traced once, either label_0 or label_1 will be missed in # this case. Indirect jump resolution might be able to get it, but that's another story. Ideally, # "call eax" should be traced twice with *different* sim_successors keys, which requires block ID being flow # sensitive, but it is way too expensive. cfg_node = self . _nodes [ block_id ] # Increment tracing count for this block self . _traced_addrs [ job . call_stack_suffix ] [ addr ] += 1 if self . _keep_state : # TODO: if we are reusing an existing CFGNode, we will be overwriting the original input state here. we # TODO: should save them all, which, unfortunately, requires some redesigning :-( cfg_node . input_state = sim_successors . initial_state # See if this job cancels another FakeRet # This should be done regardless of whether this job should be skipped or not, otherwise edges will go missing # in the CFG or function transition graphs. if job . jumpkind == 'Ijk_FakeRet' or ( job . jumpkind == 'Ijk_Ret' and block_id in self . _pending_jobs ) : # The fake ret is confirmed (since we are returning from the function it calls). Create an edge for it # in the graph. the_jobs = [ ] if block_id in self . _pending_jobs : the_jobs = self . _pending_jobs . pop ( block_id ) for the_job in the_jobs : # type: Pendingjob self . _deregister_analysis_job ( the_job . caller_func_addr , the_job ) else : the_jobs = [ job ] for the_job in the_jobs : self . _graph_add_edge ( the_job . src_block_id , block_id , jumpkind = 'Ijk_FakeRet' , stmt_idx = the_job . src_exit_stmt_idx , ins_addr = src_ins_addr ) self . _update_function_transition_graph ( the_job . src_block_id , block_id , jumpkind = 'Ijk_FakeRet' , ins_addr = src_ins_addr , stmt_idx = the_job . src_exit_stmt_idx , confirmed = True ) if sim_successors is None or should_skip : # We cannot retrieve the block, or we should skip the analysis of this node # But we create the edge anyway. If the sim_successors does not exist, it will be an edge from the previous # node to a PathTerminator self . _graph_add_edge ( src_block_id , block_id , jumpkind = job . jumpkind , stmt_idx = src_exit_stmt_idx , ins_addr = src_ins_addr ) self . _update_function_transition_graph ( src_block_id , block_id , jumpkind = job . jumpkind , ins_addr = src_ins_addr , stmt_idx = src_exit_stmt_idx ) # We are good. Raise the exception and leave raise AngrSkipJobNotice ( ) self . _update_thumb_addrs ( sim_successors , job . state ) # We store the function hints first. Function hints will be checked at the end of the analysis to avoid # any duplication with existing jumping targets if self . _enable_function_hints : if sim_successors . sort == 'IRSB' and sim_successors . all_successors : function_hints = self . _search_for_function_hints ( sim_successors . all_successors [ 0 ] ) for f in function_hints : self . _pending_function_hints . add ( f ) self . _graph_add_edge ( src_block_id , block_id , jumpkind = job . jumpkind , stmt_idx = src_exit_stmt_idx , ins_addr = src_ins_addr ) self . _update_function_transition_graph ( src_block_id , block_id , jumpkind = job . jumpkind , ins_addr = src_ins_addr , stmt_idx = src_exit_stmt_idx ) if block_id in self . _pending_edges : # there are some edges waiting to be created. do it here. for src_key , dst_key , data in self . _pending_edges [ block_id ] : self . _graph_add_edge ( src_key , dst_key , * * data ) del self . _pending_edges [ block_id ] block_info = { reg : sim_successors . initial_state . registers . load ( reg ) for reg in self . project . arch . persistent_regs } self . _block_artifacts [ addr ] = block_info job . cfg_node = cfg_node job . sim_successors = sim_successors job . exception_info = exception_info # For debugging purposes! job . successor_status = { }