使用 VS2012 开发 IDA GUI 插件 WIN32 SDK 和 内置函数 AskUsingForm_c
1. 执行菜单的File->New->Project… (Ctrl-Shift-N)打开新建工程窗口。
2. 展开左侧的Visual C++项目模板分支,然后选择右侧的Win32 Project条目,输入工程名称,然后点击确定。
3.出现Win32Application Wizard
4. 使用默认设置
5. 生成的文件夹及文件
6. VS 解决方案资源管理器视图
7. 运行结果
8. 修改项目属性 - 选择所有配置
常规 将目标文件扩展名修改为.plw 配置类型 动态库(.dll)
C/C++->常规 附加包含目录添加ida sdk include目录,例如 D:\IDA64\IDASDK64\Include
C/C++->预处理器,添加__NT__;__IDP__字段到预处理器定义中。
C/C++->代码生成,关闭安全检查
连接器->输入,将 ida.lib 添加到附加依赖项中。C:\IDA64\idasdk64\lib\x86_win_vc_32\ida.lib
连接器->命令行添加/EXPORT:PLUGIN
生成事件->后期生成事件,将生成的文件复制到 ida 插件目录
xcopy /c /d /r /y "$(TargetPath)" "D:\IDA64\plugins\"
8. 新建配置用于调试 EXE
修改配置属性 -
常规 将目标文件扩展名修改为.exe 配置类型 应用程序(.exe)
连接器->命令行删除 /EXPORT:PLUGIN
生成事件->后期生成事件,删除 copy /c /d /r /y "$(TargetPath)" "D:\IDA64\plugins\"
C/C++->预处理器,添加_DEBUG_EXE 字段到预处理器定义中。
8. 添加新文件 ida_plug_main.cpp
ida_plug_main.cpp
#include "stdafx.h" int __stdcall IDAP_init ( void ) { // Do checks here to ensure your plug-in is being used within // an environment it was written for. Return PLUGIN_SKIP if the // checks fail, otherwise return PLUGIN_KEEP. return ( PLUGIN.flags & PLUGIN_UNL ) ? PLUGIN_OK : PLUGIN_KEEP; } void __stdcall IDAP_term ( void ) { // Stuff to do when exiting, generally you'd put any sort // of clean-up jobs here. return; } // The plugin can be passed an integer argument from the plugins.cfg // file. This can be useful when you want the one plug-in to do // something different depending on the hot-key pressed or menu // item selected. void __stdcall IDAP_run ( int arg ) { // The "meat" of your plug-in msg ( "ida plug-in run!\n" ); } // There isn't much use for these yet, but I set them anyway. char IDAP_comment[] = "ida plug-in template"; char IDAP_help[] = "ida plug-in template"; // The name of the plug-in displayed in the Edit->Plugins menu. It can // be overridden in the user's plugins.cfg file. char IDAP_name[] = "ida plug-in template"; // The hot-key the user can use to run your plug-in. char IDAP_hotkey[] = "Ctrl-Alt-X"; // The all-important exported PLUGIN object plugin_t PLUGIN = { IDP_INTERFACE_VERSION, // IDA version plug-in is written for PLUGIN_UNL, // Flags (see below) IDAP_init, // Initialisation function IDAP_term, // Clean-up function IDAP_run, // Main plug-in body IDAP_comment, // Comment unused IDAP_help, // As above unused IDAP_name, // Plug-in name shown in IDAP_hotkey // Hot key to run the plug-in };
修改 "stdafx.h" 文件, 添加 ida sdk 头文件
// stdafx.h : 标准系统包含文件的包含文件, // 或是经常使用但不常更改的 // 特定于项目的包含文件 // #pragma once #include "targetver.h" #define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的信息 // Windows 头文件: #include <windows.h> // C 运行时头文件 #include <stdlib.h> #include <malloc.h> #include <memory.h> #include <tchar.h> // TODO: 在此处引用程序需要的其他头文件 #include <ida.hpp> #include <idp.hpp> #include <loader.hpp> #include <kernwin.hpp> #include <area.hpp> #include <segment.hpp> #include <funcs.hpp> #include <lines.hpp> #include <srarea.hpp> #include <ua.hpp> #include <xref.hpp> #include <offset.hpp> #include <bytes.hpp> #include <name.hpp> #include <enum.hpp> #include <struct.hpp>
现在通过VS2012调试 ida_plugin_template.exe, 提示 : 无法启动此程序,因为计算机中丢失 IDA.WLL。尝试重新安装该程序以解决此问题。
复制 D:\IDA64 文件夹中的 ida.wll 到 ida_plugin_template.exe 所在文件夹 D:\workspace_c++\ida_sdk_projects\ida_plugin_template\DebugExe 即可调试
ida_plugin_template.plw 也可以被 ida 加载 < Ctrl-Alt-X >
ida 输出 // msg ( "ida plug-in run!\n" );
在 ida_plug_main.cpp 源代码中设置断点, 然后使用 VS2012 工具 - 附加到进程...
选择 idaq.exe 即可调试 ida_plugin_template.plw
现在再次调用插件, 会在设置的断点处停下来, 等待调试
修改 ida_plug_main.cpp 和 ida_plugin_template.cpp 使插件显示 GUI 窗口
同时修改 D:\IDA64\plugins\plugins.cfg 可是传入不同的参数给插件
ida_plugin_template_0 ida_plugin_template Ctrl-Alt-0 0 ida_plugin_template_1 ida_plugin_template Ctrl-Alt-1 1
#include "stdafx.h" int __stdcall IDAP_init ( void ) { // Do checks here to ensure your plug-in is being used within // an environment it was written for. Return PLUGIN_SKIP if the // checks fail, otherwise return PLUGIN_KEEP. return ( PLUGIN.flags & PLUGIN_UNL ) ? PLUGIN_OK : PLUGIN_KEEP; } void __stdcall IDAP_term ( void ) { // Stuff to do when exiting, generally you'd put any sort // of clean-up jobs here. return; } void __stdcall IDAP_run ( int arg ); // There isn't much use for these yet, but I set them anyway. char IDAP_comment[] = "ida plug-in template"; char IDAP_help[] = "ida plug-in template"; // The name of the plug-in displayed in the Edit->Plugins menu. It can // be overridden in the user's plugins.cfg file. char IDAP_name[] = "ida plug-in template"; // The hot-key the user can use to run your plug-in. char IDAP_hotkey[] = "Ctrl-Alt-X"; // The all-important exported PLUGIN object plugin_t PLUGIN = { IDP_INTERFACE_VERSION, // IDA version plug-in is written for PLUGIN_UNL, // Flags (see below) IDAP_init, // Initialisation function IDAP_term, // Clean-up function IDAP_run, // Main plug-in body IDAP_comment, // Comment unused IDAP_help, // As above unused IDAP_name, // Plug-in name shown in IDAP_hotkey // Hot key to run the plug-in };
//------------------------------------------------------------------------------------- BOOL CALLBACK EnumIdaMainWindow ( HWND hwnd, LPARAM lParam ) { WINDOWINFO winInfo; DWORD dwIdaProcessId = * ( ( DWORD* ) lParam ); DWORD dwProcessId; GetWindowThreadProcessId ( hwnd, &dwProcessId ); winInfo.cbSize = sizeof ( WINDOWINFO ); GetWindowInfo ( hwnd, &winInfo ); if ( dwProcessId == dwIdaProcessId && GetParent ( hwnd ) == NULL && winInfo.dwStyle & WS_VISIBLE ) { * ( ( HWND * ) lParam ) = hwnd; return FALSE; // stop EnumWindow() } return TRUE; }
//------------------------------------------------------------------------------------ HWND GetIdaMainWindow ( void ) { DWORD dwIdaProcessId = GetCurrentProcessId(); if ( !EnumWindows ( EnumIdaMainWindow, ( LPARAM ) &dwIdaProcessId ) ) { return ( HWND ) dwIdaProcessId; } return NULL; } HWND GetIdaMainWindow ( void ); int APIENTRY _tWinMain ( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow ); // The plugin can be passed an integer argument from the plugins.cfg // file. This can be useful when you want the one plug-in to do // something different depending on the hot-key pressed or menu // item selected. void __stdcall IDAP_run ( int arg ) { // The "meat" of your plug-in msg ( "ida plug-in run!\n" );
HWND hIdaMainWindow = GetIdaMainWindow(); if ( hIdaMainWindow == NULL ) return; switch ( arg ) { case 0: case 1: _tWinMain ( NULL, ( HINSTANCE ) hIdaMainWindow, ( LPWSTR ) arg, SW_SHOWNORMAL ); break; default: break; } }
// ida_plugin_template.cpp : 定义应用程序的入口点。 // #include "stdafx.h" #include "ida_plugin_template.h" #define MAX_LOADSTRING 100 // 全局变量: HINSTANCE hInst; // 当前实例 TCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本 TCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名 // 此代码模块中包含的函数的前向声明: ATOM MyRegisterClass ( HINSTANCE hInstance ); BOOL InitInstance ( HINSTANCE, int ); LRESULT CALLBACK WndProc ( HWND, UINT, WPARAM, LPARAM ); INT_PTR CALLBACK About ( HWND, UINT, WPARAM, LPARAM ); HWND hIdaMainWindow = NULL; int idaArg; int APIENTRY _tWinMain ( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow ) { UNREFERENCED_PARAMETER ( hPrevInstance ); UNREFERENCED_PARAMETER ( lpCmdLine ); // TODO: 在此放置代码。 // #ifndef _DEBUG_EXE // if ( hInstance == NULL ) { hInstance = GetModuleHandle ( L"ida_plugin_template.plw" ); if ( hInstance == NULL ) return 0; idaArg = ( int ) lpCmdLine; hIdaMainWindow = ( HWND ) hPrevInstance; } #endif
MSG msg; HACCEL hAccelTable; // 初始化全局字符串 LoadString ( hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING ); LoadString ( hInstance, IDC_IDA_PLUGIN_TEMPLATE, szWindowClass, MAX_LOADSTRING ); MyRegisterClass ( hInstance ); // 执行应用程序初始化: if ( !InitInstance ( hInstance, nCmdShow ) ) return FALSE; hAccelTable = LoadAccelerators ( hInstance, MAKEINTRESOURCE ( IDC_IDA_PLUGIN_TEMPLATE ) ); // 主消息循环: // retrieves a message other than WM_QUIT BOOL bRet; while ( ( bRet = GetMessage ( &msg, NULL, 0, 0 ) ) != 0 ) { if ( bRet == - 1 ) break; if ( !TranslateAccelerator ( msg.hwnd, hAccelTable, &msg ) ) { TranslateMessage ( &msg ); DispatchMessage ( &msg ); } } // retrieves the WM_QUIT message // or hWnd is an invalid window handle or lpMsg is an invalid pointer. // return ( int ) msg.wParam; // WM_QUIT wParam } // // 函数: MyRegisterClass() // // 目的: 注册窗口类。 // ATOM MyRegisterClass ( HINSTANCE hInstance ) { WNDCLASSEX wcex; wcex.cbSize = sizeof ( WNDCLASSEX ); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon ( hInstance, MAKEINTRESOURCE ( IDI_IDA_PLUGIN_TEMPLATE ) ); wcex.hCursor = LoadCursor ( NULL, IDC_ARROW ); wcex.hbrBackground = ( HBRUSH ) ( COLOR_WINDOW + 1 ); wcex.lpszMenuName = MAKEINTRESOURCE ( IDC_IDA_PLUGIN_TEMPLATE ); wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon ( wcex.hInstance, MAKEINTRESOURCE ( IDI_SMALL ) ); return RegisterClassEx ( &wcex ); } // // 函数: InitInstance(HINSTANCE, int) // // 目的: 保存实例句柄并创建主窗口 // // 注释: // // 在此函数中,我们在全局变量中保存实例句柄并 // 创建和显示主程序窗口。 // BOOL InitInstance ( HINSTANCE hInstance, int nCmdShow ) { HWND hWnd; hInst = hInstance; // 将实例句柄存储在全局变量中 hWnd = CreateWindow ( szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
hIdaMainWindow,
NULL, hInstance, NULL );
if ( !hWnd ) { return FALSE; } ShowWindow ( hWnd, nCmdShow ); UpdateWindow ( hWnd ); return TRUE; } // // 函数: WndProc(HWND, UINT, WPARAM, LPARAM) // // 目的: 处理主窗口的消息。 // // WM_COMMAND - 处理应用程序菜单 // WM_PAINT - 绘制主窗口 // WM_DESTROY - 发送退出消息并返回 // // LRESULT CALLBACK WndProc ( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) { int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc; switch ( message ) { case WM_COMMAND: wmId = LOWORD ( wParam ); wmEvent = HIWORD ( wParam ); // 分析菜单选择: switch ( wmId ) { case IDM_ABOUT: DialogBox ( hInst, MAKEINTRESOURCE ( IDD_ABOUTBOX ), hWnd, About ); break; case IDM_EXIT: DestroyWindow ( hWnd ); break; default: return DefWindowProc ( hWnd, message, wParam, lParam ); } break; case WM_PAINT: hdc = BeginPaint ( hWnd, &ps ); // TODO: 在此添加任意绘图代码... EndPaint ( hWnd, &ps ); break; case WM_CREATE: if ( hIdaMainWindow ) EnableWindow ( hIdaMainWindow, FALSE ); break; case WM_DESTROY: if ( hIdaMainWindow ) EnableWindow ( hIdaMainWindow, TRUE ); PostQuitMessage ( 0 ); // WM_QUIT, wParam = 0 break; default: return DefWindowProc ( hWnd, message, wParam, lParam ); } return 0; } // “关于”框的消息处理程序。 INT_PTR CALLBACK About ( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) { UNREFERENCED_PARAMETER ( lParam ); switch ( message ) { case WM_INITDIALOG: return ( INT_PTR ) TRUE; case WM_COMMAND: if ( LOWORD ( wParam ) == IDOK || LOWORD ( wParam ) == IDCANCEL ) { EndDialog ( hDlg, LOWORD ( wParam ) ); return ( INT_PTR ) TRUE; } break; } return ( INT_PTR ) FALSE; }
修改 ida_plug_main.cpp 使用内置的界面显示
static void __stdcall AskUsingForm ( void ); // The plugin can be passed an integer argument from the plugins.cfg // file. This can be useful when you want the one plug-in to do // something different depending on the hot-key pressed or menu // item selected. void __stdcall IDAP_run ( int arg ) { // The "meat" of your plug-in msg ( "ida plug-in run!\n" ); HWND hIdaMainWindow = GetIdaMainWindow(); if ( hIdaMainWindow == NULL ) return; switch ( arg ) { case 0: _tWinMain ( NULL, ( HINSTANCE ) hIdaMainWindow, ( LPWSTR ) arg, SW_SHOWNORMAL ); break; case 1: AskUsingForm(); break; default: break; } } static const char *dialog1 = // "This is the title\n\n"// dialog title "<##Radio Buttons##Radio 1:R>\n" "<Radio 2:R>>\n"//ushort* number of selected radio "<##Radio Buttons##Radio 1:R>\n" "<Radio 2:R>>\n"//ushort* number of selected radio "<##Check Boxes##Check 1:C>\n" "<Check 2:C>>\n"//ushort* bitmask of checks "<##Check Boxes##Check 1:C>\n" "<Check 2:C>>\n";//ushort* bitmask of checks static void __stdcall AskUsingForm ( void ) { ushort bitMask, bitMask1 = 0; ushort btnIndex, bitIndex1; int ok = AskUsingForm_c ( dialog1, &btnIndex, &bitIndex1, &bitMask, &bitMask1 ); }