Repository: mandiant/capa
File: capa/features/extractors/ghidra/basicblock.py (Python)

Code before:
        return all(c < 127 and chr(c) in string.printable for c in chars_)

    def is_printable_utf16le(chars_: bytes):
        if all(c == 0x00 for c in chars_[1::2]):
            return is_printable_ascii(chars_[::2])

    if is_printable_ascii(chars):
        return int(op_bit_len / 8)

    if is_printable_utf16le(chars):
        return int(op_bit_len / 8)

    return 0


def is_mov_imm_to_stack(insn: ghidra.program.database.code.InstructionDB) -> bool:
    """verify instruction moves immediate onto stack"""

    # Ghidra will Bitwise OR the OperandTypes to assign multiple
    # i.e., the first operand is a stackvar (dynamically allocated),
    # and the second is a scalar value (single int/char/float/etc.)
    mov_its_ops = [(OperandType.ADDRESS | OperandType.DYNAMIC), OperandType.SCALAR]
    found = False

    # MOV dword ptr [EBP + local_*], 0x65
    if insn.getMnemonicString() == "MOV":
        found = all(insn.getOperandType(i) == mov_its_ops[i] for i in range(2))

    return found


def bb_contains_stackstring(bb: ghidra.program.model.block.CodeBlock) -> bool:
    """check basic block for stackstring indicators

    true if basic block contains enough moves of constant bytes to the stack
    """
    count = 0
    for insn in listing.getInstructions(bb, True):
        if is_mov_imm_to_stack(insn):
            count += get_printable_len(insn.getOpObjects(1)[0])
        if count > MIN_STACKSTRING_LEN:
            return True
    return False


def _bb_has_tight_loop(bb: ghidra.program.model.block.CodeBlock):
    """
    parse tight loops, true if last instruction in basic block branches to bb start
    """
    last_insn = listing.getInstructionAt(bb.getMaxAddress().add(-0x1))  # all last insns are TERMINATOR


Review comment (suggestion):
```suggestion
    if insn.getMnemonicString().startswith("MOV"):
```

Code after:
    def is_printable_ascii(chars_: bytes):
        return all(c < 127 and chr(c) in string.printable for c in chars_)

    def is_printable_utf16le(chars_: bytes):
        if all(c == 0x00 for c in chars_[1::2]):
            return is_printable_ascii(chars_[::2])

    if is_printable_ascii(chars):
        return op_byte_len

    if is_printable_utf16le(chars):
        return op_byte_len

    return 0


def is_mov_imm_to_stack(insn: ghidra.program.database.code.InstructionDB) -> bool:
    """verify instruction moves immediate onto stack"""

    # Ghidra will Bitwise OR the OperandTypes to assign multiple
    # i.e., the first operand is a stackvar (dynamically allocated),
    # and the second is a scalar value (single int/char/float/etc.)
    mov_its_ops = [(OperandType.ADDRESS | OperandType.DYNAMIC), OperandType.SCALAR]
    found = False

    # MOV dword ptr [EBP + local_*], 0x65
    if insn.getMnemonicString().startswith("MOV"):
        found = all(insn.getOperandType(i) == mov_its_ops[i] for i in range(2))

    return found


def bb_contains_stackstring(bb: ghidra.program.model.block.CodeBlock) -> bool:
    """check basic block for stackstring indicators

    true if basic block contains enough moves of constant bytes to the stack
    """
    count = 0
    for insn in listing.getInstructions(bb, True):
        if is_mov_imm_to_stack(insn):
            count += get_printable_len(insn.getScalar(1))
        if count > MIN_STACKSTRING_LEN:
            return True
    return False


def _bb_has_tight_loop(bb: ghidra.program.model.block.CodeBlock):
    """
    parse tight loops, true if last instruction in basic block branches to bb start
    """
    last_insn = listing.getInstructions(bb, False).next()  # Reverse Ordered, first InstructionDB