Instruction:
Can you implement a function in Python 3 that
normalizes an address into a list of addresses.

Response:
def normalize_address(self, addr, is_write=False, convert_to_valueset=False, target_region=None, condition=None): #pylint:disable=arguments-differ
        """
        Convert a ValueSet object into a list of addresses.

        :param addr: A ValueSet object (which describes an address)
        :param is_write: Is this address used in a write or not
        :param convert_to_valueset: True if you want to have a list of ValueSet instances instead of AddressWrappers,
                                    False otherwise
        :param target_region: Which region to normalize the address to. To leave the decision to SimuVEX, set it to None
        :return: A list of AddressWrapper or ValueSet objects
        """
        targets_limit = WRITE_TARGETS_LIMIT if is_write else READ_TARGETS_LIMIT

        if type(addr) is not int:
            for constraint in self.state.solver.constraints:
                if getattr(addr, 'variables', set()) & constraint.variables:
                    addr = self._apply_condition_to_symbolic_addr(addr, constraint)

        # Apply the condition if necessary
        if condition is not None:
            addr = self._apply_condition_to_symbolic_addr(addr, condition)

        if type(addr) is int:
            addr = self.state.solver.BVV(addr, self.state.arch.bits)

        addr_with_regions = self._normalize_address_type(addr)
        address_wrappers = [ ]

        for region, addr_si in addr_with_regions:
            concrete_addrs = addr_si.eval(targets_limit)

            if len(concrete_addrs) == targets_limit and HYBRID_SOLVER in self.state.options:
                exact = True if APPROXIMATE_FIRST not in self.state.options else None
                solutions = self.state.solver.eval_upto(addr, targets_limit, exact=exact)

                if len(solutions) < len(concrete_addrs):
                    concrete_addrs = [addr_si.intersection(s).eval(1)[0] for s in solutions]

            if len(concrete_addrs) == targets_limit:
                self.state.history.add_event('mem', message='concretized too many targets. address = %s' % addr_si)

            for c in concrete_addrs:
                aw = self._normalize_address(region, c, target_region=target_region)
                address_wrappers.append(aw)

        if convert_to_valueset:
            return [ i.to_valueset(self.state) for i in address_wrappers ]

        else:
            return address_wrappers


Instruction:
How would you explain what the following Python 3 function does
def _normalize_address_type(self, addr): #pylint:disable=no-self-use

        addr_e = _raw_ast(addr)

        if isinstance(addr_e, (claripy.bv.BVV, claripy.vsa.StridedInterval, claripy.vsa.ValueSet)):
            raise SimMemoryError('_normalize_address_type() does not take claripy models.')

        if isinstance(addr_e, claripy.ast.Base):
            if not isinstance(addr_e._model_vsa, ValueSet):
                # Convert it to a ValueSet first by annotating it
                addr_e = addr_e.annotate(RegionAnnotation('global', 0, addr_e._model_vsa))

            return addr_e._model_vsa.items()

        else:
            raise SimAbstractMemoryError('Unsupported address type %s' % type(addr_e))

Response: Convert an address of different types to a list of mapping between region IDs and offsets.


Instruction:
Write a Python 3 script for
getting a list of segments that are available in memory for the given address and size.

Response:
def get_segments(self, addr, size):
        """
        Get a segmented memory region based on AbstractLocation information available from VSA.

        Here are some assumptions to make this method fast:
            - The entire memory region [addr, addr + size] is located within the same MemoryRegion
            - The address 'addr' has only one concrete value. It cannot be concretized to multiple values.

        :param addr: An address
        :param size: Size of the memory area in bytes
        :return: An ordered list of sizes each segment in the requested memory region
        """

        address_wrappers = self.normalize_address(addr, is_write=False)
        # assert len(address_wrappers) > 0

        aw = address_wrappers[0]
        region_id = aw.region

        if region_id in self.regions:
            region = self.regions[region_id]
            alocs = region.get_abstract_locations(aw.address, size)

            # Collect all segments and sort them
            segments = [ ]
            for aloc in alocs:
                segments.extend(aloc.segments)
            segments = sorted(segments, key=lambda x: x.offset)

            # Remove all overlapping segments
            processed_segments = [ ]
            last_seg = None
            for seg in segments:
                if last_seg is None:
                    last_seg = seg
                    processed_segments.append(seg)
                else:
                    # Are they overlapping?
                    if seg.offset >= last_seg.offset and seg.offset <= last_seg.offset + size:
                        continue
                    processed_segments.append(seg)

            # Make it a list of sizes
            sizes = [ ]
            next_pos = aw.address
            for seg in processed_segments:
                if seg.offset > next_pos:
                    gap = seg.offset - next_pos
                    assert gap > 0
                    sizes.append(gap)
                    next_pos += gap
                if seg.size + next_pos > aw.address + size:
                    sizes.append(aw.address + size - next_pos)
                    next_pos += aw.address + size - next_pos
                else:
                    sizes.append(seg.size)
                    next_pos += seg.size

            if not sizes:
                return [ size ]
            return sizes
        else:
            # The region doesn't exist. Then there is only one segment!
            return [ size ]


Instruction:
How would you implement a function in Python 3 that
makes a copy of this SimAbstractMemory object.

Response:
def copy(self, memo):
        """
        Make a copy of this SimAbstractMemory object
        :return:
        """
        am = SimAbstractMemory(
            memory_id=self._memory_id,
            endness=self.endness,
            stack_region_map=self._stack_region_map,
            generic_region_map=self._generic_region_map
        )
        for region_id, region in self._regions.items():
            am._regions[region_id] = region.copy(memo)
        am._stack_size = self._stack_size
        return am


Instruction:
Can you generate the documentation for the following Python 3 function
def merge(self, others, merge_conditions, common_ancestor=None):
        merging_occurred = False

        for o in others:
            for region_id, region in o._regions.items():
                if region_id in self._regions:
                    merging_occurred |= self._regions[region_id].merge(
                        [region], merge_conditions, common_ancestor=common_ancestor
                    )
                else:
                    merging_occurred = True
                    self._regions[region_id] = region

        return merging_occurred

Response: Merge this guy with another guy.


Instruction:
Given the following Python 3 function, write the documentation
def dbg_print(self):
        for region_id, region in self.regions.items():
            print("Region [%s]:" % region_id)
            region.dbg_print(indent=2)

Response: Print out debugging information about the current set of resources.


Instruction:
Create a Python 3 function for
initializing the ptable for ctype_toupper_loc. c in libc implementation.

Response:
def _initialize_toupper_loc_table(self):
        """
        Initialize ptable for ctype

        See __ctype_toupper_loc.c in libc implementation
        """
        malloc = angr.SIM_PROCEDURES['libc']['malloc']
        # 384 entries, 4 bytes each
        table = self.inline_call(malloc, 384*4).ret_expr
        table_ptr = self.inline_call(malloc, self.state.arch.bytes).ret_expr

        for pos, c in enumerate(self.state.libc.TOUPPER_LOC_ARRAY):
            self.state.memory.store(table + (pos * 4),
                                    self.state.solver.BVV(c, 32),
                                    endness=self.state.arch.memory_endness,
                                    inspect=False,
                                    disable_actions=True,
                                    )

        # Offset for negative chars: -128 index (4 bytes per index)
        table += (128 * 4)
        self.state.memory.store(table_ptr,
                                table,
                                size=self.state.arch.bytes,
                                endness=self.state.arch.memory_endness,
                                inspect=False,
                                disable_actions=True,
                                )

        self.state.libc.ctype_toupper_loc_table_ptr = table_ptr


Instruction:
Implement a function in Python 3 to
extract the arguments and set them to

Response:
def _extract_args(state, main, argc, argv, init, fini):
        """
        Extract arguments and set them to

        :param angr.sim_state.SimState state: The program state.
        :param main: An argument to __libc_start_main.
        :param argc: An argument to __libc_start_main.
        :param argv: An argument to __libc_start_main.
        :param init: An argument to __libc_start_main.
        :param fini: An argument to __libc_start_main.
        :return: A tuple of five elements: (main, argc, argv, init, fini)
        :rtype: tuple
        """

        main_ = main
        argc_ = argc
        argv_ = argv
        init_ = init
        fini_ = fini

        if state.arch.name == "PPC32":
            # for some dumb reason, PPC passes arguments to libc_start_main in some completely absurd way
            argv_ = argc_
            argc_ = main_
            main_ = state.mem[state.regs.r8 + 4:].int.resolved
            init_ = state.mem[state.regs.r8 + 8:].int.resolved
            fini_ = state.mem[state.regs.r8 + 12:].int.resolved

        elif state.arch.name == "PPC64":
            main_ = state.mem[state.regs.r8 + 8:].long.resolved
            init_ = state.mem[state.regs.r8 + 16:].long.resolved
            fini_ = state.mem[state.regs.r8 + 24:].long.resolved

        return main_, argc_, argv_, init_, fini_


Instruction:
Given the following Python 3 function, write the documentation
def dbg_repr(self, max_display=10):

        s = repr(self) + "\n"

        if len(self.chosen_statements) > max_display:
            s += "%d SimRuns in program slice, displaying %d.\n" % (len(self.chosen_statements), max_display)
        else:
            s += "%d SimRuns in program slice.\n" % len(self.chosen_statements)

        # Pretty-print the first `max_display` basic blocks
        if max_display is None:
            # Output all
            run_addrs = sorted(self.chosen_statements.keys())

        else:
            # Only output the first "max_display" ones
            run_addrs = sorted(self.chosen_statements.keys())[ : max_display]

        for run_addr in run_addrs:
            s += self.dbg_repr_run(run_addr) + "\n"

        return s

Response: Returns a string representation of the current slice.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def dbg_repr_run(self, run_addr):

        if self.project.is_hooked(run_addr):
            ss = "%#x Hooked\n" % run_addr

        else:
            ss = "%#x\n" % run_addr

            # statements
            chosen_statements = self.chosen_statements[run_addr]

            vex_block = self.project.factory.block(run_addr).vex

            statements = vex_block.statements
            for i in range(0, len(statements)):
                if i in chosen_statements:
                    line = "+"
                else:
                    line = "-"
                line += "[% 3d] " % i
                line += str(statements[i])
                ss += line + "\n"

            # exits
            targets = self.chosen_exits[run_addr]
            addr_strs = [ ]
            for exit_stmt_id, target_addr in targets:
                if target_addr is None:
                    addr_strs.append("default")
                else:
                    addr_strs.append("%#x" % target_addr)

            ss += "Chosen exits: " + ", ".join(addr_strs)

        return ss

