Instruction:
How would you implement a function in Python 3 that
calls when we cannot resolve an indirect jump.

Response:
def _indirect_jump_unresolved(self, jump):
        """
        Called when we cannot resolve an indirect jump.

        :param IndirectJump jump: The unresolved indirect jump.

        :return:    None
        """

        # add a node from this node to UnresolvableJumpTarget or UnresolvalbeCallTarget node,
        # depending on its jump kind
        src_node = self._nodes[jump.addr]
        if jump.jumpkind == 'Ijk_Boring':
            unresolvable_target_addr = self._unresolvable_jump_target_addr
            simprocedure_name = 'UnresolvableJumpTarget'
        elif jump.jumpkind == 'Ijk_Call':
            unresolvable_target_addr = self._unresolvable_call_target_addr
            simprocedure_name = 'UnresolvableCallTarget'
        else:
            raise AngrCFGError('It should be impossible')

        dst_node = CFGNode(unresolvable_target_addr, 0, self.model,
                           function_address=unresolvable_target_addr,
                           simprocedure_name=simprocedure_name,
                           block_id=unresolvable_target_addr,
                           )

        # add the dst_node to self._nodes
        if unresolvable_target_addr not in self._nodes:
            self._nodes[unresolvable_target_addr] = dst_node
            self._nodes_by_addr[unresolvable_target_addr].append(dst_node)

        self._graph_add_edge(dst_node, src_node, jump.jumpkind, jump.ins_addr, jump.stmt_idx)
        # mark it as a jumpout site for that function
        self._function_add_transition_edge(unresolvable_target_addr, src_node, jump.func_addr,
                                           to_outside=True,
                                           dst_func_addr=unresolvable_target_addr,
                                           ins_addr=jump.ins_addr,
                                           stmt_idx=jump.stmt_idx,
                                           )

        self._deregister_analysis_job(jump.func_addr, jump)

        CFGBase._indirect_jump_unresolved(self, jump)


Instruction:
Create a Python 3 function to
remove redundant blocks that are not in the function.

Response:
def _remove_redundant_overlapping_blocks(self):
        """
        On some architectures there are sometimes garbage bytes (usually nops) between functions in order to properly
        align the succeeding function. CFGFast does a linear sweeping which might create duplicated blocks for
        function epilogues where one block starts before the garbage bytes and the other starts after the garbage bytes.

        This method enumerates all blocks and remove overlapping blocks if one of them is aligned to 0x10 and the other
        contains only garbage bytes.

        :return: None
        """

        sorted_nodes = sorted(self.graph.nodes(), key=lambda n: n.addr if n is not None else 0)

        all_plt_stub_addrs = set(itertools.chain.from_iterable(obj.reverse_plt.keys() for obj in self.project.loader.all_objects if isinstance(obj, cle.MetaELF)))

        # go over the list. for each node that is the beginning of a function and is not properly aligned, if its
        # leading instruction is a single-byte or multi-byte nop, make sure there is another CFGNode starts after the
        # nop instruction

        nodes_to_append = {}
        # pylint:disable=too-many-nested-blocks
        for a in sorted_nodes:
            if a.addr in self.functions and a.addr not in all_plt_stub_addrs and \
                    not self._addr_hooked_or_syscall(a.addr):
                all_in_edges = self.graph.in_edges(a, data=True)
                if not any([data['jumpkind'] == 'Ijk_Call' for _, _, data in all_in_edges]):
                    # no one is calling it
                    # this function might be created from linear sweeping
                    try:
                        block = self._lift(a.addr, size=0x10 - (a.addr % 0x10), opt_level=1)
                    except SimTranslationError:
                        continue

                    nop_length = None

                    if self._is_noop_block(self.project.arch, block):
                        # fast path: in most cases, the entire block is a single byte or multi-byte nop, which VEX
                        # optimizer is able to tell
                        nop_length = block.size

                    else:
                        # this is not a no-op block. Determine where nop instructions terminate.
                        insns = block.capstone.insns
                        if insns:
                            nop_length = self._get_nop_length(insns)

                    if nop_length is None or nop_length <= 0:
                        continue

                    # leading nop for alignment.
                    next_node_addr = a.addr + nop_length
                    if nop_length < a.size and \
                            not (next_node_addr in self._nodes or next_node_addr in nodes_to_append):
                        # create a new CFGNode that starts there
                        next_node_size = a.size - nop_length
                        next_node = CFGNode(next_node_addr, next_node_size, self.model,
                                            function_address=next_node_addr,
                                            instruction_addrs=[i for i in a.instruction_addrs
                                                                      if next_node_addr <= i
                                                                      < next_node_addr + next_node_size
                                                                    ],
                                            thumb=a.thumb,
                                            byte_string=None if a.byte_string is None else a.byte_string[nop_length:],
                                            block_id=next_node_addr,
                                            )
                        self.graph.add_node(next_node)

                        # create edges accordingly
                        all_out_edges = self.graph.out_edges(a, data=True)
                        for _, dst, data in all_out_edges:
                            self.graph.add_edge(next_node, dst, **data)

                        nodes_to_append[next_node_addr] = next_node

                        # make sure there is a function begins there
                        try:
                            snippet = self._to_snippet(addr=next_node_addr, size=next_node_size,
                                                       base_state=self._base_state)
                            self.functions._add_node(next_node_addr, snippet)
                        except (SimEngineError, SimMemoryError):
                            continue

        # append all new nodes to sorted nodes
        if nodes_to_append:
            sorted_nodes = sorted(sorted_nodes + list(nodes_to_append.values()), key=lambda n: n.addr if n is not None else 0)

        removed_nodes = set()

        a = None  # it always hold the very recent non-removed node

        for i in range(len(sorted_nodes)):  # pylint:disable=consider-using-enumerate

            if a is None:
                a = sorted_nodes[0]
                continue

            b = sorted_nodes[i]
            if self._addr_hooked_or_syscall(b.addr):
                continue

            if b in removed_nodes:
                # skip all removed nodes
                continue

            if a.addr <= b.addr and \
                    (a.addr + a.size > b.addr):
                # They are overlapping

                try:
                    block = self.project.factory.fresh_block(a.addr, b.addr - a.addr, backup_state=self._base_state)
                except SimTranslationError:
                    a = b
                    continue
                if block.capstone.insns and all([ self._is_noop_insn(insn) for insn in block.capstone.insns ]):
                    # It's a big nop - no function starts with nop

                    # add b to indices
                    self._nodes[b.addr] = b
                    self._nodes_by_addr[b.addr].append(b)

                    # shrink a
                    self._shrink_node(a, b.addr - a.addr, remove_function=False)

                    a = b
                    continue

                all_functions = self.kb.functions

                # now things are a little harder
                # if there is no incoming edge to b, we should replace b with a
                # this is mostly because we misidentified the function beginning. In fact a is the function beginning,
                # but somehow we thought b is the beginning
                if a.addr + a.size == b.addr + b.size:
                    in_edges = len([ _ for _, _, data in self.graph.in_edges([b], data=True) ])
                    if in_edges == 0:
                        # we use node a to replace node b
                        # link all successors of b to a
                        for _, dst, data in self.graph.out_edges([b], data=True):
                            self.graph.add_edge(a, dst, **data)

                        if b.addr in self._nodes:
                            del self._nodes[b.addr]
                        if b.addr in self._nodes_by_addr and b in self._nodes_by_addr[b.addr]:
                            self._nodes_by_addr[b.addr].remove(b)

                        self.graph.remove_node(b)

                        if b.addr in all_functions:
                            del all_functions[b.addr]

                        # skip b
                        removed_nodes.add(b)

                        continue

                # next case - if b is directly from function prologue detection, or a basic block that is a successor of
                # a wrongly identified basic block, we might be totally misdecoding b
                if b.instruction_addrs[0] not in a.instruction_addrs:
                    # use a, truncate b

                    new_b_addr = a.addr + a.size  # b starts right after a terminates
                    new_b_size = b.addr + b.size - new_b_addr  # this may not be the size we want, since b might be
                                                               # misdecoded

                    # totally remove b
                    if b.addr in self._nodes:
                        del self._nodes[b.addr]
                    if b.addr in self._nodes_by_addr and b in self._nodes_by_addr[b.addr]:
                        self._nodes_by_addr[b.addr].remove(b)

                    self.graph.remove_node(b)

                    if b.addr in all_functions:
                        del all_functions[b.addr]

                    removed_nodes.add(b)

                    if new_b_size > 0:
                        # there are still some parts left in node b - we don't want to lose it
                        dummy_job = CFGJob(new_b_addr, a.function_address, None)
                        self._scan_block(dummy_job)

                    continue

                # for other cases, we'll let them be for now

            a = b


Instruction:
Make a summary of the following Python 3 code
def _remove_node(self, node):

        self.graph.remove_node(node)
        if node.addr in self._nodes:
            del self._nodes[node.addr]

        # We wanna remove the function as well
        if node.addr in self.kb.functions:
            del self.kb.functions[node.addr]

        if node.addr in self.kb.functions.callgraph:
            self.kb.functions.callgraph.remove_node(node.addr)

Response: Remove a CFGNode from the graph as well as from the function manager.


