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

Code before:

    fm = currentProgram.getFunctionManager().getExternalFunctions()
    while fm.hasNext():
        f = fm.next()
        addr = int(f.getEntryPoint().toString().split(':')[1], 16)  # format: EXTERNAL:<hex_addr>
        fstr = f.getName()  # format: 'importname' / 'Ordinal_*'
        if 'Ordinal_' in fstr:
            fstr = f"#{fstr.split('_')[1]}"
        yield Import(fstr), AbsoluteVirtualAddress(addr)


def extract_file_section_names() -> Iterator[Tuple[Feature, Address]]:
    """extract section names"""

    for block in currentProgram.getMemory().getBlocks():
        yield Section(block.getName()), AbsoluteVirtualAddress(block.getStart().getOffset())


def extract_file_strings() -> Iterator[Tuple[Feature, Address]]:
    """extract ASCII and UTF-16 LE strings"""

    dat = getFirstData()
    while (dat != None):
        if (dat.hasStringValue()):
            yield String(dat.getValue()), FileOffsetAddress(dat.getAddress().getOffset()) 
        dat = getDataAfter(dat)


def extract_file_function_names() -> Iterator[Tuple[Feature, Address]]:
    """
    extract the names of statically-linked library functions.
    """

    fm = currentProgram.getFunctionManager().getExternalFunctions()
    while fm.hasNext():
        f = fm.next()
        addr = int(f.getEntryPoint().toString().split(':')[1], 16)  # format: EXTERNAL:<hex_addr>
        name = f.getName()
        yield FunctionName(name), AbsoluteVirtualAddress(addr)
        if name.startswith("_"):
            # some linkers may prefix linked routines with a `_` to avoid name collisions.
            # extract features for both the mangled and un-mangled representations.
            # e.g. `_fwrite` -> `fwrite`
            # see: https://stackoverflow.com/a/2628384/87207
            yield FunctionName(name[1:]), AbsoluteVirtualAddress(addr)


def extract_file_format() -> Iterator[Tuple[Feature, Address]]:

    ef = currentProgram.getExecutableFormat()
    if 'PE' in ef:

Review comment (refactor):
We have had issues in the past with relying on a tool's built-in string identification. e.g. for IDA we opted to manually identify strings for each loaded section: https://github.com/mandiant/capa/blob/2ce4f8769d1f333141be3689a56bd75fa1a66948/capa/features/extractors/ida/file.py#L144-L152

I suggest we do the same here.

Code after:
            fstr[1] = f"#{fstr[1].split('_')[1]}"

        for name in capa.features.extractors.helpers.generate_symbols(fstr[0][:-4], fstr[1]):
            yield Import(name), AbsoluteVirtualAddress(addr)


def extract_file_section_names() -> Iterator[Tuple[Feature, Address]]:
    """extract section names"""

    for block in currentProgram.getMemory().getBlocks():
        yield Section(block.getName()), AbsoluteVirtualAddress(block.getStart().getOffset())


def extract_file_strings() -> Iterator[Tuple[Feature, Address]]:
    """extract ASCII and UTF-16 LE strings"""

    for block in currentProgram.getMemory().getBlocks():
        if block.isInitialized():
            p_bytes = capa.features.extractors.ghidra.helpers.get_block_bytes(block)
            if len(p_bytes) == 0:
                break

        for s in capa.features.extractors.strings.extract_ascii_strings(p_bytes):
            offset = block.getStart().getOffset() + s.offset
            yield String(s.s), FileOffsetAddress(offset)

        for s in capa.features.extractors.strings.extract_unicode_strings(p_bytes):
            offset = block.getStart().getOffset() + s.offset
            yield String(s.s), FileOffsetAddress(offset)


def extract_file_function_names() -> Iterator[Tuple[Feature, Address]]:
    """
    extract the names of statically-linked library functions.
    """

    for sym in currentProgram.getSymbolTable().getAllSymbols(True):
        # .isExternal() misses more than this config for the function symbols
        if sym.getSymbolType() == SymbolType.FUNCTION and sym.getSource() == SourceType.ANALYSIS and sym.isGlobal():
            name = sym.getName()  # starts to resolve names based on Ghidra's FidDB
            if name.startswith("FID_conflict:"):  # format: FID_conflict:<function-name>
                name = name[13:]
            addr = AbsoluteVirtualAddress(sym.getAddress().getOffset())
            yield FunctionName(name), addr
            if name.startswith("_"):
                # some linkers may prefix linked routines with a `_` to avoid name collisions.
                # extract features for both the mangled and un-mangled representations.
                # e.g. `_fwrite` -> `fwrite`
                # see: https://stackoverflow.com/a/2628384/87207
                yield FunctionName(name[1:]), addr