Instruction:
Write a Python 3 script to
get the named plugin.

Response:
def get_plugin(self, name):
        """
        Get the plugin named ``name``. If no such plugin is currently active, try to activate a new
        one using the current preset.
        """
        if name in self._active_plugins:
            return self._active_plugins[name]

        elif self.has_plugin_preset:
            plugin_cls = self._active_preset.request_plugin(name)
            plugin = self._init_plugin(plugin_cls)

            # Remember that this plugin was provided by preset.
            self._provided_by_preset.append(id(plugin))

            self.register_plugin(name, plugin)
            return plugin

        else:
            raise AngrNoPluginError("No such plugin: %s" % name)


Instruction:
Given the following Python 3 function, write the documentation
def register_plugin(self, name, plugin):
        if self.has_plugin(name):
            self.release_plugin(name)
        self._active_plugins[name] = plugin
        setattr(self, name, plugin)
        return plugin

Response: Register a new plugin with the given name.


Instruction:
Create a Python 3 function to
deactivate and remove the plugin with name name.

Response:
def release_plugin(self, name):
        """
        Deactivate and remove the plugin with name ``name``.
        """
        plugin = self._active_plugins[name]
        if id(plugin) in self._provided_by_preset:
            self._provided_by_preset.remove(id(plugin))

        del self._active_plugins[name]
        delattr(self, name)


Instruction:
Here you have a function in Python 3, explain what it does
def copy(self):
        cls = self.__class__
        result = cls.__new__(cls)
        result._default_plugins = dict(self._default_plugins)  # pylint:disable=protected-access
        return result

Response: Return a copy of self.


Instruction:
Here you have a function in Python 3, explain what it does
def _grab_concretization_results(cls, state):
        # only grab ones that match the constrained addrs
        if cls._should_add_constraints(state):
            addr = state.inspect.address_concretization_expr
            result = state.inspect.address_concretization_result
            if result is None:
                l.warning("addr concretization result is None")
                return
            state.preconstrainer.address_concretization.append((addr, result))

Response: Grabs the concretized result so we can add the constraint ourselves.


Instruction:
Create a Python 3 function for
checking to see if the current concretization variable is any of the constrained_addrs we want to allow concretization for

Response:
def _should_add_constraints(cls, state):
        """
        Check to see if the current address concretization variable is any of the registered
        constrained_addrs we want to allow concretization for
        """
        expr = state.inspect.address_concretization_expr
        hit_indices = cls._to_indices(state, expr)

        for action in state.preconstrainer._constrained_addrs:
            var_indices = cls._to_indices(state, action.addr)
            if var_indices == hit_indices:
                return True
        return False


Instruction:
Make a summary of the following Python 3 code
def allocate(self, sim_size):
        size = self._conc_alloc_size(sim_size)
        addr = self.state.heap.heap_location
        self.state.heap.heap_location += size
        l.debug("Allocating %d bytes at address %#08x", size, addr)
        return addr

Response: Allocates memory for the current heap entry.


Instruction:
Can you create a Python 3 function that
gets the function name from a C - style function declaration string.

Response:
def get_function_name(s):
    """
    Get the function name from a C-style function declaration string.

    :param str s: A C-style function declaration string.
    :return:      The function name.
    :rtype:       str
    """

    s = s.strip()
    if s.startswith("__attribute__"):
        # Remove "__attribute__ ((foobar))"
        if "))" not in s:
            raise ValueError("__attribute__ is present, but I cannot find double-right parenthesis in the function "
                             "declaration string.")

        s = s[s.index("))") + 2 : ].strip()

    if '(' not in s:
        raise ValueError("Cannot find any left parenthesis in the function declaration string.")

    func_name = s[:s.index('(')].strip()

    for i, ch in enumerate(reversed(func_name)):
        if ch == ' ':
            pos = len(func_name) - 1 - i
            break
    else:
        raise ValueError('Cannot find any space in the function declaration string.')

    func_name = func_name[pos + 1 : ]
    return func_name


Instruction:
Write a Python 3 function for
converting a C - style function declaration string to its corresponding SimTypes - based Python representation.

Response:
def convert_cproto_to_py(c_decl):
    """
    Convert a C-style function declaration string to its corresponding SimTypes-based Python representation.

    :param str c_decl:              The C-style function declaration string.
    :return:                        A tuple of the function name, the prototype, and a string representing the
                                    SimType-based Python representation.
    :rtype:                         tuple
    """

    s = [ ]

    try:
        s.append('# %s' % c_decl)  # comment string

        parsed = parse_file(c_decl)
        parsed_decl = parsed[0]
        if not parsed_decl:
            raise ValueError('Cannot parse the function prototype.')

        func_name, func_proto = next(iter(parsed_decl.items()))

        s.append('"%s": %s,' % (func_name, func_proto._init_str()))  # The real Python string

    except Exception:  # pylint:disable=broad-except
        # Silently catch all parsing errors... supporting all function declarations is impossible
        try:
            func_name = get_function_name(c_decl)
            func_proto = None
            s.append('"%s": None,' % func_name)
        except ValueError:
            # Failed to extract the function name. Is it a function declaration?
            func_name, func_proto = None, None

    return func_name, func_proto, "\n".join(s)


Instruction:
Explain what the following Python 3 code does
def set_size(self, size, is_free=None):  #pylint:disable=arguments-differ
        self._set_leading_size(size)
        next_chunk = self.next_chunk()
        if is_free is not None:
            if next_chunk is not None:
                next_chunk.set_prev_freeness(is_free)
            else:
                self.heap._set_final_freeness(is_free)
        if is_free is not None and is_free or self.is_free():
            if next_chunk is not None:
                self.state.memory.store(next_chunk.base, size)

Response: Set the size of the an arbitrary number of entries in the cache.


Instruction:
Write a Python 3 function that can
set the flag controlling whether the previous chunk is free.

Response:
def set_prev_freeness(self, is_free):
        """
        Sets (or unsets) the flag controlling whether the previous chunk is free.

        :param is_free: if True, sets the previous chunk to be free; if False, sets it to be allocated
        """
        level = silence_logger()
        size_field = self.state.memory.load(self.base + self._chunk_size_t_size, self._chunk_size_t_size)
        unsilence_logger(level)
        if is_free:
            self.state.memory.store(self.base + self._chunk_size_t_size, size_field & ~CHUNK_P_MASK)
        else:
            self.state.memory.store(self.base + self._chunk_size_t_size, size_field | CHUNK_P_MASK)


Instruction:
Given the following Python 3 function, write the documentation
def is_prev_free(self):
        flag = self.state.memory.load(self.base + self._chunk_size_t_size, self._chunk_size_t_size) & CHUNK_P_MASK

        def sym_flag_handler(flag):
            l.warning("A chunk's P flag is symbolic; assuming it is not set")
            return self.state.solver.min_int(flag)

        flag = concretize(flag, self.state.solver, sym_flag_handler)
        return False if flag else True

Response: Returns a concrete state of the flag indicating whether the previous chunk is free.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def prev_size(self):
        return self.state.memory.load(self.base, self._chunk_size_t_size) & ~CHUNK_FLAGS_MASK

Response: Returns the size of the previous chunk.


Instruction:
How would you implement a function in Python 3 that
returns the next chunk in the PT.

Response:
def next_chunk(self):
        """
        Returns the chunk immediately following (and adjacent to) this one, if it exists.

        :returns: The following chunk, or None if applicable
        """

        def sym_base_handler(base):
            l.warning("A computed chunk base is symbolic; maximizing it")
            return self.state.solver.max_int(base)

        base = concretize(self.base + self.get_size(), self.state.solver, sym_base_handler)
        if base >= self.heap.heap_base + self.heap.heap_size - 2 * self._chunk_size_t_size:
            return None
        else:
            return PTChunk(base, self.state)


Instruction:
Implement a function in Python 3 to
return the chunk immediately prior to this one.

Response:
def prev_chunk(self):
        """
        Returns the chunk immediately prior (and adjacent) to this one, if that chunk is free. If the prior chunk is not
        free, then its base cannot be located and this method raises an error.

        :returns: If possible, the previous chunk; otherwise, raises an error
        """
        if self.is_prev_free():
            return PTChunk(self.base - self.prev_size(), self.state)
        else:
            raise SimHeapError("Attempted to access the previous chunk, but it was not free")


Instruction:
Can you tell what is the following Python 3 function doing
def fwd_chunk(self):
        if self.is_free():
            base = self.state.memory.load(self.base + 2 * self._chunk_size_t_size, self._chunk_size_t_size)
            return PTChunk(base, self.state)
        else:
            raise SimHeapError("Attempted to access the forward chunk of an allocated chunk")

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


Instruction:
Create a Python 3 function for
giving a pointer to a user payload return the base of the chunk associated with that payload. Returns None if the user payload is null.

Response:
def chunk_from_mem(self, ptr):
        """
        Given a pointer to a user payload, return the base of the chunk associated with that payload (i.e. the chunk
        pointer). Returns None if ptr is null.

        :param ptr: a pointer to the base of a user payload in the heap
        :returns: a pointer to the base of the associated heap chunk, or None if ptr is null
        """
        if self.state.solver.symbolic(ptr):
            try:
                ptr = self.state.solver.eval_one(ptr)
            except SimSolverError:
                l.warning("A pointer to a chunk is symbolic; maximizing it")
                ptr = self.state.solver.max_int(ptr)
        else:
            ptr = self.state.solver.eval(ptr)
        return PTChunk(ptr - (2 * self._chunk_size_t_size), self.state) if ptr != 0 else None


Instruction:
Create a Python 3 function for
finding the free chunk that would be the backwards chunk relative to the chunk at ptr.

Response:
def _find_bck(self, chunk):
        """
        Simply finds the free chunk that would be the backwards chunk relative to the chunk at ptr. Hence, the free head
        and all other metadata are unaltered by this function.
        """
        cur = self.free_head_chunk
        if cur is None:
            return None
        fwd = cur.fwd_chunk()
        if cur == fwd:
            return cur
        # At this point there should be at least two free chunks in the heap
        if cur < chunk:
            while cur < fwd < chunk:
                cur = fwd
                fwd = cur.fwd_chunk()
            return cur
        else:
            while fwd != self.free_head_chunk:
                cur = fwd
                fwd = cur.fwd_chunk()
            return cur


Instruction:
Implement a Python 3 function for
setting the freedom of the final chunk.