Instruction:
Explain what the following Python 3 code does
def _shrink_node(self, node, new_size, remove_function=True):

        # Generate the new node
        new_node = CFGNode(node.addr, new_size, self.model,
                           function_address=None if remove_function else node.function_address,
                           instruction_addrs=[i for i in node.instruction_addrs
                                                     if node.addr <= i < node.addr + new_size
                                                   ],
                           thumb=node.thumb,
                           byte_string=None if node.byte_string is None else node.byte_string[:new_size],
                           block_id=node.addr,
                           )

        old_in_edges = self.graph.in_edges(node, data=True)

        for src, _, data in old_in_edges:
            self.graph.add_edge(src, new_node, **data)

        successor_node_addr = node.addr + new_size
        if successor_node_addr in self._nodes:
            successor = self._nodes[successor_node_addr]
        else:
            successor_size = node.size - new_size
            successor = CFGNode(successor_node_addr, successor_size, self.model,
                                function_address=successor_node_addr if remove_function else node.function_address,
                                instruction_addrs=[i for i in node.instruction_addrs if i >= node.addr + new_size],
                                thumb=node.thumb,
                                byte_string=None if node.byte_string is None else node.byte_string[new_size:]
                                )
        self.graph.add_edge(new_node, successor, jumpkind='Ijk_Boring')

        # if the node B already has resolved targets, we will skip all unresolvable successors when adding old out edges
        # from node A to node B.
        # this matters in cases where node B is resolved as a special indirect jump entry (like a PLT stub), but (node
        # A + node B) wasn't properly resolved.
        unresolvable_target_addrs = (self._unresolvable_jump_target_addr, self._unresolvable_call_target_addr)

        has_resolved_targets = any([ node_.addr not in unresolvable_target_addrs
                                     for node_ in self.graph.successors(successor) ]
                                   )

        old_out_edges = self.graph.out_edges(node, data=True)
        for _, dst, data in old_out_edges:
            if (has_resolved_targets and dst.addr not in unresolvable_target_addrs) or \
                    not has_resolved_targets:
                self.graph.add_edge(successor, dst, **data)

        # remove the old node from indices
        if node.addr in self._nodes and self._nodes[node.addr] is node:
            del self._nodes[node.addr]
        if node.addr in self._nodes_by_addr and node in self._nodes_by_addr[node.addr]:
            self._nodes_by_addr[node.addr].remove(node)

        # remove the old node form the graph
        self.graph.remove_node(node)

        # add the new node to indices
        self._nodes[new_node.addr] = new_node
        self._nodes_by_addr[new_node.addr].append(new_node)

        # the function starting at this point is probably totally incorrect
        # hopefull future call to `make_functions()` will correct everything
        if node.addr in self.kb.functions:
            del self.kb.functions[node.addr]

            if not remove_function:
                # add functions back
                self._function_add_node(node, node.addr)
                successor_node = self.model.get_any_node(successor_node_addr)
                if successor_node and successor_node.function_address == node.addr:
                    # if there is absolutely no predecessors to successor_node, we'd like to add it as a new function
                    # so that it will not be left behind
                    if not list(self.graph.predecessors(successor_node)):
                        self._function_add_node(successor_node, successor_node_addr)

Response: Shrinks the size of a basic block.


Instruction:
Implement a function in Python 3 to
analyze all functions and update their returning attribute.

Response:
def _analyze_all_function_features(self, all_funcs_completed=False):
        """
        Iteratively analyze all changed functions, update their returning attribute, until a fix-point is reached (i.e.
        no new returning/not-returning functions are found).

        :return: None
        """

        while True:
            new_changes = self._iteratively_analyze_function_features(all_funcs_completed=all_funcs_completed)
            new_returning_functions = new_changes['functions_return']
            new_not_returning_functions = new_changes['functions_do_not_return']

            if not new_returning_functions and not new_not_returning_functions:
                break

            for returning_function in new_returning_functions:
                self._pending_jobs.add_returning_function(returning_function.addr)
                if returning_function.addr in self._function_returns:
                    for fr in self._function_returns[returning_function.addr]:
                        # Confirm them all
                        if not self.kb.functions.contains_addr(fr.caller_func_addr):
                            # FIXME: A potential bug might arise here. After post processing (phase 2), if the function
                            # specified by fr.caller_func_addr has been merged to another function during phase 2, we
                            # will simply skip this FunctionReturn here. It might lead to unconfirmed fake_ret edges
                            # in the newly merged function. Fix this bug in the future when it becomes an issue.
                            continue

                        if self.kb.functions.get_by_addr(fr.caller_func_addr).returning is not True:
                            self._updated_nonreturning_functions.add(fr.caller_func_addr)

                        return_to_node = self._nodes.get(fr.return_to, None)
                        if return_to_node is None:
                            return_to_snippet = self._to_snippet(addr=fr.return_to, base_state=self._base_state)
                        else:
                            return_to_snippet = self._to_snippet(cfg_node=self._nodes[fr.return_to])

                        self.kb.functions._add_return_from_call(fr.caller_func_addr, fr.callee_func_addr,
                                                                return_to_snippet)

                    del self._function_returns[returning_function.addr]

            for nonreturning_function in new_not_returning_functions:
                self._pending_jobs.add_nonreturning_function(nonreturning_function.addr)
                if nonreturning_function.addr in self._function_returns:
                    for fr in self._function_returns[nonreturning_function.addr]:
                        # Remove all those FakeRet edges
                        if self.kb.functions.contains_addr(fr.caller_func_addr) and \
                                self.kb.functions.get_by_addr(fr.caller_func_addr).returning is not True:
                            self._updated_nonreturning_functions.add(fr.caller_func_addr)

                    del self._function_returns[nonreturning_function.addr]


Instruction:
Write a Python 3 script to
add edge between nodes or add node if entry point is not jumped from None.

Response:
def _graph_add_edge(self, cfg_node, src_node, src_jumpkind, src_ins_addr, src_stmt_idx):
        """
        Add edge between nodes, or add node if entry point

        :param CFGNode cfg_node: node which is jumped to
        :param CFGNode src_node: node which is jumped from none if entry point
        :param str src_jumpkind: what type of jump the edge takes
        :param int or str src_stmt_idx: source statements ID
        :return: None
        """

        if src_node is None:
            self.graph.add_node(cfg_node)
        else:
            self.graph.add_edge(src_node, cfg_node, jumpkind=src_jumpkind, ins_addr=src_ins_addr,
                                stmt_idx=src_stmt_idx)


Instruction:
Can you tell what is the following Python 3 function doing
def _make_return_edges(self):

        for func_addr, func in self.functions.items():
            if func.returning is False:
                continue

            # get the node on CFG
            if func.startpoint is None:
                l.warning('Function %#x does not have a startpoint (yet).', func_addr)
                continue

            startpoint = self.model.get_any_node(func.startpoint.addr)
            if startpoint is None:
                # weird...
                l.warning('No CFGNode is found for function %#x in _make_return_edges().', func_addr)
                continue

            endpoints = self._get_return_sources(func)

            # get all callers
            callers = self.model.get_predecessors(startpoint, jumpkind='Ijk_Call')
            # for each caller, since they all end with a call instruction, get the immediate successor
            return_targets = itertools.chain.from_iterable(
                self.model.get_successors(caller, excluding_fakeret=False, jumpkind='Ijk_FakeRet') for caller in callers
            )
            return_targets = set(return_targets)

            for ep in endpoints:
                src = self.model.get_any_node(ep.addr)
                for rt in return_targets:
                    if not src.instruction_addrs:
                        ins_addr = None
                    else:
                        if self.project.arch.branch_delay_slot:
                            if len(src.instruction_addrs) > 1:
                                ins_addr = src.instruction_addrs[-2]
                            else:
                                l.error('At %s: expecting more than one instruction. Only got one.', src)
                                ins_addr = None
                        else:
                            ins_addr = src.instruction_addrs[-1]

                    self._graph_add_edge(rt, src, 'Ijk_Ret', ins_addr, DEFAULT_STATEMENT)

Response: Create edges for each returning function.


Instruction:
Write a Python 3 function that can
add a node to the function manager

Response:
def _function_add_node(self, cfg_node, function_addr):
        """
        Adds node to function manager, converting address to CodeNode if
        possible

        :param CFGNode cfg_node:    A CFGNode instance.
        :param int function_addr:   Address of the current function.
        :return: None
        """
        snippet = self._to_snippet(cfg_node=cfg_node)
        self.kb.functions._add_node(function_addr, snippet)


Instruction:
Explain what the following Python 3 code does
def _function_add_transition_edge(self, dst_addr, src_node, src_func_addr, to_outside=False, dst_func_addr=None,
                                      stmt_idx=None, ins_addr=None):
        """
        Add a transition edge to the function transiton map.

        :param int dst_addr: Address that the control flow transits to.
        :param CFGNode src_node: The source node that the control flow transits from.
        :param int src_func_addr: Function address.
        :return: True if the edge is correctly added. False if any exception occurred (for example, the target address
                 does not exist)
        :rtype: bool
        """

        try:
            target_node = self._nodes.get(dst_addr, None)
            if target_node is None:
                target_snippet = self._to_snippet(addr=dst_addr, base_state=self._base_state)
            else:
                target_snippet = self._to_snippet(cfg_node=target_node)

            if src_node is None:
                # Add this basic block into the function manager
                self.kb.functions._add_node(src_func_addr, target_snippet)
            else:
                src_snippet = self._to_snippet(cfg_node=src_node)
                if not to_outside:
                    self.kb.functions._add_transition_to(src_func_addr, src_snippet, target_snippet, stmt_idx=stmt_idx,
                                                         ins_addr=ins_addr
                                                         )
                else:
                    self.kb.functions._add_outside_transition_to(src_func_addr, src_snippet, target_snippet,
                                                                 to_function_addr=dst_func_addr,
                                                                 stmt_idx=stmt_idx, ins_addr=ins_addr
                                                                 )
            return True
        except (SimMemoryError, SimEngineError):
            return False

