
みなさんこんにちは
前の記事で、 Sega Mega Drive
/ Genesis
ゲームエミュレータのコアを正常に変更し、デバッグ機能を追加しました。 次に、 IDA Pro
バージョン7.0
用のデバッガプラグインを作成します。 始めましょう。
パート2:デバッガプラグイン
最初に、新しい空のDLL
プロジェクトを作成しDLL
。 File
-> New
-> Project
-> Windows Desktop Wizard
-> Dynamic link library (.dll)
で、 Empty Project
チェックボックスをオンにし、その他すべてのチェックを外します

IDA SDK
解凍し、 Visual Studio
マクロで記述して( 2017 Community
を使用します)、将来的に簡単に参照できるようにします。 同時に、 IDA Pro
へのパス用のマクロを追加します。
View
-> [ Other Windows
View
-> [ Property Manager
View
移動しView

なぜなら SDK 7.0
バージョンSDK 7.0
おり、コンパイルはx64
コンパイラによって行われます。 したがって、[ Debug | x64
[ Debug | x64
> Microsoft.Cpp.x64.user
> Properties
:

[ User Macros
セクションのUser Macros
のAdd Macro
]ボタンをクリックし、 IDA_SDK
マクロをIDA_SDK
にSDK
を解凍したパスで記述しSDK
。

IDA_DIR
( IDA Pro
へのパス) IDA_DIR
同じことをIDA_DIR
ます。

IDA
はデフォルトで%Program Files%
に設定されていることに注意してください。これには管理者権限が必要です。
また、 Win32
構成を削除し(この記事ではx86
システムでのコンパイルには影響しません)、 x64
オプションのみを残します。
デバッガーイベントキュークラステンプレートを取得します。
ソースパッケージida_debmod.hのダウンロード #pragma once #include <deque> #include <ida.hpp> #include <idd.hpp> //-------------------------------------------------------------------------- // Very simple class to store pending events enum queue_pos_t { IN_FRONT, IN_BACK }; struct eventlist_t : public std::deque<debug_event_t> { private: bool synced; public: // save a pending event void enqueue(const debug_event_t &ev, queue_pos_t pos) { if (pos != IN_BACK) push_front(ev); else push_back(ev); } // retrieve a pending event bool retrieve(debug_event_t *event) { if (empty()) return false; // get the first event and return it *event = front(); pop_front(); return true; } };
これで、スタジオプロジェクトでコンパイラの定義を設定できるようになるため、次を追加します。
__NT__ __IDP__ __X64__
新しい空のida_debug.cpp
ファイルを追加し、次のテンプレートをそのファイルに貼り付けます。
ソースパッケージida_debug.cppをダウンロード #include <ida.hpp> #include <idd.hpp> #include <auto.hpp> #include <funcs.hpp> #include <idp.hpp> #include <dbg.hpp> #include "ida_debmod.h" #include "debug_wrap.h" static dbg_request_t *dbg_req = NULL; static void pause_execution() { send_dbg_request(dbg_req, REQ_PAUSE); } static void continue_execution() { send_dbg_request(dbg_req, REQ_RESUME); } static void stop_debugging() { send_dbg_request(dbg_req, REQ_STOP); } eventlist_t g_events; static qthread_t events_thread = NULL; // TODO: Implement status register bits mask static const char *const SRReg[] = { }; #define RC_GENERAL (1 << 0) // TODO: define different register types register_info_t registers[] = { // TODO: Implement registers description }; static const char *register_classes[] = { "General Registers", // TODO: Add other register group names NULL }; static void finish_execution() { if (events_thread != NULL) { qthread_join(events_thread); qthread_free(events_thread); qthread_kill(events_thread); events_thread = NULL; } } static bool idaapi init_debugger(const char *hostname, int portnum, const char *password) { set_processor_type(ph.psnames[0], SETPROC_LOADER); // reset proc to "M68000" return true; } static bool idaapi term_debugger(void) { dbg_req->is_ida = 0; close_shared_mem(&dbg_req); return true; } static int idaapi process_get_info(procinfo_vec_t *procs) { return 0; } static int idaapi check_debugger_events(void *ud) { while (dbg_req->dbg_active || dbg_req->dbg_events_count) { dbg_req->is_ida = 1; int event_index = recv_dbg_event(dbg_req, 0); if (event_index == -1) { qsleep(10); continue; } debugger_event_t *dbg_event = &dbg_req->dbg_events[event_index]; debug_event_t ev; switch (dbg_event->type) { case DBG_EVT_STARTED: ev.eid = PROCESS_START; ev.pid = 1; ev.tid = 1; ev.ea = BADADDR; ev.handled = true; ev.modinfo.name[0] = 'E'; ev.modinfo.name[1] = 'M'; ev.modinfo.name[2] = 'U'; ev.modinfo.name[3] = 'L'; ev.modinfo.name[4] = '\0'; ev.modinfo.base = 0; ev.modinfo.size = 0; ev.modinfo.rebase_to = BADADDR; g_events.enqueue(ev, IN_FRONT); break; case DBG_EVT_PAUSED: ev.pid = 1; ev.tid = 1; ev.ea = dbg_event->pc; ev.handled = true; ev.eid = PROCESS_SUSPEND; g_events.enqueue(ev, IN_BACK); break; case DBG_EVT_BREAK: ev.pid = 1; ev.tid = 1; ev.ea = dbg_event->pc; ev.handled = true; ev.eid = BREAKPOINT; ev.bpt.hea = ev.bpt.kea = ev.ea; g_events.enqueue(ev, IN_BACK); break; case DBG_EVT_STEP: ev.pid = 1; ev.tid = 1; ev.ea = dbg_event->pc; ev.handled = true; ev.eid = STEP; g_events.enqueue(ev, IN_BACK); break; case DBG_EVT_STOPPED: ev.eid = PROCESS_EXIT; ev.pid = 1; ev.handled = true; ev.exit_code = 0; g_events.enqueue(ev, IN_BACK); break; default: break; } dbg_event->type = DBG_EVT_NO_EVENT; qsleep(10); } return 0; } static int idaapi start_process(const char *path, const char *args, const char *startdir, int dbg_proc_flags, const char *input_path, uint32 input_file_crc32) { g_events.clear(); dbg_req = open_shared_mem(); if (!dbg_req) { show_wait_box("HIDECANCEL\nWaiting for connection to plugin..."); while (!dbg_req) { dbg_req = open_shared_mem(); } hide_wait_box(); } events_thread = qthread_create(check_debugger_events, NULL); send_dbg_request(dbg_req, REQ_ATTACH); return 1; } static void idaapi rebase_if_required_to(ea_t new_base) { } static int idaapi prepare_to_pause_process(void) { pause_execution(); return 1; } static int idaapi emul_exit_process(void) { stop_debugging(); finish_execution(); return 1; } static gdecode_t idaapi get_debug_event(debug_event_t *event, int timeout_ms) { while (true) { // are there any pending events? if (g_events.retrieve(event)) { return g_events.empty() ? GDE_ONE_EVENT : GDE_MANY_EVENTS; } if (g_events.empty()) break; } return GDE_NO_EVENT; } static int idaapi continue_after_event(const debug_event_t *event) { dbg_notification_t req = get_running_notification(); switch (event->eid) { case STEP: case BREAKPOINT: case PROCESS_SUSPEND: if (req == dbg_null || req == dbg_run_to) continue_execution(); break; } return 1; } static void idaapi stopped_at_debug_event(bool dlls_added) { } static int idaapi thread_suspend(thid_t tid) // Suspend a running thread { return 0; } static int idaapi thread_continue(thid_t tid) // Resume a suspended thread { return 0; } static int idaapi set_step_mode(thid_t tid, resume_mode_t resmod) // Run one instruction in the thread { switch (resmod) { case RESMOD_INTO: ///< step into call (the most typical single stepping) send_dbg_request(dbg_req, REQ_STEP_INTO); break; case RESMOD_OVER: ///< step over call send_dbg_request(dbg_req, REQ_STEP_OVER); break; } return 1; } static int idaapi read_registers(thid_t tid, int clsmask, regval_t *values) { if (!dbg_req) return 0; if (clsmask & RC_GENERAL) { dbg_req->regs_data.type = REG_TYPE_M68K; send_dbg_request(dbg_req, REQ_GET_REGS); // TODO: Set register values for IDA } // TODO: Implement other registers reading return 1; } static void set_reg(register_type_t type, int reg_index, unsigned int value) { dbg_req->regs_data.type = type; dbg_req->regs_data.any_reg.index = reg_index; dbg_req->regs_data.any_reg.val = value; send_dbg_request(dbg_req, REQ_SET_REG); } static int idaapi write_register(thid_t tid, int regidx, const regval_t *value) { // TODO: Implement set registers for emulator return 1; } static int idaapi get_memory_info(meminfo_vec_t &areas) { memory_info_t info; // Don't remove this loop for (int i = 0; i < get_segm_qty(); ++i) { segment_t *segm = getnseg(i); info.start_ea = segm->start_ea; info.end_ea = segm->end_ea; qstring buf; get_segm_name(&buf, segm); info.name = buf; get_segm_class(&buf, segm); info.sclass = buf; info.sbase = 0; info.perm = SEGPERM_READ | SEGPERM_WRITE; info.bitness = 1; areas.push_back(info); } // Don't remove this loop return 1; } static ssize_t idaapi read_memory(ea_t ea, void *buffer, size_t size) { // TODO: Implement memory regions reading return size; } static ssize_t idaapi write_memory(ea_t ea, const void *buffer, size_t size) { return 0; } static int idaapi is_ok_bpt(bpttype_t type, ea_t ea, int len) { switch (type) { //case BPT_SOFT: case BPT_EXEC: case BPT_READ: // there is no such constant in sdk61 case BPT_WRITE: case BPT_RDWR: return BPT_OK; } return BPT_BAD_TYPE; } static int idaapi update_bpts(update_bpt_info_t *bpts, int nadd, int ndel) { for (int i = 0; i < nadd; ++i) { ea_t start = bpts[i].ea; ea_t end = bpts[i].ea + bpts[i].size - 1; bpt_data_t *bpt_data = &dbg_req->bpt_data; switch (bpts[i].type) { case BPT_EXEC: bpt_data->type = BPT_M68K_E; break; case BPT_READ: bpt_data->type = BPT_M68K_R; break; case BPT_WRITE: bpt_data->type = BPT_M68K_W; break; case BPT_RDWR: bpt_data->type = BPT_M68K_RW; break; } bpt_data->address = start; bpt_data->width = bpts[i].size; send_dbg_request(dbg_req, REQ_ADD_BREAK); bpts[i].code = BPT_OK; } for (int i = 0; i < ndel; ++i) { ea_t start = bpts[nadd + i].ea; ea_t end = bpts[nadd + i].ea + bpts[nadd + i].size - 1; bpt_data_t *bpt_data = &dbg_req->bpt_data; switch (bpts[nadd + i].type) { case BPT_EXEC: bpt_data->type = BPT_M68K_E; break; case BPT_READ: bpt_data->type = BPT_M68K_R; break; case BPT_WRITE: bpt_data->type = BPT_M68K_W; break; case BPT_RDWR: bpt_data->type = BPT_M68K_RW; break; } bpt_data->address = start; send_dbg_request(dbg_req, REQ_DEL_BREAK); bpts[nadd + i].code = BPT_OK; } return (ndel + nadd); } //-------------------------------------------------------------------------- // // DEBUGGER DESCRIPTION BLOCK // //-------------------------------------------------------------------------- debugger_t debugger = { IDD_INTERFACE_VERSION, "DBGNAME", 0x8000 + 1, "m68k", DBG_FLAG_NOHOST | DBG_FLAG_CAN_CONT_BPT | DBG_FLAG_FAKE_ATTACH | DBG_FLAG_SAFE | DBG_FLAG_NOPASSWORD | DBG_FLAG_NOSTARTDIR | DBG_FLAG_CONNSTRING | DBG_FLAG_ANYSIZE_HWBPT | DBG_FLAG_DEBTHREAD, register_classes, RC_GENERAL, registers, qnumber(registers), 0x1000, NULL, NULL, 0, DBG_RESMOD_STEP_INTO | DBG_RESMOD_STEP_OVER, init_debugger, term_debugger, process_get_info, start_process, NULL, NULL, rebase_if_required_to, prepare_to_pause_process, emul_exit_process, get_debug_event, continue_after_event, NULL, stopped_at_debug_event, thread_suspend, thread_continue, set_step_mode, read_registers, write_register, NULL, get_memory_info, read_memory, write_memory, is_ok_bpt, update_bpts, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, };
次に、別のファイルを作成し、 ida_plugin.cpp
というida_plugin.cpp
を付けて、次のコードを貼り付けます。
ソースパッケージをダウンロードida_plugin.cpp #include <ida.hpp> #include <dbg.hpp> #include <idd.hpp> #include <loader.hpp> #include <idp.hpp> #include <offset.hpp> #include <kernwin.hpp> #include "ida_plugin.h" #include "ida_debmod.h" extern debugger_t debugger; static bool plugin_inited; static bool my_dbg; static int idaapi idp_to_dbg_reg(int idp_reg) { int reg_idx = idp_reg; if (idp_reg >= 0 && idp_reg <= 7) reg_idx = 0 + idp_reg; else if (idp_reg >= 8 && idp_reg <= 39) reg_idx = 8 + (idp_reg % 8); else if (idp_reg == 91) reg_idx = 16; else if (idp_reg == 92 || idp_reg == 93) reg_idx = 17; else if (idp_reg == 94) reg_idx = 15; else { char buf[MAXSTR]; ::qsnprintf(buf, MAXSTR, "reg: %d\n", idp_reg); warning("SEND THIS MESSAGE TO you@mail.com:\n%s\n", buf); return 0; } return reg_idx; } #ifdef _DEBUG static const char* const optype_names[] = { "o_void", "o_reg", "o_mem", "o_phrase", "o_displ", "o_imm", "o_far", "o_near", "o_idpspec0", "o_idpspec1", "o_idpspec2", "o_idpspec3", "o_idpspec4", "o_idpspec5", }; static const char* const dtyp_names[] = { "dt_byte", "dt_word", "dt_dword", "dt_float", "dt_double", "dt_tbyte", "dt_packreal", "dt_qword", "dt_byte16", "dt_code", "dt_void", "dt_fword", "dt_bitfild", "dt_string", "dt_unicode", "dt_3byte", "dt_ldbl", "dt_byte32", "dt_byte64", }; static void print_insn(insn_t *insn) { if (my_dbg) { msg("cs=%x, ", insn->cs); msg("ip=%x, ", insn->ip); msg("ea=%x, ", insn->ea); msg("itype=%x, ", insn->itype); msg("size=%x, ", insn->size); msg("auxpref=%x, ", insn->auxpref); msg("segpref=%x, ", insn->segpref); msg("insnpref=%x, ", insn->insnpref); msg("insnpref=%x, ", insn->insnpref); msg("flags["); if (insn->flags & INSN_MACRO) msg("INSN_MACRO|"); if (insn->flags & INSN_MODMAC) msg("OF_OUTER_DISP"); msg("]\n"); } } static void print_op(ea_t ea, op_t *op) { if (my_dbg) { msg("type[%s], ", optype_names[op->type]); msg("flags["); if (op->flags & OF_NO_BASE_DISP) msg("OF_NO_BASE_DISP|"); if (op->flags & OF_OUTER_DISP) msg("OF_OUTER_DISP|"); if (op->flags & PACK_FORM_DEF) msg("PACK_FORM_DEF|"); if (op->flags & OF_NUMBER) msg("OF_NUMBER|"); if (op->flags & OF_SHOW) msg("OF_SHOW"); msg("], "); msg("dtyp[%s], ", dtyp_names[op->dtype]); if (op->type == o_reg) msg("reg=%x, ", op->reg); else if (op->type == o_displ || op->type == o_phrase) msg("phrase=%x, ", op->phrase); else msg("reg_phrase=%x, ", op->phrase); msg("addr=%x, ", op->addr); msg("value=%x, ", op->value); msg("specval=%x, ", op->specval); msg("specflag1=%x, ", op->specflag1); msg("specflag2=%x, ", op->specflag2); msg("specflag3=%x, ", op->specflag3); msg("specflag4=%x, ", op->specflag4); msg("refinfo["); opinfo_t buf; if (get_opinfo(&buf, ea, op->n, op->flags)) { msg("target=%x, ", buf.ri.target); msg("base=%x, ", buf.ri.base); msg("tdelta=%x, ", buf.ri.tdelta); msg("flags["); if (buf.ri.flags & REFINFO_TYPE) msg("REFINFO_TYPE|"); if (buf.ri.flags & REFINFO_RVAOFF) msg("REFINFO_RVAOFF|"); if (buf.ri.flags & REFINFO_PASTEND) msg("REFINFO_PASTEND|"); if (buf.ri.flags & REFINFO_CUSTOM) msg("REFINFO_CUSTOM|"); if (buf.ri.flags & REFINFO_NOBASE) msg("REFINFO_NOBASE|"); if (buf.ri.flags & REFINFO_SUBTRACT) msg("REFINFO_SUBTRACT|"); if (buf.ri.flags & REFINFO_SIGNEDOP) msg("REFINFO_SIGNEDOP"); msg("]"); } msg("]\n"); } } #endif typedef const regval_t &(idaapi *getreg_func_t)(const char *name, const regval_t *regvalues); static ssize_t idaapi hook_idp(void *user_data, int notification_code, va_list va) { switch (notification_code) { case processor_t::ev_get_idd_opinfo: { idd_opinfo_t * opinf = va_arg(va, idd_opinfo_t *); ea_t ea = va_arg(va, ea_t); int n = va_arg(va, int); int thread_id = va_arg(va, int); getreg_func_t getreg = va_arg(va, getreg_func_t); const regval_t *regvalues = va_arg(va, const regval_t *); opinf->ea = BADADDR; opinf->debregidx = 0; opinf->modified = false; opinf->value.ival = 0; opinf->value_size = 4; insn_t out; if (decode_insn(&out, ea)) { op_t op = out.ops[n]; #ifdef _DEBUG print_insn(&out); #endif int size = 0; switch (op.dtype) { case dt_byte: size = 1; break; case dt_word: size = 2; break; default: size = 4; break; } opinf->value_size = size; switch (op.type) { case o_mem: case o_near: case o_imm: { flags_t flags; switch (n) { case 0: flags = get_optype_flags0(get_flags(ea)); break; case 1: flags = get_optype_flags1(get_flags(ea)); break; default: flags = 0; break; } switch (op.type) { case o_mem: case o_near: opinf->ea = op.addr; break; case o_imm: opinf->ea = op.value; break; } opinfo_t info; if (get_opinfo(&info, ea, n, flags) != NULL) { opinf->ea += info.ri.base; } } break; case o_phrase: case o_reg: { int reg_idx = idp_to_dbg_reg(op.reg); regval_t reg = getreg(dbg->registers(reg_idx).name, regvalues); if (op.phrase >= 0x10 && op.phrase <= 0x1F || // (A0)..(A7), (A0)+..(A7)+ op.phrase >= 0x20 && op.phrase <= 0x27) // -(A0)..-(A7) { if (op.phrase >= 0x20 && op.phrase <= 0x27) reg.ival -= size; opinf->ea = (ea_t)reg.ival; switch (size) { case 1: { uint8_t b = 0; dbg->read_memory((ea_t)reg.ival, &b, 1); opinf->value.ival = b; } break; case 2: { uint16_t w = 0; dbg->read_memory((ea_t)reg.ival, &w, 2); w = swap16(w); opinf->value.ival = w; } break; default: { uint32_t l = 0; dbg->read_memory((ea_t)reg.ival, &l, 4); l = swap32(l); opinf->value.ival = l; } break; } } else opinf->value = reg; opinf->debregidx = reg_idx; } break; case o_displ: { regval_t main_reg, add_reg; int main_reg_idx = idp_to_dbg_reg(op.reg); int add_reg_idx = idp_to_dbg_reg(op.specflag1 & 0xF); main_reg.ival = 0; add_reg.ival = 0; if (op.specflag2 & 0x10) { add_reg = getreg(dbg->registers(add_reg_idx).name, regvalues); if (op.specflag1 & 0x10) { add_reg.ival &= 0xFFFF; add_reg.ival = (uint64)((int16_t)add_reg.ival); } } if (main_reg_idx != 16) main_reg = getreg(dbg->registers(main_reg_idx).name, regvalues); ea_t addr = (ea_t)main_reg.ival + op.addr + (ea_t)add_reg.ival; opinf->ea = addr; switch (size) { case 1: { uint8_t b = 0; dbg->read_memory(addr, &b, 1); opinf->value.ival = b; } break; case 2: { uint16_t w = 0; dbg->read_memory(addr, &w, 2); w = swap16(w); opinf->value.ival = w; } break; default: { uint32_t l = 0; dbg->read_memory(addr, &l, 4); l = swap32(l); opinf->value.ival = l; } break; } } break; } opinf->ea &= 0xFFFFFF; return 1; } } break; default: { #ifdef _DEBUG if (my_dbg) { msg("msg = %d\n", notification_code); } #endif } break; } return 0; } //-------------------------------------------------------------------------- static void print_version() { static const char format[] = NAME " debugger plugin v%s;\nAuthor: Dr. MefistO."; info(format, VERSION); msg(format, VERSION); } //-------------------------------------------------------------------------- // Initialize debugger plugin static int idaapi init(void) { if (ph.id == PLFM_68K) { dbg = &debugger; plugin_inited = true; my_dbg = false; hook_to_notification_point(HT_IDP, hook_idp, NULL); print_version(); return PLUGIN_KEEP; } return PLUGIN_SKIP; } //-------------------------------------------------------------------------- // Terminate debugger plugin static void idaapi term(void) { if (plugin_inited) { unhook_from_notification_point(HT_IDP, hook_idp); plugin_inited = false; } } //-------------------------------------------------------------------------- // The plugin method - usually is not used for debugger plugins static bool idaapi run(size_t arg) { return false; } //-------------------------------------------------------------------------- char comment[] = NAME " debugger plugin by Dr. MefistO."; char help[] = NAME " debugger plugin by Dr. MefistO.\n" "\n" "This module lets you debug Genesis roms in IDA.\n"; //-------------------------------------------------------------------------- // // PLUGIN DESCRIPTION BLOCK // //-------------------------------------------------------------------------- plugin_t PLUGIN = { IDP_INTERFACE_VERSION, PLUGIN_PROC | PLUGIN_DBG | PLUGIN_MOD, // plugin flags init, // initialize term, // terminate. this pointer may be NULL. run, // invoke plugin comment, // long comment about the plugin // it could appear in the status line // or as a hint help, // multiline help about the plugin NAME " debugger plugin", // the preferred short name of the plugin "" // the preferred hotkey to run the plugin };
さあ、理解して、同時にコードを書きましょう。
デバッガーの実装
dbg_req
変数は、デバッガーのカーネルと共有されるメモリへのポインターを格納します。 そこにリクエストを送信し、そこから回答を受け取ります。
関数pause_execution()
、 continue_execution()
、およびstop_debugging()
は、デバッグプロセスを制御するために必要です。
eventlist_t g_events
は、一部のアクション(たとえば、エミュレーションの開始/停止、トリガーされたブレーク)に応じてIDA
がeventlist_t g_events
するデバッガーイベントのリストです。
さて、 events_thread
はこのリストを補充し、共有メモリ内のデバッガーイベントの存在を監視し、対応するIDA
イベントに変換します。
finish_execution()
関数を作成します。これは、イベントをデバッグするための待機スレッドを単純に終了します。
static void finish_execution() { if (events_thread != NULL) { qthread_join(events_thread); qthread_free(events_thread); qthread_kill(events_thread); events_thread = NULL; } }
だから、私たちはそれを理解しました。 それでは、説明を登録しましょう。
レジスタに関する情報は、次の形式の構造です。
struct register_info_t { const char *name; uint32 flags; register_class_t register_class; op_dtype_t dtype; const char *const *bit_strings; uval_t default_bit_strings_mask; };
name
フィールドは、レジスタのテキスト名です。 また、レジスタの異なるグループでは、同じ名前にすることはできません。 たとえば、2つの異なるプロセッサからPC
レジスタを表示する場合( Sega Mega Drive
にはMotorola 68000
とZ80
2つがあります)、名前を変更する必要があります。
flags
フィールドには、次のフラグの1つ以上を含めることができます。
#define REGISTER_READONLY 0x0001
REGISTER_IP
とREGISTER SP
を組み合わせることはできませんが、 REGISTER_ADDRESS
フラグを使用してフィールドにアドレスが含まれるように指定できます。
register_class
は、実装したレジスタのグループのマスク番号です。 たとえば、次の3つを追加しました。
#define RC_GENERAL (1 << 0) #define RC_VDP (1 << 1) #define RC_Z80 (1 << 2)
dtype
は、レジスタのサイズを示します。 オプションは次のとおりです。
#define dt_byte 0
実際には、 dt_word
、 dt_dword
のみが必要dt_dword
。
たとえば、ある種のレジスタを個々のビットの形式で表示する場合は、 bit_strings
フィールドbit_strings
必要です。 特に、これはレジスタフラグに使用できます: Negative
、 Overflow
、 Zero
、 Carry
など。 例:
static const char *const SRReg[] = { "C", "V", "Z", "N", "X", NULL, NULL, NULL, "I", "I", "I", NULL, NULL, "S", NULL, "T" };
ビットは上から下(低から高)から始まります。 名前の代わりにビット値を表示しない場合は、 NULL
指定しNULL
。 レジスタで複数のビットが1つのフラグに属している場合、同じ名前を必要な回数だけ示します。
さて、最後のdefault_bit_strings_mask
フィールドは、レジスタビット値を受け取る前に適用されるビットマスクです。
Sega Mega Drive
レジスタリストの実装例を次に示しSega Mega Drive
(M68K、Z80、およびVDPレジスタ、およびいくつかのカスタムレジスタをオンにしました)。
デバッガーのレジスターの説明 register_info_t registers[] = { { "D0", REGISTER_ADDRESS, RC_GENERAL, dt_dword, NULL, 0 }, { "D1", REGISTER_ADDRESS, RC_GENERAL, dt_dword, NULL, 0 }, { "D2", REGISTER_ADDRESS, RC_GENERAL, dt_dword, NULL, 0 }, { "D3", REGISTER_ADDRESS, RC_GENERAL, dt_dword, NULL, 0 }, { "D4", REGISTER_ADDRESS, RC_GENERAL, dt_dword, NULL, 0 }, { "D5", REGISTER_ADDRESS, RC_GENERAL, dt_dword, NULL, 0 }, { "D6", REGISTER_ADDRESS, RC_GENERAL, dt_dword, NULL, 0 }, { "D7", REGISTER_ADDRESS, RC_GENERAL, dt_dword, NULL, 0 }, { "A0", REGISTER_ADDRESS, RC_GENERAL, dt_dword, NULL, 0 }, { "A1", REGISTER_ADDRESS, RC_GENERAL, dt_dword, NULL, 0 }, { "A2", REGISTER_ADDRESS, RC_GENERAL, dt_dword, NULL, 0 }, { "A3", REGISTER_ADDRESS, RC_GENERAL, dt_dword, NULL, 0 }, { "A4", REGISTER_ADDRESS, RC_GENERAL, dt_dword, NULL, 0 }, { "A5", REGISTER_ADDRESS, RC_GENERAL, dt_dword, NULL, 0 }, { "A6", REGISTER_ADDRESS, RC_GENERAL, dt_dword, NULL, 0 }, { "A7", REGISTER_ADDRESS, RC_GENERAL, dt_dword, NULL, 0 }, { "PC", REGISTER_ADDRESS | REGISTER_IP, RC_GENERAL, dt_dword, NULL, 0 }, { "SR", NULL, RC_GENERAL, dt_word, SRReg, 0xFFFF }, { "SP", REGISTER_ADDRESS | REGISTER_SP, RC_GENERAL, dt_dword, NULL, 0 }, { "USP", REGISTER_ADDRESS, RC_GENERAL, dt_dword, NULL, 0 }, { "ISP", REGISTER_ADDRESS, RC_GENERAL, dt_dword, NULL, 0 }, { "PPC", REGISTER_ADDRESS | REGISTER_READONLY, RC_GENERAL, dt_dword, NULL, 0 }, { "IR", NULL, RC_GENERAL, dt_dword, NULL, 0 },
次にregister_classes[]
リストがあり、ここでレジスタグループのテキスト名を指定する必要があります。 デバッグ中に別のウィンドウで開くことができます。

これが私の実装です(最後の要素はNULL
なければなりません):
static const char *register_classes[] = { "General Registers", "VDP Registers", "Z80 Registers", NULL };
IDAが必要とするコールバック
init_debugger()
static bool idaapi init_debugger(const char *hostname, int portnum, const char *password) { set_processor_type(ph.psnames[0], SETPROC_LOADER);
IDA
はMotorolaプロセッサのいくつかのバージョンを実装しているため、リストの最初に強制的にリセットしました。
term_debugger()
static bool idaapi term_debugger(void) { dbg_req->is_ida = 0; close_shared_mem(&dbg_req); return true; }
init_debugger()
事実 : init_debugger()
関数はセッションごとのエミュレーションの最初の開始時に1回term_debugger()
、 term_debugger()
関数はデバッグプロセスが完了するたびにterm_debugger()
れます。 したがって、ここで開いている共有メモリを閉じます。
成功した場合、両方の関数はtrue
を返す必要がありtrue
。
process_get_info()
static int idaapi process_get_info(procinfo_vec_t *procs) { return 0; }
デバッグ中に複数のプロセスを使用している場合、このコールバックを実装する必要があります。このコールバックは、各プロセスのIDA
情報、つまりPID
と名前を報告します。
この関数は必要ないので、 0
を返し0
。
check_debugger_events()-コールバックではないが、非常に重要
実際には、イベントのデバッグを待機しているスレッドです。 ここで、より詳細に伝える必要があります。
デバッグを開始するとき、 IDA
を受け取ることを期待する最初のイベントはPROCESS_START
なければなりません。 たとえば、エミュレーションの一時停止に関するメッセージなど、最初のものが来ると、 IDA
は単純に落ちます。
その後、すでに他のメッセージを受信できます。 使用される主なものは次のとおりです。
PROCESS_SUSPEND
エミュレーションが一時停止され、デバッガーユーザーはレジスタの値を監視または変更したり、メモリを読み取ったり変更したりできるようになりました。BREAKPOINT
- IDA
が伝えたいときに受け取ることができるこのメッセージ-ブレーカーはハードウェアとソフトウェアの両方で機能しました。 なぜ正確にできますか? デバッグを停止するには、 PROCESS_SUSPEND
受け入れるだけで、他のすべてはIDA
が報告できる詳細を停止するだけです。STEP
- Step Into
またはStep Over
目標に到達したことをIDA
に通知するためにこのメッセージを取得できますが、再び、 PROCESS_SUSPEND
メッセージで降りることができますPROCESS_EXIT
デバッグされたプロセスまたはデバッグプロセスを停止した後、 IDA
が受け入れる必要があります。 デバッガーインターフェースで[ Stop
]ボタンをクリックした場合、 IDA
はこのメッセージが到着するまで待機します。 または世界の終わりが来る または、手動で彼女のプロセスを強制終了しません。
イベントオブジェクト自体の構造は次のとおりです。
struct debug_event_t { event_id_t eid;
eid
は、上記で説明した同じタイプのイベントです
pid
、 tid
実際には、イベントが発生したプロセスIDとスレッドID
ea
イベントが発生したアドレス
handled
-このパラメーターの実際の目的は私にはIDA SDK
ませんが、 IDA SDK
テキストから判断すると、例外がシステムによって処理されたかどうか(およびその理由)を示すために使用されます。 私はtrue
設定しました
次は、イベントのタイプに応じて入力する必要があるフィールドです。
PROCESS_START
は、エミュレータプロセスの名前( ImageBase
できる)、 ImageBase
を指定しますImageBase
、ラム、サイズ、およびIDB
作成時に指定したものと異なる場合は新しいImageBase
を読み込みます。 プロセスの開始時にこれが不明な場合は、単にゼロまたはBADADDR
示します。
case DBG_EVT_STARTED: ev.eid = PROCESS_START; ev.pid = 1; ev.tid = 1; ev.ea = BADADDR; ev.handled = true; ev.modinfo.name[0] = 'G'; ev.modinfo.name[1] = 'P'; ev.modinfo.name[2] = 'G'; ev.modinfo.name[3] = 'X'; ev.modinfo.name[4] = '\0'; ev.modinfo.base = 0; ev.modinfo.size = 0; ev.modinfo.rebase_to = BADADDR; g_events.enqueue(ev, IN_FRONT); break;
BREAKPOINT
、 bpt
フィールドで、 bpt
たクラックのhardware
およびkernel
アドレスを指定します。 IDA
が知らないブレークポイントアドレスでそのようなイベントが到着した場合、不明なブレークポイントメッセージがログウィンドウに表示されます。
case DBG_EVT_BREAK: ev.pid = 1; ev.tid = 1; ev.ea = dbg_event->pc; ev.handled = true; ev.eid = BREAKPOINT; ev.bpt.hea = ev.bpt.kea = ev.ea; g_events.enqueue(ev, IN_BACK); break;
PROCESS_EXIT
exit_code
.
start_process()
, , , .
, ( ), , .
1
.
rebase_if_required_to()
, .. , , :
rebase_if_required_to static void idaapi rebase_if_required_to(ea_t new_base) { ea_t currentbase = new_base; ea_t imagebase = inf.startIP; if (imagebase != currentbase) { adiff_t delta = currentbase - imagebase; int code = rebase_program(delta, MSF_FIXONCE); if (code != MOVE_SEGM_OK) { msg("Failed to rebase program, error code %d\n", code); warning("IDA failed to rebase the program.\n" "Most likely it happened because of the debugger\n" "segments created to reflect the real memory state.\n\n" "Please stop the debugger and rebase the program manually.\n" "For that, please select the whole program and\n" "use Edit, Segments, Rebase program with delta 0x%08a", delta); } } }
prepare_to_pause_process()
Pause
IDA
, .
1
.
get_debug_event()
, IDA
, . (?) . , debug_event_t
*event
, :
GDE_ONE_EVENT
, ,GDE_MANY_EVENTS
, ,GDE_NO_EVENT
,
static gdecode_t idaapi get_debug_event(debug_event_t *event, int timeout_ms) { while (true) {
continue_after_event()
, - IDA
. .
, , , - . :
STEP
IDA
, ..- , ,
Step In
continue_after_event()
, . — STEP
IDA
STEP
, , , , !
, , 3
Step Into
, Step Over
, F9
, .
STEP
, BREAKPOINT
PROCESS_SUSPEND
, F9
, continue_execution()
. F9
Run to
:
dbg_notification_t req = get_running_notification(); if (req == dbg_null || req == dbg_run_to) continue_execution();
stopped_at_debug_event()
, . .
thread_suspend(), thread_continue()
, / , . , , 0
. , .
set_step_mode()
, Step Into
, Step Over
, Step Out
. ( debugger_t
, ).
1
.
static int idaapi set_step_mode(thid_t tid, resume_mode_t resmod) { switch (resmod) { case RESMOD_INTO: send_dbg_request(dbg_req, REQ_STEP_INTO); break; case RESMOD_OVER: send_dbg_request(dbg_req, REQ_STEP_OVER); break; } return 1; }
read_registers()
IDA
STEP
, BREAKPOINT
PROCESS_SUSPEND
(.. , ), , .
:
clsmask
— ? .values
— , . registers[]
.
:
read_registers() static int idaapi read_registers(thid_t tid, int clsmask, regval_t *values) { if (!dbg_req) return 0; if (clsmask & RC_GENERAL) { dbg_req->regs_data.type = REG_TYPE_M68K; send_dbg_request(dbg_req, REQ_GET_REGS); regs_68k_data_t *reg_vals = &dbg_req->regs_data.regs_68k; values[REG_68K_D0].ival = reg_vals->d0; values[REG_68K_D1].ival = reg_vals->d1; values[REG_68K_D2].ival = reg_vals->d2; values[REG_68K_D3].ival = reg_vals->d3; values[REG_68K_D4].ival = reg_vals->d4; values[REG_68K_D5].ival = reg_vals->d5; values[REG_68K_D6].ival = reg_vals->d6; values[REG_68K_D7].ival = reg_vals->d7; values[REG_68K_A0].ival = reg_vals->a0; values[REG_68K_A1].ival = reg_vals->a1; values[REG_68K_A2].ival = reg_vals->a2; values[REG_68K_A3].ival = reg_vals->a3; values[REG_68K_A4].ival = reg_vals->a4; values[REG_68K_A5].ival = reg_vals->a5; values[REG_68K_A6].ival = reg_vals->a6; values[REG_68K_A7].ival = reg_vals->a7; values[REG_68K_PC].ival = reg_vals->pc & 0xFFFFFF; values[REG_68K_SR].ival = reg_vals->sr; values[REG_68K_SP].ival = reg_vals->sp & 0xFFFFFF; values[REG_68K_PPC].ival = reg_vals->ppc & 0xFFFFFF; values[REG_68K_IR].ival = reg_vals->ir; } if (clsmask & RC_VDP) { dbg_req->regs_data.type = REG_TYPE_VDP; send_dbg_request(dbg_req, REQ_GET_REGS); vdp_regs_t *vdp_regs = &dbg_req->regs_data.vdp_regs; for (int i = 0; i < sizeof(vdp_regs->regs_vdp) / sizeof(vdp_regs->regs_vdp[0]); ++i) { values[REG_VDP_00 + i].ival = vdp_regs->regs_vdp[i]; } values[REG_VDP_DMA_LEN].ival = vdp_regs->dma_len; values[REG_VDP_DMA_SRC].ival = vdp_regs->dma_src; values[REG_VDP_DMA_DST].ival = vdp_regs->dma_dst; } if (clsmask & RC_Z80) { dbg_req->regs_data.type = REG_TYPE_Z80; send_dbg_request(dbg_req, REQ_GET_REGS); regs_z80_data_t *z80_regs = &dbg_req->regs_data.regs_z80; for (int i = 0; i < (REG_Z80_I - REG_Z80_PC + 1); ++i) { if (i >= 0 && i <= 12)
1
.
write_register()
— . .
:
write_register() static int idaapi write_register(thid_t tid, int regidx, const regval_t *value) { if (regidx >= REG_68K_D0 && regidx <= REG_68K_D7) { set_reg(REG_TYPE_M68K, regidx - REG_68K_D0, (uint32)value->ival); } else if (regidx >= REG_68K_A0 && regidx <= REG_68K_A7) { set_reg(REG_TYPE_M68K, regidx - REG_68K_A0, (uint32)value->ival); } else if (regidx == REG_68K_PC) { set_reg(REG_TYPE_M68K, REG_68K_PC, (uint32)value->ival & 0xFFFFFF); } else if (regidx == REG_68K_SR) { set_reg(REG_TYPE_M68K, REG_68K_SR, (uint16)value->ival); } else if (regidx == REG_68K_SP) { set_reg(REG_TYPE_M68K, REG_68K_SP, (uint32)value->ival & 0xFFFFFF); } else if (regidx == REG_68K_USP) { set_reg(REG_TYPE_M68K, REG_68K_USP, (uint32)value->ival & 0xFFFFFF); } else if (regidx == REG_68K_ISP) { set_reg(REG_TYPE_M68K, REG_68K_ISP, (uint32)value->ival & 0xFFFFFF); } else if (regidx >= REG_VDP_00 && regidx <= REG_VDP_1F) { set_reg(REG_TYPE_VDP, regidx - REG_VDP_00, value->ival & 0xFF); } else if (regidx >= REG_Z80_PC && regidx <= REG_Z80_I) { set_reg(REG_TYPE_Z80, regidx - REG_Z80_PC, value->ival); } return 1; }
1
.
get_memory_info()
, . : IDB
- , . .
- , , . :
info.name = "DBG_VDP_VRAM"; info.start_ea = 0xD00000; info.end_ea = info.start_ea + 0x10000; info.bitness = 1; areas.push_back(info); info.name = "DBG_VDP_CRAM"; info.start_ea = info.end_ea; info.end_ea = info.start_ea + 0x10000; info.bitness = 1; areas.push_back(info); info.name = "DBG_VDP_VSRAM"; info.start_ea = info.end_ea; info.end_ea = info.start_ea + 0x10000; info.bitness = 1; areas.push_back(info);
1
.
read_memory()
. , , XTRN
, IDA
.
ea
, , size
, , buffer
, .
read_memory() static ssize_t idaapi read_memory(ea_t ea, void *buffer, size_t size) { if ((ea >= 0xA00000 && ea < 0xA0FFFF)) { dbg_req->mem_data.address = ea; dbg_req->mem_data.size = size; send_dbg_request(dbg_req, REQ_READ_Z80); memcpy(buffer, &dbg_req->mem_data.z80_ram[ea & 0x1FFF], size);
write_memory()
, - : , RAM, , .
, 0
. 1
.
, , , buffer
— , .
is_ok_bpt()
len
type
ea
.
, BPT_OK
, — BPT_BAD_TYPE
.
update_bpts()
, IDA
. , , . F9
( Continue ).
:
bpts
, , : , , , nadd
ndel
, .- , ,
bpts[i].code
BPT_OK
.
+ . nadd + ndel
.
update_bpts() static int idaapi update_bpts(update_bpt_info_t *bpts, int nadd, int ndel) { for (int i = 0; i < nadd; ++i) { ea_t start = bpts[i].ea; ea_t end = bpts[i].ea + bpts[i].size - 1; bpt_data_t *bpt_data = &dbg_req->bpt_data; switch (bpts[i].type) { case BPT_EXEC: bpt_data->type = BPT_M68K_E; break; case BPT_READ: bpt_data->type = BPT_M68K_R; break; case BPT_WRITE: bpt_data->type = BPT_M68K_W; break; case BPT_RDWR: bpt_data->type = BPT_M68K_RW; break; } bpt_data->address = start; bpt_data->width = bpts[i].size; send_dbg_request(dbg_req, REQ_ADD_BREAK); bpts[i].code = BPT_OK; } for (int i = 0; i < ndel; ++i) { ea_t start = bpts[nadd + i].ea; ea_t end = bpts[nadd + i].ea + bpts[nadd + i].size - 1; bpt_data_t *bpt_data = &dbg_req->bpt_data; switch (bpts[nadd + i].type) { case BPT_EXEC: bpt_data->type = BPT_M68K_E; break; case BPT_READ: bpt_data->type = BPT_M68K_R; break; case BPT_WRITE: bpt_data->type = BPT_M68K_W; break; case BPT_RDWR: bpt_data->type = BPT_M68K_RW; break; } bpt_data->address = start; send_dbg_request(dbg_req, REQ_DEL_BREAK); bpts[nadd + i].code = BPT_OK; } return (ndel + nadd); }
debugger_t
, (, , IDA
, , , , ).
:
debugger_t struct debugger_t { int version;
, , , .
はい、はい。 ida_plugin.cpp
. , .
idp_to_dbg_reg()
( ) .
static int idaapi idp_to_dbg_reg(int idp_reg) { int reg_idx = idp_reg; if (idp_reg >= 0 && idp_reg <= 7) reg_idx = 0 + idp_reg; else if (idp_reg >= 8 && idp_reg <= 39) reg_idx = 8 + (idp_reg % 8); else if (idp_reg == 91) reg_idx = 16; else if (idp_reg == 92 || idp_reg == 93) reg_idx = 17; else if (idp_reg == 94) reg_idx = 15; else { char buf[MAXSTR]; ::qsnprintf(buf, MAXSTR, "reg: %d\n", idp_reg); warning("SEND THIS MESSAGE TO you@mail.com:\n%s\n", buf); return 0; } return reg_idx; }
hook_idp()
hook_to_notification_point()
:
hook_to_notification_point(HT_IDP, hook_idp, NULL);
unhook_from_notification_point()
:
unhook_from_notification_point(HT_IDP, hook_idp);
: debugger hints . "":

, , - , . , , IDA
, . .
notification_code
ev_get_idd_opinfo
. , , , , , .
case processor_t::ev_get_idd_opinfo: { idd_opinfo_t * opinf = va_arg(va, idd_opinfo_t *); ea_t ea = va_arg(va, ea_t); int n = va_arg(va, int); int thread_id = va_arg(va, int); getreg_func_t getreg = va_arg(va, getreg_func_t); const regval_t *regvalues = va_arg(va, const regval_t *); opinf->ea = BADADDR; opinf->debregidx = 0; opinf->modified = false; opinf->value.ival = 0; opinf->value_size = 4; insn_t out; if (decode_insn(&out, ea)) { op_t op = out.ops[n]; #ifdef _DEBUG print_insn(&out); #endif int size = 0; switch (op.dtype) { case dt_byte: size = 1; break; case dt_word: size = 2; break; default: size = 4; break; } opinf->value_size = size; switch (op.type) { case o_mem: case o_near: case o_imm: { flags_t flags; switch (n) { case 0: flags = get_optype_flags0(get_flags(ea)); break; case 1: flags = get_optype_flags1(get_flags(ea)); break; default: flags = 0; break; } switch (op.type) { case o_mem: case o_near: opinf->ea = op.addr; break; case o_imm: opinf->ea = op.value; break; } opinfo_t info; if (get_opinfo(&info, ea, n, flags) != NULL) { opinf->ea += info.ri.base; } } break; case o_phrase: case o_reg: { int reg_idx = idp_to_dbg_reg(op.reg); regval_t reg = getreg(dbg->registers(reg_idx).name, regvalues); if (op.phrase >= 0x10 && op.phrase <= 0x1F || // (A0)..(A7), (A0)+..(A7)+ op.phrase >= 0x20 && op.phrase <= 0x27) // -(A0)..-(A7) { if (op.phrase >= 0x20 && op.phrase <= 0x27) reg.ival -= size; opinf->ea = (ea_t)reg.ival; switch (size) { case 1: { uint8_t b = 0; dbg->read_memory((ea_t)reg.ival, &b, 1); opinf->value.ival = b; } break; case 2: { uint16_t w = 0; dbg->read_memory((ea_t)reg.ival, &w, 2); w = swap16(w); opinf->value.ival = w; } break; default: { uint32_t l = 0; dbg->read_memory((ea_t)reg.ival, &l, 4); l = swap32(l); opinf->value.ival = l; } break; } } else opinf->value = reg; opinf->debregidx = reg_idx; } break; case o_displ: { regval_t main_reg, add_reg; int main_reg_idx = idp_to_dbg_reg(op.reg); int add_reg_idx = idp_to_dbg_reg(op.specflag1 & 0xF); main_reg.ival = 0; add_reg.ival = 0; if (op.specflag2 & 0x10) { add_reg = getreg(dbg->registers(add_reg_idx).name, regvalues); if (op.specflag1 & 0x10) { add_reg.ival &= 0xFFFF; add_reg.ival = (uint64)((int16_t)add_reg.ival); } } if (main_reg_idx != 16) main_reg = getreg(dbg->registers(main_reg_idx).name, regvalues); ea_t addr = (ea_t)main_reg.ival + op.addr + (ea_t)add_reg.ival; opinf->ea = addr; switch (size) { case 1: { uint8_t b = 0; dbg->read_memory(addr, &b, 1); opinf->value.ival = b; } break; case 2: { uint16_t w = 0; dbg->read_memory(addr, &w, 2); w = swap16(w); opinf->value.ival = w; } break; default: { uint32_t l = 0; dbg->read_memory(addr, &l, 4); l = swap32(l); opinf->value.ival = l; } break; } } break; } opinf->ea &= 0xFFFFFF; return 1; } } break;
, .
init()
, .
, id
ph
.
PLFM_68K
. dbg
debugger_t
.
HT_IDP
, PLUGIN_KEEP
. , , PLUGIN_SKIP
.
static int idaapi init(void) { if (ph.id == PLFM_68K) { dbg = &debugger; plugin_inited = true; my_dbg = false; hook_to_notification_point(HT_IDP, hook_idp, NULL); print_version(); return PLUGIN_KEEP; } return PLUGIN_SKIP; }
term()
IDB
-. , init()
.
static void idaapi term(void) { if (plugin_inited) { unhook_from_notification_point(HT_IDP, hook_idp); plugin_inited = false; } }
run()
- . false
.
plugin_t
ここではすべてが簡単です。プラグインのバージョン、フラグ、初期化関数、プラグインの完了、およびヒント:
Plugin_t構造 class plugin_t { public: int version;
おそらくそれだけです。1つの記事にはたくさんのコードがありますが、これがおもしろいことを願っています。ご覧のように、IDA
非常に興味深いものの、何かを書くプロセスは完全な苦痛に変わります。
約束どおり、ソースリンク:
GPGXデバッガー
Smd IDAツール