Response: Returns a string representation of a single SimRun slice.


Instruction:
Create a Python 3 function for
returning an AnnotatedCFG based on slicing result.

Response:
def annotated_cfg(self, start_point=None):
        """
        Returns an AnnotatedCFG based on slicing result.
        """

        # TODO: Support context-sensitivity

        targets = [ ]

        for simrun, stmt_idx in self._targets:
            targets.append((simrun.addr, stmt_idx))

        l.debug("Initializing AnnoCFG...")
        anno_cfg = AnnotatedCFG(self.project, self._cfg)

        for simrun, stmt_idx in self._targets:
            if stmt_idx is not -1:
                anno_cfg.set_last_statement(simrun.addr, stmt_idx)

        for n in self._cfg.graph.nodes():
            run = n

            if run.addr in self.chosen_statements:
                if self.chosen_statements[run.addr] is True:
                    anno_cfg.add_block_to_whitelist(run.addr)
                else:
                    anno_cfg.add_statements_to_whitelist(run.addr, self.chosen_statements[run.addr])

        for src, dst in self._cfg.graph.edges():
            run = src

            if dst.addr in self.chosen_statements and src.addr in self.chosen_statements:
                anno_cfg.add_exit_to_whitelist(run.addr, dst.addr)

        return anno_cfg


Instruction:
Explain what the following Python 3 code does
def is_taint_related_to_ip(self, simrun_addr, stmt_idx, taint_type, simrun_whitelist=None):

        if simrun_whitelist is None:
            simrun_whitelist = set()
        if type(simrun_whitelist) is not set:
            simrun_whitelist = set(simrun_whitelist)

        # Find the specific taint in our graph
        taint = None
        for n in self.taint_graph.nodes():
            if n.type == taint_type and n.addr == simrun_addr and n.stmt_id == stmt_idx:
                taint = n
                break

        if taint is None:
            raise AngrBackwardSlicingError('The specific taint is not found')

        bfs_tree = networkx.bfs_tree(self.taint_graph, taint)

        # A node is tainting the IP if one of the following criteria holds:
        # - a descendant tmp variable is used as a default exit or a conditional exit of its corresponding SimRun
        # - a descendant register is the IP itself

        for descendant in bfs_tree.nodes():
            if descendant.type == 'exit':
                if descendant.addr not in simrun_whitelist:
                    return True
            elif descendant.type == 'reg' and descendant.reg == self.project.arch.ip_offset:
                return True

        return False

Response: Checks if a specific taint will taint the IP in the future or not.


Instruction:
Given the following Python 3 function, write the documentation
def _construct(self, targets, control_flow_slice=False):

        if control_flow_slice:
            simruns = [ r for r, _ in targets ]
            self._construct_control_flow_slice(simruns)

        else:
            self._construct_default(targets)

Response: Construct a dependency graph based on given parameters.


Instruction:
Write a Python 3 function for
building a slice of the program without considering the effect of data dependencies.

Response:
def _construct_control_flow_slice(self, simruns):
        """
        Build a slice of the program without considering the effect of data dependencies.
        This is an incorrect hack, but it should work fine with small programs.

        :param simruns: A list of SimRun targets. You probably wanna get it from the CFG somehow. It must exist in the
                        CFG.
        """

        # TODO: Support context-sensitivity!

        if self._cfg is None:
            l.error('Please build CFG first.')

        cfg = self._cfg.graph
        for simrun in simruns:
            if simrun not in cfg:
                l.error('SimRun instance %s is not in the CFG.', simrun)

        stack = [ ]
        for simrun in simruns:
            stack.append(simrun)

        self.runs_in_slice = networkx.DiGraph()
        self.cfg_nodes_in_slice = networkx.DiGraph()

        self.chosen_statements = { }
        while stack:
            # Pop one out
            block = stack.pop()
            if block.addr not in self.chosen_statements:
                self.chosen_statements[block.addr] = True
                # Get all predecessors of that block
                predecessors = cfg.predecessors(block)
                for pred in predecessors:
                    stack.append(pred)
                    self.cfg_nodes_in_slice.add_edge(pred, block)
                    self.runs_in_slice.add_edge(pred.addr, block.addr)


Instruction:
Can you create a Python 3 function that
creates a default backward slice from a list of statements.

Response:
def _construct_default(self, targets):
        """
        Create a backward slice from a specific statement in a specific block. This is done by traverse the CFG
        backwards, and mark all tainted statements based on dependence graphs (CDG and DDG) provided initially. The
        traversal terminated when we reach the entry point, or when there is no unresolved dependencies.

        :param targets: A list of tuples like (cfg_node, stmt_idx), where cfg_node is a CFGNode instance where the
                        backward slice starts, and it must be included in CFG and CDG. stmt_idx is the ID of the target
                        statement where the backward slice starts.
        """

        # TODO: Support context-sensitivity

        l.debug("Constructing a default backward program slice")

        self.taint_graph = networkx.DiGraph()

        taints = set()
        accessed_taints = set()

        # Fill in the taint set

        for cfg_node, stmt_idx in targets:
            if cfg_node not in self._cfg.graph:
                raise AngrBackwardSlicingError('Target CFGNode %s is not in the CFG.' % cfg_node)

            if stmt_idx == -1:
                new_taints = self._handle_control_dependence(cfg_node)
                taints |= new_taints

            else:
                cl = CodeLocation(cfg_node.addr, stmt_idx)
                taints.add(cl)

        while taints:
            # Pop a tainted code location
            tainted_cl = taints.pop()

            l.debug("Checking taint %s...", tainted_cl)

            # Mark it as picked
            if tainted_cl.block_addr is not None and tainted_cl.stmt_idx is not None:
                # Skip SimProcedures
                self._pick_statement(tainted_cl.block_addr, tainted_cl.stmt_idx)

            # Mark it as accessed
            accessed_taints.add(tainted_cl)

            # Pick all its data dependencies from data dependency graph
            if self._ddg is not None and tainted_cl in self._ddg:
                if isinstance(self._ddg, networkx.DiGraph):
                    predecessors = list(self._ddg.predecessors(tainted_cl))
                else:
                    # angr.analyses.DDG
                    predecessors = list(self._ddg.get_predecessors(tainted_cl))
                l.debug("Returned %d predecessors for %s from data dependence graph", len(predecessors), tainted_cl)

                for p in predecessors:
                    if p not in accessed_taints:
                        taints.add(p)

                    self.taint_graph.add_edge(p, tainted_cl)

            # Handle the control dependence
            for n in self._cfg.get_all_nodes(tainted_cl.block_addr):
                new_taints = self._handle_control_dependence(n)

                l.debug("Returned %d taints for %s from control dependence graph", len(new_taints), n)

                for taint in new_taints:
                    if taint not in accessed_taints:
                        taints.add(taint)

                    self.taint_graph.add_edge(taint, tainted_cl)

        # In the end, map the taint graph onto CFG
        self._map_to_cfg()


Instruction:
Given the following Python 3 function, write the documentation
def _find_exits(self, src_block, target_block):

        # Enumerate all statements and find exit statements
        # Since we don't have a state, we have to rely on the pyvex block instead of SimIRSB
        # Just create the block from pyvex again - not a big deal

        if self.project.is_hooked(src_block.addr):
            # Just return all exits for now
            return { -1: [ target_block.addr ] }

        block = self.project.factory.block(src_block.addr)
        vex_block = block.vex

        exit_stmt_ids = { }

        for stmt_idx, stmt in enumerate(vex_block.statements):
            if isinstance(stmt, pyvex.IRStmt.Exit):
                exit_stmt_ids[stmt_idx] = None

        # And of course, it has a default exit
        # Don't forget about it.
        exit_stmt_ids[DEFAULT_STATEMENT] = None

        # Find all paths from src_block to target_block
        # FIXME: This is some crappy code written in a hurry. Replace the all_simple_paths() later.
        all_simple_paths = list(networkx.all_simple_paths(self._cfg.graph, src_block, target_block, cutoff=3))

        for simple_path in all_simple_paths:
            if len(simple_path) <= 1:
                # Oops, it looks that src_block and target_block are the same guy?
                continue

            if self._same_function:
                # Examine this path and make sure it does not have call or return edge
                for i in range(len(simple_path) - 1):
                    jumpkind = self._cfg.graph[simple_path[i]][simple_path[i + 1]]['jumpkind']
                    if jumpkind in ('Ijk_Call', 'Ijk_Ret'):
                        return {  }

            # Get the first two nodes
            a, b = simple_path[0], simple_path[1]
            # Get the exit statement ID from CFG
            exit_stmt_id = self._cfg.get_exit_stmt_idx(a, b)
            if exit_stmt_id is None:
                continue

            # Mark it!
            if exit_stmt_ids[exit_stmt_id] is None:
                exit_stmt_ids[exit_stmt_id] = [ b.addr ]
            else:
                exit_stmt_ids[exit_stmt_id].append(b.addr)

        return exit_stmt_ids

Response: Find all exit statements that lead to the target block.


Instruction:
Can you write a function in Python 3 where it
bases on control dependence graph, pick all exits (statements) that lead to the target. :param target_node: A CFGNode instance. :returns: A set of new tainted code locations.