Response: Add a transition edge to the function transiton map.


Instruction:
Given the following Python 3 function, write the documentation
def _function_add_call_edge(self, addr, src_node, function_addr, syscall=False, stmt_idx=None, ins_addr=None):
        try:
            if src_node is None:
                self.kb.functions._add_node(function_addr, addr, syscall=syscall)
            else:
                src_snippet = self._to_snippet(cfg_node=src_node)

                return_to_outside = False

                ret_snippet = None

                self.kb.functions._add_call_to(function_addr, src_snippet, addr, ret_snippet, syscall=syscall,
                                               stmt_idx=stmt_idx, ins_addr=ins_addr,
                                               return_to_outside=return_to_outside,
                                               )
            return True
        except (SimMemoryError, SimEngineError):
            return False

Response: Adds a call edge to the function transition map.


Instruction:
How would you explain what the following Python 3 function does
def _function_add_fakeret_edge(self, addr, src_node, src_func_addr, confirmed=None):

        target_node = self._nodes.get(addr, None)
        if target_node is None:
            target_snippet = self._to_snippet(addr=addr, base_state=self._base_state)
        else:
            target_snippet = self._to_snippet(cfg_node=target_node)

        if src_node is None:
            self.kb.functions._add_node(src_func_addr, target_snippet)
        else:
            src_snippet = self._to_snippet(cfg_node=src_node)
            self.kb.functions._add_fakeret_to(src_func_addr, src_snippet, target_snippet, confirmed=confirmed)

Response: Generate CodeNodes for target and source node add node to function manager


Instruction:
Given the following Python 3 function, write the documentation
def _function_add_return_site(self, addr, function_addr):
        try:
            target = self._to_snippet(self._nodes[addr])
        except KeyError:
            target = addr

        self.kb.functions._add_return_from(function_addr, target)

Response:
Generate CodeNodes for target address registers node for function to
        function manager as return site


Instruction:
Can you tell what is the following Python 3 function doing
def _function_add_return_edge(self, return_from_addr, return_to_addr, function_addr):

        return_to_node = self._nodes.get(return_to_addr, None)
        if return_to_node is None:
            return_to_snippet = self._to_snippet(addr=return_to_addr, base_state=self._base_state)
            to_outside = False
        else:
            return_to_snippet = self._to_snippet(cfg_node=return_to_node)
            to_outside = return_to_node.function_address != function_addr

        self.kb.functions._add_return_from_call(function_addr, return_from_addr, return_to_snippet,
                                                to_outside=to_outside)

Response: Add a return edge to the function manager


Instruction:
Can you tell what is the following Python 3 function doing
def _arm_track_lr_on_stack(self, addr, irsb, function):

        if irsb.statements is None:
            return

        if 'lr_saved_on_stack' in function.info:
            return

        # if it does, we log it down to the Function object.
        lr_offset = self.project.arch.registers['lr'][0]
        sp_offset = self.project.arch.sp_offset
        initial_sp = 0x7fff0000
        initial_lr = 0xabcdef
        tmps = {}

        # pylint:disable=too-many-nested-blocks
        for stmt in irsb.statements:
            if isinstance(stmt, pyvex.IRStmt.IMark):
                if stmt.addr + stmt.delta != addr:
                    break
            elif isinstance(stmt, pyvex.IRStmt.WrTmp):
                data = stmt.data
                if isinstance(data, pyvex.IRExpr.Get):
                    if data.offset == sp_offset:
                        tmps[stmt.tmp] = initial_sp
                    elif data.offset == lr_offset:
                        tmps[stmt.tmp] = initial_lr
                elif isinstance(data, pyvex.IRExpr.Binop):
                    if data.op == 'Iop_Sub32':
                        arg0, arg1 = data.args
                        if isinstance(arg0, pyvex.IRExpr.RdTmp) and isinstance(arg1, pyvex.IRExpr.Const):
                            if arg0.tmp in tmps:
                                tmps[stmt.tmp] = tmps[arg0.tmp] - arg1.con.value

            elif isinstance(stmt, (pyvex.IRStmt.Store, pyvex.IRStmt.StoreG)):
                data = stmt.data
                storing_lr = False
                if isinstance(data, pyvex.IRExpr.RdTmp):
                    if data.tmp in tmps:
                        val = tmps[data.tmp]
                        if val == initial_lr:
                            # we are storing LR to somewhere
                            storing_lr = True
                if storing_lr:
                    if isinstance(stmt.addr, pyvex.IRExpr.RdTmp):
                        if stmt.addr.tmp in tmps:
                            storing_addr = tmps[stmt.addr.tmp]

                            function.info['lr_saved_on_stack'] = True
                            function.info['lr_on_stack_offset'] = storing_addr - initial_sp
                            break

        if 'lr_saved_on_stack' not in function.info:
            function.info['lr_saved_on_stack'] = False

Response: This method is used to track the LR register on the stack.


Instruction:
Can you create a Python 3 function that
simulates the read from the stack and return the jumpkind of the last instruction.

Response:
def _arm_track_read_lr_from_stack(self, irsb, function):  # pylint:disable=unused-argument
        """
        At the end of a basic block, simulate the very last instruction to see if the return address is read from the
        stack and written in PC. If so, the jumpkind of this IRSB will be set to Ijk_Ret. For detailed explanations,
        please see the documentation of _arm_track_lr_on_stack().

        :param pyvex.IRSB irsb: The basic block object.
        :param Function function: The function instance.
        :return: None
        """

        if 'lr_saved_on_stack' not in function.info or not function.info['lr_saved_on_stack']:
            return

        sp_offset = self.project.arch.sp_offset
        initial_sp = 0x7fff0000
        last_sp = None
        tmps = {}
        tmp_irsb = self._lift(irsb.instruction_addresses[-1], opt_level=self._iropt_level).vex
        # pylint:disable=too-many-nested-blocks
        for stmt in tmp_irsb.statements:
            if isinstance(stmt, pyvex.IRStmt.WrTmp):
                data = stmt.data
                if isinstance(data, pyvex.IRExpr.Get) and data.offset == sp_offset:
                    # t0 = GET:I32(sp)
                    tmps[stmt.tmp] = initial_sp
                elif isinstance(data, pyvex.IRExpr.Binop):
                    # only support Add
                    if data.op == 'Iop_Add32':
                        arg0, arg1 = data.args
                        if isinstance(arg0, pyvex.IRExpr.RdTmp) and isinstance(arg1, pyvex.IRExpr.Const):
                            if arg0.tmp in tmps:
                                tmps[stmt.tmp] = tmps[arg0.tmp] + arg1.con.value
                elif isinstance(data, pyvex.IRExpr.Load):
                    if isinstance(data.addr, pyvex.IRExpr.RdTmp):
                        if data.addr.tmp in tmps:
                            tmps[stmt.tmp] = ('load', tmps[data.addr.tmp])
            elif isinstance(stmt, pyvex.IRStmt.Put):
                if stmt.offset == sp_offset and isinstance(stmt.data, pyvex.IRExpr.RdTmp):
                    if stmt.data.tmp in tmps:
                        # loading things into sp
                        last_sp = tmps[stmt.data.tmp]

        if last_sp is not None and isinstance(tmp_irsb.next, pyvex.IRExpr.RdTmp):
            val = tmps.get(tmp_irsb.next.tmp, None)
            # val being None means there are statements that we do not handle
            if isinstance(val, tuple) and val[0] == 'load':
                # the value comes from memory
                memory_addr = val[1]
                if isinstance(last_sp, int):
                    lr_on_stack_offset = memory_addr - last_sp
                else:
                    lr_on_stack_offset = memory_addr - last_sp[1]

                if lr_on_stack_offset == function.info['lr_on_stack_offset']:
                    # the jumpkind should be Ret instead of boring
                    irsb.jumpkind = 'Ijk_Ret'


Instruction:
Can you create a Python 3 function that
generates a CFGNode that starts at cfg_job. addr.

