지난 번 포스팅(http://www.xeronichs.com/2016/10/study-x64dbg-plugin-02.html)에서
눈에 보이는 '메뉴' 까지 구현을 했습니다.
플러그인의 메뉴 |
이번에는 '메뉴' 를 클릭할 때, 지정된 동작을 실행하도록 기능 추가를 해보겠습니다 :)
디버거에서 지원해주는 함수들을 사용하기 위해서 bridge 라이브러리가 필요한데요...
x32dbg.lib/x64dbg.lib 라이브러리를 포함하는 부분에 아래처럼 코드를 추가해줍니다.
#include "pluginsdk\_plugins.h" #ifdef _WIN64 #pragma comment(lib, "pluginsdk\\x64dbg.lib") #pragma comment(lib, "pluginsdk\\x64bridge.lib") // 추가 #else #pragma comment(lib, "pluginsdk\\x32dbg.lib") #pragma comment(lib, "pluginsdk\\x32bridge.lib") // 추가 #endif //_WIN64
'메뉴' 실행 처리
'메뉴'에 대한 처리를 위해서 "CBMENUENTRY" 라는 export 함수를 추가로 구현해줘야 합니다.
#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 //------------------------------------------------------------------- extern "C" __declspec(dllexport) void CBMENUENTRY(CBTYPE cbType, PLUG_CB_MENUENTRY* info); //------------------------------------------------------------------- extern "C" __declspec(dllexport) void CBMENUENTRY(CBTYPE cbType, PLUG_CB_MENUENTRY* info) { switch (info->hEntry) { case MENU_TEST1: break; case MENU_TEST2: break; case MENU_DISASM_TEST1: break; case MENU_DISASM_TEST2: break; case MENU_DUMP_TEST1: break; case MENU_DUMP_TEST2: break; case MENU_STACK_TEST1: break; case MENU_STACK_TEST2: break; } }
switch 구문 안에서 info->hEntry 의 값에 따라 다르게 처리를 하고 있는데요...
이 때 hEntry 는 지난 번에 "_plugin_menuaddentry" 함수로 메뉴를 등록할 때 지정한 ID 값입니다.
각 메뉴에 해당하는 case 구문 안에서 원하는 동작을 하도록 코드를 작성해주면 됩니다 :)
//------------------------------------------------------------------- extern "C" __declspec(dllexport) void CBMENUENTRY(CBTYPE cbType, PLUG_CB_MENUENTRY* info) { SELECTIONDATA sel; BASIC_INSTRUCTION_INFO basicinfo; duint uiAddr = 0; switch (info->hEntry) { case MENU_TEST1: MessageBox(g_hwndDlg, L"Menu Test 1", L"BasicPlugin", MB_ICONINFORMATION | MB_OK); break; case MENU_TEST2: if (DbgIsDebugging()) MessageBox(g_hwndDlg, L"Menu Test 2\r\n\r\n[ DbgIsDebugging() -> true ]", L"BasicPlugin", MB_ICONINFORMATION | MB_OK); else MessageBox(g_hwndDlg, L"Menu Test 2\r\n\r\n[ DbgIsDebugging() -> false ]", L"BasicPlugin", MB_ICONINFORMATION | MB_OK); break; case MENU_DISASM_TEST1: GuiSelectionGet(GUI_DISASSEMBLY, &sel); _plugin_logprintf("[BasicPlugin] Disasm [ START : 0x%p - END : 0x%p ]\n", sel.start, sel.end); break; case MENU_DISASM_TEST2: GuiSelectionGet(GUI_DISASSEMBLY, &sel); uiAddr = sel.start; while (uiAddr <= sel.end) { DbgDisasmFastAt(uiAddr, &basicinfo); _plugin_logprintf("[BasicPlugin] [0x%p] : %s\n", uiAddr, basicinfo.instruction); uiAddr += basicinfo.size; } break; case MENU_DUMP_TEST1: GuiSelectionGet(GUI_DUMP, &sel); _plugin_logprintf("[BasicPlugin] Dump [ START : 0x%p - END : 0x%p ]\n", sel.start, sel.end); break; case MENU_DUMP_TEST2: GuiSelectionGet(GUI_DUMP, &sel); _plugin_logprintf("[BasicPlugin] Dump : "); uiAddr = sel.start; while (uiAddr <= sel.end) { _plugin_logprintf("%02X", *(BYTE*)uiAddr); uiAddr++; } _plugin_logprintf("\n"); break; case MENU_STACK_TEST1: case MENU_STACK_TEST2: GuiSelectionGet(GUI_STACK, &sel); _plugin_logprintf("[BasicPlugin] Stack [ START : 0x%p - END : 0x%p ]\n", sel.start, sel.end); break; } }
메인 메뉴의 'MENU_TEST1', 'MENU_TEST2' 를 클릭할 경우...
MessageBox 를 이용해서 메시지를 띄우도록 해봤습니다.
'MENU_TEST2' 에서는 "DbgIsDebugging" 이라는 함수를 이용해
결과에 따라 실행이 달라지도록 처리를 했습니다.
bool DbgIsDebugging(); |
현재 디버거가 디버깅 중인지 확인하는 함수. 디버깅 중일 땐 true 를 리턴, 아닐 땐 false 를 리턴. |
디버깅 중이 아닐 때... false |
디버깅 중일 때... true |
Disasm 창 팝업 메뉴의 'MENU_DISASM_TEST1', 'MENU_DISASM_TEST2' 를 클릭할 경우...
"GuiSelectionGet" 함수로 선택된 영역에 대한 주소 정보를 가져와서 처리를 하도록 했습니다.
bool GuiSelectionGet(int hWindow, SELECTIONDATA *selection); |
선택된 영역에 대한 주소 정보(시작 주소, 끝 주소)를 가져오는 함수 hWindow : 정보를 가져올 대상 창 지정 - GUI_DISASSEMBLY : Disasm 창 - GUI_DUMP : Dump 창 - GUI_STACK : Stack 창 selection : 주소 정보를 담을 구조체 - start 에 시작 주소, end 에 끝 주소가 담긴다. |
typedef struct { duint start; duint end; } SELECTIONDATA;
Disasm 창 팝업 메뉴의 'MENU_DISASM_TEST1' |
Disasm 창 팝업 메뉴의 'MENU_DISASM_TEST1' 실행 결과 |
"DbgDisasmFastAt" 함수를 이용해 선택된 영역의 디스어셈블 정보도 구할 수 있습니다.
void DbgDisasmFastAt(duint addr, BASIC_INSTRUCTION_INFO *basicinfo); |
지정한 주소에 대한 디스어셈블 정보를 구하는 함수 addr : 디스어셈블 정보를 구할 주소 basicinfo : 디스어셈블 정보를 담을 구조체 |
typedef struct { DWORD type; //value|memory|addr VALUE_INFO value; //immediat MEMORY_INFO memory; duint addr; //addrvalue (jumps + calls) bool branch; //jumps/calls bool call; //instruction is a call int size; char instruction[MAX_MNEMONIC_SIZE * 4]; } BASIC_INSTRUCTION_INFO;
Disasm 창 팝업 메뉴의 'MENU_DISASM_TEST2' |
Disasm 창 팝업 메뉴의 'MENU_DISASM_TEST2' 실행 결과 |
( 참고로 "_plugin_logprintf" 함수로 출력을 했기에 Log 창에 결과가 출력됩니다. )
이처럼 디버거에서 지원해주는 함수를 이용해서 플러그인 기능 구현이 가능한데요...
이 함수들의 리스트는 x64dbg 사이트의 "Help" 페이지에 정리가 되어있습니다.
[ http://help.x64dbg.com/en/latest/developers/index.html ]
단, 함수 리스트는 제공하지만 함수에 대한 상세한 스펙은 아직 완전히 정리가 되지는 않은 느낌이라...
어떤 역할을 하는 함수인지... 어떻게 사용하는지는 직접 코드 작성을 해가며 확인할 수 밖에 없을 듯 합니다. ^^;;;;
[ BasicPlugin 소스 다운로드 ]
마치며...
3회에 걸쳐 x64dbg 플러그인을 만드는 과정을 살펴봤는데요...
플러그인 등록 및 메뉴 처리까지 가능하다면 플러그인 개발의 기본 정도는 익힌게 아닐까... 생각됩니다. ^^;;;
나머지는 플러그인으로 어떤 기능을 구현할 것인지... 디버거 지원 함수를 어떻게 사용할 것인지...
응용 차원의 문제인지라... ^^;;;
x64dbg 도 좋은 플러그인이 많이 나오길 바라면서 살포시 마무리 합니다.