Response:
def _handle_control_dependence(self, target_node):
        """
        Based on control dependence graph, pick all exits (statements) that lead to the target.

        :param target_node: A CFGNode instance.
        :returns:           A set of new tainted code locations.
        """

        new_taints = set()

        # Query the CDG and figure out all control flow transitions to reach this target
        cdg_guardians = self._cdg.get_guardians(target_node)
        if not cdg_guardians:
            # this block is directly reachable from the entry point
            pass

        else:
            # For each predecessor on CDG, find the correct exit to take, and continue slicing from those exits
            for predecessor in cdg_guardians:
                exits = self._find_exits(predecessor, target_node)

                for stmt_idx, target_addresses in exits.items():
                    if stmt_idx is not None:
                        # If it's an exit statement, mark it as picked
                        self._pick_statement(predecessor.addr,
                                             self._normalize_stmt_idx(predecessor.addr, stmt_idx)
                                             )
                        # If it's the default statement, we should also pick other conditional exit statements
                        if stmt_idx == DEFAULT_STATEMENT:
                            conditional_exits = self._conditional_exits(predecessor.addr)
                            for conditional_exit_stmt_id in conditional_exits:
                                cl = CodeLocation(predecessor.addr,
                                                  self._normalize_stmt_idx(predecessor.addr, conditional_exit_stmt_id)
                                                  )
                                new_taints.add(cl)

                                self._pick_statement(predecessor.addr,
                                                     self._normalize_stmt_idx(predecessor.addr, conditional_exit_stmt_id)
                                                     )

                    if target_addresses is not None:

                        if stmt_idx is not None:
                            # If it's an exit statement, we create a new tainted code location
                            cl = CodeLocation(predecessor.addr,
                                              self._normalize_stmt_idx(predecessor.addr, stmt_idx)
                                              )
                            new_taints.add(cl)

                        # Mark those exits as picked
                        for target_address in target_addresses:
                            self._pick_exit(predecessor.addr, stmt_idx, target_address)

                    # On CFG, pick default exits of all nodes between predecessor and our target node
                    # Usually this is not required if basic blocks strictly end at control flow transitions. But this is
                    # not always the case for some architectures
                    all_simple_paths = list(networkx.all_simple_paths(self._cfg.graph, predecessor, target_node, cutoff=3))

                    previous_node = None
                    for path in all_simple_paths:
                        for node in path:
                            self._pick_statement(node.addr,
                                                 self._normalize_stmt_idx(node.addr, DEFAULT_STATEMENT))
                            if previous_node is not None:
                                self._pick_exit(previous_node.addr, DEFAULT_STATEMENT, node.addr)

        return new_taints


Instruction:
Can you write a function in Python 3 where it
maps our current slice to CFG.

Response:
def _map_to_cfg(self):
        """
        Map our current slice to CFG.

        Based on self._statements_per_run and self._exit_statements_per_run, this method will traverse the CFG and
        check if there is any missing block on the path. If there is, the default exit of that missing block will be
        included in the slice. This is because Slicecutor cannot skip individual basic blocks along a path.
        """

        exit_statements_per_run = self.chosen_exits
        new_exit_statements_per_run = defaultdict(list)

        while len(exit_statements_per_run):
            for block_address, exits in exit_statements_per_run.items():
                for stmt_idx, exit_target in exits:
                    if exit_target not in self.chosen_exits:
                        # Oh we found one!
                        # The default exit should be taken no matter where it leads to
                        # Add it to the new set
                        tpl = (DEFAULT_STATEMENT, None)
                        if tpl not in new_exit_statements_per_run[exit_target]:
                            new_exit_statements_per_run[exit_target].append(tpl)

            # Add the new ones to our global dict
            for block_address, exits in new_exit_statements_per_run.items():
                for ex in exits:
                    if ex not in self.chosen_exits[block_address]:
                        self.chosen_exits[block_address].append(ex)

            # Switch them so we can process the new set
            exit_statements_per_run = new_exit_statements_per_run
            new_exit_statements_per_run = defaultdict(list)


Instruction:
Implement a Python 3 function for
picking a statement in the final slice.

Response:
def _pick_statement(self, block_address, stmt_idx):
        """
        Include a statement in the final slice.

        :param int block_address:   Address of the basic block.
        :param int stmt_idx:        Statement ID.
        """

        # TODO: Support context-sensitivity

        # Sanity check
        if not isinstance(block_address, int):
            raise AngrBackwardSlicingError("Invalid block address %s." % block_address)
        if not isinstance(stmt_idx, int):
            raise AngrBackwardSlicingError("Invalid statement ID %s." % stmt_idx)

        self.chosen_statements[block_address].add(stmt_idx)


Instruction:
Create a Python 3 function to
pick an exit from the final slice.

Response:
def _pick_exit(self, block_address, stmt_idx, target_ips):
        """
        Include an exit in the final slice.

        :param block_address:   Address of the basic block.
        :param stmt_idx:        ID of the exit statement.
        :param target_ips:      The target address of this exit statement.
        """

        # TODO: Support context-sensitivity

        tpl = (stmt_idx, target_ips)
        if tpl not in self.chosen_exits[block_address]:
            self.chosen_exits[block_address].append(tpl)


Instruction:
Can you generate the documentation for the following Python 3 function
def _conditional_exits(self, block_addr):

        vex_block = self.project.factory.block(block_addr).vex
        lst = [ ]

        for i, stmt in enumerate(vex_block.statements):
            if isinstance(stmt, pyvex.IRStmt.Exit):
                lst.append(i)

        return lst

Response: Return a list of conditional statements with respect to a basic block.


Instruction:
Explain what the following Python 3 code does
def _normalize_stmt_idx(self, block_addr, stmt_idx):

        if type(stmt_idx) is int:
            return stmt_idx

        if stmt_idx == DEFAULT_STATEMENT:
            vex_block = self.project.factory.block(block_addr).vex
            return len(vex_block.statements)

        raise AngrBackwardSlicingError('Unsupported statement ID "%s"' % stmt_idx)

Response: Normalizes the statement ID.


Instruction:
Explain what the following Python 3 code does
def _last_branching_statement(statements):
        cmp_stmt_id = None
        cmp_tmp_id = None
        total_stmts = len(statements)
        statements = reversed(statements)
        for stmt_rev_idx, stmt in enumerate(statements):
            if isinstance(stmt, pyvex.IRStmt.Exit):
                stmt_idx = total_stmts - stmt_rev_idx - 1
                cmp_stmt_id = stmt_idx
                cmp_tmp_id = stmt.guard.tmp

        return cmp_stmt_id, cmp_tmp_id

Response:
Search for the last branching exit and taint the temp variable inside if predicate
        is True


Instruction:
Implement a function in Python 3 to
return the address of the methods basic block that contains the given instruction.

Response:
def _get_bb_addr_from_instr(self, instr):
        """
        Returns the address of the methods basic block that contains the given
        instruction.

        :param instr: The index of the instruction (within the current method).
        :rtype: SootAddressDescriptor
        """
        current_method = self.state.addr.method
        try:
            bb = current_method.block_by_label[instr]
        except KeyError:
            l.error("Possible jump to a non-existing bb %s --> %d",
                    self.state.addr, instr)
            raise IncorrectLocationException()

        return SootAddressDescriptor(current_method, bb.idx, 0)


Instruction:
Implement a function in Python 3 to
translate an address into a value.

Response:
def _translate_addr(self, a): #pylint:disable=no-self-use
        """
        Resolves this address.
        """
        if isinstance(a, claripy.ast.Base) and not a.singlevalued:
            raise SimFastMemoryError("address not supported")
        return self.state.solver.eval(a)


Instruction:
Can you implement a function in Python 3 that
checks whether this size can be supported by FastMemory.

Response:
def _translate_size(self, s): #pylint:disable=no-self-use
        """
        Checks whether this size can be supported by FastMemory."
        """
        if isinstance(s, claripy.ast.Base) and not s.singlevalued:
            raise SimFastMemoryError("size not supported")
        if s is None:
            return s
        return self.state.solver.eval(s)


Instruction:
Make a summary of the following Python 3 code
def _translate_cond(self, c): #pylint:disable=no-self-use
        if isinstance(c, claripy.ast.Base) and not c.singlevalued:
            raise SimFastMemoryError("size not supported")
        if c is None:
            return True
        else:
            return self.state.solver.eval_upto(c, 1)[0]

Response: Checks whether this condition can be supported by FastMemory.


Instruction:
Implement a function in Python 3 to
resolve a memory access of a certain size. Returns a sequence of bases offsets and sizes of the accesses required by this module.

Response:
def _resolve_access(self, addr, size):
        """
        Resolves a memory access of a certain size. Returns a sequence of the bases, offsets, and sizes of the accesses required
        to fulfil this.
        """

        # if we fit in one word
        first_offset = addr % self.width
        first_base = addr - first_offset
        if first_offset + size <= self.width:
            return [ (first_base, first_offset, size) ]

        last_size = (addr + size) % self.width
        last_base = addr + size - last_size

        accesses = [ ]
        accesses.append((first_base, first_offset, self.width - first_offset))
        accesses.extend((a, 0, self.width) for a in range(first_base+self.width, last_base, self.width))
        if last_size != 0:
            accesses.append((last_base, 0, last_size))

        return accesses


Instruction:
Can you create a Python 3 function that
performs a single load of a single object.

Response:
def _single_load(self, addr, offset, size, inspect=True, events=True):
        """
        Performs a single load.
        """
        try:
            d = self._contents[addr]
        except KeyError:
            d = self._handle_uninitialized_read(addr, inspect=inspect, events=events)
            self._contents[addr] = d

        if offset == 0 and size == self.width:
            return d
        else:
            return d.get_bytes(offset, size)


Instruction:
Make a summary of the following Python 3 code
def _single_store(self, addr, offset, size, data):

        if offset == 0 and size == self.width:
            self._contents[addr] = data
        elif offset == 0:
            cur = self._single_load(addr, size, self.width - size)
            self._contents[addr] = data.concat(cur)
        elif offset + size == self.width:
            cur = self._single_load(addr, 0, offset)
            self._contents[addr] = cur.concat(data)
        else:
            cur = self._single_load(addr, 0, self.width)
            start = cur.get_bytes(0, offset)
            end = cur.get_bytes(offset+size, self.width-offset-size)
            self._contents[addr] = start.concat(data, end)

Response: Performs a single store.


Instruction:
Write a Python 3 script for
getting the set of bytes that have been changed between self and other.

Response:
def changed_bytes(self, other):
        """
        Gets the set of changed bytes between self and other.
        """

        changes = set()

        l.warning("FastMemory.changed_bytes(): This implementation is very slow and only for debug purposes.")
        for addr,v in self._contents.items():
            for i in range(self.width):
                other_byte = other.load(addr+i, 1)
                our_byte = v.get_byte(i)
                if other_byte is our_byte:
                    changes.add(addr+i)

        return changes