Response:
def _generate_cfgnode(self, cfg_job, current_function_addr):
        """
        Generate a CFGNode that starts at `cfg_job.addr`.

        Since lifting machine code to IRSBs is slow, self._nodes is used as a cache of CFGNodes.

        If the current architecture is ARM, this method will try to lift the block in the mode specified by the address
        (determined by the parity of the address: even for ARM, odd for THUMB), and in case of decoding failures, try
        the other mode. If the basic block is successfully decoded in the other mode (different from the initial one),
         `addr` and `current_function_addr` are updated.

        :param CFGJob cfg_job: The CFGJob instance.
        :param int current_function_addr: Address of the current function.
        :return: A 4-tuple of (new address, new function address, CFGNode instance, IRSB object)
        :rtype: tuple
        """

        addr = cfg_job.addr

        try:

            if addr in self._nodes:
                cfg_node = self._nodes[addr]
                irsb = cfg_node.irsb

                if cfg_node.function_address != current_function_addr:
                    # the node has been assigned to another function before.
                    # we should update the function address.
                    current_function_addr = cfg_node.function_address

                return addr, current_function_addr, cfg_node, irsb

            is_x86_x64_arch = self.project.arch.name in ('X86', 'AMD64')

            if is_arm_arch(self.project.arch):
                real_addr = addr & (~1)
            else:
                real_addr = addr

            # if possible, check the distance between `addr` and the end of this section
            distance = VEX_IRSB_MAX_SIZE
            obj = self.project.loader.find_object_containing(addr, membership_check=False)
            if obj:
                # is there a section?
                has_executable_section = len([ sec for sec in obj.sections if sec.is_executable ]) > 0  # pylint:disable=len-as-condition
                section = self.project.loader.find_section_containing(addr)
                if has_executable_section and section is None:
                    # the basic block should not exist here...
                    return None, None, None, None
                if section is not None:
                    if not section.is_executable:
                        # the section is not executable...
                        return None, None, None, None
                    distance = section.vaddr + section.memsize - real_addr
                    distance = min(distance, VEX_IRSB_MAX_SIZE)
                # TODO: handle segment information as well

            # also check the distance between `addr` and the closest function.
            # we don't want to have a basic block that spans across function boundaries
            next_func = self.functions.ceiling_func(addr + 1)
            if next_func is not None:
                distance_to_func = (next_func.addr & (~1) if is_arm_arch(self.project.arch) else next_func.addr) - real_addr
                if distance_to_func != 0:
                    if distance is None:
                        distance = distance_to_func
                    else:
                        distance = min(distance, distance_to_func)

            # in the end, check the distance between `addr` and the closest occupied region in segment list
            next_noncode_addr = self._seg_list.next_pos_with_sort_not_in(addr, { "code" }, max_distance=distance)
            if next_noncode_addr is not None:
                distance_to_noncode_addr = next_noncode_addr - addr
                distance = min(distance, distance_to_noncode_addr)

            # Let's try to create the pyvex IRSB directly, since it's much faster
            nodecode = False
            irsb = None
            irsb_string = None
            try:
                lifted_block = self._lift(addr, size=distance, opt_level=self._iropt_level, collect_data_refs=True)
                irsb = lifted_block.vex_nostmt
                irsb_string = lifted_block.bytes[:irsb.size]
            except SimTranslationError:
                nodecode = True

            if (nodecode or irsb.size == 0 or irsb.jumpkind == 'Ijk_NoDecode') and \
                    is_arm_arch(self.project.arch) and \
                    self._arch_options.switch_mode_on_nodecode:
                # maybe the current mode is wrong?
                nodecode = False
                if addr % 2 == 0:
                    addr_0 = addr + 1
                else:
                    addr_0 = addr - 1

                if addr_0 in self._nodes:
                    # it has been analyzed before
                    cfg_node = self._nodes[addr_0]
                    irsb = cfg_node.irsb
                    return addr_0, cfg_node.function_address, cfg_node, irsb

                try:
                    lifted_block = self._lift(addr_0, size=distance, opt_level=self._iropt_level,
                                              collect_data_refs=True)
                    irsb = lifted_block.vex_nostmt
                    irsb_string = lifted_block.bytes[:irsb.size]
                except SimTranslationError:
                    nodecode = True

                if not (nodecode or irsb.size == 0 or irsb.jumpkind == 'Ijk_NoDecode'):
                    # it is decodeable
                    if current_function_addr == addr:
                        current_function_addr = addr_0
                    addr = addr_0

            if nodecode or irsb.size == 0 or irsb.jumpkind == 'Ijk_NoDecode':
                # decoding error
                # we still occupy that location since it cannot be decoded anyways
                if irsb is None:
                    irsb_size = 0
                else:
                    irsb_size = irsb.size
                # special handling for ud, ud1, and ud2 on x86 and x86-64
                if is_x86_x64_arch \
                        and len(irsb_string) >= 2 \
                        and irsb_string[-2:] in {
                            b'\x0f\xff',  # ud0
                            b'\x0f\xb9',  # ud1
                            b'\x0f\x0b',  # ud2
                        }:
                    # ud0, ud1, and ud2 are actually valid instructions.
                    valid_ins = True
                    nodecode_size = 2
                else:
                    valid_ins = False
                    nodecode_size = 1
                self._seg_list.occupy(addr, irsb_size, 'code')
                self._seg_list.occupy(addr + irsb_size, nodecode_size, 'nodecode')
                if not valid_ins:
                    l.error("Decoding error occurred at address %#x of function %#x.",
                            addr + irsb_size,
                            current_function_addr
                            )
                    return None, None, None, None

            is_thumb = False
            # Occupy the block in segment list
            if irsb.size > 0:
                if is_arm_arch(self.project.arch) and addr % 2 == 1:
                    # thumb mode
                    is_thumb=True
                self._seg_list.occupy(real_addr, irsb.size, "code")

            # Create a CFG node, and add it to the graph
            cfg_node = CFGNode(addr, irsb.size, self.model,
                               function_address=current_function_addr,
                               block_id=addr,
                               irsb=irsb,
                               thumb=is_thumb,
                               byte_string=irsb_string,
                               )
            if self._cfb is not None:
                self._cfb.add_obj(addr, lifted_block)

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

            return addr, current_function_addr, cfg_node, irsb

        except (SimMemoryError, SimEngineError):
            return None, None, None, None


Instruction:
How would you code a function in Python 3 to
process the arch specific block.

Response:
def _process_block_arch_specific(self, addr, irsb, func_addr):  # pylint: disable=unused-argument
        """
        According to arch types ['ARMEL', 'ARMHF', 'MIPS32'] does different
        fixes

        For ARM deals with link register on the stack
        (see _arm_track_lr_on_stack)
        For MIPS32 simulates a new state where the global pointer is 0xffffffff
        from current address after three steps if the first successor does not
        adjust this value updates this function address (in function manager)
        to use a conrete global pointer

        :param int addr: irsb address
        :param pyvex.IRSB irsb: irsb
        :param func_addr: function address
        :return: None
        """
        if is_arm_arch(self.project.arch):
            if self._arch_options.ret_jumpkind_heuristics:
                if addr == func_addr:
                    self._arm_track_lr_on_stack(addr, irsb, self.functions[func_addr])

                elif 'lr_saved_on_stack' in self.functions[func_addr].info and \
                        self.functions[func_addr].info['lr_saved_on_stack'] and \
                        irsb.jumpkind == 'Ijk_Boring' and \
                        irsb.next is not None and \
                        isinstance(irsb.next, pyvex.IRExpr.RdTmp):
                    # do a bunch of checks to avoid unnecessary simulation from happening
                    self._arm_track_read_lr_from_stack(irsb, self.functions[func_addr])

        elif self.project.arch.name == "MIPS32":
            function = self.kb.functions.function(func_addr)
            if addr >= func_addr and addr - func_addr < 15 * 4 and 'gp' not in function.info:
                # check if gp is being written to
                last_gp_setting_insn_id = None
                insn_ctr = 0

                if not irsb.statements:
                    # Get an IRSB with statements

                    irsb = self.project.factory.block(irsb.addr, size=irsb.size).vex

                for stmt in irsb.statements:
                    if isinstance(stmt, pyvex.IRStmt.IMark):
                        insn_ctr += 1
                        if insn_ctr >= 10:
                            break
                    elif isinstance(stmt, pyvex.IRStmt.Put) and stmt.offset == self.project.arch.registers['gp'][0]:
                        last_gp_setting_insn_id = insn_ctr
                        break

                if last_gp_setting_insn_id is None:
                    return

                # Prudently search for $gp values
                state = self.project.factory.blank_state(addr=addr, mode="fastpath",
                                                         remove_options={o.OPTIMIZE_IR}
                                                         )
                state.regs.t9 = func_addr
                state.regs.gp = 0xffffffff
                succ = self.project.factory.successors(state, num_inst=last_gp_setting_insn_id + 1)

                if not succ.flat_successors:
                    return

                state = succ.flat_successors[0]
                if not state.regs.gp.symbolic and state.solver.is_false(state.regs.gp == 0xffffffff):
                    function.info['gp'] = state.regs.gp._model_concrete.value


Instruction:
Create a Python 3 function for
generating a list of all recovered basic blocks.

Response:
def generate_code_cover(self):
        """
        Generate a list of all recovered basic blocks.
        """

        lst = []
        for cfg_node in self.graph.nodes():
            size = cfg_node.size
            lst.append((cfg_node.addr, size))

        lst = sorted(lst, key=lambda x: x[0])
        return lst


Instruction:
How would you explain what the following Python 3 function does
def se(self):
        global _complained_se
        if not _complained_se:
            _complained_se = True
            l.critical("The name state.se is deprecated; please use state.solver.")
        return self.get_plugin('solver')

Response: Deprecated alias for solver


Instruction:
Implement a function in Python 3 to
return the concrete address of the instruction pointer without triggering SimInspect breakpoints or generating SimActions.

Response:
def addr(self):
        """
        Get the concrete address of the instruction pointer, without triggering SimInspect breakpoints or generating
        SimActions. An integer is returned, or an exception is raised if the instruction pointer is symbolic.

        :return: an int
        """

        ip = self.regs._ip
        if isinstance(ip, SootAddressDescriptor):
            return ip
        return self.solver.eval_one(self.regs._ip)


