지난 번 포스팅(http://www.xeronichs.com/2016/10/study-x64dbg-plugin-01.html)에서

플러그인(껍데기 뿐이지만;;)을 x64dbg 에 로딩하는 것까지 했습니다.


이번에는 '아~ 플러그인이 확실히 로딩되었구나~!!' 알 수 있도록...

눈에 보이는(?) 플러그인 '메뉴' 를 추가하는 방법을 알아보도록 하겠습니다.


그 전에...

32비트용, 64비트용 개발할 때마다 라이브러리 포함 코드를 바꾸는건 귀찮으니

이 부분부터 정리를 하고 넘어가죠~ ^^;;;;


매크로로 32비트 / 64비트 구별하기


64비트 프로젝트의 경우 기본적으로 '_WIN64' 라는 매크로가 정의되어 있는데요...

이걸로 32비트 / 64비트 프로젝트를 구별할 수 있습니다.

#include "pluginsdk\_plugins.h"

#ifdef _WIN64
#pragma comment(lib, "pluginsdk\\x64dbg.lib")
#else
#pragma comment(lib, "pluginsdk\\x32dbg.lib")
#endif //_WIN64

이렇게하면...

'_WIN64' 매크로가 있으면 64비트 프로젝트로 판단해서 "x64dbg.lib" 를 포함시키고,

없으면 32비트 프로젝트로 판단해서 "x32dbg.lib" 를 포함하게 됩니다.

이후에 다른 코드를 작성할 때도 32비트 / 64비트를 다르게 처리해야 될 경우,

이런 방식으로 매크로를 이용하면 됩니다 :)


지극히 개인적인 편의(?)를 위해 '프로젝트 속성' 에서 '출력 디렉터리', '중간 디렉터리' 를 살짝 바꿨습니다.

프로젝트 속성 '출력 디렉터리, 중간디렉터리' 변경
'플랫폼'이 하위폴더가 되도록 변경

x86(Release & Debug), x64(Release & Debug) 모든 구성, 플랫폼에 대해 변경을 했습니다.

'일괄빌드' 를 이용해서 x86, x64 프로젝트를 동시에 빌드를 해보면...

Debug, Release 폴더 안에 Win32, x64 폴더로 나뉘어져 결과물이 저장됩니다

Win32, x64 프로젝트 동시에 빌드
Release 폴더가 생성됨
Release 폴더 안에 Win32, x64 폴더가 생성됨

테스트를 좀 더 편하게(?) 하기 위해 프로젝트 폴더에 BAT 파일을 하나 만들어서...

32비트, 64비트 플러그인을 디버거 폴더에 각각 복사하도록 해뒀습니다.

@echo off
copy .\Release\Win32\x64dbg_basic_plugin.dll C:\MyTools\x64dbg\release\x32\plugins\basic_plugin.dp32
copy .\Release\x64\x64dbg_basic_plugin.dll C:\MyTools\x64dbg\release\x64\plugins\basic_plugin.dp64
pause


플러그인 '메뉴' 구현


지난 번에 플러그인을 인식시키기 위해서 "pluginit" 이라는 export 함수를 구현을 했구요.

이번에는 디버거의 UI 쪽 정보를 얻기 위해 "plugsetup" 이라는 export 함수 를 구현해줘야 합니다.

HWND g_hwndDlg;
int g_hMenu;
int g_hMenuDisasm;
int g_hMenuDump;
int g_hMenuStack;

//-------------------------------------------------------------------
.
.
extern "C" __declspec(dllexport) void plugsetup(PLUG_SETUPSTRUCT* setupStruct);
.
.
//-------------------------------------------------------------------
__declspec(dllexport) void plugsetup(PLUG_SETUPSTRUCT* setupStruct)
{
    g_hwndDlg = setupStruct->hwndDlg;  // x64dbg 윈도우 핸들
    g_hMenu = setupStruct->hMenu; // x64dbg 메인 메뉴 핸들
    g_hMenuDisasm = setupStruct->hMenuDisasm; // x64dbg Disasm 창의 팝업메뉴 핸들
    g_hMenuDump = setupStruct->hMenuDump; // x64dbg Dump 창의 팝업메뉴 핸들
    g_hMenuStack = setupStruct->hMenuStack; // x64dbg Stack 창의 팝업메뉴 핸들
}