Instruction:
Here you have a function in Python 3, explain what it does
def _register_reallocation(self, function, data_graph):

        # make sure this function does not call other functions
        if function.callout_sites:
            return

        if len(function.endpoints) != 1:
            return

        # identify function prologue and epilogue
        startpoint_block = self.project.factory.block(function.startpoint.addr).capstone
        startpoint_insns = startpoint_block.insns

        # supported function prologues:
        #
        # push  ebp
        # mov   ebp, esp
        # sub   esp, [0-9a-f]+h
        #
        # push  ebp
        # mov   ebp, esp
        # push  eax

        if len(startpoint_insns) < 3:
            return

        insn0, insn1, insn2 = startpoint_insns[:3]

        if not (insn0.mnemonic == 'push' and insn0.op_str == 'ebp'):
            return
        if not (insn1.mnemonic == 'mov' and insn1.op_str == 'ebp, esp'):
            return
        if not (insn2.mnemonic == 'sub' and re.match(r"esp, [0-9a-fx]+", insn2.op_str)) and \
                not (insn2.mnemonic == 'push' and insn2.op_str == 'eax'):
            return

        endpoint_block = self.project.factory.block(function.endpoints[0].addr).capstone
        endpoint_insns = endpoint_block.insns

        # supported function epilogues:
        #
        # add   esp, [0-9a-f]+h
        # pop   ebp
        # ret

        if len(endpoint_insns) < 3:
            return

        insn3, insn4, insn5 = endpoint_insns[-3:]

        if not (insn3.mnemonic == 'add' and re.match(r"esp, [0-9a-fx]+", insn3.op_str)):
            return
        if not (insn4.mnemonic == 'pop' and insn4.op_str == 'ebp'):
            return
        if not insn5.mnemonic == 'ret':
            return

        # make sure esp is not used anywhere else - all stack variables must be indexed using ebp
        esp_offset = self.project.arch.registers['esp'][0]
        ebp_offset = self.project.arch.registers['ebp'][0]
        esp_variables = [ ]
        for n in data_graph.nodes():
            if isinstance(n.variable, SimRegisterVariable) and n.variable.reg == esp_offset:
                esp_variables.append(n)

        # find out all call instructions
        call_insns = set()
        for src, dst, data in function.transition_graph.edges(data=True):
            if 'type' in data and data['type'] == 'call':
                src_block = function._get_block(src.addr)
                call_insns.add(src_block.instruction_addrs[-1])

        # there should be six esp variables + all call sites
        # push ebp (insn0 - read, insn0 - write) ; sub esp, 0xXX (insn2) ;
        # add esp, 0xXX (insn3) ; pop ebp (insn4) ; ret (insn5)

        esp_insns = set( n.location.ins_addr for n in esp_variables )
        if esp_insns != { insn0.address, insn2.address, insn3.address, insn4.address, insn5.address } | call_insns:
            return

        prologue_addr = insn0.address
        prologue_size = insn0.size + insn1.size + insn2.size
        epilogue_addr = insn3.address
        epilogue_size = insn3.size + insn4.size + insn5.size

        # look at consumer of those esp variables. no other instruction should be consuming them
        # esp_consumer_insns = { insn0.address, insn1.address, insn2.address, insn3.address, insn4.address,
        #                        insn5.address} | esp_insns
        # for esp_variable in esp_variables:  # type: angr.analyses.ddg.ProgramVariable
        #     consumers = data_graph.successors(esp_variable)
        #     if any([ consumer.location.ins_addr not in esp_consumer_insns for consumer in consumers ]):
        #         return

        # make sure we never gets the address of those stack variables into any register
        # say, lea edx, [ebp-0x4] is forbidden
        # check all edges in data graph
        for src, dst, data in data_graph.edges(data=True):
            if isinstance(dst.variable, SimRegisterVariable) and \
                    dst.variable.reg != ebp_offset and \
                    dst.variable.reg < 40:
                #to a register other than ebp
                if isinstance(src.variable, SimRegisterVariable) and \
                        src.variable.reg == ebp_offset:
                    # from ebp
                    l.debug("Found a lea operation from ebp at %#x. Function %s cannot be optimized.",
                            dst.location.ins_addr,
                            repr(function),
                            )
                    return

        # we definitely don't want to mess with fp or sse operations
        for node in data_graph.nodes():
            if isinstance(node.variable, SimRegisterVariable) and \
                    72 <= node.variable.reg < 288:  # offset(mm0) <= node.variable.reg < offset(cs)
                l.debug('Found a float-point/SSE register access at %#x. Function %s cannot be optimized.',
                        node.location.ins_addr,
                        repr(function)
                        )
                return

        l.debug("RegisterReallocation: function %s satisfies the criteria.", repr(function))

        # nice. let's see if we can optimize this function
        # do we have free registers?

        used_general_registers = set()
        for n in data_graph.nodes():
            if isinstance(n.variable, SimRegisterVariable):
                if n.variable.reg < 40:  # this is a hardcoded limit - we only care about general registers
                    used_general_registers.add(n.variable.reg)
        registers = self.project.arch.registers
        all_general_registers = { #registers['eax'][0], registers['ecx'][0], registers['edx'][0],
                                  registers['ebx'][0], registers['edi'][0], registers['esi'][0],
                                  registers['esp'][0], registers['ebp'][0]
                                  }
        unused_general_registers = all_general_registers - used_general_registers

        if not unused_general_registers:
            l.debug("RegisterReallocation: function %s does not have any free register.", repr(function))
            return
        l.debug("RegisterReallocation: function %s has %d free register(s): %s",
                repr(function),
                len(unused_general_registers),
                ", ".join([self.project.arch.register_names[u] for u in unused_general_registers ])
                )

        # find local stack variables of size 4
        stack_variables = set()
        for n in data_graph.nodes():
            if isinstance(n.variable, SimStackVariable) and \
                    n.variable.base == 'bp' and \
                    n.variable.size == 4 and \
                    n.variable.offset < 0:
                stack_variables.add(n)

        # alright, now we need to make sure that stack variables are never accessed by indexes
        # in other words, they must be accessed directly in forms of 'dword ptr [ebp+x]'
        # it's easy to do this: we get mem_addr predecessors of each stack variable, and make sure there are only two of
        # them: one is ebp, the other one is a constant
        #
        # ah, also, since we do not want to mess with crazy fp registers, we further require none of the stack variable
        # sources and consumers is a FP register.

        filtered_stack_variables = set()
        for stack_variable in stack_variables:

            failed = False

            # check how they are accessed
            in_edges = data_graph.in_edges(stack_variable, data=True)
            for src, _, data in in_edges:
                if 'type' in data and data['type'] == 'mem_addr':
                    if isinstance(src.variable, SimRegisterVariable) and src.variable.reg == ebp_offset:
                        # ebp
                        pass
                    elif isinstance(src.variable, SimConstantVariable):
                        # the constant
                        pass
                    else:
                        # ouch
                        failed = True
                        break

                if isinstance(src.variable, SimRegisterVariable) and src.variable.reg >= 72:
                    # it comes from a FP register
                    failed = True
                    break

            if failed:
                continue

            # check consumers
            out_edges = data_graph.out_edges(stack_variable, data=True)
            for _, dst, data in out_edges:
                if 'type' in data and data['type'] == 'kill':
                    continue
                if isinstance(dst.variable, SimRegisterVariable) and dst.variable.reg >= 72:
                    # an FP register is the consumer
                    failed = True
                    break

            if failed:
                continue

            filtered_stack_variables.add(stack_variable)

        # order the stack variables by the sum of their in and out degrees.
        stack_variable_to_degree = defaultdict(int)
        stack_variable_sources = defaultdict(list)
        for sv in filtered_stack_variables:
            stack_variable_to_degree[sv.variable] += data_graph.in_degree(sv)
            stack_variable_to_degree[sv.variable] += data_graph.out_degree(sv)
            stack_variable_sources[sv.variable].append(sv)

        sorted_stack_variables = sorted(stack_variable_to_degree.keys(),
                                       key=lambda sv: stack_variable_to_degree[sv],
                                       reverse=True
                                       )

        # aha these are the ones that we can replace!
        for reg, sv in zip(unused_general_registers, sorted_stack_variables):

            non_initial_sources = [src for src in stack_variable_sources[sv] if not src.initial]

            if not non_initial_sources:
                # we failed to find any source for it, which indicates a failure in our dependence analysis
                # skip
                continue

            # get consumers
            consumers = set()
            for src in stack_variable_sources[sv]:
                out_edges = data_graph.out_edges(src, data=True)
                for _, dst, data in out_edges:
                    if 'type' not in data or data['type'] != 'kill':
                        consumers.add(dst)

            rr = RegisterReallocation(sv, SimRegisterVariable(reg, 4), non_initial_sources,
                                      list(consumers), prologue_addr, prologue_size, epilogue_addr, epilogue_size
                                      )
            self.register_reallocations.append(rr)

            l.debug("RegisterReallocation: %s will replace %s in function %s.",
                    rr.register_variable,
                    rr.stack_variable,
                    repr(function)
                    )

Response: This method is used to register reallocation of a function.


Instruction:
Write a Python 3 script to
remove assignments to registers that have no consumers but immediately killed.

Response:
def _dead_assignment_elimination(self, function, data_graph):  #pylint:disable=unused-argument
        """
        Remove assignments to registers that has no consumers, but immediately killed.

        BROKEN - DO NOT USE IT

        :param angr.knowledge.Function function:
        :param networkx.MultiDiGraph data_graph:
        :return: None
        """

        register_pvs = set()
        for node in data_graph.nodes():
            if isinstance(node.variable, SimRegisterVariable) and \
                    node.variable.reg is not None and \
                    node.variable.reg < 40:
                register_pvs.add(node)

        for reg in register_pvs:
            # does it have a consumer?
            out_edges = data_graph.out_edges(reg, data=True)
            consumers = [ ]
            killers = [ ]
            for _, _, data in out_edges:
                if 'type' in data and data['type'] == 'kill':
                    killers.append(data)
                else:
                    consumers.append(data)

            if not consumers and killers:
                # we can remove the assignment!
                da = DeadAssignment(reg)
                self.dead_assignments.append(da)


Instruction:
Make a summary of the following Python 3 code
def initialize_segment_register_x64(self, state, concrete_target):
        _l.debug("Synchronizing gs segment register")
        state.regs.gs = self._read_gs_register_x64(concrete_target)