Instruction:
How would you explain what the following Python 3 function does
def add_constraints(self, *args, **kwargs):
        if len(args) > 0 and isinstance(args[0], (list, tuple)):
            raise Exception("Tuple or list passed to add_constraints!")

        if o.TRACK_CONSTRAINTS in self.options and len(args) > 0:
            if o.SIMPLIFY_CONSTRAINTS in self.options:
                constraints = [ self.simplify(a) for a in args ]
            else:
                constraints = args

            self._inspect('constraints', BP_BEFORE, added_constraints=constraints)
            constraints = self._inspect_getattr("added_constraints", constraints)
            added = self.solver.add(*constraints)
            self._inspect('constraints', BP_AFTER)

            # add actions for the added constraints
            if o.TRACK_CONSTRAINT_ACTIONS in self.options:
                for c in added:
                    sac = SimActionConstraint(self, c)
                    self.history.add_action(sac)
        else:
            # preserve the old action logic for when we don't track constraints (why?)
            if (
                'action' in kwargs and kwargs['action'] and
                o.TRACK_CONSTRAINT_ACTIONS in self.options and len(args) > 0
            ):
                for arg in args:
                    if self.solver.symbolic(arg):
                        sac = SimActionConstraint(self, arg)
                        self.history.add_action(sac)

        if o.ABSTRACT_SOLVER in self.options and len(args) > 0:
            for arg in args:
                if self.solver.is_false(arg):
                    self._satisfiable = False
                    return

                if self.solver.is_true(arg):
                    continue

                # `is_true` and `is_false` does not use VSABackend currently (see commits 97a75366 and 2dfba73e in
                # claripy). There is a chance that VSA backend can in fact handle it.
                # Therefore we try to resolve it with VSABackend again
                if claripy.backends.vsa.is_false(arg):
                    self._satisfiable = False
                    return

                if claripy.backends.vsa.is_true(arg):
                    continue

                # It's neither True or False. Let's try to apply the condition

                # We take the argument, extract a list of constrained SIs out of it (if we could, of course), and
                # then replace each original SI the intersection of original SI and the constrained one.

                _, converted = self.solver.constraint_to_si(arg)

                for original_expr, constrained_si in converted:
                    if not original_expr.variables:
                        l.error('Incorrect original_expression to replace in add_constraints(). '
                                'This is due to defects in VSA logics inside claripy. Please report '
                                'to Fish and he will fix it if he\'s free.')
                        continue

                    new_expr = constrained_si
                    self.registers.replace_all(original_expr, new_expr)
                    for _, region in self.memory.regions.items():
                        region.memory.replace_all(original_expr, new_expr)

                    l.debug("SimState.add_constraints: Applied to final state.")
        elif o.SYMBOLIC not in self.options and len(args) > 0:
            for arg in args:
                if self.solver.is_false(arg):
                    self._satisfiable = False
                    return

Response: Add some constraints to the state.


Instruction:
Can you create a Python 3 function that
returns True if the state s constraints are satisfiable.

Response:
def satisfiable(self, **kwargs):
        """
        Whether the state's constraints are satisfiable
        """
        if o.ABSTRACT_SOLVER in self.options or o.SYMBOLIC not in self.options:
            extra_constraints = kwargs.pop('extra_constraints', ())
            for e in extra_constraints:
                if self.solver.is_false(e):
                    return False

            return self._satisfiable
        else:
            return self.solver.satisfiable(**kwargs)


Instruction:
Make a summary of the following Python 3 code
def block(self, *args, **kwargs):
        if not args and 'addr' not in kwargs:
            kwargs['addr'] = self.addr
        return self.project.factory.block(*args, backup_state=self, **kwargs)

Response: Returns a basic block at this point.


Instruction:
Write a Python 3 function for
returning a copy of the state.

Response:
def copy(self):
        """
        Returns a copy of the state.
        """

        if self._global_condition is not None:
            raise SimStateError("global condition was not cleared before state.copy().")

        c_plugins = self._copy_plugins()
        state = SimState(project=self.project, arch=self.arch, plugins=c_plugins, options=self.options.copy(),
                         mode=self.mode, os_name=self.os_name)

        if self._is_java_jni_project:
            state.ip_is_soot_addr = self.ip_is_soot_addr

        state.uninitialized_access_handler = self.uninitialized_access_handler
        state._special_memory_filler = self._special_memory_filler
        state.ip_constraints = self.ip_constraints

        return state


Instruction:
How would you code a function in Python 3 to
merge this state with the other state.

Response:
def merge(self, *others, **kwargs):
        """
        Merges this state with the other states. Returns the merging result, merged state, and the merge flag.

        :param states: the states to merge
        :param merge_conditions: a tuple of the conditions under which each state holds
        :param common_ancestor:  a state that represents the common history between the states being merged. Usually it
                                 is only available when EFFICIENT_STATE_MERGING is enabled, otherwise weak-refed states
                                 might be dropped from state history instances.
        :param plugin_whitelist: a list of plugin names that will be merged. If this option is given and is not None,
                                 any plugin that is not inside this list will not be merged, and will be created as a
                                 fresh instance in the new state.
        :param common_ancestor_history:
                                 a SimStateHistory instance that represents the common history between the states being
                                 merged. This is to allow optimal state merging when EFFICIENT_STATE_MERGING is
                                 disabled.
        :return: (merged state, merge flag, a bool indicating if any merging occured)
        """

        merge_conditions = kwargs.pop('merge_conditions', None)
        common_ancestor = kwargs.pop('common_ancestor', None)
        plugin_whitelist = kwargs.pop('plugin_whitelist', None)
        common_ancestor_history = kwargs.pop('common_ancestor_history', None)

        if len(kwargs) != 0:
            raise ValueError("invalid arguments: %s" % kwargs.keys())

        if merge_conditions is None:
            # TODO: maybe make the length of this smaller? Maybe: math.ceil(math.log(len(others)+1, 2))
            merge_flag = self.solver.BVS("state_merge_%d" % next(merge_counter), 16)
            merge_values = range(len(others)+1)
            merge_conditions = [ merge_flag == b for b in merge_values ]
        else:
            merge_conditions = [
                (self.solver.true if len(mc) == 0 else self.solver.And(*mc)) for mc in merge_conditions
            ]

        if len(set(o.arch.name for o in others)) != 1:
            raise SimMergeError("Unable to merge due to different architectures.")

        all_plugins = set(self.plugins.keys()) | set.union(*(set(o.plugins.keys()) for o in others))

        if plugin_whitelist is not None:
            all_plugins = all_plugins.intersection(set(plugin_whitelist))

        merged = self.copy()
        merging_occurred = False

        # fix parent
        merged.history.parent = self.history

        # plugins
        for p in all_plugins:
            our_plugin = merged.plugins[p] if p in merged.plugins else None
            their_plugins = [ (pl.plugins[p] if p in pl.plugins else None) for pl in others ]

            plugin_classes = (
                set([our_plugin.__class__]) | set(pl.__class__ for pl in their_plugins)
            ) - set([None.__class__])
            if len(plugin_classes) != 1:
                raise SimMergeError(
                    "There are differing plugin classes (%s) for plugin %s" % (plugin_classes, p)
                )
            plugin_class = plugin_classes.pop()

            our_filled_plugin = our_plugin if our_plugin is not None else merged.register_plugin(
                p, plugin_class()
            )
            their_filled_plugins = [
                (tp if tp is not None else t.register_plugin(p, plugin_class()))
                for t,tp in zip(others, their_plugins)
            ]

            plugin_common_ancestor = (
                common_ancestor.plugins[p] if
                (common_ancestor is not None and p in common_ancestor.plugins) else
                None
            )
            if plugin_common_ancestor is None and \
                    plugin_class is SimStateHistory and \
                    common_ancestor_history is not None:
                plugin_common_ancestor = common_ancestor_history

            plugin_state_merged = our_filled_plugin.merge(
                their_filled_plugins, merge_conditions, common_ancestor=plugin_common_ancestor,
            )
            if plugin_state_merged:
                l.debug('Merging occurred in %s', p)
                merging_occurred = True

        merged.add_constraints(merged.solver.Or(*merge_conditions))
        return merged, merge_conditions, merging_occurred


Instruction:
Can you implement a function in Python 3 that
performs a widening between self and others.

Response:
def widen(self, *others):
        """
        Perform a widening between self and other states
        :param others:
        :return:
        """

        if len(set(frozenset(o.plugins.keys()) for o in others)) != 1:
            raise SimMergeError("Unable to widen due to different sets of plugins.")
        if len(set(o.arch.name for o in others)) != 1:
            raise SimMergeError("Unable to widen due to different architectures.")

        widened = self.copy()
        widening_occurred = False

        # plugins
        for p in self.plugins:
            if p in ('solver', 'unicorn'):
                continue
            plugin_state_widened = widened.plugins[p].widen([_.plugins[p] for _ in others])
            if plugin_state_widened:
                l.debug('Widening occured in %s', p)
                widening_occurred = True

        return widened, widening_occurred


Instruction:
How would you explain what the following Python 3 function does
def reg_concrete(self, *args, **kwargs):
        e = self.registers.load(*args, **kwargs)
        if self.solver.symbolic(e):
            raise SimValueError("target of reg_concrete is symbolic!")
        return self.solver.eval(e)

Response: Returns the contents of a register but raises a SimValueError.


Instruction:
Write a Python 3 function that can
return the contents of a memory object but raises a SimValueError if the contents are symbolic.

Response:
def mem_concrete(self, *args, **kwargs):
        """
        Returns the contents of a memory but, if the contents are symbolic,
        raises a SimValueError.
        """
        e = self.memory.load(*args, **kwargs)
        if self.solver.symbolic(e):
            raise SimValueError("target of mem_concrete is symbolic!")
        return self.solver.eval(e)


Instruction:
Can you tell what is the following Python 3 function doing
def stack_push(self, thing):
        # increment sp
        sp = self.regs.sp + self.arch.stack_change
        self.regs.sp = sp
        return self.memory.store(sp, thing, endness=self.arch.memory_endness)

Response: Push a thing onto the stack and adjust the stack pointer.


Instruction:
Write a Python 3 script for
popping from the stack and returns the popped thing.

Response:
def stack_pop(self):
        """
        Pops from the stack and returns the popped thing. The length will be the architecture word size.
        """
        sp = self.regs.sp
        self.regs.sp = sp - self.arch.stack_change
        return self.memory.load(sp, self.arch.bytes, endness=self.arch.memory_endness)


