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

Code before:
        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
            # {'type':'number', 'number':80}
            # hex() will cast this int to a str type
            return hex(node_dict)

        if isinstance(node_dict, str):
            # expect the "description" key's string value
            # {'description':'PEB->OSMajorVersion'}
            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"""

        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"""

        # self.locations[0] will always be the largest
        # scoped offset yielded i.e. closest to an entrypoint
        addr = toAddr(hex(self.locations[0]))  # type: ignore [name-defined] # noqa: F821
        func = getFunctionContaining(addr)  # type: ignore [name-defined] # noqa: F821

        # bookmark Mitre ATT&CK tactics & MBC @ function scope
        if func:
            func.addTag(self.capability)
            for item in self.attack:
                attack_txt = item.get("tactic") + Namespace.DELIMITER + item.get("id")
                add_bookmark(addr, attack_txt, "CapaExplorer::Mitre ATT&CK")

            for item in self.mbc:
                mbc_txt = item.get("objective") + Namespace.DELIMITER + item.get("id")
                add_bookmark(addr, mbc_txt, "CapaExplorer::MBC")

    def bookmark_locations(self):

Review comment (suggestion):
```suggestion
        """create new Ghidra namespace for each capa namespace"""
```

Code after:

    # 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
            func = getFunctionContaining(addr)  # type: ignore [name-defined] # noqa: F821

            # bookmark & tag MITRE ATT&CK tactics & MBC @ function scope
            if func is not None:
                func_addr = func.getEntryPoint()

                if self.attack != []:
                    for item in self.attack:
                        attack_txt = ""
                        for part in item.get("parts", {}):
                            attack_txt = attack_txt + part + Namespace.DELIMITER
                        attack_txt = attack_txt + item.get("id", {})
                        add_bookmark(func_addr, attack_txt, "CapaExplorer::MITRE ATT&CK")

                if self.mbc != []: