
みなさんこんにちは
前の記事で、 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 . .
, , , - . :
STEPIDA , ..- , ,
Step In continue_after_event() , . — STEPIDA 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ツール