Repository: mandiant/capa
File: capa/features/extractors/binexport2/insn.py (Python)

Code before:
        expr.symbol.lower() not in ("bp", "esp", "ebp", "rbp", "rsp")
        for expr in op1_exprs
    ):
        return False

    # check_nzxor_security_cookie_delta
    # if insn falls at the start of first entry block of the parent function.
    flow_graph = be2.flow_graph[fhi.flow_graph_index]
    basic_block_index = bbi.basic_block_index
    bb = be2.basic_block[basic_block_index]
    if flow_graph.entry_basic_block_index == basic_block_index:
        first_addr = min((
            be2.instruction[ir.begin_index].address
            for ir in bb.instruction_index))
        if instruction.address < first_addr + SECURITY_COOKIE_BYTES_DELTA:
            return True
    # or insn falls at the end before return
    fbbi = set(flow_graph.basic_block_index)
    for edge in flow_graph.edge:
        if (edge.source_basic_block_index == basic_block_index and
            edge.target_basic_block_index not in fbbi):
            last_addr = max((
                be2.instruction[ir.end_index - 1].address
                for ir in bb.instruction_index))
            if instruction.address > last_addr - SECURITY_COOKIE_BYTES_DELTA:
                return True
    return False


def extract_insn_nzxor_characteristic_features(
    fh: FunctionHandle, bbh: BBHandle, ih: InsnHandle
) -> Iterator[Tuple[Feature, Address]]:
    """
    parse non-zeroing XOR instruction from the given instruction.
    ignore expected non-zeroing XORs, e.g. security cookies.
    """
    fhi: FunctionContext = fh.inner
    ii: InstructionContext = ih.inner

    be2 = fhi.ctx.be2

    instruction = be2.instruction[ii.instruction_index]
    mnemonic = be2.mnemonic[instruction.mnemonic_index]
    mnemonic_name = mnemonic.name.lower()
    if mnemonic_name not in (
        "xor", "xorpd", "xorps", "pxor",  # x86 / amd64
        "eor",  # arm / aarch64
    ):
        return

    operands = [

Review comment (security):
This assumes that the last instruction, ordered by virtual address, will be the exit and the security cookie check is found nearby.  While I think this probably mostly works in practice, I don't think its robust. I'd suggest a more complete solution might be: find all terminal basic blocks (no successors) in the function flow graph and consider each of those a potential exit block.

It may also be worth pre-computing the instruction indicies that fall within these ranges, since a function may have hundreds or thousands of instructions, and this routine might get invoked a lot. On the other hand, I think this is only invoked once the mnemonic and operands are checked, so maybe its not a problem in practice.





Code after:
        return False

    # check_nzxor_security_cookie_delta
    # if insn falls at the start of first entry block of the parent function.
    flow_graph = be2.flow_graph[fhi.flow_graph_index]
    basic_block_index = bbi.basic_block_index
    bb = be2.basic_block[basic_block_index]
    if flow_graph.entry_basic_block_index == basic_block_index:
        first_addr = min((be2.instruction[ir.begin_index].address for ir in bb.instruction_index))
        if instruction.address < first_addr + SECURITY_COOKIE_BYTES_DELTA:
            return True
    # or insn falls at the end before return in a terminal basic block.
    if basic_block_index not in (e.source_basic_block_index for e in flow_graph.edge):
        last_addr = max((be2.instruction[ir.end_index - 1].address for ir in bb.instruction_index))
        if instruction.address > last_addr - SECURITY_COOKIE_BYTES_DELTA:
            return True
    return False


def extract_insn_nzxor_characteristic_features(
    fh: FunctionHandle, bbh: BBHandle, ih: InsnHandle
) -> Iterator[Tuple[Feature, Address]]:
    """
    parse non-zeroing XOR instruction from the given instruction.
    ignore expected non-zeroing XORs, e.g. security cookies.
    """
    fhi: FunctionContext = fh.inner
    ii: InstructionContext = ih.inner

    be2 = fhi.ctx.be2

    instruction = be2.instruction[ii.instruction_index]
    mnemonic = be2.mnemonic[instruction.mnemonic_index]
    mnemonic_name = mnemonic.name.lower()
    if mnemonic_name not in (
        "xor",
        "xorpd",
        "xorps",
        "pxor",  # x86 / amd64
        "eor",  # arm / aarch64
    ):
        return

    operands = [be2.operand[operand_index] for operand_index in instruction.operand_index]

    # check whether operands are same for x86 / amd64
    if mnemonic_name in ("xor", "xorpd", "xorps", "pxor"):
        if operands[0] == operands[1]:
            return
        if is_security_cookie(fhi, bbh.inner, instruction):
            return