(self, r, addr, cfg, annotatedcfg, load_stmt, load_size, stmts_adding_base_addr,
                                  all_addr_holders)
return jump_table, min_jumptable_addr, load_size, total_cases * load_size, all_targets, sort
Try loading all jump targets from a jump table or a vtable.
Try loading all jump targets from a jump table or a vtable.
Try loading all jump targets from a jump table or a vtable .
def _try_resolve_targets_load(self, r, addr, cfg, annotatedcfg, load_stmt, load_size, stmts_adding_base_addr,
                                  all_addr_holders):
        """
        Try loading all jump targets from a jump table or a vtable.
        """

        # shorthand
        project = self.project

        try:
            whitelist = annotatedcfg.get_whitelisted_statements(r.addr)
            last_stmt = annotatedcfg.get_last_statement_index(r.addr)
            succ = project.factory.successors(r, whitelist=whitelist, last_stmt=last_stmt)
        except (AngrError, SimError):
            # oops there are errors
            l.warning('Cannot get jump successor states from a path that has reached the target. Skip it.')
            return None

        all_states = succ.flat_successors + succ.unconstrained_successors
        if not all_states:
            l.warning("Slicecutor failed to execute the program slice. No output state is available.")
            return None

        state = all_states[0]  # Just take the first state
        self._cached_memread_addrs.clear()  # clear the cache to save some memory (and avoid confusion when debugging)

        # Parse the memory load statement and get the memory address of where the jump table is stored
        jumptable_addr = self._parse_load_statement(load_stmt, state)
        if jumptable_addr is None:
            return None

        # sanity check and necessary pre-processing
        if stmts_adding_base_addr:
            if len(stmts_adding_base_addr) != 1:
                # We do not support the cases where the base address involves more than one addition.
                # One such case exists in libc-2.27.so shipped with Ubuntu x86 where esi is used as the address of the
                # data region.
                #
                # .text:00047316 mov     eax, esi
                # .text:00047318 mov     esi, [ebp+data_region_ptr]
                # .text:0004731E movsx   eax, al
                # .text:00047321 movzx   eax, byte ptr [esi+eax-603A0h]
                # .text:00047329 mov     eax, ds:(jpt_47337 - 1D8000h)[esi+eax*4] ; switch 32 cases
                # .text:00047330 lea     eax, (loc_47033 - 1D8000h)[esi+eax] ; jumptable 00047337 cases 0-13,27-31
                # .text:00047337 jmp     eax             ; switch
                #
                # the proper solution requires angr to correctly determine that esi is the beginning address of the data
                # region (in this case, 0x1d8000). we give up in such cases until we can reasonably perform a
                # full-function data propagation before performing jump table recovery.
                return None
            jump_base_addr = stmts_adding_base_addr[0]
            if jump_base_addr.base_addr_available:
                addr_holders = {(jump_base_addr.stmt_loc[0], jump_base_addr.tmp)}
            else:
                addr_holders = {(jump_base_addr.stmt_loc[0], jump_base_addr.tmp),
                                (jump_base_addr.stmt_loc[0], jump_base_addr.tmp_1)
                                }
            if len(set(all_addr_holders.keys()).intersection(addr_holders)) != 1:
                # for some reason it's trying to add a base address onto a different temporary variable that we
                # are not aware of. skip.
                return None

            if not jump_base_addr.base_addr_available:
                # we need to decide which tmp is the address holder and which tmp holds the base address
                addr_holder = next(iter(set(all_addr_holders.keys()).intersection(addr_holders)))
                if jump_base_addr.tmp_1 == addr_holder[1]:
                    # swap the two tmps
                    jump_base_addr.tmp, jump_base_addr.tmp_1 = jump_base_addr.tmp_1, jump_base_addr.tmp
                # Load the concrete base address
                jump_base_addr.base_addr = state.solver.eval(state.scratch.temps[jump_base_addr.tmp_1])

        all_targets = [ ]
        jumptable_addr_vsa = jumptable_addr._model_vsa

        if not isinstance(jumptable_addr_vsa, claripy.vsa.StridedInterval):
            return None

        # we may resolve a vtable (in C, e.g., the IO_JUMPS_FUNC in libc), but the stride of this load is usually 1
        # while the read statement reads a word size at a time.
        # we use this to differentiate between traditional jump tables (where each entry is some blocks that belong to
        # the current function) and vtables (where each entry is a function).
        if jumptable_addr_vsa.stride < load_size:
            stride = load_size
            total_cases = jumptable_addr_vsa.cardinality // load_size
            sort = 'vtable'  # it's probably a vtable!
        else:
            stride = jumptable_addr_vsa.stride
            total_cases = jumptable_addr_vsa.cardinality
            sort = 'jumptable'

        if total_cases > self._max_targets:
            # We resolved too many targets for this indirect jump. Something might have gone wrong.
            l.debug("%d targets are resolved for the indirect jump at %#x. It may not be a jump table. Try the "
                    "next source, if there is any.",
                    total_cases, addr)
            return None

            # Or alternatively, we can ask user, which is meh...
            #
            # jump_base_addr = int(raw_input("please give me the jump base addr: "), 16)
            # total_cases = int(raw_input("please give me the total cases: "))
            # jump_target = state.solver.SI(bits=64, lower_bound=jump_base_addr, upper_bound=jump_base_addr +
            # (total_cases - 1) * 8, stride=8)

        jump_table = [ ]

        min_jumptable_addr = state.solver.min(jumptable_addr)
        max_jumptable_addr = state.solver.max(jumptable_addr)

        # Both the min jump target and the max jump target should be within a mapped memory region
        # i.e., we shouldn't be jumping to the stack or somewhere unmapped
        if (not project.loader.find_segment_containing(min_jumptable_addr) or
                not project.loader.find_segment_containing(max_jumptable_addr)):
            if (not project.loader.find_section_containing(min_jumptable_addr) or
                    not project.loader.find_section_containing(max_jumptable_addr)):
                l.debug("Jump table %#x might have jump targets outside mapped memory regions. "
                        "Continue to resolve it from the next data source.", addr)
                return None

        # Load the jump table from memory
        should_skip = False
        for idx, a in enumerate(range(min_jumptable_addr, max_jumptable_addr + 1, stride)):
            if idx % 100 == 0 and idx != 0:
                l.debug("%d targets have been resolved for the indirect jump at %#x...", idx, addr)
            if idx >= total_cases:
                break
            target = cfg._fast_memory_load_pointer(a, size=load_size)
            if target is None:
                l.debug("Cannot load pointer from address %#x. Skip.", a)
                should_skip = True
                break
            all_targets.append(target)
        if should_skip:
            return None

        # Adjust entries inside the jump table
        if stmts_adding_base_addr:
            stmt_adding_base_addr = stmts_adding_base_addr[0]
            base_addr = stmt_adding_base_addr.base_addr
            conversions = list(reversed(list(v for v in all_addr_holders.values()
                                                if v[0] is not AddressTransferringTypes.Assignment)))
            if conversions:
                invert_conversion_ops = []
                for conv in conversions:
                    if len(conv) == 1:
                        conversion_op, args = conv[0], None
                    else:
                        conversion_op, args = conv[0], conv[1:]
                    if conversion_op is AddressTransferringTypes.SignedExtension:
                        if args == (32, 64):
                            lam = lambda a: (a | 0xffffffff00000000) if a >= 0x80000000 else a
                        else:
                            raise NotImplementedError("Unsupported signed extension operation.")
                    elif conversion_op is AddressTransferringTypes.UnsignedExtension:
                        lam = lambda a: a
                    elif conversion_op is AddressTransferringTypes.Truncation:
                        if args == (64, 32):
                            lam = lambda a: a & 0xffffffff
                        else:
                            raise NotImplementedError("Unsupported truncation operation.")
                    elif conversion_op is AddressTransferringTypes.Or1:
                        lam = lambda a: a | 1
                    elif conversion_op is AddressTransferringTypes.ShiftLeft:
                        shift_amount = args[0]
                        lam = lambda a, sl=shift_amount: a << sl
                    else:
                        raise NotImplementedError("Unsupported conversion operation.")
                    invert_conversion_ops.append(lam)
                all_targets_copy = all_targets
                all_targets = []
                for target_ in all_targets_copy:
                    for lam in invert_conversion_ops:
                        target_ = lam(target_)
                    all_targets.append(target_)
            mask = (2 ** self.project.arch.bits) - 1
            all_targets = [(target + base_addr) & mask for target in all_targets]

        # special case for ARM: if the source block is in THUMB mode, all jump targets should be in THUMB mode, too
        if is_arm_arch(self.project.arch) and (addr & 1) == 1:
            all_targets = [ target | 1 for target in all_targets ]

        # Finally... all targets are ready
        illegal_target_found = False
        for target in all_targets:
            # if the total number of targets is suspicious (it usually implies a failure in applying the
            # constraints), check if all jump targets are legal
            if len(all_targets) in {1, 0x100, 0x10000} and not self._is_jumptarget_legal(target):
                l.info("Jump target %#x is probably illegal. Try to resolve indirect jump at %#x from the next source.",
                       target, addr)
                illegal_target_found = True
                break
            jump_table.append(target)
        if illegal_target_found:
            return None

        return jump_table, min_jumptable_addr, load_size, total_cases * load_size, all_targets, sort