"plugsetup" 함수는 PLUG_SETUPSTRUCT 구조체를 인자로 사용하는데요...

이 구조체의 멤버 변수에 디버거 윈도우 핸들 및 각종 메뉴의 핸들이 담겨져 있습니다.

x64dbg UI 구성

메뉴를 추가할 때는 "_plugin_menuaddentry" 함수를 사용하며 사용법은 다음과 같습니다.

bool _plugin_menuaddentry(int hMenu, int hEntry, const char *title);
- hMenu : 플러그인 메뉴를 추가할 대상의 핸들
- hEntry : 플러그인 메뉴 ID 값 (이후 메뉴에 대한 실행코드 작성 시 사용됨)
- title : 화면에 보여지는 메뉴 텍스트

다음은 메뉴를 실제로 추가하는 코드입니다.

.
.
#define MENU_TEST1 0
#define MENU_TEST2 1
#define MENU_DISASM_TEST1 2
#define MENU_DISASM_TEST2 3
#define MENU_DUMP_TEST1 4
#define MENU_DUMP_TEST2 5
#define MENU_STACK_TEST1 6
#define MENU_STACK_TEST2 7
.
.
__declspec(dllexport) void plugsetup(PLUG_SETUPSTRUCT* setupStruct)
{
.
.
.
    // 메인 메뉴
    _plugin_menuaddentry(g_hMenu, MENU_TEST1, "Menu Test 1");
    _plugin_menuaddseparator(g_hMenu); // 메뉴 분리선
    _plugin_menuaddentry(g_hMenu, MENU_TEST2, "Menu Test 2");

    // 디스어셈블 창 메뉴
    _plugin_menuaddentry(g_hMenuDisasm, MENU_DISASM_TEST1, "Menu Disasm Test 1");
    _plugin_menuaddseparator(g_hMenuDisasm); // 메뉴 분리선
    _plugin_menuaddentry(g_hMenuDisasm, MENU_DISASM_TEST2, "Menu Disasm Test 2");

    // 덤프 창 메뉴
    _plugin_menuaddentry(g_hMenuDump, MENU_DUMP_TEST1, "Menu Dump Test 1");
    _plugin_menuaddentry(g_hMenuDump, MENU_DUMP_TEST2, "Menu Dump Test 2");

    // 스택 창 메뉴
    _plugin_menuaddentry(g_hMenuStack, MENU_STACK_TEST1, "Menu Stack Test 1");
    _plugin_menuaddentry(g_hMenuStack, MENU_STACK_TEST2, "Menu Stack Test 2");
}

플러그인이 종료될 때 정리작업을 해주는 "plugstop" export 함수도 작성을 해줍니다.

//-------------------------------------------------------------------
.
.
extern "C" __declspec(dllexport) bool plugstop(void);
.
.
//-------------------------------------------------------------------
__declspec(dllexport) bool plugstop(void)
{
    // 메뉴에 설정한 내용들 정리
    _plugin_menuclear(g_hMenu);
    _plugin_menuclear(g_hMenuDisasm);
    _plugin_menuclear(g_hMenuDump);
    _plugin_menuclear(g_hMenuStack);

    return true;
}

어제 작업했던 껍데기(?) 플러그인 코드에 오늘 내용을 덧붙이면 아래처럼 되겠네요~ :)

// plugin.cpp
//
//-------------------------------------------------------------------
// basic plugin [ x64dbg plugin ]
//-------------------------------------------------------------------

#include <Windows.h>

#include "pluginsdk\_plugins.h"

#ifdef _WIN64
#pragma comment(lib, "pluginsdk\\x64dbg.lib")
#else
#pragma comment(lib, "pluginsdk\\x32dbg.lib")
#endif //_WIN64

//-------------------------------------------------------------------
#define plugin_name "BasicPlugin"
#define plugin_version 1

