(self, functions, predetermined_function_addrs, blockaddr_to_function)
return set(functions_to_remove.keys())
When force_complete_scan is enabled, for unresolveable indirect jumps, angr will find jump targets and mark
        them as individual functions. For example, usually the following pattern is seen:

        sub_0x400010:
            push ebp
            mov esp, ebp
            ...
            cmp eax, 10
            ja end
            mov eax, jumptable[eax]
            jmp eax

        sub_0x400080:
            # do something here
            jmp end

        end (0x400e00):
            pop ebp
            ret

        In the example above, `process_irrational_functions` will remove function 0x400080, and merge it with function
        0x400010.

        :param angr.knowledge_plugins.FunctionManager functions: all functions that angr recovers, including those ones
            that are misidentified as functions.
        :param dict blockaddr_to_function: A mapping between block addresses and Function instances.
        :return: A set of addresses of all removed functions
        :rtype: set
When force_complete_scan is enabled, for unresolveable indirect jumps, angr will find jump targets and mark
        them as individual functions. For example, usually the following pattern is seen:
When force_complete_scan is enabled for unresolveable indirect jumps angr will find jump targets and mark them as individual functions . For example usually the following pattern is seen :
def _process_irrational_functions(self, functions, predetermined_function_addrs, blockaddr_to_function):
        """
        When force_complete_scan is enabled, for unresolveable indirect jumps, angr will find jump targets and mark
        them as individual functions. For example, usually the following pattern is seen:

        sub_0x400010:
            push ebp
            mov esp, ebp
            ...
            cmp eax, 10
            ja end
            mov eax, jumptable[eax]
            jmp eax

        sub_0x400080:
            # do something here
            jmp end

        end (0x400e00):
            pop ebp
            ret

        In the example above, `process_irrational_functions` will remove function 0x400080, and merge it with function
        0x400010.

        :param angr.knowledge_plugins.FunctionManager functions: all functions that angr recovers, including those ones
            that are misidentified as functions.
        :param dict blockaddr_to_function: A mapping between block addresses and Function instances.
        :return: A set of addresses of all removed functions
        :rtype: set
        """

        functions_to_remove = { }

        all_func_addrs = sorted(set(functions.keys()))

        for func_pos, (func_addr, function) in enumerate(functions.items()):

            if func_addr in functions_to_remove:
                continue

            # check all blocks and see if any block ends with an indirect jump and is not resolved
            has_unresolved_jumps = False
            # the functions to merge with must be locating between the unresolved basic block address and the endpoint
            # of the current function
            max_unresolved_jump_addr = 0
            for block_addr in function.block_addrs_set:
                if block_addr in self.indirect_jumps and \
                        self.indirect_jumps[block_addr].jumpkind == 'Ijk_Boring' and \
                        not self.indirect_jumps[block_addr].resolved_targets:
                    # it's not resolved
                    # we should also make sure it's a jump, not a call
                    has_unresolved_jumps = True
                    max_unresolved_jump_addr = max(max_unresolved_jump_addr, block_addr)

            if not has_unresolved_jumps:
                continue

            if function.startpoint is None:
                continue

            startpoint_addr = function.startpoint.addr
            if not function.endpoints:
                # Function should have at least one endpoint
                continue
            endpoint_addr = max([ a.addr for a in function.endpoints ])
            the_endpoint = next(a for a in function.endpoints if a.addr == endpoint_addr)
            endpoint_addr += the_endpoint.size

            # sanity check: startpoint of the function should be greater than its endpoint
            if startpoint_addr >= endpoint_addr:
                continue
            if max_unresolved_jump_addr <= startpoint_addr or max_unresolved_jump_addr >= endpoint_addr:
                continue

            # scan forward from the endpoint to include any function-tail jumps
            # Here is an example:
            # loc_8049562:
            #       mov eax, ebp
            #       add esp, 3ch
            #       ...
            #       ret
            # loc_804956c:
            #       mov ebp, 3
            #       jmp loc_8049562
            # loc_8049573:
            #       mov ebp, 4
            #       jmp loc_8049562
            #
            last_addr = endpoint_addr
            while True:
                try:
                    # do not follow hooked addresses (such as SimProcedures)
                    if self.project.is_hooked(last_addr):
                        break

                    next_block = self._lift(last_addr)
                    next_block_irsb = next_block.vex_nostmt
                    if next_block_irsb.jumpkind not in ('Ijk_Boring', 'Ijk_InvalICache'):
                        break
                    if not isinstance(next_block_irsb.next, pyvex.IRExpr.Const):
                        break
                    suc_addr = next_block_irsb.next.con.value
                    if max(startpoint_addr, the_endpoint.addr - 0x40) <= suc_addr < the_endpoint.addr + the_endpoint.size:
                        # increment the endpoint_addr
                        endpoint_addr = next_block.addr + next_block.size
                    else:
                        break

                    last_addr = next_block.addr + next_block.size

                except (SimTranslationError, SimMemoryError, SimIRSBError, SimEngineError):
                    break

            # find all functions that are between [ startpoint, endpoint ]

            should_merge = True
            functions_to_merge = set()
            i = func_pos + 1
            while i < len(all_func_addrs):
                f_addr = all_func_addrs[i]
                i += 1
                f = functions[f_addr]
                if f_addr == func_addr:
                    continue
                if max_unresolved_jump_addr < f_addr < endpoint_addr and \
                        all(max_unresolved_jump_addr < b_addr < endpoint_addr for b_addr in f.block_addrs):
                    if f_addr in functions_to_remove:
                        # this function has already been merged with other functions before... it cannot be merged with
                        # this function anymore
                        should_merge = False
                        break
                    if f_addr in predetermined_function_addrs:
                        # this function is a legit one. it shouldn't be removed/merged
                        should_merge = False
                        break
                    functions_to_merge.add(f_addr)

            if not should_merge:
                # we shouldn't merge...
                continue

            for f_addr in functions_to_merge:
                functions_to_remove[f_addr] = func_addr

        # merge all functions
        for to_remove, merge_with in functions_to_remove.items():
            func_merge_with = self._addr_to_function(merge_with, blockaddr_to_function, functions)

            for block_addr in functions[to_remove].block_addrs:
                blockaddr_to_function[block_addr] = func_merge_with

            del functions[to_remove]

        return set(functions_to_remove.keys())
