Repository: mandiant/capa
File: capa/ghidra/capa_explorer.py (Python)

Code before:
class CapaMatchData:
    def __init__(
        self, namespace, scope, capability, locations, node, attack: Dict[Any, Any], mbc: List[Dict[Any, Any]]
    ):
        self.namespace = namespace
        self.scope = scope
        self.capability = capability
        self.locations = locations
        self.node = node
        self.attack = attack
        self.mbc = mbc

    def recurse_node(self, node_dict):
        """pull match descriptions by recursing node dicts"""

        if not node_dict:
            # mismatched key, skip
            # ex. {'type':'subscope', 'scope':'basic block'}
            return ""

        if isinstance(node_dict, int):
            # Number operands, usually parameters or immediates
            return hex(node_dict)

        if isinstance(node_dict, str):
            # expect the "description" key's string value
            return node_dict

        if "description" in node_dict:
            return node_dict.get("description")
        else:
            # if no "description" key, the "type" key's value is the
            # expected key.
            # ex. {'type':'api', 'api':'RegisterServiceCtrlHandler'}
            return self.recurse_node(node_dict.get(node_dict.get("type")))

    def create_namespace(self):
        """create new ghidra namespace for each capa namespace"""

        # handle rules w/o namespace -> capa lib rule
        if self.namespace == "capa":
            lib_str = Namespace.DELIMITER + "lib" + Namespace.DELIMITER
            self.namespace = self.namespace + lib_str + self.capability.replace(" ", "-")
        cmd = CreateNamespacesCmd(self.namespace, SourceType.USER_DEFINED)
        cmd.applyTo(currentProgram())  # type: ignore [name-defined] # noqa: F821

        return cmd.getNamespace()

    def tag_functions(self):
        """create function tags for capabilities"""


Review comment (suggestion):
Can you expand on what you mean by this comment? It's slightly confusing when considered w/ the statement on line `65`.

Code after:
    """create new Ghidra namespace for each capa namespace"""

    cmd = CreateNamespacesCmd(namespace_str, SourceType.USER_DEFINED)
    cmd.applyTo(currentProgram())  # type: ignore [name-defined] # noqa: F821
    return cmd.getNamespace()


def create_label(ghidra_addr, name, capa_namespace):
    """custom label cmd to overlay symbols under capa-generated namespaces"""

    # prevent duplicate labels under the same capa-generated namespace
    symbol_table = currentProgram().getSymbolTable()  # type: ignore [name-defined] # noqa: F821
    for sym in symbol_table.getSymbols(ghidra_addr):
        if sym.getName(True) == capa_namespace.getName(True) + Namespace.DELIMITER + name:
            return

    # create SymbolType.LABEL at addr
    # prioritize capa-generated namespace (duplicate match @ new addr), else put under global Ghidra one (new match)
    cmd = AddLabelCmd(ghidra_addr, name, True, SourceType.USER_DEFINED)
    cmd.applyTo(currentProgram())  # type: ignore [name-defined] # noqa: F821

    # assign new match overlay label to capa-generated namespace
    cmd.getSymbol().setNamespace(capa_namespace)
    return


class CapaMatchData:
    def __init__(
        self,
        namespace,
        scope,
        capability,
        matches,
        attack: List[Dict[Any, Any]],
        mbc: List[Dict[Any, Any]],
    ):
        self.namespace = namespace
        self.scope = scope
        self.capability = capability
        self.matches = matches
        self.attack = attack
        self.mbc = mbc

    def bookmark_functions(self):
        """create bookmarks for MITRE ATT&CK & MBC mappings"""

        if self.attack == [] and self.mbc == []:
            return

        for key in self.matches.keys():
            addr = toAddr(hex(key))  # type: ignore [name-defined] # noqa: F821