def _try_resolve_targets_load ( self , r , addr , cfg , annotatedcfg , load_stmt , load_size , stmts_adding_base_addr , all_addr_holders ) : # shorthand project = self . project try : whitelist = annotatedcfg . get_whitelisted_statements ( r . addr ) last_stmt = annotatedcfg . get_last_statement_index ( r . addr ) succ = project . factory . successors ( r , whitelist = whitelist , last_stmt = last_stmt ) except ( AngrError , SimError ) : # oops there are errors l . warning ( 'Cannot get jump successor states from a path that has reached the target. Skip it.' ) return None all_states = succ . flat_successors + succ . unconstrained_successors if not all_states : l . warning ( "Slicecutor failed to execute the program slice. No output state is available." ) return None state = all_states [ 0 ] # Just take the first state self . _cached_memread_addrs . clear ( ) # clear the cache to save some memory (and avoid confusion when debugging) # Parse the memory load statement and get the memory address of where the jump table is stored jumptable_addr = self . _parse_load_statement ( load_stmt , state ) if jumptable_addr is None : return None # sanity check and necessary pre-processing if stmts_adding_base_addr : if len ( stmts_adding_base_addr ) != 1 : # We do not support the cases where the base address involves more than one addition. # One such case exists in libc-2.27.so shipped with Ubuntu x86 where esi is used as the address of the # data region. # # .text:00047316 mov     eax, esi # .text:00047318 mov     esi, [ebp+data_region_ptr] # .text:0004731E movsx   eax, al # .text:00047321 movzx   eax, byte ptr [esi+eax-603A0h] # .text:00047329 mov     eax, ds:(jpt_47337 - 1D8000h)[esi+eax*4] ; switch 32 cases # .text:00047330 lea     eax, (loc_47033 - 1D8000h)[esi+eax] ; jumptable 00047337 cases 0-13,27-31 # .text:00047337 jmp     eax             ; switch # # the proper solution requires angr to correctly determine that esi is the beginning address of the data # region (in this case, 0x1d8000). we give up in such cases until we can reasonably perform a # full-function data propagation before performing jump table recovery. return None jump_base_addr = stmts_adding_base_addr [ 0 ] if jump_base_addr . base_addr_available : addr_holders = { ( jump_base_addr . stmt_loc [ 0 ] , jump_base_addr . tmp ) } else : addr_holders = { ( jump_base_addr . stmt_loc [ 0 ] , jump_base_addr . tmp ) , ( jump_base_addr . stmt_loc [ 0 ] , jump_base_addr . tmp_1 ) } if len ( set ( all_addr_holders . keys ( ) ) . intersection ( addr_holders ) ) != 1 : # for some reason it's trying to add a base address onto a different temporary variable that we # are not aware of. skip. return None if not jump_base_addr . base_addr_available : # we need to decide which tmp is the address holder and which tmp holds the base address addr_holder = next ( iter ( set ( all_addr_holders . keys ( ) ) . intersection ( addr_holders ) ) ) if jump_base_addr . tmp_1 == addr_holder [ 1 ] : # swap the two tmps jump_base_addr . tmp , jump_base_addr . tmp_1 = jump_base_addr . tmp_1 , jump_base_addr . tmp # Load the concrete base address jump_base_addr . base_addr = state . solver . eval ( state . scratch . temps [ jump_base_addr . tmp_1 ] ) all_targets = [ ] jumptable_addr_vsa = jumptable_addr . _model_vsa if not isinstance ( jumptable_addr_vsa , claripy . vsa . StridedInterval ) : return None # we may resolve a vtable (in C, e.g., the IO_JUMPS_FUNC in libc), but the stride of this load is usually 1 # while the read statement reads a word size at a time. # we use this to differentiate between traditional jump tables (where each entry is some blocks that belong to # the current function) and vtables (where each entry is a function). if jumptable_addr_vsa . stride < load_size : stride = load_size total_cases = jumptable_addr_vsa . cardinality // load_size sort = 'vtable' # it's probably a vtable! else : stride = jumptable_addr_vsa . stride total_cases = jumptable_addr_vsa . cardinality sort = 'jumptable' if total_cases > self . _max_targets : # We resolved too many targets for this indirect jump. Something might have gone wrong. l . debug ( "%d targets are resolved for the indirect jump at %#x. It may not be a jump table. Try the " "next source, if there is any." , total_cases , addr ) return None # Or alternatively, we can ask user, which is meh... # # jump_base_addr = int(raw_input("please give me the jump base addr: "), 16) # total_cases = int(raw_input("please give me the total cases: ")) # jump_target = state.solver.SI(bits=64, lower_bound=jump_base_addr, upper_bound=jump_base_addr + # (total_cases - 1) * 8, stride=8) jump_table = [ ] min_jumptable_addr = state . solver . min ( jumptable_addr ) max_jumptable_addr = state . solver . max ( jumptable_addr ) # Both the min jump target and the max jump target should be within a mapped memory region # i.e., we shouldn't be jumping to the stack or somewhere unmapped if ( not project . loader . find_segment_containing ( min_jumptable_addr ) or not project . loader . find_segment_containing ( max_jumptable_addr ) ) : if ( not project . loader . find_section_containing ( min_jumptable_addr ) or not project . loader . find_section_containing ( max_jumptable_addr ) ) : l . debug ( "Jump table %#x might have jump targets outside mapped memory regions. " "Continue to resolve it from the next data source." , addr ) return None # Load the jump table from memory should_skip = False for idx , a in enumerate ( range ( min_jumptable_addr , max_jumptable_addr + 1 , stride ) ) : if idx % 100 == 0 and idx != 0 : l . debug ( "%d targets have been resolved for the indirect jump at %#x..." , idx , addr ) if idx >= total_cases : break target = cfg . _fast_memory_load_pointer ( a , size = load_size ) if target is None : l . debug ( "Cannot load pointer from address %#x. Skip." , a ) should_skip = True break all_targets . append ( target ) if should_skip : return None # Adjust entries inside the jump table if stmts_adding_base_addr : stmt_adding_base_addr = stmts_adding_base_addr [ 0 ] base_addr = stmt_adding_base_addr . base_addr conversions = list ( reversed ( list ( v for v in all_addr_holders . values ( ) if v [ 0 ] is not AddressTransferringTypes . Assignment ) ) ) if conversions : invert_conversion_ops = [ ] for conv in conversions : if len ( conv ) == 1 : conversion_op , args = conv [ 0 ] , None else : conversion_op , args = conv [ 0 ] , conv [ 1 : ] if conversion_op is AddressTransferringTypes . SignedExtension : if args == ( 32 , 64 ) : lam = lambda a : ( a | 0xffffffff00000000 ) if a >= 0x80000000 else a else : raise NotImplementedError ( "Unsupported signed extension operation." ) elif conversion_op is AddressTransferringTypes . UnsignedExtension : lam = lambda a : a elif conversion_op is AddressTransferringTypes . Truncation : if args == ( 64 , 32 ) : lam = lambda a : a & 0xffffffff else : raise NotImplementedError ( "Unsupported truncation operation." ) elif conversion_op is AddressTransferringTypes . Or1 : lam = lambda a : a | 1 elif conversion_op is AddressTransferringTypes . ShiftLeft : shift_amount = args [ 0 ] lam = lambda a , sl = shift_amount : a << sl else : raise NotImplementedError ( "Unsupported conversion operation." ) invert_conversion_ops . append ( lam ) all_targets_copy = all_targets all_targets = [ ] for target_ in all_targets_copy : for lam in invert_conversion_ops : target_ = lam ( target_ ) all_targets . append ( target_ ) mask = ( 2 ** self . project . arch . bits ) - 1 all_targets = [ ( target + base_addr ) & mask for target in all_targets ] # special case for ARM: if the source block is in THUMB mode, all jump targets should be in THUMB mode, too if is_arm_arch ( self . project . arch ) and ( addr & 1 ) == 1 : all_targets = [ target | 1 for target in all_targets ] # Finally... all targets are ready illegal_target_found = False for target in all_targets : # if the total number of targets is suspicious (it usually implies a failure in applying the # constraints), check if all jump targets are legal if len ( all_targets ) in { 1 , 0x100 , 0x10000 } and not self . _is_jumptarget_legal ( target ) : l . info ( "Jump target %#x is probably illegal. Try to resolve indirect jump at %#x from the next source." , target , addr ) illegal_target_found = True break jump_table . append ( target ) if illegal_target_found : return None return jump_table , min_jumptable_addr , load_size , total_cases * load_size , all_targets , sort