Instruction:
How would you implement a function in Python 3 that
reads length bytes at an offset into the stack.

Response:
def stack_read(self, offset, length, bp=False):
        """
        Reads length bytes, at an offset into the stack.

        :param offset:  The offset from the stack pointer.
        :param length:  The number of bytes to read.
        :param bp:      If True, offset from the BP instead of the SP. Default: False.
        """
        sp = self.regs.bp if bp else self.regs.sp
        return self.memory.load(sp+offset, length, endness=self.arch.memory_endness)


Instruction:
Can you write a function in Python 3 where it
converts each stack value to a string

Response:
def _stack_values_to_string(self, stack_values):
        """
        Convert each stack value to a string

        :param stack_values: A list of values
        :return: The converted string
        """

        strings = [ ]
        for stack_value in stack_values:
            if self.solver.symbolic(stack_value):
                concretized_value = "SYMBOLIC - %s" % repr(stack_value)
            else:
                if len(self.solver.eval_upto(stack_value, 2)) == 2:
                    concretized_value = repr(stack_value)
                else:
                    concretized_value = repr(stack_value)
            strings.append(concretized_value)

        return " .. ".join(strings)


Instruction:
Given the following Python 3 function, write the documentation
def dbg_print_stack(self, depth=None, sp=None):

        var_size = self.arch.bytes
        sp_sim = self.regs._sp
        bp_sim = self.regs._bp
        if self.solver.symbolic(sp_sim) and sp is None:
            result = "SP is SYMBOLIC"
        elif self.solver.symbolic(bp_sim) and depth is None:
            result = "BP is SYMBOLIC"
        else:
            sp_value = sp if sp is not None else self.solver.eval(sp_sim)
            if self.solver.symbolic(bp_sim):
                result = "SP = 0x%08x, BP is symbolic\n" % (sp_value)
                bp_value = None
            else:
                bp_value = self.solver.eval(bp_sim)
                result = "SP = 0x%08x, BP = 0x%08x\n" % (sp_value, bp_value)
            if depth is None:
                # bp_value cannot be None here
                depth = (bp_value - sp_value) // var_size + 1 # Print one more value
            pointer_value = sp_value
            for i in range(depth):
                # For AbstractMemory, we wanna utilize more information from VSA
                stack_values = [ ]

                if o.ABSTRACT_MEMORY in self.options:
                    sp = self.regs._sp
                    segment_sizes = self.memory.get_segments(sp + i * var_size, var_size)

                    pos = i * var_size
                    for segment_size in segment_sizes:
                        stack_values.append(self.stack_read(pos, segment_size, bp=False))
                        pos += segment_size
                else:
                    stack_values.append(self.stack_read(i * var_size, var_size, bp=False))

                # Convert it into a big string!
                val = self._stack_values_to_string(stack_values)

                if pointer_value == sp_value:
                    line = "(sp)% 16x | %s" % (pointer_value, val)
                elif pointer_value == bp_value:
                    line = "(bp)% 16x | %s" % (pointer_value, val)
                else:
                    line = "% 20x | %s" % (pointer_value, val)

                pointer_value += var_size
                result += line + "\n"
        return result

Response: Print the current stack frame in a string.


Instruction:
Make a summary of the following Python 3 code
def _conc_alloc_size(self, sim_size):
        if self.state.solver.symbolic(sim_size):
            size = self.state.solver.max_int(sim_size)
            if size > self.state.libc.max_variable_size:
                l.warning("Allocation request of %d bytes exceeded maximum of %d bytes; allocating %d bytes",
                          size, self.state.libc.max_variable_size, size)
                size = self.state.libc.max_variable_size
        else:
            size = self.state.solver.eval(sim_size)
        return size

Response: Concretizes a size argument to something that makes sense when allocating space. This function is used to allocate a new size for a new entry in the heap.


Instruction:
Make a summary of the following Python 3 code
def _calloc(self, sim_nmemb, sim_size):
        raise NotImplementedError("%s not implemented for %s" % (self._calloc.__func__.__name__,
                                                                 self.__class__.__name__))

Response: Handler for any libc calloc SimProcedure call.


Instruction:
Implement a Python 3 function for
converting multiple supported forms of stack pointer representations into stack offsets.

Response:
def parse_stack_pointer(sp):
    """
    Convert multiple supported forms of stack pointer representations into stack offsets.

    :param sp:  A stack pointer representation.
    :return:    A stack pointer offset.
    :rtype:     int
    """
    if isinstance(sp, int):
        return sp

    if isinstance(sp, StackBaseOffset):
        return sp.offset

    if isinstance(sp, BinaryOp):
        op0, op1 = sp.operands
        off0 = parse_stack_pointer(op0)
        off1 = parse_stack_pointer(op1)
        if sp.op == "Sub":
            return off0 - off1
        elif sp.op == "Add":
            return off0 + off1

    raise NotImplementedError("Unsupported stack pointer representation type %s." % type(sp))


Instruction:
Given the following Python 3 function, write the documentation
def get_variable_definitions(self, block_addr):

        if block_addr in self._outstates:
            return self._outstates[block_addr].variables
        return set()

Response: Get variables that are defined at the specified block.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def _phi_node_contains(self, phi_variable, variable):

        if self.variable_manager[self.function.addr].is_phi_variable(phi_variable):
            return variable in self.variable_manager[self.function.addr].get_phi_subvariables(phi_variable)
        return False

Response: Checks if the phi variable contains variable as a sub - variable.


Instruction:
Write a Python 3 function for
returning the lineage of histories leading up to h.

Response:
def lineage(self, h):
        """
        Returns the lineage of histories leading up to `h`.
        """

        lineage = [ ]

        predecessors = list(self._graph.predecessors(h))
        while len(predecessors):
            lineage.append(predecessors[0])
            predecessors = list(self._graph.predecessors(predecessors[0]))

        lineage.reverse()
        return lineage


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

        histories = set(self.get_ref(s.history) for s in states)

        for n in networkx.algorithms.dfs_postorder_nodes(self._graph):
            intersection = histories.intersection(self.all_successors(n))
            if len(intersection) > 1:
                return (
                    [ s for s in states if self.get_ref(s.history) in intersection ],
                    n(),
                    [ s for s in states if self.get_ref(s.history) not in intersection ]
                )

        # didn't find any?
        return set(), None, states

Response: Find the most mergeable set of states from those provided.


Instruction:
Given the following Python 3 function, write the documentation
def process(self, state, *args, **kwargs):
        inline = kwargs.pop('inline', False)
        force_addr = kwargs.pop('force_addr', None)

        ip = state._ip
        addr = (ip if isinstance(ip, SootAddressDescriptor) else state.solver.eval(ip)) \
            if force_addr is None else force_addr

        # make a copy of the initial state for actual processing, if needed
        if not inline and o.COPY_STATES in state.options:
            new_state = state.copy()
        else:
            new_state = state
        # enforce this distinction
        old_state = state
        del state

        # we have now officially begun the stepping process! now is where we "cycle" a state's
        # data - move the "present" into the "past" by pushing an entry on the history stack.
        # nuance: make sure to copy from the PREVIOUS state to the CURRENT one
        # to avoid creating a dead link in the history, messing up the statehierarchy
        new_state.register_plugin('history', old_state.history.make_child())
        new_state.history.recent_bbl_addrs.append(addr)
        if new_state.arch.unicorn_support:
            new_state.scratch.executed_pages_set = {addr & ~0xFFF}

        successors = SimSuccessors(addr, old_state)

        new_state._inspect('engine_process', when=BP_BEFORE, sim_engine=self, sim_successors=successors, address=addr)
        successors = new_state._inspect_getattr('sim_successors', successors)
        try:
            self._process(new_state, successors, *args, **kwargs)
        except SimException:
            if o.EXCEPTION_HANDLING not in old_state.options:
                raise
            old_state.project.simos.handle_exception(successors, self, *sys.exc_info())

        new_state._inspect('engine_process', when=BP_AFTER, sim_successors=successors, address=addr)
        successors = new_state._inspect_getattr('sim_successors', successors)

        # downsizing
        if new_state.supports_inspect:
            new_state.inspect.downsize()
        # if not TRACK, clear actions on OLD state
        #if o.TRACK_ACTION_HISTORY not in old_state.options:
        #    old_state.history.recent_events = []

        # fix up the descriptions...
        description = str(successors)
        l.info("Ticked state: %s", description)
        for succ in successors.all_successors:
            succ.history.recent_description = description
        for succ in successors.flat_successors:
            succ.history.recent_description = description

        return successors

Response: This method is called by the engine to process the state.


Instruction:
Write a Python 3 function that can
check if this engine can be used for execution on the current state.

Response:
def check(self, state, *args, **kwargs):
        """
        Check if this engine can be used for execution on the current state. A callback `check_failure` is called upon
        failed checks. Note that the execution can still fail even if check() returns True.

        You should only override this method in a subclass in order to provide the correct method signature and
        docstring. You should override the ``_check`` method to do your actual execution.

        :param SimState state: The state with which to execute.
        :param args:                   Positional arguments that will be passed to process().
        :param kwargs:                 Keyword arguments that will be passed to process().
        :return:                       True if the state can be handled by the current engine, False otherwise.
        """

        return self._check(state, *args, **kwargs)


Instruction:
Can you implement a function in Python 3 that
checks if the resolved target is valid.