Response: Initialize the gs register in the angr to the value of the fs register in the concrete target.


Instruction:
Given the following Python 3 function, write the documentation
def initialize_gdt_x86(self, state, concrete_target):
        _l.debug("Creating Global Descriptor Table and synchronizing fs segment register")
        fs = self._read_fs_register_x86(concrete_target)
        gdt = self.generate_gdt(fs,0x0)
        self.setup_gdt(state,gdt)
        return gdt

Response: Create a Global Descriptor Table in the state memory and populate the segment registers.


Instruction:
Explain what the following Python 3 code does
def create_new_reference(self, obj, global_ref=False):
        # get an unique address
        opaque_ref = self.state.project.loader.extern_object.allocate()
        # map the object to that address
        l.debug("Map %s to opaque reference 0x%x", obj, opaque_ref)
        if global_ref:
            self.global_refs[opaque_ref] = obj
        else:
            self.local_refs[opaque_ref] = obj
        return opaque_ref

Response: Create a new reference that maps to the given object.


Instruction:
Can you implement a function in Python 3 that
deletes the stored mapping of a reference.

Response:
def delete_reference(self, opaque_ref, global_ref=False):
        """
        Delete the stored mapping of a reference.

        :param opaque_ref:       Reference which should be removed.
        :param bool global_ref:  Whether opaque_ref is a local or global
                                 reference.
        """
        opaque_ref_value = self._get_reference_value(opaque_ref)
        if global_ref:
            del self.global_refs[opaque_ref_value]
        else:
            del self.local_refs[opaque_ref_value]


Instruction:
How would you explain what the following Python 3 function does
def pop_job(self, returning=True):

        if not self:
            return None

        if not returning:
            return self._pop_job(next(reversed(self._jobs.keys())))

        # Prioritize returning functions
        for func_addr in reversed(self._jobs.keys()):
            if func_addr not in self._returning_functions:
                continue
            return self._pop_job(func_addr)

        return None

Response: Pops a pending job from the pending jobs list.


Instruction:
Can you write a function in Python 3 where it
removes all pending exit and remove all functions that are not returning.

Response:
def cleanup(self):
        """
        Remove those pending exits if:
        a) they are the return exits of non-returning SimProcedures
        b) they are the return exits of non-returning syscalls
        b) they are the return exits of non-returning functions

        :return: None
        """

        pending_exits_to_remove = defaultdict(list)

        for func_addr in self._updated_functions:
            if func_addr not in self._jobs:
                continue
            jobs = self._jobs[func_addr]
            for i, pe in enumerate(jobs):
                if pe.returning_source is None:
                    # The original call failed. This pending exit must be followed.
                    continue

                func = self._functions.function(pe.returning_source)
                if func is None:
                    # Why does it happen?
                    l.warning("An expected function at %s is not found. Please report it to Fish.",
                              pe.returning_source if pe.returning_source is not None else 'None')
                    continue

                if func.returning is False:
                    # Oops, it's not returning
                    # Remove this pending exit
                    pending_exits_to_remove[pe.returning_source].append(i)

        for func_addr, indices in pending_exits_to_remove.items():
            jobs = self._jobs[func_addr]
            for index in reversed(indices):
                job = jobs[index]
                self._deregister_job_callback(job.func_addr, job)
                del jobs[index]
                self._job_count -= 1
            if not jobs:
                del self._jobs[func_addr]

        self.clear_updated_functions()


Instruction:
Can you create a Python 3 function that
adds a function to the returning list.

Response:
def add_returning_function(self, func_addr):
        """
        Mark a function as returning.

        :param int func_addr: Address of the function that returns.
        :return:              None
        """

        self._returning_functions.add(func_addr)
        self._updated_functions.add(func_addr)


Instruction:
Can you generate a brief explanation for the following Python 3 code
def _calc_entropy(data, size=None):

        if not data:
            return 0
        entropy = 0
        if size is None:
            size = len(data)

        data = bytes(pyvex.ffi.buffer(data, size))
        for x in range(0, 256):
            p_x = float(data.count(x)) / size
            if p_x > 0:
                entropy += - p_x * math.log(p_x, 2)
        return entropy

Response: Calculate the entropy of a piece of data.


Instruction:
Can you write a function in Python 3 where it
checks if the given address is inside one of the memory regions.

Response:
def _inside_regions(self, address):
        """
        Check if the address is inside any existing region.

        :param int address: Address to check.
        :return:            True if the address is within one of the memory regions, False otherwise.
        :rtype:             bool
        """

        try:
            start_addr = next(self._regions.irange(maximum=address, reverse=True))
        except StopIteration:
            return False
        else:
            return address < self._regions[start_addr]


Instruction:
Can you implement a function in Python 3 that
gets the minimum address out of all regions.

Response:
def _get_min_addr(self):
        """
        Get the minimum address out of all regions. We assume self._regions is sorted.

        :return: The minimum address.
        :rtype:  int
        """

        if not self._regions:
            if self.project.arch.name != "Soot":
                l.error("self._regions is empty or not properly set.")
            return None

        return next(self._regions.irange())


Instruction:
Can you write a function in Python 3 where it
returns the next immediate address that is inside one of the memory regions.

Response:
def _next_address_in_regions(self, address):
        """
        Return the next immediate address that is inside any of the regions.

        :param int address: The address to start scanning.
        :return:            The next address that is inside one of the memory regions.
        :rtype:             int
        """

        if self._inside_regions(address):
            return address

        try:
            return next(self._regions.irange(minimum=address, reverse=False))
        except StopIteration:
            return None


Instruction:
How would you explain what the following Python 3 function does
def _next_unscanned_addr(self, alignment=None):

        # TODO: Take care of those functions that are already generated
        if self._next_addr is None:
            self._next_addr = self._get_min_addr()
            curr_addr = self._next_addr
        else:
            curr_addr = self._next_addr + 1

        if not self._inside_regions(curr_addr):
            curr_addr = self._next_address_in_regions(curr_addr)

        if curr_addr is None:
            l.debug("All addresses within memory regions have been scanned.")
            return None

        if self._seg_list.has_blocks:
            curr_addr = self._seg_list.next_free_pos(curr_addr)

        if alignment is not None:
            if curr_addr % alignment > 0:
                curr_addr = curr_addr - (curr_addr % alignment) + alignment

        # Make sure curr_addr exists in binary
        accepted = False
        for start, end in self._regions.items():
            if start <= curr_addr < end:
                # accept
                accepted = True
                break
            if curr_addr < start:
                # accept, but we are skipping the gap
                accepted = True
                curr_addr = start
                break

        if not accepted:
            # No memory available!
            return None

        self._next_addr = curr_addr
        if self._inside_regions(curr_addr):
            l.debug("Returning a new recon address: %#x", curr_addr)
            return curr_addr

        l.debug("%#x is beyond the ending point. Returning None.", curr_addr)
        return None

Response: Find the next unscanned address in the memory.


Instruction:
Create a Python 3 function for
returning the next code address in the core segment.

Response:
def _next_code_addr_core(self):
        """
        Call _next_unscanned_addr() first to get the next address that is not scanned. Then check if data locates at
        that address seems to be code or not. If not, we'll continue to for the next un-scanned address.
        """

        next_addr = self._next_unscanned_addr()
        if next_addr is None:
            return None

        start_addr = next_addr

        while True:
            string_length = self._scan_for_printable_strings(start_addr)
            if string_length:
                self._seg_list.occupy(start_addr, string_length, "string")
                start_addr += string_length

            if self.project.arch.name in ('X86', 'AMD64'):
                cc_length = self._scan_for_repeating_bytes(start_addr, '\xcc')
                if cc_length:
                    self._seg_list.occupy(start_addr, cc_length, "alignment")
                    start_addr += cc_length
            else:
                cc_length = 0

            zeros_length = self._scan_for_repeating_bytes(start_addr, '\x00')
            if zeros_length:
                self._seg_list.occupy(start_addr, zeros_length, "alignment")
                start_addr += zeros_length

            if string_length == 0 and cc_length == 0 and zeros_length == 0:
                # umm now it's probably code
                break

        instr_alignment = self._initial_state.arch.instruction_alignment
        if start_addr % instr_alignment > 0:
            # occupy those few bytes
            self._seg_list.occupy(start_addr, instr_alignment - (start_addr % instr_alignment), 'alignment')
            start_addr = start_addr - start_addr % instr_alignment + \
                         instr_alignment

        return start_addr


Instruction:
Write a Python 3 function for
getting all possible function addresses that are specified by the symbols in the binary

Response:
def _func_addrs_from_symbols(self):
        """
        Get all possible function addresses that are specified by the symbols in the binary

        :return: A set of addresses that are probably functions
        :rtype: set
        """

        return {sym.rebased_addr for sym in self._binary.symbols if sym.is_function}


Instruction:
Make a summary of the following Python 3 code
def _func_addrs_from_prologues(self):

        # Pre-compile all regexes
        regexes = list()
        for ins_regex in self.project.arch.function_prologs:
            r = re.compile(ins_regex)
            regexes.append(r)
        # EDG says: I challenge anyone bothering to read this to come up with a better
        # way to handle CPU modes that affect instruction decoding.
        # Since the only one we care about is ARM/Thumb right now
        # we have this gross hack. Sorry about that.
        thumb_regexes = list()
        if hasattr(self.project.arch, 'thumb_prologs'):
            for ins_regex in self.project.arch.thumb_prologs:
                # Thumb prologues are found at even addrs, but their actual addr is odd!
                # Isn't that great?
                r = re.compile(ins_regex)
                thumb_regexes.append(r)

        # Construct the binary blob first
        unassured_functions = [ ]

        for start_, bytes_ in self._binary.memory.backers():
            for regex in regexes:
                # Match them!
                for mo in regex.finditer(bytes_):
                    position = mo.start() + start_
                    if position % self.project.arch.instruction_alignment == 0:
                        mapped_position = AT.from_rva(position, self._binary).to_mva()
                        if self._addr_in_exec_memory_regions(mapped_position):
                            unassured_functions.append(mapped_position)
            # HACK part 2: Yes, i really have to do this
            for regex in thumb_regexes:
                # Match them!
                for mo in regex.finditer(bytes_):
                    position = mo.start() + start_
                    if position % self.project.arch.instruction_alignment == 0:
                        mapped_position = AT.from_rva(position, self._binary).to_mva()
                        if self._addr_in_exec_memory_regions(mapped_position):
                            unassured_functions.append(mapped_position+1)

        l.info("Found %d functions with prologue scanning.", len(unassured_functions))
        return unassured_functions