Response:
def _set_final_freeness(self, flag):
        """
        Sets the freedom of the final chunk. Since no proper chunk follows the final chunk, the heap itself manages
        this. Nonetheless, for now it is implemented as if an additional chunk followed the final chunk.
        """
        if flag:
            self.state.memory.store(self.heap_base + self.heap_size - self._chunk_size_t_size, ~CHUNK_P_MASK)
        else:
            self.state.memory.store(self.heap_base + self.heap_size - self._chunk_size_t_size, CHUNK_P_MASK)


Instruction:
Can you implement a function in Python 3 that
makes a suitable chunk size for the given allocation size.

Response:
def _make_chunk_size(self, req_size):
        """
        Takes an allocation size as requested by the user and modifies it to be a suitable chunk size.
        """
        size = req_size
        size += 2 * self._chunk_size_t_size  # Two size fields
        size = self._chunk_min_size if size < self._chunk_min_size else size
        if size & self._chunk_align_mask:                                         # If the chunk would not be aligned
            size = (size & ~self._chunk_align_mask) + self._chunk_align_mask + 1  # Fix it
        return size


Instruction:
Implement a Python 3 function for
allocating and initialize a new string in the context of the state passed.

Response:
def new_string(state, value):
        """
        Allocate and initialize a new string in the context of the state passed.

        The method returns the reference to the newly allocated string

        :param state: angr state where we want to allocate the string
        :type SimState
        :param value: value of the string to initialize
        :type claripy.String

        :return SimSootValue_StringRef
        """
        str_ref = SimSootValue_StringRef(state.memory.get_new_uuid())
        state.memory.store(str_ref, value)
        return str_ref


Instruction:
Make a summary of the following Python 3 code
def strtol_inner(s, state, region, base, signed, read_length=None):

        # sanity check
        if base < 2 or base > 36:
            raise SimProcedureError("base should be in the range [2,36]")

        # order matters here since we will use an if then else tree, and -0x will have precedence over -
        prefixes = [b"-", b"+", b""]
        if base == 16:
            prefixes = [b"0x", b"-0x", b"+0x"] + prefixes

        cases = []
        conditions = []
        possible_num_bytes = []

        for prefix in prefixes:
            if read_length and read_length < len(prefix):
                continue
            condition, value, num_bytes = strtol._load_num_with_prefix(prefix, s, region, state, base, signed, read_length)
            conditions.append(condition)
            cases.append((condition, value))
            possible_num_bytes.append(num_bytes)

        # only one of the cases needed to match
        result = state.solver.ite_cases(cases[:-1], cases[-1][1])
        expression = state.solver.Or(*conditions)
        num_bytes = state.solver.ite_cases(zip(conditions, possible_num_bytes), 0)
        return expression, result, num_bytes

Response: Internal function to parse a string in the specified region.


Instruction:
Given the following Python 3 function, write the documentation
def _load_num_with_prefix(prefix, addr, region, state, base, signed, read_length=None):
        length = len(prefix)
        read_length = (read_length-length) if read_length else None
        condition, value, num_bytes = strtol._string_to_int(addr+length, state, region, base, signed, read_length)

        # the prefix must match
        if len(prefix) > 0:
            loaded_prefix = region.load(addr, length)
            condition = state.solver.And(loaded_prefix == state.solver.BVV(prefix), condition)
        total_num_bytes = num_bytes + length

        # negatives
        if prefix.startswith(b"-"):
            value = state.solver.BVV(0, state.arch.bits) - value
        return condition, value, total_num_bytes

Response: Loads a number from addr and returns a condition that the prefix matches the current base.


Instruction:
Write a Python 3 function for
reading a string and generates a symbolic number that is equal to the value of the current entry in the given region.

Response:
def _string_to_int(s, state, region, base, signed, read_length=None):
        """
        reads values from s and generates the symbolic number that it would equal
        the first char is either a number in the given base, or the result is 0
        expression indicates whether or not it was successful
        """

        # if length wasn't provided, read the maximum bytes
        length = state.libc.max_strtol_len if read_length is None else read_length

        # expression whether or not it was valid at all
        expression, _ = strtol._char_to_val(region.load(s, 1), base)

        cases = []

        # to detect overflows we keep it in a larger bv and extract it at the end
        num_bits = min(state.arch.bits*2, 128)
        current_val = state.solver.BVV(0, num_bits)
        num_bytes = state.solver.BVS("num_bytes", state.arch.bits)
        constraints_num_bytes = []
        conditions = []

        cutoff = False
        # we need all the conditions to hold except the last one to have found a value
        for i in range(length):
            char = region.load(s + i, 1)
            condition, value = strtol._char_to_val(char, base)

            # if it was the end we'll get the current val
            cases.append((num_bytes == i, current_val))

            # identify the constraints necessary to set num_bytes to the current value
            # the current char (i.e. the terminator if this is satisfied) should not be a char,
            # so `condition` should be false, plus all the previous conditions should be satisfied
            case_constraints = conditions + [state.solver.Not(condition)] + [num_bytes == i]
            constraints_num_bytes.append(state.solver.And(*case_constraints))

            # break the loop early if no value past this is viable
            if condition.is_false():
                cutoff = True  # ???
                break

            # add the value and the condition
            current_val = current_val*base + value.zero_extend(num_bits-8)
            conditions.append(condition)

        # the last one is unterminated, let's ignore it
        if not cutoff:
            cases.append((num_bytes == length, current_val))
            case_constraints = conditions + [num_bytes == length]
            constraints_num_bytes.append(state.solver.And(*case_constraints))

        # only one of the constraints need to hold
        # since the constraints look like (num_bytes == 2 and the first 2 chars are valid, and the 3rd isn't)

        final_constraint = state.solver.Or(*constraints_num_bytes)
        if final_constraint.op == '__eq__' and final_constraint.args[0] is num_bytes and not final_constraint.args[1].symbolic:
            # CONCRETE CASE
            result = cases[state.solver.eval(final_constraint.args[1])][1]
            num_bytes = final_constraint.args[1]
        else:
            # symbolic case
            state.add_constraints(final_constraint)
            result = state.solver.ite_cases(cases, 0)

        # overflow check
        max_bits = state.arch.bits-1 if signed else state.arch.bits
        max_val = 2**max_bits - 1
        result = state.solver.If(result < max_val, state.solver.Extract(state.arch.bits-1, 0, result),
                             state.solver.BVV(max_val, state.arch.bits))

        return expression, result, num_bytes


Instruction:
How would you implement a function in Python 3 that
converts a symbolic char to a number in the given base.

Response:
def _char_to_val(char, base):
        """
        converts a symbolic char to a number in the given base
        returns expression, result
        expression is a symbolic boolean indicating whether or not it was a valid number
        result is the value
        """
        cases = []
        # 0-9
        max_digit = claripy.BVV(b"9")
        min_digit = claripy.BVV(b"0")
        if base < 10:
            max_digit = claripy.BVV(ord("0") + base, 8)
        is_digit = claripy.And(char >= min_digit, char <= max_digit)
        # return early here so we don't add unnecessary statements
        if base <= 10:
            return is_digit, char - min_digit

        # handle alphabetic chars
        max_char_lower = claripy.BVV(ord("a") + base-10 - 1, 8)
        max_char_upper = claripy.BVV(ord("A") + base-10 - 1, 8)
        min_char_lower = claripy.BVV(ord("a"), 8)
        min_char_upper = claripy.BVV(ord("A"), 8)

        cases.append((is_digit, char - min_digit))
        is_alpha_lower = claripy.And(char >= min_char_lower, char <= max_char_lower)
        cases.append((is_alpha_lower, char - min_char_lower + 10))
        is_alpha_upper = claripy.And(char >= min_char_upper, char <= max_char_upper)
        cases.append((is_alpha_upper, char - min_char_upper + 10))

        expression = claripy.Or(is_digit, is_alpha_lower, is_alpha_upper)
        # use the last case as the default, the expression will encode whether or not it's satisfiable
        result = claripy.ite_cases(cases[:-1], cases[-1][1])

        return expression, result


Instruction:
Explain what the following Python 3 code does
def replace(self, startpos, args):

        argpos = startpos
        string = None

        for component in self.components:
            # if this is just concrete data
            if isinstance(component, bytes):
                string = self._add_to_string(string, self.parser.state.solver.BVV(component))
            elif isinstance(component, str):
                raise Exception("this branch should be impossible?")
            elif isinstance(component, claripy.ast.BV):
                string = self._add_to_string(string, component)
            else:
                # okay now for the interesting stuff
                # what type of format specifier is it?
                fmt_spec = component
                if fmt_spec.spec_type == b's':
                    if fmt_spec.length_spec == b".*":
                        str_length = args(argpos)
                        argpos += 1
                    else:
                        str_length = None
                    str_ptr = args(argpos)
                    string = self._add_to_string(string, self._get_str_at(str_ptr, max_length=str_length))
                # integers, for most of these we'll end up concretizing values..
                else:
                    i_val = args(argpos)
                    c_val = int(self.parser.state.solver.eval(i_val))
                    c_val &= (1 << (fmt_spec.size * 8)) - 1
                    if fmt_spec.signed and (c_val & (1 << ((fmt_spec.size * 8) - 1))):
                        c_val -= (1 << fmt_spec.size * 8)

                    if fmt_spec.spec_type in (b'd', b'i'):
                        s_val = str(c_val)
                    elif fmt_spec.spec_type == b'u':
                        s_val = str(c_val)
                    elif fmt_spec.spec_type == b'c':
                        s_val = chr(c_val & 0xff)
                    elif fmt_spec.spec_type == b'x':
                        s_val = hex(c_val)[2:]
                    elif fmt_spec.spec_type == b'o':
                        s_val = oct(c_val)[2:]
                    elif fmt_spec.spec_type == b'p':
                        s_val = hex(c_val)
                    else:
                        raise SimProcedureError("Unimplemented format specifier '%s'" % fmt_spec.spec_type)

                    string = self._add_to_string(string, self.parser.state.solver.BVV(s_val.encode()))

                argpos += 1

        return string

Response: Implement printf - based on the stored format specifier information.


Instruction:
Write a Python 3 function for
interpreting the format of the format element in memory or a file.