#define MENU_TEST1 0
#define MENU_TEST2 1
#define MENU_DISASM_TEST1 2
#define MENU_DISASM_TEST2 3
#define MENU_DUMP_TEST1 4
#define MENU_DUMP_TEST2 5
#define MENU_STACK_TEST1 6
#define MENU_STACK_TEST2 7

int g_iPluginHandle;

HWND g_hwndDlg;
int g_hMenu;
int g_hMenuDisasm;
int g_hMenuDump;
int g_hMenuStack;

//-------------------------------------------------------------------
extern "C" __declspec(dllexport) bool pluginit(PLUG_INITSTRUCT* initStruct);
extern "C" __declspec(dllexport) void plugsetup(PLUG_SETUPSTRUCT* setupStruct);
extern "C" __declspec(dllexport) bool plugstop(void);

//-------------------------------------------------------------------
__declspec(dllexport) bool pluginit(PLUG_INITSTRUCT* initStruct)
{
    initStruct->sdkVersion = PLUG_SDKVERSION;
    initStruct->pluginVersion = plugin_version;
    strcpy_s(initStruct->pluginName, 256, plugin_name);
    g_iPluginHandle = initStruct->pluginHandle;

    _plugin_logprintf("[BasicPlugin] pluginHandle : %d\n", g_iPluginHandle);

    return true;
}

__declspec(dllexport) void plugsetup(PLUG_SETUPSTRUCT* setupStruct)
{
    g_hwndDlg = setupStruct->hwndDlg;
    g_hMenu = setupStruct->hMenu;
    g_hMenuDisasm = setupStruct->hMenuDisasm;
    g_hMenuDump = setupStruct->hMenuDump;
    g_hMenuStack = setupStruct->hMenuStack;

    // 메인 메뉴
    _plugin_menuaddentry(g_hMenu, MENU_TEST1, "Menu Test 1");
    _plugin_menuaddseparator(g_hMenu);
    _plugin_menuaddentry(g_hMenu, MENU_TEST2, "Menu Test 2");

    // 디스어셈블 창 메뉴
    _plugin_menuaddentry(g_hMenuDisasm, MENU_DISASM_TEST1, "Menu Disasm Test 1");
    _plugin_menuaddseparator(g_hMenuDisasm);
    _plugin_menuaddentry(g_hMenuDisasm, MENU_DISASM_TEST2, "Menu Disasm Test 2");

    // 덤프 창 메뉴
    _plugin_menuaddentry(g_hMenuDump, MENU_DUMP_TEST1, "Menu Dump Test 1");
    _plugin_menuaddentry(g_hMenuDump, MENU_DUMP_TEST2, "Menu Dump Test 2");

    // 스택 창 메뉴
    _plugin_menuaddentry(g_hMenuStack, MENU_STACK_TEST1, "Menu Stack Test 1");
    _plugin_menuaddentry(g_hMenuStack, MENU_STACK_TEST2, "Menu Stack Test 2");
}

__declspec(dllexport) bool plugstop(void)
{
    _plugin_menuclear(g_hMenu);
    _plugin_menuclear(g_hMenuDisasm);
    _plugin_menuclear(g_hMenuDump);
    _plugin_menuclear(g_hMenuStack);

    return true;
}

//-------------------------------------------------------------------
// DllMain
//-------------------------------------------------------------------
BOOL WINAPI DllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved)
{
    return TRUE;
}

이 코드를 빌드를 해서 디버거 플러그인 폴더에 복사해서 실행을 해보면 메뉴가 뜨는 걸 볼 수 있을 겁니다 :)

'메인 메뉴' 의 플러그인 메뉴
'Disasm 창 팝업 메뉴' 의 플러그인 메뉴
'Dump 창 팝업 메뉴' 의 플러그인 메뉴
'Stack 창 팝업 메뉴' 의 플러그인 메뉴

이번에는 여기까지~~ 플러그인 '메뉴' 를 추가하는 방법을 알아봤구요...

다음에는  이렇게 추가한 '메뉴' 를 클릭할 때, 특정 동작을 실행시키는 걸 알아보도록 하겠습니다.


[ BasicPlugin 소스 다운로드 ]