Response:
def _is_target_valid(self, cfg, target):  # pylint:disable=no-self-use
        """
        Check if the resolved target is valid.

        :param cfg:         The CFG analysis object.
        :param int target:  The target to check.
        :return:            True if the target is valid. False otherwise.
        :rtype:             bool
        """

        if self.base_state is not None:
            try:
                if self.base_state.solver.is_true((self.base_state.memory.permissions(target) & 4) == 4):
                    return True
            except SimMemoryError:
                pass
            return False

        if cfg._addr_in_exec_memory_regions(target):
            # the jump target is executable
            return True
        if self.project.is_hooked(target):
            # the jump target is hooked
            return True
        return False


Instruction:
Implement a Python 3 function for
executing the base execution of the base execution.

Response:
def process(self, state,
            step=None,
            extra_stop_points=None,
            inline=False,
            force_addr=None,
            **kwargs):
        """
        :param state:               The state with which to execute
        :param step:                How many basic blocks we want to execute
        :param extra_stop_points:   A collection of addresses at which execution should halt
        :param inline:              This is an inline execution. Do not bother copying the state.
        :param force_addr:          Force execution to pretend that we're working at this concrete
                                    address
        :returns:                   A SimSuccessors object categorizing the results of the run and
                                    whether it succeeded.
        """
        return super(SimEngineUnicorn, self).process(state,
                step=step,
                extra_stop_points=extra_stop_points,
                inline=inline,
                force_addr=force_addr)


Instruction:
Make a summary of the following Python 3 code
def get_size(self):
        raise NotImplementedError("%s not implemented for %s" % (self.get_size.__func__.__name__,
                                                                 self.__class__.__name__))

Response: Returns the actual size of a resource.


Instruction:
How would you code a function in Python 3 to
set the size of the chunk preserving any flags.

Response:
def set_size(self, size):
        """
        Sets the size of the chunk, preserving any flags.
        """
        raise NotImplementedError("%s not implemented for %s" % (self.set_size.__func__.__name__,
                                                                 self.__class__.__name__))


Instruction:
Given the following Python 3 function, write the documentation
def data_ptr(self):
        raise NotImplementedError("%s not implemented for %s" % (self.data_ptr.__func__.__name__,
                                                                 self.__class__.__name__))

Response: Returns the address of the payload of the chunk.


Instruction:
Make a summary of the following Python 3 code
def is_free(self):
        raise NotImplementedError("%s not implemented for %s" % (self.is_free.__func__.__name__,
                                                                 self.__class__.__name__))

Response: Returns a concrete determination as to whether the chunk is free.


Instruction:
Can you write a function in Python 3 where it
returns the next chunk in the sequence immediately following this one.

Response:
def next_chunk(self):
        """
        Returns the chunk immediately following (and adjacent to) this one.
        """
        raise NotImplementedError("%s not implemented for %s" % (self.next_chunk.__func__.__name__,
                                                                 self.__class__.__name__))


Instruction:
How would you implement a function in Python 3 that
returns the chunk immediately prior to this one.

Response:
def prev_chunk(self):
        """
        Returns the chunk immediately prior (and adjacent) to this one.
        """
        raise NotImplementedError("%s not implemented for %s" % (self.prev_chunk.__func__.__name__,
                                                                 self.__class__.__name__))


Instruction:
Can you generate a brief explanation for the following Python 3 code
def fwd_chunk(self):
        raise NotImplementedError("%s not implemented for %s" % (self.fwd_chunk.__func__.__name__,
                                                                 self.__class__.__name__))

Response: Returns the next free chunk in the list of free chunks.


Instruction:
How would you implement a function in Python 3 that
sets the chunk following this chunk in the free chunks list.

Response:
def set_fwd_chunk(self, fwd):
        """
        Sets the chunk following this chunk in the list of free chunks.

        :param fwd: the chunk to follow this chunk in the list of free chunks
        """
        raise NotImplementedError("%s not implemented for %s" % (self.set_fwd_chunk.__func__.__name__,
                                                                 self.__class__.__name__))


Instruction:
Here you have a function in Python 3, explain what it does
def bck_chunk(self):
        raise NotImplementedError("%s not implemented for %s" % (self.bck_chunk.__func__.__name__,
                                                                 self.__class__.__name__))

Response: Returns the chunk backward from this chunk in the list of free chunks.


Instruction:
Write a Python 3 script to
set the chunk backward from this chunk in the free chunks list.

Response:
def set_bck_chunk(self, bck):
        """
        Sets the chunk backward from this chunk in the list of free chunks.

        :param bck: the chunk to precede this chunk in the list of free chunks
        """
        raise NotImplementedError("%s not implemented for %s" % (self.set_bck_chunk.__func__.__name__,
                                                                 self.__class__.__name__))


Instruction:
Implement a function in Python 3 to
return an iterator over all the chunks in the heap.

Response:
def chunks(self):
        """
        Returns an iterator over all the chunks in the heap.
        """
        raise NotImplementedError("%s not implemented for %s" % (self.chunks.__func__.__name__,
                                                                 self.__class__.__name__))


Instruction:
Can you generate the documentation for the following Python 3 function
def allocated_chunks(self):
        raise NotImplementedError("%s not implemented for %s" % (self.allocated_chunks.__func__.__name__,
                                                                 self.__class__.__name__))

Response: Returns an iterator over all the allocated chunks in the heap.


Instruction:
Explain what the following Python 3 code does
def free_chunks(self):
        raise NotImplementedError("%s not implemented for %s" % (self.free_chunks.__func__.__name__,
                                                                 self.__class__.__name__))

Response: Returns an iterator over all the free chunks in the heap.


Instruction:
How would you explain what the following Python 3 function does
def chunk_from_mem(self, ptr):
        raise NotImplementedError("%s not implemented for %s" % (self.chunk_from_mem.__func__.__name__,
                                                                 self.__class__.__name__))

Response: Given a pointer to a user payload return the associated heap chunk.


Instruction:
How would you explain what the following Python 3 function does
def successors(self, state, addr=None, jumpkind=None, default_engine=False, procedure_engine=False,
                   engines=None, **kwargs):
        """
        Perform execution using any applicable engine. Enumerate the current engines and use the
        first one that works. Engines are enumerated in order, specified by the ``order`` attribute.

        :param state:               The state to analyze
        :param addr:                optional, an address to execute at instead of the state's ip
        :param jumpkind:            optional, the jumpkind of the previous exit
        :param default_engine:      Whether we should only attempt to use the default engine (usually VEX)
        :param procedure_engine:    Whether we should only attempt to use the procedure engine
        :param engines:             A list of engines to try to use, instead of the default.
                                    This list is expected to contain engine names or engine instances.

        Additional keyword arguments will be passed directly into each engine's process method.

        :return SimSuccessors:      A SimSuccessors object classifying the results of the run.
        """
        if addr is not None or jumpkind is not None:
            state = state.copy()
            if addr is not None:
                state.ip = addr
            if jumpkind is not None:
                state.history.jumpkind = jumpkind

        if default_engine and self.has_default_engine():
            engines = [self.default_engine]
        elif procedure_engine and self.has_procedure_engine():
            engines = [self.procedure_engine]
        elif engines is None:
            engines = (self.get_plugin(name) for name in self.order)
        else:
            engines = (self.get_plugin(e) if isinstance(e, str) else e for e in engines)

        for engine in engines:
            if engine.check(state, **kwargs):
                r = engine.process(state, **kwargs)
                if r.processed:
                    return r

        raise AngrExitError("All engines failed to execute!")

Response: Returns a SimSuccessors object representing the successors of the current state.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def _get_next_code_addr(self, initial_state):
        next_addr = self._get_next_addr_to_search()
        if next_addr is None:
            return None

        start_addr = next_addr
        sz = ""
        is_sz = True
        while is_sz:
            # Get data until we meet a 0
            while next_addr in initial_state.memory:
                try:
                    l.debug("Searching address %x", next_addr)
                    val = initial_state.mem_concrete(next_addr, 1)
                    if val == 0:
                        if len(sz) < 4:
                            is_sz = False
                        else:
                            reach_end = True
                        break
                    if chr(val) not in string.printable:
                        is_sz = False
                        break
                    sz += chr(val)
                    next_addr += 1
                except SimValueError:
                    # Not concretizable
                    l.debug("Address 0x%08x is not concretizable!", next_addr)
                    break

            if len(sz) > 0 and is_sz:
                l.debug("Got a string of %d chars: [%s]", len(sz), sz)
                # l.debug("Occpuy %x - %x", start_addr, start_addr + len(sz) + 1)
                self._seg_list.occupy(start_addr, len(sz) + 1)
                sz = ""
                next_addr = self._get_next_addr_to_search()
                if next_addr is None:
                    return None
                # l.debug("next addr = %x", next_addr)
                start_addr = next_addr

            if is_sz:
                next_addr += 1

        instr_alignment = initial_state.arch.instruction_alignment
        if start_addr % instr_alignment > 0:
            start_addr = start_addr - start_addr % instr_alignment + \
                instr_alignment

        l.debug('_get_next_code_addr() returns 0x%x', start_addr)
        return start_addr

Response: This function searches for the next code address in the memory.


Instruction:
Explain what the following Python 3 code does
def _symbolic_reconnoiter(self, addr, target_addr, max_depth=10):
        state = self.project.factory.blank_state(addr=addr,
                                                  mode="symbolic",
                                                  add_options={o.CALLLESS}
                                                 )
        initial_exit = self.project.factory.path(state)
        explorer = Explorer(self.project,
                            start=initial_exit,
                            max_depth=max_depth,
                            find=(target_addr), num_find=1).run()
        if len(explorer.found) > 0:
            path = explorer.found[0]
            last_run = path.last_run
            return last_run.flat_exits()
        else:
            return []