Response:
def interpret(self, startpos, args, addr=None, simfd=None):
        """
        implement scanf - extract formatted data from memory or a file according to the stored format
        specifiers and store them into the pointers extracted from `args`.

        :param startpos:    The index of the first argument corresponding to the first format element
        :param args:        A function which, given the index of an argument to the function, returns that argument
        :param addr:        The address in the memory to extract data from, or...
        :param simfd:       A file descriptor to use for reading data from
        :return:            The number of arguments parsed
        """
        if simfd is not None and isinstance(simfd.read_storage, SimPackets):
            argnum = startpos
            for component in self.components:
                if type(component) is bytes:
                    sdata, _ = simfd.read_data(len(component), short_reads=False)
                    self.state.solver.add(sdata == component)
                elif isinstance(component, claripy.Bits):
                    sdata, _ = simfd.read_data(len(component) // 8, short_reads=False)
                    self.state.solver.add(sdata == component)
                elif component.spec_type == b's':
                    if component.length_spec is None:
                        sdata, slen = simfd.read_data(self.state.libc.buf_symbolic_bytes)
                    else:
                        sdata, slen = simfd.read_data(component.length_spec)
                    for byte in sdata.chop(8):
                        self.state.solver.add(claripy.And(*[byte != char for char in self.SCANF_DELIMITERS]))
                    self.state.memory.store(args(argnum), sdata, size=slen)
                    self.state.memory.store(args(argnum) + slen, claripy.BVV(0, 8))
                    argnum += 1
                elif component.spec_type == b'c':
                    sdata, _ = simfd.read_data(1, short_reads=False)
                    self.state.memory.store(args(argnum), sdata)
                    argnum += 1
                else:
                    bits = component.size * 8
                    if component.spec_type == b'x':
                        base = 16
                    elif component.spec_type == b'o':
                        base = 8
                    else:
                        base = 10

                    # here's the variable representing the result of the parsing
                    target_variable = self.state.solver.BVS('scanf_' + component.string.decode(), bits,
                            key=('api', 'scanf', argnum - startpos, component.string))
                    negative = claripy.SLT(target_variable, 0)

                    # how many digits does it take to represent this variable fully?
                    max_digits = int(math.ceil(math.log(2**bits, base)))

                    # how many digits does the format specify?
                    spec_digits = component.length_spec

                    # how many bits can we specify as input?
                    available_bits = float('inf') if spec_digits is None else spec_digits * math.log(base, 2)
                    not_enough_bits = available_bits < bits

                    # how many digits will we model this input as?
                    digits = max_digits if spec_digits is None else spec_digits

                    # constrain target variable range explicitly if it can't take on all possible values
                    if not_enough_bits:
                        self.state.solver.add(self.state.solver.And(
                            self.state.solver.SLE(target_variable, (base**digits) - 1),
                            self.state.solver.SGE(target_variable, -(base**(digits - 1) - 1))))

                    # perform the parsing in reverse - constrain the input digits to be the string version of the input
                    # this only works because we're reading from a packet stream and therefore nobody has the ability
                    # to add other constraints to this data!
                    # this makes z3's job EXTREMELY easy
                    sdata, _ = simfd.read_data(digits, short_reads=False)
                    for i, digit in enumerate(reversed(sdata.chop(8))):
                        digit_value = (target_variable // (base**i)) % base
                        digit_ascii = digit_value + ord('0')
                        if base > 10:
                            digit_ascii = claripy.If(digit_value >= 10, digit_value + (-10 + ord('a')), digit_ascii)

                        # if there aren't enough bits, we can increase the range by accounting for the possibility that
                        # the first digit is a minus sign
                        if not_enough_bits:
                            if i == digits - 1:
                                neg_digit_ascii = ord('-')
                            else:
                                neg_digit_value = (-target_variable // (base**i)) % base
                                neg_digit_ascii = neg_digit_value + ord('0')
                                if base > 10:
                                    neg_digit_ascii = claripy.If(neg_digit_value >= 10, neg_digit_value + (-10 + ord('a')), neg_digit_ascii)

                            digit_ascii = claripy.If(negative, neg_digit_ascii, digit_ascii)

                        self.state.solver.add(digit == digit_ascii[7:0])

                    self.state.memory.store(args(argnum), target_variable, endness=self.state.arch.memory_endness)
                    argnum += 1

            return argnum - startpos

        # TODO: we only support one format specifier in interpretation for now

        format_specifier_count = sum(1 for x in self.components if isinstance(x, FormatSpecifier))
        if format_specifier_count > 1:
            l.warning("We don't support more than one format specifiers in format strings.")

        if simfd is not None:
            region = simfd.read_storage
            addr = simfd._pos if hasattr(simfd, '_pos') else simfd._read_pos # XXX THIS IS BAD
        else:
            region = self.parser.state.memory

        bits = self.parser.state.arch.bits
        failed = self.parser.state.solver.BVV(0, bits)
        argpos = startpos
        position = addr
        for component in self.components:
            if isinstance(component, bytes):
                # TODO we skip non-format-specifiers in format string interpretation for now
                # if the region doesn't match the concrete component, we need to return immediately
                pass
            else:
                fmt_spec = component
                try:
                    dest = args(argpos)
                except SimProcedureArgumentError:
                    dest = None
                if fmt_spec.spec_type == b's':
                    # set some limits for the find
                    max_str_len = self.parser.state.libc.max_str_len
                    max_sym_bytes = self.parser.state.libc.buf_symbolic_bytes

                    # has the length of the format been limited by the string itself?
                    if fmt_spec.length_spec is not None:
                        max_str_len = fmt_spec.length_spec
                        max_sym_bytes = fmt_spec.length_spec

                    # TODO: look for limits on other characters which scanf is sensitive to, '\x00', '\x20'
                    ohr, ohc, ohi = region.find(position, self.parser.state.solver.BVV(b'\n'), max_str_len, max_symbolic_bytes=max_sym_bytes)

                    # if no newline is found, mm is position + max_strlen
                    # If-branch will really only happen for format specifiers with a length
                    mm = self.parser.state.solver.If(ohr == 0, position + max_str_len, ohr)
                    # we're just going to concretize the length, load will do this anyways
                    length = self.parser.state.solver.max_int(mm - position)
                    src_str = region.load(position, length)

                    # TODO all of these should be delimiters we search for above
                    # add that the contents of the string cannot be any scanf %s string delimiters
                    for delimiter in set(FormatString.SCANF_DELIMITERS):
                        delim_bvv = self.parser.state.solver.BVV(delimiter)
                        for i in range(length):
                            self.parser.state.add_constraints(region.load(position + i, 1) != delim_bvv)

                    # write it out to the pointer
                    self.parser.state.memory.store(dest, src_str)
                    # store the terminating null byte
                    self.parser.state.memory.store(dest + length, self.parser.state.solver.BVV(0, 8))

                    position += length

                else:

                    # XXX: atoi only supports strings of one byte
                    if fmt_spec.spec_type in [b'd', b'i', b'u', b'x']:
                        base = 16 if fmt_spec.spec_type == b'x' else 10
                        status, i, num_bytes = self.parser._sim_atoi_inner(position, region, base=base, read_length=fmt_spec.length_spec)
                        # increase failed count if we were unable to parse it
                        failed = self.parser.state.solver.If(status, failed, failed + 1)
                        position += num_bytes
                    elif fmt_spec.spec_type == b'c':
                        i = region.load(position, 1)
                        i = i.zero_extend(bits - 8)
                        position += 1
                    else:
                        raise SimProcedureError("unsupported format spec '%s' in interpret" % fmt_spec.spec_type)

                    i = self.parser.state.solver.Extract(fmt_spec.size*8-1, 0, i)
                    self.parser.state.memory.store(dest, i, size=fmt_spec.size, endness=self.parser.state.arch.memory_endness)

                argpos += 1

        if simfd is not None:
            simfd.read_data(position - addr)

        return (argpos - startpos) - failed


Instruction:
Write a Python 3 function that can
return a dictionary of modified length modifiers and conversion specifiers.

Response:
def _mod_spec(self):
        """
        Modified length specifiers: mapping between length modifiers and conversion specifiers. This generates all the
        possibilities, i.e. hhd, etc.
        """
        mod_spec={}

        for mod, sizes in self.int_len_mod.items():

            for conv in self.int_sign['signed']:
                mod_spec[mod + conv] = sizes[0]

            for conv in self.int_sign['unsigned']:
                mod_spec[mod + conv] = sizes[1]

        return mod_spec


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

        base = self._mod_spec

        for spec in self.basic_spec:
            base[spec] = self.basic_spec[spec]

        return base

Response: Return a dictionary of all the specifiers and their lengths.


Instruction:
Can you generate the documentation for the following Python 3 function
def _match_spec(self, nugget):
        # TODO: handle positional modifiers and other similar format string tricks.
        all_spec = self._all_spec

        # iterate through nugget throwing away anything which is an int
        # TODO store this in a size variable

        original_nugget = nugget
        length_str = [ ]
        length_spec = None
        length_spec_str_len = 0

        if nugget.startswith(b".*"):
            # ".*": precision is specified as an argument
            nugget = nugget[2:]
            length_spec = b".*"
            length_spec_str_len = 2

        for j, c in enumerate(nugget):
            if c in ascii_digits:
                length_str.append(c)
            else:
                nugget = nugget[j:]
                if length_spec is None:
                    length_spec = None if len(length_str) == 0 else int(bytes(length_str))
                break

        # we need the length of the format's length specifier to extract the format and nothing else
        if length_spec_str_len == 0 and length_str:
            length_spec_str_len = len(length_str)
        # is it an actual format?
        for spec in all_spec:
            if nugget.startswith(spec):
                # this is gross coz sim_type is gross..
                nugget = nugget[:len(spec)]
                original_nugget = original_nugget[:(length_spec_str_len + len(spec))]
                nugtype = all_spec[nugget]
                try:
                    typeobj = sim_type.parse_type(nugtype).with_arch(self.state.arch)
                except:
                    raise SimProcedureError("format specifier uses unknown type '%s'" % repr(nugtype))
                return FormatSpecifier(original_nugget, length_spec, typeobj.size // 8, typeobj.signed)

        return None

Response: Match the string nugget to a format specifier.


Instruction:
How would you explain what the following Python 3 function does
def _get_fmt(self, fmt):

        # iterate over the format string looking for format specifiers
        components = [ ]
        i = 0
        while i < len(fmt):
            if type(fmt[i]) is bytes and fmt[i] == b"%":
                # Note that we only support concrete format specifiers
                # grab the specifier
                # go to the space
                specifier = b""
                for c in fmt[i+1:]:
                    if type(c) is bytes:
                        specifier += c
                    else:
                        break

                specifier = self._match_spec(specifier)
                if specifier is not None:
                    i += len(specifier)
                    components.append(specifier)
                else:
                    # if we get here we didn't match any specs, the first char will be thrown away
                    # and we'll add the percent
                    i += 1
                    components.append(b'%')
            else:
                # claripy ASTs, which are usually symbolic variables
                # They will be kept as they are - even if those chars can be evaluated to "%"
                components.append(fmt[i])
            i += 1

        return FormatString(self, components)

Response: Extract the actual formats from the format string fmt.


Instruction:
Here you have a function in Python 3, explain what it does
def _sim_atoi_inner(self, str_addr, region, base=10, read_length=None):

        from .. import SIM_PROCEDURES
        strtol = SIM_PROCEDURES['libc']['strtol']

        return strtol.strtol_inner(str_addr, self.state, region, base, True, read_length=read_length)

Response: Internal function to invoke the atoi simprocedure on the given string address.


Instruction:
Write a Python 3 function that can
return the result of invoking the strlen simprocedure on str_addr.

Response:
def _sim_strlen(self, str_addr):
        """
        Return the result of invoking the strlen simprocedure on `str_addr`.
        """

        from .. import SIM_PROCEDURES
        strlen = SIM_PROCEDURES['libc']['strlen']

        return self.inline_call(strlen, str_addr).ret_expr


Instruction:
Can you create a Python 3 function that
parses a format string.

Response:
def _parse(self, fmt_idx):
        """
        Parse format strings.

        :param fmt_idx: The index of the (pointer to the) format string in the arguments list.
        :returns:       A FormatString object which can be used for replacing the format specifiers with arguments or
                        for scanning into arguments.
        """

        fmtstr_ptr = self.arg(fmt_idx)

        if self.state.solver.symbolic(fmtstr_ptr):
            raise SimProcedureError("Symbolic pointer to (format) string :(")

        length = self._sim_strlen(fmtstr_ptr)
        if self.state.solver.symbolic(length):
            all_lengths = self.state.solver.eval_upto(length, 2)
            if len(all_lengths) != 1:
                raise SimProcedureError("Symbolic (format) string, game over :(")
            length = all_lengths[0]

        if self.state.solver.is_true(length == 0):
            return FormatString(self, [b""])

        fmt_xpr = self.state.memory.load(fmtstr_ptr, length)

        fmt = [ ]
        for i in range(fmt_xpr.size(), 0, -8):
            char = fmt_xpr[i - 1 : i - 8]
            try:
                conc_char = self.state.solver.eval_one(char)
            except SimSolverError:
                # For symbolic chars, just keep them symbolic
                fmt.append(char)
            else:
                # Concrete chars are directly appended to the list
                fmt.append(bytes([conc_char]))

        # make a FormatString object
        fmt_str = self._get_fmt(fmt)

        l.debug("Fmt: %r", fmt_str)

        return fmt_str


Instruction:
How would you explain what the following Python 3 function does
def _hook_mem_unmapped(self, uc, access, address, size, value, user_data, size_extension=True): #pylint:disable=unused-argument
        # FIXME check angr hooks at `address`

        if size_extension:
            start = address & (0xfffffffffffff0000)
            length = ((address + size + 0xffff) & (0xfffffffffffff0000)) - start
        else:
            start = address & (0xffffffffffffff000)
            length = ((address + size + 0xfff) & (0xffffffffffffff000)) - start

        if (start == 0 or ((start + length) & ((1 << self.state.arch.bits) - 1)) == 0) and options.UNICORN_ZEROPAGE_GUARD in self.state.options:
            # sometimes it happens because of %fs is not correctly set
            self.error = 'accessing zero page [%#x, %#x] (%#x)' % (address, address + length - 1, access)
            l.warning(self.error)

            # tell uc_state to rollback
            _UC_NATIVE.stop(self._uc_state, STOP.STOP_ZEROPAGE)
            return False

        ret = False
        try:
            best_effort_read = size_extension
            ret = self._hook_mem_unmapped_core(uc, access, start, length, best_effort_read=best_effort_read)

        except AccessingZeroPageError:
            # raised when STRICT_PAGE_ACCESS is enabled
            if not size_extension:
                _UC_NATIVE.stop(self._uc_state, STOP.STOP_SEGFAULT)
                ret = False

        except FetchingZeroPageError:
            # raised when trying to execute code on an unmapped page
            if not size_extension:
                self.error = 'fetching empty page [%#x, %#x]' % (start, start + length - 1)
                l.warning(self.error)
                _UC_NATIVE.stop(self._uc_state, STOP.STOP_EXECNONE)
                ret = False

        except SimMemoryError:
            if not size_extension:
                raise

        except SegfaultError:
            if not size_extension:
                _UC_NATIVE.stop(self._uc_state, STOP.STOP_SEGFAULT)
                ret = False

        except MixedPermissonsError:
            if not size_extension:
                # weird... it shouldn't be raised at all
                l.error('MixedPermissionsError is raised when size-extension is disabled. Please report it.')
                _UC_NATIVE.stop(self._uc_state, STOP.STOP_SEGFAULT)
                ret = False

        except unicorn.UcError as ex:
            if not size_extension:
                if ex.errno == 11:
                    # Mapping failed. Probably because of size extension... let's try to redo it without size extension
                    pass
                else:
                    # just raise the exception
                    raise

        finally:
            if size_extension and not ret:
                # retry without size-extension if size-extension was enabled
                # any exception will not be caught
                ret = self._hook_mem_unmapped(uc, access, address, size, value, user_data, size_extension=False)

        return ret

Response: Internal hook for unicorn s mem_unmapped method.


Instruction:
Can you implement a function in Python 3 that
sets the unicorn registers

Response:
def set_regs(self):
        ''' setting unicorn registers '''
        uc = self.uc

        if self.state.arch.qemu_name == 'x86_64':
            fs = self.state.solver.eval(self.state.regs.fs)
            gs = self.state.solver.eval(self.state.regs.gs)
            self.write_msr(fs, 0xC0000100)
            self.write_msr(gs, 0xC0000101)
            flags = self._process_value(self.state.regs.eflags, 'reg')
            if flags is None:
                raise SimValueError('symbolic eflags')
            uc.reg_write(self._uc_const.UC_X86_REG_EFLAGS, self.state.solver.eval(flags))
        elif self.state.arch.qemu_name == 'i386':
            flags = self._process_value(self.state.regs.eflags, 'reg')
            if flags is None:
                raise SimValueError('symbolic eflags')

            uc.reg_write(self._uc_const.UC_X86_REG_EFLAGS, self.state.solver.eval(flags))
            fs = self.state.solver.eval(self.state.regs.fs) << 16
            gs = self.state.solver.eval(self.state.regs.gs) << 16
            self.setup_gdt(fs, gs)

        for r, c in self._uc_regs.items():
            if r in self.reg_blacklist:
                continue
            v = self._process_value(getattr(self.state.regs, r), 'reg')
            if v is None:
                    raise SimValueError('setting a symbolic register')
            # l.debug('setting $%s = %#x', r, self.state.solver.eval(v))
            uc.reg_write(c, self.state.solver.eval(v))

        if self.state.arch.name in ('X86', 'AMD64'):
            # sync the fp clerical data
            c3210 = self.state.solver.eval(self.state.regs.fc3210)
            top = self.state.solver.eval(self.state.regs.ftop[2:0])
            rm = self.state.solver.eval(self.state.regs.fpround[1:0])
            control = 0x037F | (rm << 10)
            status = (top << 11) | c3210
            uc.reg_write(unicorn.x86_const.UC_X86_REG_FPCW, control)
            uc.reg_write(unicorn.x86_const.UC_X86_REG_FPSW, status)

            # we gotta convert the 64-bit doubles values to 80-bit extended precision!
            uc_offset = unicorn.x86_const.UC_X86_REG_FP0
            vex_offset = self.state.arch.registers['fpu_regs'][0]
            vex_tag_offset = self.state.arch.registers['fpu_tags'][0]
            tag_word = 0
            for _ in range(8):
                tag = self.state.solver.eval(self.state.registers.load(vex_tag_offset, size=1))
                tag_word <<= 2
                if tag == 0:
                    tag_word |= 3       # unicorn doesn't care about any value other than 3 for setting
                else:
                    val = self._process_value(self.state.registers.load(vex_offset, size=8), 'reg')
                    if val is None:
                        raise SimValueError('setting a symbolic fp register')
                    val = self.state.solver.eval(val)

                    sign = bool(val & 0x8000000000000000)
                    exponent = (val & 0x7FF0000000000000) >> 52
                    mantissa =  val & 0x000FFFFFFFFFFFFF
                    if exponent not in (0, 0x7FF): # normal value
                        exponent = exponent - 1023 + 16383
                        mantissa <<= 11
                        mantissa |= 0x8000000000000000  # set integer part bit, implicit to double
                    elif exponent == 0:     # zero or subnormal value
                        mantissa = 0
                    elif exponent == 0x7FF:    # nan or infinity
                        exponent = 0x7FFF
                        if mantissa != 0:
                            mantissa = 0x8000000000000000
                        else:
                            mantissa = 0xFFFFFFFFFFFFFFFF

                    if sign:
                        exponent |= 0x8000

                    uc.reg_write(uc_offset, (exponent, mantissa))

                uc_offset += 1
                vex_offset += 8
                vex_tag_offset += 1

            uc.reg_write(unicorn.x86_const.UC_X86_REG_FPTAG, tag_word)


Instruction:
Explain what the following Python 3 code does
def get_regs(self):
        ''' loading registers from unicorn '''

        # first, get the ignore list (in case of symbolic registers)
        if options.UNICORN_SYM_REGS_SUPPORT in self.state.options:
            highest_reg_offset, reg_size = max(self.state.arch.registers.values())
            symbolic_list = (ctypes.c_uint64*(highest_reg_offset + reg_size))()
            num_regs = _UC_NATIVE.get_symbolic_registers(self._uc_state, symbolic_list)

            # we take the approach of saving off the symbolic regs and then writing them back
            saved_registers = [ ]
            cur_group = None
            last = None
            for i in sorted(symbolic_list[:num_regs]):
                if cur_group is None:
                    cur_group = i
                elif i != last + 1 or cur_group//self.state.arch.bytes != i//self.state.arch.bytes:
                    saved_registers.append((
                        cur_group, self.state.registers.load(cur_group, last-cur_group+1)
                    ))
                    cur_group = i
                last = i
            if cur_group is not None:
                saved_registers.append((
                    cur_group, self.state.registers.load(cur_group, last-cur_group+1)
                ))

        # now we sync registers out of unicorn
        for r, c in self._uc_regs.items():
            if r in self.reg_blacklist:
                continue
            v = self.uc.reg_read(c)
            # l.debug('getting $%s = %#x', r, v)
            setattr(self.state.regs, r, v)

        # some architecture-specific register fixups
        if self.state.arch.name in ('X86', 'AMD64'):
            if self.jumpkind.startswith('Ijk_Sys'):
                self.state.registers.store('ip_at_syscall', self.state.regs.ip - 2)

            # update the eflags
            self.state.regs.eflags = self.state.solver.BVV(self.uc.reg_read(self._uc_const.UC_X86_REG_EFLAGS), self.state.arch.bits)

            # sync the fp clerical data
            status = self.uc.reg_read(unicorn.x86_const.UC_X86_REG_FPSW)
            c3210 = status & 0x4700
            top = (status & 0x3800) >> 11
            control = self.uc.reg_read(unicorn.x86_const.UC_X86_REG_FPCW)
            rm = (control & 0x0C00) >> 10
            self.state.regs.fpround = rm
            self.state.regs.fc3210 = c3210
            self.state.regs.ftop = top

            # sync the stx registers
            # we gotta round the 80-bit extended precision values to 64-bit doubles!
            uc_offset = unicorn.x86_const.UC_X86_REG_FP0
            vex_offset = self.state.arch.registers['fpu_regs'][0]
            vex_tag_offset = self.state.arch.registers['fpu_tags'][0] + 7
            tag_word = self.uc.reg_read(unicorn.x86_const.UC_X86_REG_FPTAG)

            for _ in range(8):
                if tag_word & 3 == 3:
                    self.state.registers.store(vex_tag_offset, 0, size=1)
                else:
                    self.state.registers.store(vex_tag_offset, 1, size=1)

                    mantissa, exponent = self.uc.reg_read(uc_offset)
                    sign = bool(exponent & 0x8000)
                    exponent = (exponent & 0x7FFF)
                    if exponent not in (0, 0x7FFF): # normal value
                        exponent = exponent - 16383 + 1023
                        if exponent <= 0:   # underflow to zero
                            exponent = 0
                            mantissa = 0
                        elif exponent >= 0x7FF: # overflow to infinity
                            exponent = 0x7FF
                            mantissa = 0
                    elif exponent == 0:     # zero or subnormal value
                        mantissa = 0
                    elif exponent == 0x7FFF:    # nan or infinity
                        exponent = 0x7FF
                        if mantissa != 0:
                            mantissa = 0xFFFF

                    val = 0x8000000000000000 if sign else 0
                    val |= exponent << 52
                    val |= (mantissa >> 11) & 0xFFFFFFFFFFFFF
                    # the mantissa calculation is to convert from the 64-bit mantissa to 52-bit
                    # additionally, extended precision keeps around an high bit that we don't care about
                    # so 11-shift, not 12

                    self.state.registers.store(vex_offset, val, size=8)

                uc_offset += 1
                vex_offset += 8
                tag_word >>= 2
                vex_tag_offset -= 1

        # now, we restore the symbolic registers
        if options.UNICORN_SYM_REGS_SUPPORT in self.state.options:
            for o,r in saved_registers:
                self.state.registers.store(o, r)

Response: load the unicorn registers into the state. regs attribute


Instruction:
Write a Python 3 script to
check if this state might be used in unicorn

Response:
def _check_registers(self, report=True):
        ''' check if this state might be used in unicorn (has no concrete register)'''
        for r in self.state.arch.uc_regs.keys():
            v = getattr(self.state.regs, r)
            processed_v = self._process_value(v, 'reg')
            if processed_v is None or processed_v.symbolic:
                #l.info('detected symbolic register %s', r)
                if report:
                    self._report_symbolic_blocker(v, 'reg')
                return False

        if self.state.arch.vex_conditional_helpers:
            flags = ccall._get_flags(self.state)[0]
            processed_flags = self._process_value(flags, 'reg')
            if processed_flags is None or processed_flags.symbolic:
                #l.info("detected symbolic rflags/eflags")
                if report:
                    self._report_symbolic_blocker(flags, 'reg')
                return False

        #l.debug('passed quick check')
        return True


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

        regs = {self.project.arch.sp_offset}
        if hasattr(self.project.arch, 'bp_offset') and self.project.arch.bp_offset is not None:
            regs.add(self.project.arch.bp_offset)
        spt = self.project.analyses.StackPointerTracker(self.function, regs, track_memory=self._sp_tracker_track_memory)
        if spt.inconsistent_for(self.project.arch.sp_offset):
            l.warning("Inconsistency found during stack pointer tracking. Decompilation results might be incorrect.")
        return spt

Response: For each instruction track its stack pointer offset and stack base pointer offset.


Instruction:
Given the following Python 3 function, write the documentation
def _convert(self, block_node):

        if not type(block_node) is BlockNode:
            return block_node

        block = self.project.factory.block(block_node.addr, block_node.size)

        ail_block = ailment.IRSBConverter.convert(block.vex, self._ail_manager)
        return ail_block

Response: Convert a VEX block to an AIL block.


Instruction:
Can you create a Python 3 function that
simplifies all blocks in the function graph.

Response:
def _simplify_blocks(self, stack_pointer_tracker=None):
        """
        Simplify all blocks in self._blocks.

        :param stack_pointer_tracker:   The RegisterDeltaTracker analysis instance.
        :return:                        None
        """

        # First of all, let's simplify blocks one by one

        for key in self._blocks:
            ail_block = self._blocks[key]
            simplified = self._simplify_block(ail_block, stack_pointer_tracker=stack_pointer_tracker)
            self._blocks[key] = simplified

        # Update the function graph so that we can use reaching definitions
        self._update_graph()


Instruction:
Can you write a function in Python 3 where it
simplifies a single AIL block.

Response:
def _simplify_block(self, ail_block, stack_pointer_tracker=None):
        """
        Simplify a single AIL block.

        :param ailment.Block ail_block: The AIL block to simplify.
        :param stack_pointer_tracker:   The RegisterDeltaTracker analysis instance.
        :return:                        A simplified AIL block.
        """

        simp = self.project.analyses.AILBlockSimplifier(ail_block, stack_pointer_tracker=stack_pointer_tracker)
        return simp.result_block


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

        # Computing reaching definitions
        rd = self.project.analyses.ReachingDefinitions(func=self.function, func_graph=self.graph, observe_all=True)

        simp = self.project.analyses.AILSimplifier(self.function, func_graph=self.graph, reaching_definitions=rd)

        for key in list(self._blocks.keys()):
            old_block = self._blocks[key]
            if old_block in simp.blocks:
                self._blocks[key] = simp.blocks[old_block]

        self._update_graph()

Response: Simplify the entire function.


Instruction:
Implement a function in Python 3 to
simplify all function call statements.

Response:
def _make_callsites(self, stack_pointer_tracker=None):
        """
        Simplify all function call statements.

        :return:    None
        """

        # Computing reaching definitions
        rd = self.project.analyses.ReachingDefinitions(func=self.function, func_graph=self.graph, observe_all=True)

        for key in self._blocks:
            block = self._blocks[key]
            csm = self.project.analyses.AILCallSiteMaker(block, reaching_definitions=rd)
            if csm.result_block:
                ail_block = csm.result_block
                simp = self.project.analyses.AILBlockSimplifier(ail_block, stack_pointer_tracker=stack_pointer_tracker)
                self._blocks[key] = simp.result_block

        self._update_graph()


Instruction:
Explain what the following Python 3 code does
def _link_variables_on_block(self, block, kb):

        variable_manager = kb.variables[self.function.addr]

        for stmt_idx, stmt in enumerate(block.statements):
            # I wish I could do functional programming in this method...
            stmt_type = type(stmt)
            if stmt_type is ailment.Stmt.Store:
                # find a memory variable
                mem_vars = variable_manager.find_variables_by_atom(block.addr, stmt_idx, stmt)
                if len(mem_vars) == 1:
                    stmt.variable, stmt.offset = next(iter(mem_vars))
                self._link_variables_on_expr(variable_manager, block, stmt_idx, stmt, stmt.data)

            elif stmt_type is ailment.Stmt.Assignment:
                self._link_variables_on_expr(variable_manager, block, stmt_idx, stmt, stmt.dst)
                self._link_variables_on_expr(variable_manager, block, stmt_idx, stmt, stmt.src)

            elif stmt_type is ailment.Stmt.ConditionalJump:
                self._link_variables_on_expr(variable_manager, block, stmt_idx, stmt, stmt.condition)

            elif stmt_type is ailment.Stmt.Call:
                if stmt.ret_expr:
                    self._link_variables_on_expr(variable_manager, block, stmt_idx, stmt, stmt.ret_expr)

Response: Link atoms in the given block to corresponding variables identified previously.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def _link_variables_on_expr(self, variable_manager, block, stmt_idx, stmt, expr):

        if type(expr) is ailment.Expr.Register:
            # find a register variable
            reg_vars = variable_manager.find_variables_by_atom(block.addr, stmt_idx, expr)
            # TODO: make sure it is the correct register we are looking for
            if len(reg_vars) == 1:
                reg_var, offset = next(iter(reg_vars))
                expr.variable = reg_var
                expr.offset = offset

        elif type(expr) is ailment.Expr.Load:
            # import ipdb; ipdb.set_trace()
            variables = variable_manager.find_variables_by_atom(block.addr, stmt_idx, expr)
            if len(variables) == 0:
                self._link_variables_on_expr(variable_manager, block, stmt_idx, stmt, expr.addr)
            else:
                if len(variables) > 1:
                    l.error("More than one variable are available for atom %s. Consider fixing it using phi nodes.",
                            expr
                            )
                var, offset = next(iter(variables))
                expr.variable = var
                expr.offset = offset

        elif type(expr) is ailment.Expr.BinaryOp:
            variables = variable_manager.find_variables_by_atom(block.addr, stmt_idx, expr)
            if len(variables) == 1:
                var, offset = next(iter(variables))
                expr.referenced_variable = var
                expr.offset = offset
            else:
                self._link_variables_on_expr(variable_manager, block, stmt_idx, stmt, expr.operands[0])
                self._link_variables_on_expr(variable_manager, block, stmt_idx, stmt, expr.operands[1])

        elif type(expr) is ailment.Expr.UnaryOp:
            variables = variable_manager.find_variables_by_atom(block.addr, stmt_idx, expr)
            if len(variables) == 1:
                var, offset = next(iter(variables))
                expr.referenced_variable = var
                expr.offset = offset
            else:
                self._link_variables_on_expr(variable_manager, block, stmt_idx, stmt, expr.operands)

        elif type(expr) is ailment.Expr.Convert:
            self._link_variables_on_expr(variable_manager, block, stmt_idx, stmt, expr.operand)

        elif isinstance(expr, ailment.Expr.BasePointerOffset):
            variables = variable_manager.find_variables_by_atom(block.addr, stmt_idx, expr)
            if len(variables) == 1:
                var, offset = next(iter(variables))
                expr.referenced_variable = var
                expr.offset = offset

Response: Link atoms in the given expression to corresponding variables identified previously.


Instruction:
Explain what the following Python 3 code does
def get_class(self, class_name, init_class=False, step_func=None):
        # try to get the soot class object from CLE
        java_binary = self.state.javavm_registers.load('ip_binary')
        soot_class = java_binary.get_soot_class(class_name, none_if_missing=True)
        # create class descriptor
        class_descriptor = SootClassDescriptor(class_name, soot_class)
        # load/initialize class
        if init_class:
            self.init_class(class_descriptor, step_func=step_func)
        return class_descriptor

Response: Get a class descriptor for the class.


Instruction:
Can you create a Python 3 function that
gets the superclass of the class.

Response:
def get_superclass(self, class_):
        """
        Get the superclass of the class.
        """
        if not class_.is_loaded or class_.superclass_name is None:
            return None
        return self.get_class(class_.superclass_name)


Instruction:
Write a Python 3 function for
walking up the class hierarchy and returns a list of all classes between base_class and java. lang. Object.

Response:
def get_class_hierarchy(self, base_class):
        """
        Walks up the class hierarchy and returns a list of all classes between
        base class (inclusive) and java.lang.Object (exclusive).
        """
        classes = [base_class]
        while classes[-1] is not None and classes[-1] != "java.lang.Object":
            classes.append(self.get_superclass(classes[-1]))
        return classes[:-1]


Instruction:
How would you explain what the following Python 3 function does
def init_class(self, class_, step_func=None):
        if self.is_class_initialized(class_):
            l.debug("Class %r already initialized.", class_)
            return

        l.debug("Initialize class %r.", class_)
        self.initialized_classes.add(class_)

        if not class_.is_loaded:
            l.warning("Class %r is not loaded in CLE. Skip initializiation.", class_)
            return

        clinit_method = resolve_method(self.state, '<clinit>', class_.name,
                                       include_superclasses=False, init_class=False)
        if clinit_method.is_loaded:
            javavm_simos = self.state.project.simos
            clinit_state = javavm_simos.state_call(addr=SootAddressDescriptor(clinit_method, 0, 0),
                                                   base_state=self.state,
                                                   ret_addr=SootAddressTerminator())
            simgr = self.state.project.factory.simgr(clinit_state)
            l.info(">"*15 + " Run class initializer %r ... " + ">"*15, clinit_method)
            simgr.run(step_func=step_func)
            l.debug("<"*15 + " Run class initializer %r ... done " + "<"*15, clinit_method)
            # The only thing that can be updated during initialization are
            # static or rather global information, which are either stored on
            # the heap or in the vm_static_table
            self.state.memory.vm_static_table = simgr.deadended[-1].memory.vm_static_table.copy()
            self.state.memory.heap = simgr.deadended[-1].memory.heap.copy()
        else:
            l.debug("Class initializer <clinit> is not loaded in CLE. Skip initializiation.")

Response:
This method simulates the loading of a class by the JVM, during which
        parts of the class (e.g. static fields) are initialized. For this, we
        run the class initializer method <clinit> (if available) and update
        the state accordingly.

        Note: Initialization is skipped, if the class has already been
              initialized (or if it's not loaded in CLE).


Instruction:
Write a Python 3 function for
getting the IRSB object from an address a SimRun or a CFGNode.

Response:
def _get_irsb(self, v):
        """
        Get the IRSB object from an address, a SimRun, or a CFGNode.
        :param v: Can be one of the following: an address, or a CFGNode.
        :return: The IRSB instance.
        :rtype: pyvex.IRSB
        """

        if isinstance(v, CFGNode):
            v = v.addr

        if type(v) is int:
            # Generate an IRSB from self._project

            if v in self._run_cache:
                return self._run_cache[v]

            if self.project:
                irsb = self.project.factory.block(v, backup_state=self._base_state).vex
                self._run_cache[v] = irsb
                return irsb
            else:
                raise AngrBladeError("Project must be specified if you give me all addresses for SimRuns")

        else:
            raise AngrBladeError('Unsupported SimRun argument type %s', type(v))


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

        if isinstance(v, CFGNode):
            return v.addr
        elif type(v) is int:
            return v
        else:
            raise AngrBladeError('Unsupported SimRun argument type %s' % type(v))

Response: Get the address of the basic block or CFG node specified by v.


Instruction:
Can you tell what is the following Python 3 function doing
def _concat_flags(nbits, flags_vec):

    result = claripy.BVV(0, 0)
    for offset, bit in flags_vec:
        current_position = nbits - 1 - result.length
        result = result.concat(claripy.BVV(0, current_position - offset), bit)
    result = result.concat(claripy.BVV(0, nbits - result.length))
    return result

Response: Concatenate different flag BVs to a single BV. Currently used for ARM X86 and AMD64.


Instruction:
Explain what the following Python 3 code does
def _simplify(self):

        r = self.region
        r = self._simplify_gotos(r)
        r = self._simplify_ifs(r)

        self.result = r

Response: Simplify the set of entries in the region.


Instruction:
Make a summary of the following Python 3 code
def parse(cls, s, **kwargs):

        pb2_obj = cls._get_cmsg()
        pb2_obj.ParseFromString(s)

        return cls.parse_from_cmessage(pb2_obj, **kwargs)

Response: Parse a bytes object and create a class object.


Instruction:
Write a Python 3 function for
storing the elements of the array in the memory.

Response:
def store_array_elements(self, array, start_idx, data):
        """
        Stores either a single element or a range of elements in the array.

        :param array:     Reference to the array.
        :param start_idx: Starting index for the store.
        :param data:      Either a single value or a list of values.
        """
        # we process data as a list of elements
        # => if there is only a single element, wrap it in a list
        data = data if isinstance(data, list) else [data]

        # concretize start index
        concrete_start_idxes = self.concretize_store_idx(start_idx)

        if len(concrete_start_idxes) == 1:
            # only one start index
            # => concrete store
            concrete_start_idx = concrete_start_idxes[0]
            for i, value in enumerate(data):
                self._store_array_element_on_heap(array=array,
                                                  idx=concrete_start_idx+i,
                                                  value=value,
                                                  value_type=array.element_type)
            # if the index was symbolic before concretization, this
            # constraint it to concrete start idx
            self.state.solver.add(concrete_start_idx == start_idx)

        else:
            # multiple start indexes
            # => symbolic store
            start_idx_options = []
            for concrete_start_idx in concrete_start_idxes:
                start_idx_options.append(concrete_start_idx == start_idx)
                # we store elements condtioned with the start index:
                # => if concrete_start_idx == start_idx
                #    then store the value
                #    else keep the current value
                for i, value in enumerate(data):
                    self._store_array_element_on_heap(array=array,
                                                      idx=concrete_start_idx+i,
                                                      value=value,
                                                      value_type=array.element_type,
                                                      store_condition=start_idx_options[-1])

            # constraint start_idx, s.t. it evals to one of the concretized indexes
            constraint_on_start_idx = self.state.solver.Or(*start_idx_options)
            self.state.add_constraints(constraint_on_start_idx)


Instruction:
Create a Python 3 function to
load a range of elements from the array.

Response:
def load_array_elements(self, array, start_idx, no_of_elements):
        """
        Loads either a single element or a range of elements from the array.

        :param array:           Reference to the array.
        :param start_idx:       Starting index for the load.
        :param no_of_elements:  Number of elements to load.

        """
        # concretize start index
        concrete_start_idxes = self.concretize_load_idx(start_idx)

        if len(concrete_start_idxes) == 1:
            # only one start index
            # => concrete load
            concrete_start_idx = concrete_start_idxes[0]
            load_values = [self._load_array_element_from_heap(array, idx)
                           for idx in range(concrete_start_idx, concrete_start_idx+no_of_elements)]
            # if the index was symbolic before concretization, this
            # constraint it to concrete start idx
            self.state.solver.add(start_idx == concrete_start_idx)

        else:
            # multiple start indexes
            # => symbolic load

            # start with load values for the first concrete index
            concrete_start_idx = concrete_start_idxes[0]
            load_values = [self._load_array_element_from_heap(array, idx)
                           for idx in range(concrete_start_idx, concrete_start_idx+no_of_elements)]
            start_idx_options = [concrete_start_idx == start_idx]

            # update load values with all remaining start indexes
            for concrete_start_idx in concrete_start_idxes[1:]:
                # load values for this start index
                values = [self._load_array_element_from_heap(array, idx)
                          for idx in range(concrete_start_idx, concrete_start_idx+no_of_elements)]
                # update load values with the new ones
                for i, value in enumerate(values):
                    # condition every value with the start idx
                    # => if concrete_start_idx == start_idx
                    #    then use new value
                    #    else use the current value
                    load_values[i] = self.state.solver.If(
                        concrete_start_idx == start_idx,
                        value,
                        load_values[i]
                    )
                start_idx_options.append(start_idx == concrete_start_idx)

            # constraint start_idx, s.t. it evals to one of the concretized indexes
            constraint_on_start_idx = self.state.solver.Or(*start_idx_options)
            self.state.add_constraints(constraint_on_start_idx)

        return load_values


Instruction:
Write a Python 3 script to
apply concretization strategies on the index until one of them succeeds.

Response:
def _apply_concretization_strategies(self, idx, strategies, action): # pylint: disable=unused-argument
        """
        Applies concretization strategies on the index, until one of them succeeds.
        """

        for s in strategies:
            try:
                idxes = s.concretize(self, idx)
            except SimUnsatError:
                idxes = None

            if idxes:
                return idxes

        raise SimMemoryAddressError("Unable to concretize index %s" % idx)


Instruction:
Here you have a function in Python 3, explain what it does
def concretize_store_idx(self, idx, strategies=None):
        if isinstance(idx, int):
            return [idx]
        elif not self.state.solver.symbolic(idx):
            return [self.state.solver.eval(idx)]

        strategies = self.store_strategies if strategies is None else strategies
        return self._apply_concretization_strategies(idx, strategies, 'store')

Response: Concretizes a store index.


Instruction:
Here you have a function in Python 3, explain what it does
def copy(self):
        new_cfg = CFGEmulated.__new__(CFGEmulated)
        super(CFGEmulated, self).make_copy(new_cfg)

        new_cfg._indirect_jump_target_limit = self._indirect_jump_target_limit
        new_cfg.named_errors = dict(self.named_errors)
        new_cfg.errors = list(self.errors)
        new_cfg._fail_fast = self._fail_fast
        new_cfg._max_steps = self._max_steps
        new_cfg.project = self.project

        # Intelligently (or stupidly... you tell me) fill it up
        new_cfg._edge_map = self._edge_map.copy()
        new_cfg._loop_back_edges = self._loop_back_edges[::]
        new_cfg._executable_address_ranges = self._executable_address_ranges[::]
        new_cfg._unresolvable_runs = self._unresolvable_runs.copy()
        new_cfg._overlapped_loop_headers = self._overlapped_loop_headers[::]
        new_cfg._thumb_addrs = self._thumb_addrs.copy()
        new_cfg._keep_state = self._keep_state

        return new_cfg

Response: Make a copy of the CFG.


Instruction:
Can you tell what is the following Python 3 function doing
def resume(self, starts=None, max_steps=None):

        self._starts = starts
        self._max_steps = max_steps

        self._sanitize_starts()

        self._analyze()

Response: Resume a paused or terminated control flow graph recovery.


Instruction:
Write a Python 3 script for
removing all cycles from the current graph.

Response:
def remove_cycles(self):
        """
        Forces graph to become acyclic, removes all loop back edges and edges between overlapped loop headers and their
        successors.
        """
        # loop detection
        # only detect loops after potential graph normalization
        if not self._loop_back_edges:
            l.debug("Detecting loops...")
            self._detect_loops()

        l.debug("Removing cycles...")
        l.debug("There are %d loop back edges.", len(self._loop_back_edges))
        l.debug("And there are %d overlapping loop headers.", len(self._overlapped_loop_headers))
        # First break all detected loops
        for b1, b2 in self._loop_back_edges:
            if self._graph.has_edge(b1, b2):
                l.debug("Removing loop back edge %s -> %s", b1, b2)
                self._graph.remove_edge(b1, b2)
        # Then remove all outedges from overlapped loop headers
        for b in self._overlapped_loop_headers:
            successors = self._graph.successors(b)
            for succ in successors:
                self._graph.remove_edge(b, succ)
                l.debug("Removing partial loop header edge %s -> %s", b, succ)


Instruction:
Write a Python 3 function for
unrolling loops for each function.

Response:
def unroll_loops(self, max_loop_unrolling_times):
        """
        Unroll loops for each function. The resulting CFG may still contain loops due to recursion, function calls, etc.

        :param int max_loop_unrolling_times: The maximum iterations of unrolling.
        :return: None
        """

        if not isinstance(max_loop_unrolling_times, int) or \
                        max_loop_unrolling_times < 0:
            raise AngrCFGError('Max loop unrolling times must be set to an integer greater than or equal to 0 if ' +
                               'loop unrolling is enabled.')

        def _unroll(graph, loop):
            """
            The loop callback method where loops are unrolled.

            :param networkx.DiGraph graph: The control flow graph.
            :param angr.analyses.loopfinder.Loop loop: The loop instance.
            :return: None
            """

            for back_edge in loop.continue_edges:
                loop_body_addrs = {n.addr for n in loop.body_nodes}
                src_blocknode = back_edge[0]  # type: angr.knowledge.codenode.BlockNode
                dst_blocknode = back_edge[1]  # type: angr.knowledge.codenode.BlockNode

                for src in self.get_all_nodes(src_blocknode.addr):
                    for dst in graph.successors(src):
                        if dst.addr != dst_blocknode.addr:
                            continue

                        # Duplicate the dst node
                        new_dst = dst.copy()
                        new_dst.looping_times = dst.looping_times + 1
                        if (new_dst not in graph and
                                # If the new_dst is already in the graph, we don't want to keep unrolling
                                # the this loop anymore since it may *create* a new loop. Of course we
                                # will lose some edges in this way, but in general it is acceptable.
                                new_dst.looping_times <= max_loop_unrolling_times
                            ):
                            # Log all successors of the dst node
                            dst_successors = graph.successors(dst)
                            # Add new_dst to the graph
                            edge_data = graph.get_edge_data(src, dst)
                            graph.add_edge(src, new_dst, **edge_data)
                            for ds in dst_successors:
                                if ds.looping_times == 0 and ds.addr not in loop_body_addrs:
                                    edge_data = graph.get_edge_data(dst, ds)
                                    graph.add_edge(new_dst, ds, **edge_data)

                        graph.remove_edge(src, dst)

        self._detect_loops(loop_callback=_unroll)


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

        if not isinstance(max_loop_unrolling_times, int) or \
                        max_loop_unrolling_times < 0:
            raise AngrCFGError('Max loop unrolling times must be set to an integer greater than or equal to 0 if ' +
                               'loop unrolling is enabled.')

        # Traverse the CFG and try to find the beginning of loops
        loop_backedges = []

        start = self._starts[0]
        if isinstance(start, tuple):
            start, _ = start  # pylint: disable=unpacking-non-sequence
        start_node = self.get_any_node(start)
        if start_node is None:
            raise AngrCFGError('Cannot find start node when trying to unroll loops. The CFG might be empty.')

        graph_copy = networkx.DiGraph(self.graph)

        while True:
            cycles_iter = networkx.simple_cycles(graph_copy)
            try:
                cycle = next(cycles_iter)
            except StopIteration:
                break

            loop_backedge = (None, None)

            for n in networkx.dfs_preorder_nodes(graph_copy, source=start_node):
                if n in cycle:
                    idx = cycle.index(n)
                    if idx == 0:
                        loop_backedge = (cycle[-1], cycle[idx])
                    else:
                        loop_backedge = (cycle[idx - 1], cycle[idx])
                    break

            if loop_backedge not in loop_backedges:
                loop_backedges.append(loop_backedge)

            # Create a common end node for all nodes whose out_degree is 0
            end_nodes = [n for n in graph_copy.nodes() if graph_copy.out_degree(n) == 0]
            new_end_node = "end_node"

            if not end_nodes:
                # We gotta randomly break a loop
                cycles = sorted(networkx.simple_cycles(graph_copy), key=len)
                first_cycle = cycles[0]
                if len(first_cycle) == 1:
                    graph_copy.remove_edge(first_cycle[0], first_cycle[0])
                else:
                    graph_copy.remove_edge(first_cycle[0], first_cycle[1])
                end_nodes = [n for n in graph_copy.nodes() if graph_copy.out_degree(n) == 0]

            for en in end_nodes:
                graph_copy.add_edge(en, new_end_node)

            # postdoms = self.immediate_postdominators(new_end_node, target_graph=graph_copy)
            # reverse_postdoms = defaultdict(list)
            # for k, v in postdoms.items():
            #    reverse_postdoms[v].append(k)

            # Find all loop bodies
            # for src, dst in loop_backedges:
            #    nodes_in_loop = { src, dst }

            #    while True:
            #        new_nodes = set()

            #        for n in nodes_in_loop:
            #            if n in reverse_postdoms:
            #                for node in reverse_postdoms[n]:
            #                    if node not in nodes_in_loop:
            #                        new_nodes.add(node)

            #        if not new_nodes:
            #            break

            #        nodes_in_loop |= new_nodes

            # Unroll the loop body
            # TODO: Finish the implementation

            graph_copy.remove_node(new_end_node)
            src, dst = loop_backedge
            if graph_copy.has_edge(src, dst):  # It might have been removed before
                # Duplicate the dst node
                new_dst = dst.copy()
                new_dst.looping_times = dst.looping_times + 1
                if (
                        new_dst not in graph_copy and
                        # If the new_dst is already in the graph, we don't want to keep unrolling
                        # the this loop anymore since it may *create* a new loop. Of course we
                        # will lose some edges in this way, but in general it is acceptable.
                        new_dst.looping_times <= max_loop_unrolling_times):
                    # Log all successors of the dst node
                    dst_successors = list(graph_copy.successors(dst))
                    # Add new_dst to the graph
                    edge_data = graph_copy.get_edge_data(src, dst)
                    graph_copy.add_edge(src, new_dst, **edge_data)
                    for ds in dst_successors:
                        if ds.looping_times == 0 and ds not in cycle:
                            edge_data = graph_copy.get_edge_data(dst, ds)
                            graph_copy.add_edge(new_dst, ds, **edge_data)
                # Remove the original edge
                graph_copy.remove_edge(src, dst)

        # Update loop backedges
        self._loop_back_edges = loop_backedges

        self.model.graph = graph_copy

Response:
Unroll loops globally. The resulting CFG does not contain any loop, but this method is slow on large graphs.

        :param int max_loop_unrolling_times: The maximum iterations of unrolling.
        :return: None


Instruction:
Can you implement a function in Python 3 that
gets all immediate dominators of sub graph from given node upwards.

Response:
def immediate_dominators(self, start, target_graph=None):
        """
        Get all immediate dominators of sub graph from given node upwards.

        :param str start: id of the node to navigate forwards from.
        :param networkx.classes.digraph.DiGraph target_graph: graph to analyse, default is self.graph.

        :return: each node of graph as index values, with element as respective node's immediate dominator.
        :rtype: dict
        """
        return self._immediate_dominators(start, target_graph=target_graph, reverse_graph=False)


Instruction:
Create a Python 3 function to
get all immediate postdominators of sub graph from given node upwards.

Response:
def immediate_postdominators(self, end, target_graph=None):
        """
        Get all immediate postdominators of sub graph from given node upwards.

        :param str start: id of the node to navigate forwards from.
        :param networkx.classes.digraph.DiGraph target_graph: graph to analyse, default is self.graph.

        :return: each node of graph as index values, with element as respective node's immediate dominator.
        :rtype: dict
        """
        return self._immediate_dominators(end, target_graph=target_graph, reverse_graph=True)


Instruction:
Create a Python 3 function for
removing fake returns from this CFG

Response:
def remove_fakerets(self):
        """
        Get rid of fake returns (i.e., Ijk_FakeRet edges) from this CFG

        :return: None
        """
        fakeret_edges = [ (src, dst) for src, dst, data in self.graph.edges(data=True)
                         if data['jumpkind'] == 'Ijk_FakeRet' ]
        self.graph.remove_edges_from(fakeret_edges)


Instruction:
Explain what the following Python 3 code does
def get_topological_order(self, cfg_node):

        if not self._quasi_topological_order:
            self._quasi_topological_sort()

        return self._quasi_topological_order.get(cfg_node, None)

Response: Get the topological order of a CFG Node.


Instruction:
Given the following Python 3 function, write the documentation
def get_subgraph(self, starting_node, block_addresses):

        graph = networkx.DiGraph()

        if starting_node not in self.graph:
            raise AngrCFGError('get_subgraph(): the specified "starting_node" %s does not exist in the current CFG.'
                               % starting_node
                               )

        addr_set = set(block_addresses)

        graph.add_node(starting_node)
        queue = [ starting_node ]

        while queue:
            node = queue.pop()
            for _, dst, data in self.graph.out_edges([node], data=True):
                if dst not in graph and dst.addr in addr_set:
                    graph.add_edge(node, dst, **data)
                    queue.append(dst)

        cfg = self.copy()
        cfg._graph = graph
        cfg._starts = (starting_node.addr, )

        return cfg

Response: Get a sub - graph out of a bunch of basic block addresses.


Instruction:
Can you tell what is the following Python 3 function doing
def get_function_subgraph(self, start, max_call_depth=None):

        # FIXME: syscalls are not supported
        # FIXME: start should also take a CFGNode instance

        start_node = self.get_any_node(start)

        node_wrapper = (start_node, 0)
        stack = [node_wrapper]
        traversed_nodes = {start_node}
        subgraph_nodes = set([start_node])

        while stack:
            nw = stack.pop()
            n, call_depth = nw[0], nw[1]

            # Get successors
            edges = self.graph.out_edges(n, data=True)

            for _, dst, data in edges:
                if dst not in traversed_nodes:
                    # We see a new node!
                    traversed_nodes.add(dst)

                    if data['jumpkind'] == 'Ijk_Call':
                        if max_call_depth is None or (max_call_depth is not None and call_depth < max_call_depth):
                            subgraph_nodes.add(dst)
                            new_nw = (dst, call_depth + 1)
                            stack.append(new_nw)
                    elif data['jumpkind'] == 'Ijk_Ret':
                        if call_depth > 0:
                            subgraph_nodes.add(dst)
                            new_nw = (dst, call_depth - 1)
                            stack.append(new_nw)
                    else:
                        subgraph_nodes.add(dst)
                        new_nw = (dst, call_depth)
                        stack.append(new_nw)

       #subgraph = networkx.subgraph(self.graph, subgraph_nodes)
        subgraph = self.graph.subgraph(subgraph_nodes).copy()

        # Make it a CFG instance
        subcfg = self.copy()
        subcfg._graph = subgraph
        subcfg._starts = (start,)

        return subcfg

Response: Get a sub - graph of a certain function.


Instruction:
Explain what the following Python 3 code does
def deadends(self):
        if self.graph is None:
            raise AngrCFGError('CFG hasn\'t been generated yet.')

        deadends = [i for i in self.graph if self.graph.out_degree(i) == 0]

        return deadends

Response: Get all CFGNodes that have an out - degree of 0


Instruction:
Create a Python 3 function for
sanitizing the parameters of the CFG object.

Response:
def _sanitize_parameters(self):
        """
        Perform a sanity check on parameters passed in to CFG.__init__().
        An AngrCFGError is raised if any parameter fails the sanity check.

        :return: None
        """

        # Check additional_edges
        if isinstance(self._additional_edges, (list, set, tuple)):
            new_dict = defaultdict(list)
            for s, d in self._additional_edges:
                new_dict[s].append(d)
            self._additional_edges = new_dict

        elif isinstance(self._additional_edges, dict):
            pass

        else:
            raise AngrCFGError('Additional edges can only be a list, set, tuple, or a dict.')

        # Check _advanced_backward_slicing
        if self._advanced_backward_slicing and self._enable_symbolic_back_traversal:
            raise AngrCFGError('Advanced backward slicing and symbolic back traversal cannot both be enabled.')

        if self._advanced_backward_slicing and not self._keep_state:
            raise AngrCFGError('Keep state must be enabled if advanced backward slicing is enabled.')

        # Sanitize avoid_runs
        self._avoid_runs = [ ] if self._avoid_runs is None else self._avoid_runs
        if not isinstance(self._avoid_runs, (list, set)):
            raise AngrCFGError('"avoid_runs" must either be None, or a list or a set.')

        self._sanitize_starts()


Instruction:
Explain what the following Python 3 code does
def _job_sorting_key(self, job):

        if self._base_graph is None:
            # we don't do sorting if there is no base_graph
            return 0

        MAX_JOBS = 1000000

        if job.addr not in self._node_addr_visiting_order:
            return MAX_JOBS

        return self._node_addr_visiting_order.index(job.addr)

Response: Get the sorting key of a CFGJob instance.


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

        # Fill up self._starts
        for item in self._starts:
            callstack = None
            if isinstance(item, tuple):
                # (addr, jumpkind)
                ip = item[0]
                state = self._create_initial_state(item[0], item[1])

            elif isinstance(item, SimState):
                # SimState
                state = item.copy()  # pylint: disable=no-member
                ip = state.solver.eval_one(state.ip)
                self._reset_state_mode(state, 'fastpath')

            else:
                raise AngrCFGError('Unsupported CFG start type: %s.' % str(type(item)))

            self._symbolic_function_initial_state[ip] = state
            path_wrapper = CFGJob(ip, state, self._context_sensitivity_level, None, None, call_stack=callstack)
            key = path_wrapper.block_id
            if key not in self._start_keys:
                self._start_keys.append(key)

            self._insert_job(path_wrapper)
            self._register_analysis_job(path_wrapper.func_addr, path_wrapper)

Response: Initializes work. Executed prior to the analysis.


Instruction:
Write a Python 3 script to
create a state object for a specific address and jumpkind.

Response:
def _create_initial_state(self, ip, jumpkind):
        """
        Obtain a SimState object for a specific address

        Fastpath means the CFG generation will work in an IDA-like way, in which it will not try to execute every
        single statement in the emulator, but will just do the decoding job. This is much faster than the old way.

        :param int ip: The instruction pointer
        :param str jumpkind: The jumpkind upon executing the block
        :return: The newly-generated state
        :rtype: SimState
        """

        jumpkind = "Ijk_Boring" if jumpkind is None else jumpkind

        if self._initial_state is None:
            state = self.project.factory.blank_state(addr=ip, mode="fastpath",
                                                     add_options=self._state_add_options,
                                                     remove_options=self._state_remove_options,
                                                     )
            self._initial_state = state
        else:
            # FIXME: self._initial_state is deprecated. This branch will be removed soon
            state = self._initial_state
            state.history.jumpkind = jumpkind
            self._reset_state_mode(state, 'fastpath')
            state._ip = state.solver.BVV(ip, self.project.arch.bits)

        if jumpkind is not None:
            state.history.jumpkind = jumpkind

        # THIS IS A HACK FOR MIPS
        if ip is not None and self.project.arch.name in ('MIPS32', 'MIPS64'):
            # We assume this is a function start
            state.regs.t9 = ip
        # TODO there was at one point special logic for the ppc64 table of contents but it seems to have bitrotted

        return state


Instruction:
Explain what the following Python 3 code does
def _get_one_pending_job(self):

        pending_job_key, pending_job = self._pending_jobs.popitem()
        pending_job_state = pending_job.state
        pending_job_call_stack = pending_job.call_stack
        pending_job_src_block_id = pending_job.src_block_id
        pending_job_src_exit_stmt_idx = pending_job.src_exit_stmt_idx

        self._deregister_analysis_job(pending_job.caller_func_addr, pending_job)

        # Let's check whether this address has been traced before.
        if pending_job_key in self._nodes:
            node = self._nodes[pending_job_key]
            if node in self.graph:
                pending_exit_addr = self._block_id_addr(pending_job_key)
                # That block has been traced before. Let's forget about it
                l.debug("Target 0x%08x has been traced before. Trying the next one...", pending_exit_addr)

                # However, we should still create the FakeRet edge
                self._graph_add_edge(pending_job_src_block_id, pending_job_key, jumpkind="Ijk_FakeRet",
                                     stmt_idx=pending_job_src_exit_stmt_idx, ins_addr=pending_job.src_exit_ins_addr)

                return None

        pending_job_state.history.jumpkind = 'Ijk_FakeRet'

        job = CFGJob(pending_job_state.addr,
                     pending_job_state,
                     self._context_sensitivity_level,
                     src_block_id=pending_job_src_block_id,
                     src_exit_stmt_idx=pending_job_src_exit_stmt_idx,
                     src_ins_addr=pending_job.src_exit_ins_addr,
                     call_stack=pending_job_call_stack,
        )
        l.debug("Tracing a missing return exit %s", self._block_id_repr(pending_job_key))

        return job

Response: Retrieve a pending job.


Instruction:
Write a Python 3 function that can
process function hints in the binary.

Response:
def _process_hints(self, analyzed_addrs):
        """
        Process function hints in the binary.

        :return: None
        """

        # Function hints!
        # Now let's see how many new functions we can get here...
        while self._pending_function_hints:
            f = self._pending_function_hints.pop()
            if f not in analyzed_addrs:
                new_state = self.project.factory.entry_state(mode='fastpath')
                new_state.ip = new_state.solver.BVV(f, self.project.arch.bits)

                # TOOD: Specially for MIPS
                if new_state.arch.name in ('MIPS32', 'MIPS64'):
                    # Properly set t9
                    new_state.registers.store('t9', f)

                new_path_wrapper = CFGJob(f,
                                          new_state,
                                          self._context_sensitivity_level
                                          )
                self._insert_job(new_path_wrapper)
                self._register_analysis_job(f, new_path_wrapper)
                l.debug('Picking a function 0x%x from pending function hints.', f)
                self.kb.functions.function(new_path_wrapper.func_addr, create=True)
                break