注册菜单
注册菜单相关的函数位于kernwin.hpp
中。
在IDA中,菜单的功能和界面是分离的。比如说我们想创建一个菜单,在点击该菜单后便会执行某个动作,那么我们首先得分别创建好菜单和这个动作,然后再将动作附加到对应的菜单上。
创建菜单的函数为create_menu:
bool create_menu(const char *name,const char *label,const char *menupath=NULL);
参数name
为菜单在IDA中的内部名称,必须是独一无二的。
参数label
为菜单实际显示的名称。
参数menupath
用于指示菜单插入的位置,可为空。
返回为真表示菜单成功。
创建动作的函数为register_action:
bool register_action(const action_desc_t &desc)
其中参数desc
属于类型action_desc_t
,是一个用来描述要注册的动作的结构体,结构体如下:
struct action_desc_t
{
int cb; //当前结构体大小。
const char *name; //动作的内部名称,必须独一无二。
//有一个降低名称冲突的好办法是使用
//前后缀的方式命名动作,例如"某某插件:某某动作"
const char *label; //动作所显示的标签名称,可以包含加速键(例如 "~J~ump")
action_handler_t *handler; //动作处理对象指针,用来管理注册动作的行为
const void *owner; //可为空,参考 ACTION_DESC_LITERAL_PLUGMOD
const char *shortcut; //额外的快捷键,例如"Ctrl+Enter"
const char *tooltip; //额外的提示文本
int icon; //额外的图标ID
int flags; //参考 ADF_
}
其中action_handler_t这个结构体如下表示,我们需要重写activate
和update
这两个函数:
struct action_handler_t
{
action_handler_t(int _f = 0) : flags(_f) { flags |= AHF_VERSION; }
//通过重写这个函数来实现动作的核心行为。
//返回非0值,则所有的IDA窗口都会刷新
virtual int idaapi activate(action_activation_ctx_t *ctx) = 0;
//更新动作
//当界面的上下文环境发生改变,此函数将会被调用。这个时候我们可以更新动作的一些属性(例如标签,图标...)。
//另外这个函数还可以通过设置不同的返回值来控制动作功能的开启与关闭。
virtual action_state_t idaapi update(action_update_ctx_t *ctx) = 0;
};
之后便是将动作绑定到菜单上的函数,菜单类型有IDA选项菜单、IDA弹出菜单、工具栏菜单三种:
bool attach_action_to_menu(const char *menupath,const char *name,int flags);
bool attach_action_to_popup(TWidget *widget,TPopupMenu *popup_handle,
const char *name,const char *popuppath = NULL,int flags = 0);
bool attach_action_to_toolbar(const char *toolbar_name,const char *name);
bool detach_action_from_menu(const char *menupath,const char *name);
bool detach_action_from_popup(TWidget *widget, const char *name);
bool detach_action_from_toolbar(const char *toolbar_name,const char *name);
下面示例代码展示如何注册一个简单的菜单
#include <ida.hpp>
#include <idp.hpp>
#include <loader.hpp>
#include <kernwin.hpp>
//动作和菜单都需要有一个独一的名称
#define MyActionName "ActionName"
#define MyMenuName "MenuName"
struct TestActionHandler :public action_handler_t
{
int idaapi activate(action_activation_ctx_t* ctx)
{
//执行具体的功能
msg("TestActionHandler OK\n");
return 0;
}
action_state_t idaapi update(action_update_ctx_t* ctx)
{
//表示菜单永远可以点击使用
return AST_ENABLE_ALWAYS;
}
};
//--------------------------------------------------------------------------
struct plugin_ctx_t : public plugmod_t
{
TestActionHandler MyTestHandler;
virtual bool idaapi run(size_t) override;
~plugin_ctx_t()
{
//卸载插件的时候,把菜单和动作也卸载掉
detach_action_from_menu("MenuLabel/MenuA", MyActionName);
unregister_action(MyActionName);
delete_menu(MyMenuName);
}
};
//--------------------------------------------------------------------------
bool idaapi plugin_ctx_t::run(size_t)
{
create_menu(MyMenuName, "MenuLabel");
const action_desc_t Dest = ACTION_DESC_LITERAL(MyActionName, "ActionLabel", &MyTestHandler, "Alt+Q", NULL, 0);
register_action(Dest);
attach_action_to_menu("MenuLabel/MenuA", MyActionName, SETMENU_FIRST);
return true;
}
//--------------------------------------------------------------------------
static plugmod_t* idaapi init()
{
return new plugin_ctx_t;
}
//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
IDP_INTERFACE_VERSION,
PLUGIN_MULTI, //含有注册菜单功能的插件需要长久留在IDA中,因此和PLUGIN_UNL是冲突的。
init,
nullptr,
nullptr,
nullptr,
nullptr,
"MenuTest",
nullptr,
};