Response: This method returns a list of all possible symbolic IRSBs that are at target_addr.


Instruction:
Can you tell what is the following Python 3 function doing
def _scan_function_prologues(self, traced_address, function_exits, initial_state):

        # Precompile all regexes
        regexes = set()
        for ins_regex in self.project.arch.function_prologs:
            r = re.compile(ins_regex)
            regexes.add(r)

        # TODO: Make sure self._start is aligned

        # Construct the binary blob first

        for start_, bytes_ in self.project.loader.main_object.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:
                        if position not in traced_address:
                            percentage = self._seg_list.occupied_size * 100.0 / (self._valid_memory_region_size)
                            l.info("Scanning %xh, progress %0.04f%%", position, percentage)

                            self._unassured_functions.add(position)

                            self._scan_code(traced_address, function_exits, initial_state, position)
                        else:
                            l.info("Skipping %xh", position)

Response: Scan the entire program space for prologues and start code scanning at those positions.


Instruction:
Write a Python 3 function that can
execute each basic block with an indeterminiable exit target.

Response:
def _process_indirect_jumps(self):
        """
        Execute each basic block with an indeterminiable exit target
        :returns:
        """

        function_starts = set()
        l.info("We have %d indirect jumps", len(self._indirect_jumps))

        for jumpkind, irsb_addr in self._indirect_jumps:
            # First execute the current IRSB in concrete mode

            if len(function_starts) > 20:
                break

            if jumpkind == "Ijk_Call":
                state = self.project.factory.blank_state(addr=irsb_addr, mode="concrete",
                                                    add_options={o.SYMBOLIC_INITIAL_VALUES}
                                                   )
                path = self.project.factory.path(state)
                l.debug(hex(irsb_addr))

                try:
                    r = (path.next_run.successors + path.next_run.unsat_successors)[0]
                    ip = r.solver.eval_one(r.ip)

                    function_starts.add(ip)
                    continue
                except SimSolverModeError as ex:
                    pass

                # Not resolved
                # Do a backward slicing from the call
                irsb = self.project.factory.block(irsb_addr).vex
                stmts = irsb.statements
                # Start slicing from the "next"

                b = Blade(self.cfg, irsb.addr, -1, project=self.project)

                # Debugging output
                for addr, stmt_idx in sorted(list(b.slice.nodes())):
                    irsb = self.project.factory.block(addr).vex
                    stmts = irsb.statements
                    l.debug("%x: %d | %s %d", (addr, stmt_idx), stmts[stmt_idx], b.slice.in_degree((addr, stmt_idx)))

                # Get all sources
                sources = [n for n in b.slice.nodes() if b.slice.in_degree(n) == 0]

                # Create the annotated CFG
                annotatedcfg = AnnotatedCFG(self.project, None, target_irsb_addr=irsb_addr, detect_loops=False)
                annotatedcfg.from_digraph(b.slice)

                for src_irsb, src_stmt_idx in sources:
                    # Use slicecutor to execute each one, and get the address
                    # We simply give up if any exception occurs on the way

                    start_state = self.project.factory.blank_state(addr=src_irsb,
                                                              add_options=
                                                              {o.DO_RET_EMULATION,
                                                               o.TRUE_RET_EMULATION_GUARD}
                                                             )

                    start_path = self.project.factory.path(start_state)

                    # Create the slicecutor
                    slicecutor = Slicecutor(self.project, annotatedcfg, start=start_path, targets=(irsb_addr,))

                    # Run it!
                    try:
                        slicecutor.run()
                    except KeyError as ex:
                        # This is because the program slice is incomplete.
                        # Blade will support more IRExprs and IRStmts
                        l.debug("KeyError occurred due to incomplete program slice.", exc_info=ex)
                        continue

                    # Get the jumping targets
                    for r in slicecutor.reached_targets:
                        if r.next_run.successors:
                            target_ip = r.next_run.successors[0].ip
                            se = r.next_run.successors[0].se

                            if not se.symbolic(target_ip):
                                concrete_ip = se.eval_one(target_ip)
                                function_starts.add(concrete_ip)
                                l.info("Found a function address %x", concrete_ip)

        return function_starts


Instruction:
Write a Python 3 function for
solving for the most possible base address.

Response:
def _solve_forbase_address(self, function_starts, functions):
        """
        Voting for the most possible base address.

        :param function_starts:
        :param functions:
        :returns:
        """

        pseudo_base_addr = self.project.loader.main_object.min_addr

        base_addr_ctr = { }

        for s in function_starts:
            for f in functions:
                base_addr = s - f + pseudo_base_addr
                ctr = 1

                for k in function_starts:
                    if k - base_addr + pseudo_base_addr in functions:
                        ctr += 1

                if ctr > 5:
                    base_addr_ctr[base_addr] = ctr

        if len(base_addr_ctr):
            base_addr, hits = sorted([(k, v) for k, v in base_addr_ctr.items()], key=lambda x: x[1], reverse=True)[0]

            return base_addr
        else:
            return None


Instruction:
Can you tell what is the following Python 3 function doing
def _determinebase_address(self):

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

        dump_file_prefix = self.project.filename

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

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

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

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

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

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

                self.call_map.add_node(next_addr)

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

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

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

        l.debug("Construction finished.")

Response: Determine the base address of the function that we are going to be called.


Instruction:
How would you code a function in Python 3 to
perform a full code scan on the target binary.

Response:
def _full_code_scan(self):
        """
        Perform a full code scan on the target binary.
        """

        # We gotta time this function
        start_time = datetime.now()

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

        widgets = [progressbar.Percentage(),
                   ' ',
                   progressbar.Bar(marker=progressbar.RotatingMarker()),
                   ' ',
                   progressbar.Timer(),
                   ' ',
                   progressbar.ETA()
        ]

        pb = progressbar.ProgressBar(widgets=widgets, maxval=10000 * 100).start()

        while True:
            next_addr = self._get_next_code_addr(initial_state)
            percentage = self._seg_list.occupied_size * 100.0 / (self._valid_memory_region_size)
            if percentage > 100.0: percentage = 100.0
            pb.update(percentage * 10000)

            if next_addr is not None:
                l.info("Analyzing %xh, progress %0.04f%%", next_addr, percentage)
            else:
                l.info('No more addr to analyze. Progress %0.04f%%', percentage)
                break

            self.call_map.add_node(next_addr)

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

        pb.finish()
        end_time = datetime.now()
        l.info("A full code scan takes %d seconds.", (end_time - start_time).seconds)


Instruction:
Can you generate a brief explanation for the following Python 3 code
def genenare_callmap_sif(self, filepath):
        graph = self.call_map

        if graph is None:
            raise AngrGirlScoutError('Please generate the call graph first.')

        f = open(filepath, "wb")

        for src, dst in graph.edges():
            f.write("0x%x\tDirectEdge\t0x%x\n" % (src, dst))

        f.close()

Response: Generate a sif file from the call map.


Instruction:
Write a Python 3 function that can
generate a list of all recovered basic blocks.

Response:
def generate_code_cover(self):
        """
        Generate a list of all recovered basic blocks.
        """

        lst = [ ]
        for irsb_addr in self.cfg.nodes():
            if irsb_addr not in self._block_size:
                continue
            irsb_size = self._block_size[irsb_addr]
            lst.append((irsb_addr, irsb_size))

        lst = sorted(lst, key=lambda x: x[0])

        return lst


Instruction:
Make a summary of the following Python 3 code
def reload_solver(self, constraints=None):

        if constraints is None:
            constraints = self._solver.constraints
        self._stored_solver = None
        self._solver.add(constraints)

Response: Reloads the solver. Useful when changing solver options.


Instruction:
Given the following Python 3 function, write the documentation
def get_variables(self, *keys):
        for k, v in self.eternal_tracked_variables.items():
            if len(k) >= len(keys) and all(x == y for x, y in zip(keys, k)):
                yield k, v
        for k, v in self.temporal_tracked_variables.items():
            if k[-1] is None:
                continue
            if len(k) >= len(keys) and all(x == y for x, y in zip(keys, k)):
                yield k, v

Response: Iterate over all variables for which their tracking key is a prefix of the values provided.


Instruction:
Implement a function in Python 3 to
register a value with the variable tracking system.

Response:
def register_variable(self, v, key, eternal=True):
        """
        Register a value with the variable tracking system

        :param v:       The BVS to register
        :param key:     A tuple to register the variable under
        :parma eternal: Whether this is an eternal variable, default True. If False, an incrementing counter will be
                        appended to the key.
        """
        if type(key) is not tuple:
            raise TypeError("Variable tracking key must be a tuple")
        if eternal:
            self.eternal_tracked_variables[key] = v
        else:
            self.temporal_tracked_variables = dict(self.temporal_tracked_variables)
            ctrkey = key + (None,)
            ctrval = self.temporal_tracked_variables.get(ctrkey, 0) + 1
            self.temporal_tracked_variables[ctrkey] = ctrval
            tempkey = key + (ctrval,)
            self.temporal_tracked_variables[tempkey] = v


Instruction:
Write a Python 3 script to
give an AST iterate over all the keys of all the BVS leaves in the tree which are registered.

Response:
def describe_variables(self, v):
        """
        Given an AST, iterate over all the keys of all the BVS leaves in the tree which are registered.
        """
        reverse_mapping = {next(iter(var.variables)): k for k, var in self.eternal_tracked_variables.items()}
        reverse_mapping.update({next(iter(var.variables)): k for k, var in self.temporal_tracked_variables.items() if k[-1] is not None})

        for var in v.variables:
            if var in reverse_mapping:
                yield reverse_mapping[var]