应用安全桌面应用开发工具【免费下载链接】cutterFree and Open Source Reverse Engineering Platform powered by rizin项目地址https://gitcode.com/gh_mirrors/cu/cutter点击查看免费下载Cutter 的菜单栏中有一个特殊的Plugins插件菜单它是已加载插件向用户暴露其窗口与视图的统一入口。本文基于 plugins-menu.rst 文档结合 Cutter 仓库中的MainWindow、CutterPlugin、PluginManager等源码实现深入讲解该菜单的默认行为、填充机制、插件加载流程并给出 Python 与 C 插件的完整实战示例帮助读者理解并亲手编写能在 Plugins 菜单中注册窗口的插件。一、Plugins 菜单是什么根据官方文档 plugins-menu.rst 的定义Plugins Sub-Menu插件子菜单描述该菜单包含由已加载插件loaded plugins创建的窗口windows和视图views。默认情况下此菜单为空除非插件主动将动作actions和条目items添加到该菜单。路径Windows - Plugins也就是说Plugins 菜单并不展示内置功能而是完全由第三方插件驱动。插件通过注册 Dock Widget停靠窗口等 UI 组件为每个窗口生成一个切换显示/隐藏的动作toggleViewAction这些动作会被统一收集到 Plugins 菜单中使用户可以像操作 Cutter 内置窗口一样显示、隐藏、停靠插件提供的视图。该菜单位于主菜单栏中与 File、Edit、View、Windows、Debug、Help 等菜单并列。菜单栏的整体结构可参考 menu-bar.rst其中plugins-menu是其固定的子页面之一。二、默认状态何时为空、何时被禁用文档明确指出默认情况下此菜单为空。但为空有两种不同的表现从源码中可以看得更细致。在 MainWindow.cpp 中Cutter 初始化主窗口时会根据插件情况设置 Plugins 菜单的状态if (plugins.empty()) { ui-menuPlugins-menuAction()-setToolTip( tr(No plugins are installed. Check the plugins section on Cutter documentation to learn more.)); ui-menuPlugins-setEnabled(false); } else if (ui-menuPlugins-isEmpty()) { ui-menuPlugins-menuAction()-setToolTip( tr(The installed plugins didnt add entries to this menu.)); ui-menuPlugins-setEnabled(false); }从这段代码可以归纳出三种典型状态状态触发条件表现无插件插件目录为空或所有插件加载失败菜单被禁用置灰tooltip 提示未安装插件请查阅文档有插件但未添加条目插件已加载但没有通过addPluginDockWidget()等方式注册动作菜单同样被禁用tooltip 提示已安装的插件没有向此菜单添加条目正常显示插件加载成功且注册了窗口/动作菜单可用列出所有插件窗口的切换动作这一设计说明Plugins 菜单的空是常态只有真正注册了 UI 组件的插件才会让菜单变得可用。它既不会凭空出现假条目也不会因为没有插件而报错。三、菜单如何被填充addPluginDockWidget()调用链Plugins 菜单的填充机制集中在MainWindow::addPluginDockWidget()中。在 MainWindow.cppvoid MainWindow::addPluginDockWidget(CutterDockWidget *dockWidget) { addWidget(dockWidget); ui-menuPlugins-addAction(dockWidget-toggleViewAction()); addDockWidget(Qt::DockWidgetArea::TopDockWidgetArea, dockWidget); pluginDocks.push_back(dockWidget); }该函数一次完成了四件事addWidget(dockWidget)将窗口注册到 Cutter 的窗口管理体系中与内置窗口一致ui-menuPlugins-addAction(dockWidget-toggleViewAction())把该窗口的切换视图动作Qt 内置的toggleViewAction()勾选/取消勾选即可显示/隐藏窗口追加到 Plugins 菜单中——这是填充菜单的关键一步addDockWidget(Qt::DockWidgetArea::TopDockWidgetArea, dockWidget)将窗口添加到主窗口的顶部停靠区域pluginDocks.push_back(dockWidget)把窗口指针记录到插件窗口列表中便于统一管理。此外 MainWindow.h 中还保留了一个带QAction*参数的旧版本重载并标记为CUTTER_DEPRECATED已废弃但仍会转发到新版本说明插件的菜单条目统一由窗口的 toggle 动作生成插件无需自己手工往菜单里塞QAction。getMenuByType()MainWindow.cpp还暴露了MenuType::Plugins枚举值其他代码可以通过它直接拿到ui-menuPlugins这个QMenu*指针例如enum class MenuType : ut8 { File, Edit, View, Windows, Debug, Help, Plugins }; QMenu *getMenuByType(MenuType type);从源码结构看MenuType::Plugins是专为插件设计的菜单类型允许插件或 Cutter 内部代码以统一的方式访问该菜单。四、插件接口CutterPlugin与生命周期回调插件要往 Plugins 菜单注册窗口首先必须实现插件基类CutterPluginCutterPlugin.h。该接口定义了完整的插件生命周期class CUTTER_EXPORT CutterPlugin { public: virtual void setupPlugin() 0; // 插件刚被加载时调用用于初始化 virtual void setupInterface(MainWindow *main) 0; // Cutter 核心 UI 初始化完成后调用用于注册 UI 组件 virtual void registerDecompilers() {} // 可选注册插件实现的反编译器 virtual void terminate() {}; // 插件被销毁前调用用于清理资源 virtual QString getName() const 0; virtual QString getAuthor() const 0; virtual QString getDescription() const 0; virtual QString getVersion() const 0; };其中与 Plugins 菜单关系最密切的是setupInterface(MainWindow *main)——插件在此回调中创建自己的 Dock Widget并调用main-addPluginDockWidget(widget)完成菜单注册。接口还通过 Qt 的元对象机制声明了CutterPlugin_iid re.rizin.cutter.plugins.CutterPlugin用于插件身份识别与加载校验。整个插件生命周期由PluginManagerPluginManager.h统一驱动loadPlugins(bool enablePlugins true)应用启动时调用一次扫描插件目录并加载所有插件destroyPlugins()应用退出时调用依次终止并销毁插件getPluginDirectories()/getUserPluginsDirectory()提供插件目录信息用于在编辑 - 首选项 - 插件中展示路径内部通过loadNativePlugins()与loadPythonPlugins()分别加载 C 与 Python 插件。五、插件从哪里来加载目录与 native/python 子目录根据官方 plugins.rst 文档插件从与操作系统相关的用户级目录加载该目录下包含native和python两个子目录分别存放 C 与 Python 插件由 Cutter 自动创建若想知道插件目录的绝对路径以及当前已加载的插件列表可打开Edit - Preferences - Plugins编辑 - 首选项 - 插件查看。需要注意Python 插件支持仅在 Cutter 以CUTTER_ENABLE_PYTHON和CUTTER_ENABLE_PYTHON_BINDINGS选项编译时才可用官方发布版自 1.8.0 起均默认开启。从 PluginManager.h 也可以看到loadPythonPlugins()与loadPythonPlugin()都包在#ifdef CUTTER_ENABLE_PYTHON_BINDINGS中。此外文档还特别提醒如果要做的是新文件格式或新架构支持不应写 Cutter 插件而应实现 Rizin 插件见 plugins.rst——Cutter 插件专注于 UI 集成。六、实战用 Python 插件在 Plugins 菜单注册窗口Cutter 强烈建议新手从 Python 插件入手因为其工作流更快、更简单。以下代码来自官方示例 sample_python.py它演示了最核心的注册 Dock Widget 到 Plugins 菜单流程import cutter from PySide6.QtCore import Qt from PySide6.QtWidgets import QVBoxLayout, QLabel, QWidget, QSizePolicy, QPushButton class FortuneWidget(cutter.CutterDockWidget): def __init__(self, parent): super(FortuneWidget, self).__init__(parent) self.setObjectName(FancyDockWidgetFromCoolPlugin) self.setWindowTitle(Sample Python Plugin) content QWidget() self.setWidget(content) layout QVBoxLayout(content) content.setLayout(layout) self.text QLabel(content) layout.addWidget(self.text) button QPushButton(content) button.setText(Want a fortune?) layout.addWidget(button) layout.setAlignment(button, Qt.AlignHCenter) button.clicked.connect(self.generate_fortune) cutter.core().seekChanged.connect(self.generate_fortune) self.show() def generate_fortune(self): fortune cutter.cmd(fortune).replace(\n, ) res cutter.core().cmdRaw(f?E {fortune}) self.text.setText(res) class CutterSamplePlugin(cutter.CutterPlugin): name Sample Plugin description A sample plugin written in python. version 1.2 author Cutter developers def setupPlugin(self): pass def setupInterface(self, main): # Dock widget: 这一步会把窗口的 toggle 动作自动加到 Plugins 菜单 widget FortuneWidget(main) main.addPluginDockWidget(widget) ... def create_cutter_plugin(): return CutterSamplePlugin()关键点拆解main.addPluginDockWidget(widget)在setupInterface()中调用将FortuneWidget注册到 Cutter 主窗口其toggleViewAction()会自动出现在Plugins菜单中C 版示例 CutterSamplePlugin.cpp 的写法完全一致。create_cutter_plugin()入口函数Cutter 在启动时会导入插件模块并调用模块根部的create_cutter_plugin()函数期待其返回一个cutter.CutterPlugin实例。name/description/version/author插件的元信息会显示在编辑 - 首选项 - 插件列表中。完整的从零编写 Python 插件教程包括创建 Dock Widget、用cutter.cmd()/cutter.cmdj()获取数据、连接seekChanged信号实现动态刷新、最终完整代码见 getting-started.rst其运行效果如下两图所示将插件安装到加载目录打开Edit - Preferences - Plugins查看插件目录的绝对路径将.py文件放入该目录下的python/子目录C 插件放入native/重启 Cutter插件即被自动加载。额外提示来自 getting-started.rst插件本质是 Python 模块因此也可以用包含多个.py文件 __init__.py的目录形式组织只要__init__.py定义了create_cutter_plugin()即可在 Unix 系系统上可以用符号链接把插件链接到插件目录避免反复拷贝当插件需要清理资源时实现可选的terminate()方法即可Python 版示例 sample_python.py 展示了如何从上下文菜单中移除自己添加的动作。七、实战C 插件注册 Dock WidgetC 插件的注册流程与 Python 完全对称。官方示例 CutterSamplePlugin.cpp 的核心代码void CutterSamplePlugin::setupInterface(MainWindow *main) { CutterSamplePluginWidget *widget new CutterSamplePluginWidget(main); main-addPluginDockWidget(widget); } CutterSamplePluginWidget::CutterSamplePluginWidget(MainWindow *main) : CutterDockWidget(main) { this-setObjectName(CutterSamplePluginWidget); this-setWindowTitle(Sample C Plugin); // ... 构建 QLabel、QPushButton 等 UI 组件 ... connect(Core(), CutterCore::seekChanged, this, CutterSamplePluginWidget::on_seekChanged); }要点插件类继承CutterPlugin并实现setupPlugin()、setupInterface()、getName()、getAuthor()、getDescription()、getVersion()等纯虚函数窗口类继承CutterDockWidget通过setObjectName()保证窗口布局状态可被持久化同样只需一行main-addPluginDockWidget(widget)窗口的切换动作便进入 Plugins 菜单。从源码结构还可以推断CutterDockWidget是所有可停靠插件窗口的基类因此任何继承它的自定义窗口都能获得与内置窗口一致的行为拖拽、停靠、布局保存、菜单开关等。八、插件还能往哪里加条目上下文菜单扩展除了 Plugins 菜单插件还可以扩展 Cutter 的右键上下文菜单机制是 MainWindow.h 中定义的getContextMenuExtensions()enum class ContextMenuType : ut8 { Disassembly, Addressable }; QMenu *getContextMenuExtensions(ContextMenuType type);Python 示例 sample_python.py 展示了两种典型用法# 反汇编视图右键菜单 menu main.getContextMenuExtensions(cutter.MainWindow.ContextMenuType.Disassembly) self.disas_action menu.addAction(CutterSamplePlugin dissassembly action) self.disas_action.triggered.connect(self.handle_disassembler_action) # 可寻址条目列表Flags/Functions/Strings/Search 结果等的右键菜单 addressable_item_menu main.getContextMenuExtensions(cutter.MainWindow.ContextMenuType.Addressable) self.addr_submenu addressable_item_menu.addMenu(CutterSamplePlugin) adrr_action self.addr_submenu.addAction(Action 1) self.addr_submenu.addSeparator() adrr_action2 self.addr_submenu.addAction(Action 2)注意当用户在上下文菜单中触发插件动作时Cutter 会把当前条目的地址写入QAction的data()见 sample_python.py 的回调因此插件回调里可以通过action.data()拿到当前反汇编行或当前表格项对应的地址。九、常见问题排查现象可能原因处理建议Plugins 菜单被禁用tooltip 提示未安装插件插件目录为空或插件未放入正确子目录通过Edit - Preferences - Plugins查看目录路径将插件放入python/或native/子目录Plugins 菜单被禁用tooltip 提示插件未添加条目插件已加载但setupInterface()未调用addPluginDockWidget()检查插件源码是否注册了 Dock Widget 或菜单动作Python 插件无法加载Cutter 未启用 Python 绑定确认构建选项CUTTER_ENABLE_PYTHON与CUTTER_ENABLE_PYTHON_BINDINGS已开启官方发布版默认开启插件加载后未见于列表插件缺少create_cutter_plugin()入口函数确保模块根部定义了create_cutter_plugin()并返回插件实例十、总结Cutter 的Plugins 菜单是插件 UI 的总开关面板它默认为空、无插件时自动禁用当插件通过setupInterface()回调调用addPluginDockWidget()注册窗口后窗口的切换动作会以toggleViewAction()的形式自动出现在该菜单中。这套机制将插件窗口与内置窗口统一管理用户无需学习任何额外操作即可显示、隐藏、停靠插件视图。对开发者而言只需掌握三点即可上手实现CutterPlugin接口、在setupInterface()中创建CutterDockWidget窗口、调用main.addPluginDockWidget(widget)。更多细节可继续阅读仓库中的 plugins.rst、getting-started.rst 以及两个官方示例 sample_python.py 与 CutterSamplePlugin.cpp。赞分享应用安全桌面应用开发工具【免费下载链接】cutterFree and Open Source Reverse Engineering Platform powered by rizin项目地址https://gitcode.com/gh_mirrors/cu/cutter点击查看免费下载相关推荐x64dbg 插件菜单Plugins Menu完全指南从插件加载到 Scylla 启动x64dbg 插件菜单Plugins Menu完全指南从插件加载到 Scylla 启动 本文围绕 x64dbg 的 Plugins插件菜单 展开讲解逆向工程调试器开发工具应用安全Cutter 反编译功能全解析Decompiler 插件架构、反编译视图与上下文菜单实战指南Cutter 反编译功能全解析Decompiler 插件架构、反编译视图与上下文菜单实战指南 导读 本文以 Cutter 用户文档中的 Features 章节应用安全桌面应用开发工具FaceSwap plugins 包解析插件加载机制、三大任务域与全量插件清单FaceSwap plugins 包解析插件加载机制、三大任务域与全量插件清单 本文以官方文档 docs/full/plugins 目录为骨架系统讲解 Fa人工智能深度学习计算机视觉媒体生成创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考