def _process_irrational_functions ( self , functions , predetermined_function_addrs , blockaddr_to_function ) : functions_to_remove = { } all_func_addrs = sorted ( set ( functions . keys ( ) ) ) for func_pos , ( func_addr , function ) in enumerate ( functions . items ( ) ) : if func_addr in functions_to_remove : continue # check all blocks and see if any block ends with an indirect jump and is not resolved has_unresolved_jumps = False # the functions to merge with must be locating between the unresolved basic block address and the endpoint # of the current function max_unresolved_jump_addr = 0 for block_addr in function . block_addrs_set : if block_addr in self . indirect_jumps and self . indirect_jumps [ block_addr ] . jumpkind == 'Ijk_Boring' and not self . indirect_jumps [ block_addr ] . resolved_targets : # it's not resolved # we should also make sure it's a jump, not a call has_unresolved_jumps = True max_unresolved_jump_addr = max ( max_unresolved_jump_addr , block_addr ) if not has_unresolved_jumps : continue if function . startpoint is None : continue startpoint_addr = function . startpoint . addr if not function . endpoints : # Function should have at least one endpoint continue endpoint_addr = max ( [ a . addr for a in function . endpoints ] ) the_endpoint = next ( a for a in function . endpoints if a . addr == endpoint_addr ) endpoint_addr += the_endpoint . size # sanity check: startpoint of the function should be greater than its endpoint if startpoint_addr >= endpoint_addr : continue if max_unresolved_jump_addr <= startpoint_addr or max_unresolved_jump_addr >= endpoint_addr : continue # scan forward from the endpoint to include any function-tail jumps # Here is an example: # loc_8049562: #       mov eax, ebp #       add esp, 3ch #       ... #       ret # loc_804956c: #       mov ebp, 3 #       jmp loc_8049562 # loc_8049573: #       mov ebp, 4 #       jmp loc_8049562 # last_addr = endpoint_addr while True : try : # do not follow hooked addresses (such as SimProcedures) if self . project . is_hooked ( last_addr ) : break next_block = self . _lift ( last_addr ) next_block_irsb = next_block . vex_nostmt if next_block_irsb . jumpkind not in ( 'Ijk_Boring' , 'Ijk_InvalICache' ) : break if not isinstance ( next_block_irsb . next , pyvex . IRExpr . Const ) : break suc_addr = next_block_irsb . next . con . value if max ( startpoint_addr , the_endpoint . addr - 0x40 ) <= suc_addr < the_endpoint . addr + the_endpoint . size : # increment the endpoint_addr endpoint_addr = next_block . addr + next_block . size else : break last_addr = next_block . addr + next_block . size except ( SimTranslationError , SimMemoryError , SimIRSBError , SimEngineError ) : break # find all functions that are between [ startpoint, endpoint ] should_merge = True functions_to_merge = set ( ) i = func_pos + 1 while i < len ( all_func_addrs ) : f_addr = all_func_addrs [ i ] i += 1 f = functions [ f_addr ] if f_addr == func_addr : continue if max_unresolved_jump_addr < f_addr < endpoint_addr and all ( max_unresolved_jump_addr < b_addr < endpoint_addr for b_addr in f . block_addrs ) : if f_addr in functions_to_remove : # this function has already been merged with other functions before... it cannot be merged with # this function anymore should_merge = False break if f_addr in predetermined_function_addrs : # this function is a legit one. it shouldn't be removed/merged should_merge = False break functions_to_merge . add ( f_addr ) if not should_merge : # we shouldn't merge... continue for f_addr in functions_to_merge : functions_to_remove [ f_addr ] = func_addr # merge all functions for to_remove , merge_with in functions_to_remove . items ( ) : func_merge_with = self . _addr_to_function ( merge_with , blockaddr_to_function , functions ) for block_addr in functions [ to_remove ] . block_addrs : blockaddr_to_function [ block_addr ] = func_merge_with del functions [ to_remove ] return set ( functions_to_remove . keys ( ) )