Response: Scan the entire program image for function prologues and start code scanning at those positions and return a list of possible function addresses.


Instruction:
Create a Python 3 function for
scanning a basic block starting at a specific address.

Response:
def _scan_block(self, cfg_job):
        """
        Scan a basic block starting at a specific address

        :param CFGJob cfg_job: The CFGJob instance.
        :return: a list of successors
        :rtype: list
        """

        addr = cfg_job.addr
        current_func_addr = cfg_job.func_addr

        # Fix the function address
        # This is for rare cases where we cannot successfully determine the end boundary of a previous function, and
        # as a consequence, our analysis mistakenly thinks the previous function goes all the way across the boundary,
        # resulting the missing of the second function in function manager.
        if addr in self._function_addresses_from_symbols:
            current_func_addr = addr

        if self._addr_hooked_or_syscall(addr):
            entries = self._scan_procedure(cfg_job, current_func_addr)

        else:
            entries = self._scan_irsb(cfg_job, current_func_addr)

        return entries


Instruction:
How would you explain what the following Python 3 function does
def _scan_procedure(self, cfg_job, current_func_addr):

        addr = cfg_job.addr

        try:
            if self.project.is_hooked(addr):
                procedure = self.project.hooked_by(addr)
                name = procedure.display_name
            else:
                procedure = self.project.simos.syscall_from_addr(addr)
                name = procedure.display_name

            if addr not in self._nodes:
                cfg_node = CFGNode(addr, 0, self.model,
                                   function_address=current_func_addr,
                                   simprocedure_name=name,
                                   no_ret=procedure.NO_RET,
                                   block_id=addr,
                                   )

                self._nodes[addr] = cfg_node
                self._nodes_by_addr[addr].append(cfg_node)

            else:
                cfg_node = self._nodes[addr]

        except (SimMemoryError, SimEngineError):
            return [ ]

        self._graph_add_edge(cfg_node, cfg_job.src_node, cfg_job.jumpkind, cfg_job.src_ins_addr,
                             cfg_job.src_stmt_idx
                             )
        self._function_add_node(cfg_node, current_func_addr)

        # Add edges going to this node in function graphs
        cfg_job.apply_function_edges(self, clear=True)

        # If we have traced it before, don't trace it anymore
        if addr in self._traced_addresses:
            return [ ]
        else:
            # Mark the address as traced
            self._traced_addresses.add(addr)

        entries = [ ]

        if procedure.ADDS_EXITS:
            # Get two blocks ahead
            grandparent_nodes = list(self.graph.predecessors(cfg_job.src_node))
            if not grandparent_nodes:
                l.warning("%s is supposed to yield new exits, but it fails to do so.", name)
                return [ ]
            blocks_ahead = [
                self._lift(grandparent_nodes[0].addr).vex,
                self._lift(cfg_job.src_node.addr).vex,
            ]
            procedure.project = self.project
            procedure.arch = self.project.arch
            new_exits = procedure.static_exits(blocks_ahead)

            for addr_, jumpkind in new_exits:
                if isinstance(addr_, claripy.ast.BV) and not addr_.symbolic:
                    addr_ = addr_._model_concrete.value
                if not isinstance(addr_, int):
                    continue
                entries += self._create_jobs(addr_, jumpkind, current_func_addr, None, addr_, cfg_node, None,
                                             None
                                             )

        if not procedure.NO_RET:
            # it returns
            cfg_node.has_return = True
            self._function_exits[current_func_addr].add(addr)
            self._function_add_return_site(addr, current_func_addr)
        else:
            # the procedure does not return
            self._updated_nonreturning_functions.add(current_func_addr)

        return entries

Response: Scan a CFGJob for a specific address and add the relevant exit points to the successors list.


Instruction:
Make a summary of the following Python 3 code
def _scan_irsb(self, cfg_job, current_func_addr):
        addr, function_addr, cfg_node, irsb = self._generate_cfgnode(cfg_job, current_func_addr)

        # Add edges going to this node in function graphs
        cfg_job.apply_function_edges(self, clear=True)

        # function_addr and current_function_addr can be different. e.g. when tracing an optimized tail-call that jumps
        # into another function that has been identified before.

        if cfg_node is None:
            # exceptions occurred, or we cannot get a CFGNode for other reasons
            return [ ]

        self._graph_add_edge(cfg_node, cfg_job.src_node, cfg_job.jumpkind, cfg_job.src_ins_addr,
                             cfg_job.src_stmt_idx
                             )
        self._function_add_node(cfg_node, function_addr)

        if self.functions.get_by_addr(function_addr).returning is not True:
            self._updated_nonreturning_functions.add(function_addr)

        # If we have traced it before, don't trace it anymore
        real_addr = get_real_address_if_arm(self.project.arch, addr)
        if real_addr in self._traced_addresses:
            # the address has been traced before
            return [ ]
        else:
            # Mark the address as traced
            self._traced_addresses.add(real_addr)

        # irsb cannot be None here
        # assert irsb is not None

        # IRSB is only used once per CFGNode. We should be able to clean up the CFGNode here in order to save memory
        cfg_node.irsb = None

        self._process_block_arch_specific(addr, irsb, function_addr)

        # Scan the basic block to collect data references
        if self._collect_data_ref:
            self._collect_data_references(irsb, addr)

        # Get all possible successors
        irsb_next, jumpkind = irsb.next, irsb.jumpkind
        successors = [ ]

        last_ins_addr = None
        ins_addr = addr
        if irsb.statements:
            for i, stmt in enumerate(irsb.statements):
                if isinstance(stmt, pyvex.IRStmt.Exit):
                    successors.append((i,
                                       last_ins_addr if self.project.arch.branch_delay_slot else ins_addr,
                                       stmt.dst,
                                       stmt.jumpkind
                                       )
                                      )
                elif isinstance(stmt, pyvex.IRStmt.IMark):
                    last_ins_addr = ins_addr
                    ins_addr = stmt.addr + stmt.delta
        else:
            for ins_addr, stmt_idx, exit_stmt in irsb.exit_statements:
                successors.append((
                    stmt_idx,
                    last_ins_addr if self.project.arch.branch_delay_slot else ins_addr,
                    exit_stmt.dst,
                    exit_stmt.jumpkind
                ))

        successors.append((DEFAULT_STATEMENT,
                           last_ins_addr if self.project.arch.branch_delay_slot else ins_addr, irsb_next, jumpkind)
                          )

        entries = [ ]

        successors = self._post_process_successors(addr, irsb.size, successors)

        # Process each successor
        for suc in successors:
            stmt_idx, ins_addr, target, jumpkind = suc

            entries += self._create_jobs(target, jumpkind, function_addr, irsb, addr, cfg_node, ins_addr,
                                         stmt_idx
                                         )

        return entries

Response: Scan the IRSB for the given CFGJob and return a list of successors.


Instruction:
Create a Python 3 function for
creating a list of CFGJobs that are needed to create the CFGJobs for the current CFGEntry.

Response:
def _create_jobs(self, target, jumpkind, current_function_addr, irsb, addr, cfg_node, ins_addr, stmt_idx):
        """
        Given a node and details of a successor, makes a list of CFGJobs
        and if it is a call or exit marks it appropriately so in the CFG

        :param int target:          Destination of the resultant job
        :param str jumpkind:        The jumpkind of the edge going to this node
        :param int current_function_addr: Address of the current function
        :param pyvex.IRSB irsb:     IRSB of the predecessor node
        :param int addr:            The predecessor address
        :param CFGNode cfg_node:    The CFGNode of the predecessor node
        :param int ins_addr:        Address of the source instruction.
        :param int stmt_idx:        ID of the source statement.
        :return:                    a list of CFGJobs
        :rtype:                     list
        """

        if type(target) is pyvex.IRExpr.Const:  # pylint: disable=unidiomatic-typecheck
            target_addr = target.con.value
        elif type(target) in (pyvex.IRConst.U8, pyvex.IRConst.U16, pyvex.IRConst.U32, pyvex.IRConst.U64):  # pylint: disable=unidiomatic-typecheck
            target_addr = target.value
        elif type(target) is int:  # pylint: disable=unidiomatic-typecheck
            target_addr = target
        else:
            target_addr = None

        if target_addr in self._known_thunks and jumpkind == 'Ijk_Boring':
            thunk_kind = self._known_thunks[target_addr][0]
            if thunk_kind == 'ret':
                jumpkind = 'Ijk_Ret'
                target_addr = None
            elif thunk_kind == 'jmp':
                pass # ummmmmm not sure about this one
            else:
                raise AngrCFGError("This shouldn't be possible")

        jobs = [ ]
        is_syscall = jumpkind.startswith("Ijk_Sys")

        # Special handling:
        # If a call instruction has a target that points to the immediate next instruction, we treat it as a boring jump
        if jumpkind == "Ijk_Call" and \
                not self.project.arch.call_pushes_ret and \
                cfg_node.instruction_addrs and \
                ins_addr == cfg_node.instruction_addrs[-1] and \
                target_addr == irsb.addr + irsb.size:
            jumpkind = "Ijk_Boring"

        if target_addr is None:
            # The target address is not a concrete value

            if jumpkind == "Ijk_Ret":
                # This block ends with a return instruction.
                if current_function_addr != -1:
                    self._function_exits[current_function_addr].add(addr)
                    self._function_add_return_site(addr, current_function_addr)
                    self.functions[current_function_addr].returning = True
                    self._pending_jobs.add_returning_function(current_function_addr)

                cfg_node.has_return = True

            elif self._resolve_indirect_jumps and \
                    (jumpkind in ('Ijk_Boring', 'Ijk_Call', 'Ijk_InvalICache') or jumpkind.startswith('Ijk_Sys')):
                # This is an indirect jump. Try to resolve it.
                # FIXME: in some cases, a statementless irsb will be missing its instr addresses
                # and this next part will fail. Use the real IRSB instead
                irsb = cfg_node.block.vex
                cfg_node.instruction_addrs = irsb.instruction_addresses
                resolved, resolved_targets, ij = self._indirect_jump_encountered(addr, cfg_node, irsb,
                                                                                current_function_addr, stmt_idx)
                if resolved:
                    for resolved_target in resolved_targets:
                        if jumpkind == 'Ijk_Call':
                            jobs += self._create_job_call(cfg_node.addr, irsb, cfg_node, stmt_idx, ins_addr,
                                                          current_function_addr, resolved_target, jumpkind)
                        else:
                            edge = FunctionTransitionEdge(cfg_node, resolved_target, current_function_addr,
                                                          to_outside=False, stmt_idx=stmt_idx, ins_addr=ins_addr,
                                                          )
                            ce = CFGJob(resolved_target, current_function_addr, jumpkind,
                                        last_addr=resolved_target, src_node=cfg_node, src_stmt_idx=stmt_idx,
                                        src_ins_addr=ins_addr, func_edges=[ edge ],
                                        )
                            jobs.append(ce)
                    return jobs

                if jumpkind in ("Ijk_Boring", 'Ijk_InvalICache'):
                    resolved_as_plt = False

                    if irsb and self._heuristic_plt_resolving:
                        # Test it on the initial state. Does it jump to a valid location?
                        # It will be resolved only if this is a .plt entry
                        resolved_as_plt = self._resolve_plt(addr, irsb, ij)

                        if resolved_as_plt:
                            jump_target = next(iter(ij.resolved_targets))
                            target_func_addr = jump_target  # TODO: FIX THIS

                            edge = FunctionTransitionEdge(cfg_node, jump_target, current_function_addr,
                                                          to_outside=True, dst_func_addr=jump_target,
                                                          stmt_idx=stmt_idx, ins_addr=ins_addr,
                                                          )
                            ce = CFGJob(jump_target, target_func_addr, jumpkind, last_addr=jump_target,
                                        src_node=cfg_node, src_stmt_idx=stmt_idx, src_ins_addr=ins_addr,
                                        func_edges=[edge],
                                        )
                            jobs.append(ce)

                    if resolved_as_plt:
                        # has been resolved as a PLT entry. Remove it from indirect_jumps_to_resolve
                        if ij.addr in self._indirect_jumps_to_resolve:
                            self._indirect_jumps_to_resolve.remove(ij.addr)
                            self._deregister_analysis_job(current_function_addr, ij)
                    else:
                        # add it to indirect_jumps_to_resolve
                        self._indirect_jumps_to_resolve.add(ij)

                        # register it as a job for the current function
                        self._register_analysis_job(current_function_addr, ij)

                else:  # jumpkind == "Ijk_Call" or jumpkind.startswith('Ijk_Sys')
                    self._indirect_jumps_to_resolve.add(ij)
                    self._register_analysis_job(current_function_addr, ij)

                    jobs += self._create_job_call(addr, irsb, cfg_node, stmt_idx, ins_addr, current_function_addr, None,
                                                  jumpkind, is_syscall=is_syscall
                                                  )

        elif target_addr is not None:
            # This is a direct jump with a concrete target.

            # pylint: disable=too-many-nested-blocks
            if jumpkind in ('Ijk_Boring', 'Ijk_InvalICache'):
                # if the target address is at another section, it has to be jumping to a new function
                if not self._addrs_belong_to_same_section(addr, target_addr):
                    target_func_addr = target_addr
                    to_outside = True
                else:
                    # it might be a jumpout
                    target_func_addr = None
                    real_target_addr = get_real_address_if_arm(self.project.arch, target_addr)
                    if real_target_addr in self._traced_addresses:
                        node = self.model.get_any_node(target_addr)
                        if node is not None:
                            target_func_addr = node.function_address
                    if target_func_addr is None:
                        target_func_addr = current_function_addr

                    to_outside = not target_func_addr == current_function_addr

                edge = FunctionTransitionEdge(cfg_node, target_addr, current_function_addr,
                                              to_outside=to_outside,
                                              dst_func_addr=target_func_addr,
                                              ins_addr=ins_addr,
                                              stmt_idx=stmt_idx,
                                              )

                ce = CFGJob(target_addr, target_func_addr, jumpkind, last_addr=addr, src_node=cfg_node,
                            src_ins_addr=ins_addr, src_stmt_idx=stmt_idx, func_edges=[ edge ])
                jobs.append(ce)

            elif jumpkind == 'Ijk_Call' or jumpkind.startswith("Ijk_Sys"):
                jobs += self._create_job_call(addr, irsb, cfg_node, stmt_idx, ins_addr, current_function_addr,
                                              target_addr, jumpkind, is_syscall=is_syscall
                                              )

            else:
                # TODO: Support more jumpkinds
                l.debug("Unsupported jumpkind %s", jumpkind)
                l.debug("Instruction address: %#x", ins_addr)

        return jobs


Instruction:
Can you implement a function in Python 3 that
generates a CFGJob for a given target address.

Response:
def _create_job_call(self, addr, irsb, cfg_node, stmt_idx, ins_addr, current_function_addr, target_addr, jumpkind,
                         is_syscall=False):
        """
        Generate a CFGJob for target address, also adding to _pending_entries
        if returning to succeeding position (if irsb arg is populated)

        :param int addr:            Address of the predecessor node
        :param pyvex.IRSB irsb:     IRSB of the predecessor node
        :param CFGNode cfg_node:    The CFGNode instance of the predecessor node
        :param int stmt_idx:        ID of the source statement
        :param int ins_addr:        Address of the source instruction
        :param int current_function_addr: Address of the current function
        :param int target_addr:     Destination of the call
        :param str jumpkind:        The jumpkind of the edge going to this node
        :param bool is_syscall:     Is the jump kind (and thus this) a system call
        :return:                    A list of CFGJobs
        :rtype:                     list
        """

        jobs = [ ]

        if is_syscall:
            # Fix the target_addr for syscalls
            tmp_state = self.project.factory.blank_state(mode="fastpath", addr=cfg_node.addr)
            # Find the first successor with a syscall jumpkind
            succ = next(iter(succ for succ in self.project.factory.successors(tmp_state).flat_successors
                             if succ.history.jumpkind and succ.history.jumpkind.startswith("Ijk_Sys")), None)
            if succ is None:
                # For some reason, there is no such successor with a syscall jumpkind
                target_addr = self._unresolvable_call_target_addr
            else:
                try:
                    syscall_stub = self.project.simos.syscall(succ)
                    if syscall_stub:  # can be None if simos is not a subclass of SimUserspac
                        syscall_addr = syscall_stub.addr
                        target_addr = syscall_addr
                    else:
                        target_addr = self._unresolvable_call_target_addr
                except AngrUnsupportedSyscallError:
                    target_addr = self._unresolvable_call_target_addr

        if isinstance(target_addr, SootAddressDescriptor):
            new_function_addr = target_addr.method
        else:
            new_function_addr = target_addr

        if irsb is None:
            return_site = None
        else:
            if self.project.arch.name != 'Soot':
                return_site = addr + irsb.size  # We assume the program will always return to the succeeding position
            else:
                # For Soot, we return to the next statement, which is not necessarily the next block (as Shimple does
                # not break blocks at calls)
                assert isinstance(ins_addr, SootAddressDescriptor)
                soot_block = irsb
                return_block_idx = ins_addr.block_idx
                if stmt_idx + 1 >= soot_block.label + len(soot_block.statements):
                    # tick the block ID
                    return_block_idx += 1
                return_site = SootAddressDescriptor(ins_addr.method, return_block_idx, stmt_idx + 1)

        edge = None
        if new_function_addr is not None:
            edge = FunctionCallEdge(cfg_node, new_function_addr, return_site, current_function_addr, syscall=is_syscall,
                                    ins_addr=ins_addr, stmt_idx=ins_addr,
                                    )

        if new_function_addr is not None:
            # Keep tracing from the call
            ce = CFGJob(target_addr, new_function_addr, jumpkind, last_addr=addr, src_node=cfg_node,
                        src_stmt_idx=stmt_idx, src_ins_addr=ins_addr, syscall=is_syscall, func_edges=[ edge ]
                        )
            jobs.append(ce)

        callee_might_return = True
        callee_function = None

        if new_function_addr is not None:
            if is_syscall:
                # we can create the syscall function if it does not exist yet. it has to be handled as a SimProcedure
                # anyway
                callee_function = self.kb.functions.function(addr=new_function_addr, syscall=is_syscall, create=True)
            else:
                callee_function = self.kb.functions.function(addr=new_function_addr, syscall=is_syscall)
            if callee_function is not None:
                callee_might_return = not (callee_function.returning is False)

        if callee_might_return:
            func_edges = [ ]
            if return_site is not None:
                if callee_function is not None and callee_function.returning is True:
                    fakeret_edge = FunctionFakeRetEdge(cfg_node, return_site, current_function_addr, confirmed=True)
                    func_edges.append(fakeret_edge)
                    ret_edge = FunctionReturnEdge(new_function_addr, return_site, current_function_addr)
                    func_edges.append(ret_edge)

                    # Also, keep tracing from the return site
                    ce = CFGJob(return_site, current_function_addr, 'Ijk_FakeRet', last_addr=addr, src_node=cfg_node,
                                src_stmt_idx=stmt_idx, src_ins_addr=ins_addr, returning_source=new_function_addr,
                                syscall=is_syscall, func_edges=func_edges)
                    self._pending_jobs.add_job(ce)
                    # register this job to this function
                    self._register_analysis_job(current_function_addr, ce)
                elif callee_function is not None and callee_function.returning is False:
                    pass # Don't go past a call that does not return!
                else:
                    # HACK: We don't know where we are jumping.  Let's assume we fakeret to the
                    # next instruction after the block
                    # TODO: FIXME: There are arch-specific hints to give the correct ret site
                    # Such as looking for constant values of LR in this block for ARM stuff.
                    fakeret_edge = FunctionFakeRetEdge(cfg_node, return_site, current_function_addr, confirmed=None)
                    func_edges.append(fakeret_edge)
                    fr = FunctionReturn(new_function_addr, current_function_addr, addr, return_site)
                    if fr not in self._function_returns[new_function_addr]:
                        self._function_returns[new_function_addr].add(fr)
                    ce = CFGJob(return_site, current_function_addr, 'Ijk_FakeRet', last_addr=addr, src_node=cfg_node,
                                src_stmt_idx=stmt_idx, src_ins_addr=ins_addr, returning_source=new_function_addr,
                                syscall=is_syscall, func_edges=func_edges)
                    self._pending_jobs.add_job(ce)
                    # register this job to this function
                    self._register_analysis_job(current_function_addr, ce)

        return jobs


Instruction:
Can you create a Python 3 function that
collects all data references for the given IRSB and add them to the internal list of data references.

Response:
def _collect_data_references(self, irsb, irsb_addr):
        """
        Unoptimises IRSB and _add_data_reference's for individual statements or
        for parts of statements (e.g. Store)

        :param pyvex.IRSB irsb: Block to scan for data references
        :param int irsb_addr: Address of block
        :return: None
        """

        if irsb.data_refs:
            self._process_irsb_data_refs(irsb)
        elif irsb.statements:
            irsb = self._unoptimize_irsb(irsb)
            # for each statement, collect all constants that are referenced or used.
            self._collect_data_references_by_scanning_stmts(irsb, irsb_addr)


Instruction:
Write a Python 3 script for
adding a data reference to the memory of the current object.

Response:
def _add_data_reference(self, irsb_addr, stmt_idx, insn_addr, data_addr,  # pylint: disable=unused-argument
                            data_size=None, data_type=None):
        """
        Checks addresses are in the correct segments and creates or updates
        MemoryData in _memory_data as appropriate, labelling as segment
        boundaries or data type

        :param int irsb_addr: irsb address
        :param int stmt_idx: Statement ID
        :param int insn_addr: instruction address
        :param data_addr: address of data manipulated by statement
        :param data_size: Size of the data being manipulated
        :param str data_type: Type of the data being manipulated
        :return: None
        """

        # Make sure data_addr is within a valid memory range
        if not self.project.loader.find_segment_containing(data_addr):

            # data might be at the end of some section or segment...
            # let's take a look
            for segment in self.project.loader.main_object.segments:
                if segment.vaddr + segment.memsize == data_addr:
                    # yeah!
                    new_data = False
                    if data_addr not in self._memory_data:
                        data = MemoryData(data_addr, 0, MemoryDataSort.SegmentBoundary)
                        self._memory_data[data_addr] = data
                        new_data = True

                    if new_data or self._extra_cross_references:
                        cr = CodeReference(insn_addr, irsb_addr, stmt_idx, memory_data=self.model.memory_data[data_addr])
                        self.model.references.add_ref(cr)
                    break

            return

        new_data = False
        if data_addr not in self._memory_data:
            if data_type is not None and data_size is not None:
                data = MemoryData(data_addr, data_size, data_type, max_size=data_size)
            else:
                data = MemoryData(data_addr, 0, MemoryDataSort.Unknown)
            self._memory_data[data_addr] = data
            new_data = True
        if new_data or self._extra_cross_references:
            cr = CodeReference(insn_addr, irsb_addr, stmt_idx, memory_data=self.model.memory_data[data_addr])
            self.model.references.add_ref(cr)

        self.insn_addr_to_memory_data[insn_addr] = self._memory_data[data_addr]


Instruction:
Given the following Python 3 function, write the documentation
def _guess_data_type(self, data_addr, max_size, content_holder=None):

        try:
            ref = next(iter(self.model.references.data_addr_to_ref[data_addr]))  # type: CodeReference
            irsb_addr = ref.block_addr
            stmt_idx = ref.stmt_idx
        except StopIteration:
            irsb_addr, stmt_idx = None, None

        if max_size is None:
            max_size = 0

        if self._seg_list.is_occupied(data_addr) and self._seg_list.occupied_by_sort(data_addr) == 'code':
            # it's a code reference
            # TODO: Further check if it's the beginning of an instruction
            return MemoryDataSort.CodeReference, 0

        pointer_size = self.project.arch.bytes

        # who's using it?
        if isinstance(self.project.loader.main_object, cle.MetaELF):
            plt_entry = self.project.loader.main_object.reverse_plt.get(irsb_addr, None)
            if plt_entry is not None:
                # IRSB is owned by plt!
                return MemoryDataSort.GOTPLTEntry, pointer_size

        # is it in a section with zero bytes, like .bss?
        obj = self.project.loader.find_object_containing(data_addr)
        section = obj.find_section_containing(data_addr)
        if section is not None and section.only_contains_uninitialized_data:
            # Nothing much you can do
            return None, None

        pointers_count = 0

        max_pointer_array_size = min(512 * pointer_size, max_size)
        for i in range(0, max_pointer_array_size, pointer_size):
            ptr = self._fast_memory_load_pointer(data_addr + i)

            if ptr is not None:
                #if self._seg_list.is_occupied(ptr) and self._seg_list.occupied_by_sort(ptr) == 'code':
                #    # it's a code reference
                #    # TODO: Further check if it's the beginning of an instruction
                #    pass
                if self.project.loader.find_section_containing(ptr) is not None or \
                        self.project.loader.find_segment_containing(ptr) is not None or \
                        (self._extra_memory_regions and
                         next(((a < ptr < b) for (a, b) in self._extra_memory_regions), None)
                         ):
                    # it's a pointer of some sort
                    # TODO: Determine what sort of pointer it is
                    pointers_count += 1
                else:
                    break

        if pointers_count:
            return MemoryDataSort.PointerArray, pointer_size * pointers_count

        try:
            data = self.project.loader.memory.load(data_addr, 1024)
        except KeyError:
            data = b''

        # Is it an unicode string?
        # TODO: Support unicode string longer than the max length
        if len(data) >= 4 and data[1] == 0 and data[3] == 0 and data[0] in self.PRINTABLES:
            def can_decode(n):
                try:
                    data[:n*2].decode('utf_16_le')
                except UnicodeDecodeError:
                    return False
                return True
            if can_decode(4) or can_decode(5) or can_decode(6):
                running_failures = 0
                last_success = 4
                for i in range(4, len(data) // 2):
                    if can_decode(i):
                        last_success = i
                        running_failures = 0
                        if data[i*2-2] == 0 and data[i*2-1] == 0:
                            break
                    else:
                        running_failures += 1
                        if running_failures > 3:
                            break

                return MemoryDataSort.UnicodeString, last_success

        if data:
            try:
                zero_pos = data.index(0)
            except ValueError:
                zero_pos = None
            if (zero_pos is not None and zero_pos > 0 and all(c in self.PRINTABLES for c in data[:zero_pos])) or \
                    all(c in self.PRINTABLES for c in data):
                # it's a string
                # however, it may not be terminated
                string_data = data if zero_pos is None else data[:zero_pos]
                if content_holder is not None:
                    content_holder.append(string_data)
                return MemoryDataSort.String, min(len(string_data) + 1, 1024)

        for handler in self._data_type_guessing_handlers:
            irsb = None if irsb_addr is None else self.model.get_any_node(irsb_addr).block.vex
            sort, size = handler(self, irsb, irsb_addr, stmt_idx, data_addr, max_size)
            if sort is not None:
                return sort, size

        return None, None

Response: Guesses the data type of the data entry.


Instruction:
Implement a function in Python 3 to
determine if the IRSB at the given address is a PLT stub. If it is concretely execute the basic block to get the target.

Response:
def _resolve_plt(self, addr, irsb, indir_jump):
        """
        Determine if the IRSB at the given address is a PLT stub. If it is, concretely execute the basic block to
        resolve the jump target.

        :param int addr:                Address of the block.
        :param irsb:                    The basic block.
        :param IndirectJump indir_jump: The IndirectJump instance.
        :return:                        True if the IRSB represents a PLT stub and we successfully resolved the target.
                                        False otherwise.
        :rtype:                         bool
        """

        # is the address identified by CLE as a PLT stub?
        if self.project.loader.all_elf_objects:
            # restrict this heuristics to ELF files only
            if not any([ addr in obj.reverse_plt for obj in self.project.loader.all_elf_objects ]):
                return False

        # Make sure the IRSB has statements
        if not irsb.has_statements:
            irsb = self.project.factory.block(irsb.addr, size=irsb.size).vex

        # try to resolve the jump target
        simsucc = self.project.engines.default_engine.process(self._initial_state, irsb, force_addr=addr)
        if len(simsucc.successors) == 1:
            ip = simsucc.successors[0].ip
            if ip._model_concrete is not ip:
                target_addr = ip._model_concrete.value
                if (self.project.loader.find_object_containing(target_addr, membership_check=False) is not
                        self.project.loader.main_object) \
                        or self.project.is_hooked(target_addr):
                    # resolved!
                    # Fill the IndirectJump object
                    indir_jump.resolved_targets.add(target_addr)
                    l.debug("Address %#x is resolved as a PLT entry, jumping to %#x", addr, target_addr)
                    return True

        return False


Instruction:
Make a summary of the following Python 3 code
def _indirect_jump_resolved(self, jump, jump_addr, resolved_by, targets):

        from .indirect_jump_resolvers.jumptable import JumpTableResolver

        source_addr = jump.addr

        if isinstance(resolved_by, JumpTableResolver):
            # Fill in the jump_tables dict
            self.jump_tables[jump.addr] = jump

        jump.resolved_targets = targets
        all_targets = set(targets)
        for addr in all_targets:
            to_outside = addr in self.functions or not self._addrs_belong_to_same_section(jump.addr, addr)

            # TODO: get a better estimate of the function address
            target_func_addr = jump.func_addr if not to_outside else addr
            func_edge = FunctionTransitionEdge(self._nodes[source_addr], addr, jump.func_addr, to_outside=to_outside,
                                               dst_func_addr=target_func_addr
                                               )
            job = CFGJob(addr, target_func_addr, jump.jumpkind,
                         last_addr=source_addr,
                         src_node=self._nodes[source_addr],
                         src_ins_addr=None,
                         src_stmt_idx=None,
                         func_edges=[func_edge],
                         )
            self._insert_job(job)
            self._register_analysis_job(target_func_addr, job)

        self._deregister_analysis_job(jump.func_addr, jump)

        CFGBase._indirect_jump_resolved(self, jump, jump.addr, resolved_by, targets)

Response: Called when an indirect jump is successfully resolved.