IDA Proのアップグレード。 Sega Mega Driveのデバッガー(パート2)







みなさんこんにちは







前の記事で、 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 ///< the user can't modify the current value of this register #define REGISTER_IP 0x0002 ///< instruction pointer #define REGISTER_SP 0x0004 ///< stack pointer #define REGISTER_FP 0x0008 ///< frame pointer #define REGISTER_ADDRESS 0x0010 ///< may contain an address #define REGISTER_CS 0x0020 ///< code segment #define REGISTER_SS 0x0040 ///< stack segment #define REGISTER_NOLF 0x0080 ///< displays this register without returning to the next line ///< allowing the next register to be displayed to its right (on the same line) #define REGISTER_CUSTFMT 0x0100 ///< register should be displayed using a custom data format. ///< the format name is in bit_strings[0] ///< the corresponding ::regval_t will use ::bytevec_t
      
      





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 ///< 8 bit #define dt_word 1 ///< 16 bit #define dt_dword 2 ///< 32 bit #define dt_float 3 ///< 4 byte #define dt_double 4 ///< 8 byte #define dt_tbyte 5 ///< variable size (\ph{tbyte_size}) #define dt_packreal 6 ///< packed real format for mc68040 #define dt_qword 7 ///< 64 bit #define dt_byte16 8 ///< 128 bit #define dt_code 9 ///< ptr to code (not used?) #define dt_void 10 ///< none #define dt_fword 11 ///< 48 bit #define dt_bitfild 12 ///< bit field (mc680x0) #define dt_string 13 ///< pointer to asciiz string #define dt_unicode 14 ///< pointer to unicode string #define dt_ldbl 15 ///< long double (which may be different from tbyte) #define dt_byte32 16 ///< 256 bit #define dt_byte64 17 ///< 512 bit
      
      





実際には、 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 }, // VDP Registers { "v00", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v01", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v02", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v03", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v04", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v05", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v06", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v07", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v08", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v09", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v0A", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v0B", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v0C", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v0D", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v0E", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v0F", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v10", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v11", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v12", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v13", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v14", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v15", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v16", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v17", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v18", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v19", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v1A", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v1B", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v1C", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v1D", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v1E", NULL, RC_VDP, dt_byte, NULL, 0 }, { "v1F", NULL, RC_VDP, dt_byte, NULL, 0 }, { "DMA_LEN", REGISTER_READONLY, RC_VDP, dt_word, NULL, 0 }, { "DMA_SRC", REGISTER_ADDRESS | REGISTER_READONLY, RC_VDP, dt_dword, NULL, 0 }, { "VDP_DST", REGISTER_ADDRESS | REGISTER_READONLY, RC_VDP, dt_dword, NULL, 0 }, // Z80 regs { "zPC", NULL, RC_Z80, dt_dword, NULL, 0 }, { "zSP", NULL, RC_Z80, dt_dword, NULL, 0 }, { "zAF", NULL, RC_Z80, dt_dword, NULL, 0 }, { "zBC", NULL, RC_Z80, dt_dword, NULL, 0 }, { "zDE", NULL, RC_Z80, dt_dword, NULL, 0 }, { "zHL", NULL, RC_Z80, dt_dword, NULL, 0 }, { "zIX", NULL, RC_Z80, dt_dword, NULL, 0 }, { "zIY", NULL, RC_Z80, dt_dword, NULL, 0 }, { "zWZ", NULL, RC_Z80, dt_dword, NULL, 0 }, { "zAF2", NULL, RC_Z80, dt_dword, NULL, 0 }, { "zBC2", NULL, RC_Z80, dt_dword, NULL, 0 }, { "zDE2", NULL, RC_Z80, dt_dword, NULL, 0 }, { "zHL2", NULL, RC_Z80, dt_dword, NULL, 0 }, { "zR", NULL, RC_Z80, dt_byte, NULL, 0 }, { "zR2", NULL, RC_Z80, dt_byte, NULL, 0 }, { "zIFFI1", NULL, RC_Z80, dt_byte, NULL, 0 }, { "zIFFI2", NULL, RC_Z80, dt_byte, NULL, 0 }, { "zHALT", NULL, RC_Z80, dt_byte, NULL, 0 }, { "zIM", NULL, RC_Z80, dt_byte, NULL, 0 }, { "zI", NULL, RC_Z80, dt_byte, 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); // reset proc to "M68000" return true; }
      
      





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



は単純に落ちます。







その後、すでに他のメッセージを受信できます。 使用される主なものは次のとおりです。









イベントオブジェクト自体の構造は次のとおりです。







 struct debug_event_t { event_id_t eid; ///< Event code (used to decipher 'info' union) pid_t pid; ///< Process where the event occurred thid_t tid; ///< Thread where the event occurred ea_t ea; ///< Address where the event occurred bool handled; ///< Is event handled by the debugger?. ///< (from the system's point of view) ///< Meaningful for ::EXCEPTION events union { module_info_t modinfo; ///< ::PROCESS_START, ::PROCESS_ATTACH, ::LIBRARY_LOAD int exit_code; ///< ::PROCESS_EXIT, ::THREAD_EXIT char info[MAXSTR]; ///< ::LIBRARY_UNLOAD (unloaded library name) ///< ::INFORMATION (will be displayed in the ///< messages window if not empty) e_breakpoint_t bpt; ///< ::BREAKPOINT e_exception_t exc; ///< ::EXCEPTION }; };
      
      





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



, :









 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; }
      
      





continue_after_event()



, - IDA



. .







, , , - . :







  1. STEP



  2. IDA



    , ..
  3. , , Step In



  4. continue_after_event()



    , . — STEP



  5. 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



(.. , ), , .







:









:







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) // PC <-> HL2 { values[REG_Z80_PC + i].ival = ((unsigned int *)&z80_regs->pc)[i]; } else if (i >= 13 && i <= 19) // R <-> I { values[REG_Z80_PC + i].ival = ((unsigned char *)&z80_regs->r)[i - 13]; } } } return 1; }
      
      





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); // Z80 } else if (ea < MAXROMSIZE) { dbg_req->mem_data.address = ea; dbg_req->mem_data.size = size; send_dbg_request(dbg_req, REQ_READ_68K_ROM); memcpy(buffer, &dbg_req->mem_data.m68k_rom[ea], size); } else if ((ea >= 0xFF0000 && ea < 0x1000000)) { dbg_req->mem_data.address = ea; dbg_req->mem_data.size = size; send_dbg_request(dbg_req, REQ_READ_68K_RAM); memcpy(buffer, &dbg_req->mem_data.m68k_ram[ea & 0xFFFF], size); // RAM } return size; }
      
      





write_memory()



, - : , RAM, , .







, 0



. 1



.







, , , buffer



— , .







is_ok_bpt()



len



type



ea



.







, BPT_OK



, — BPT_BAD_TYPE



.







update_bpts()



, IDA



. , , . F9



( Continue ).







:







  1. bpts



    , , : , , , nadd



    ndel



    , .
  2. , , 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; ///< Expected kernel version, ///< should be #IDD_INTERFACE_VERSION const char *name; ///< Short debugger name like win32 or linux int id; ///< one of \ref DEBUGGER_ID_ /// \defgroup DEBUGGER_ID_ Debugger API module id /// Used by debugger_t::id //@{ #define DEBUGGER_ID_X86_IA32_WIN32_USER 0 ///< Userland win32 processes (win32 debugging APIs) #define DEBUGGER_ID_X86_IA32_LINUX_USER 1 ///< Userland linux processes (ptrace()) #define DEBUGGER_ID_ARM_WINCE_ASYNC 2 ///< Windows CE ARM (ActiveSync transport) #define DEBUGGER_ID_X86_IA32_MACOSX_USER 3 ///< Userland MAC OS X processes #define DEBUGGER_ID_ARM_EPOC_USER 4 ///< Symbian OS #define DEBUGGER_ID_ARM_IPHONE_USER 5 ///< iPhone 1.x #define DEBUGGER_ID_X86_IA32_BOCHS 6 ///< BochsDbg.exe 32 #define DEBUGGER_ID_6811_EMULATOR 7 ///< MC6812 emulator (beta) #define DEBUGGER_ID_GDB_USER 8 ///< GDB remote #define DEBUGGER_ID_WINDBG 9 ///< WinDBG using Microsoft Debug engine #define DEBUGGER_ID_X86_DOSBOX_EMULATOR 10 ///< Dosbox MS-DOS emulator #define DEBUGGER_ID_ARM_LINUX_USER 11 ///< Userland arm linux #define DEBUGGER_ID_TRACE_REPLAYER 12 ///< Fake debugger to replay recorded traces #define DEBUGGER_ID_ARM_WINCE_TCPIP 13 ///< Windows CE ARM (TPC/IP transport) #define DEBUGGER_ID_X86_PIN_TRACER 14 ///< PIN Tracer module #define DEBUGGER_ID_DALVIK_USER 15 ///< Dalvik //@} const char *processor; ///< Required processor name. ///< Used for instant debugging to load the correct ///< processor module uint32 flags; ///< \ref DBG_FLAG_ /// \defgroup DBG_FLAG_ Debugger module features /// Used by debugger_t::flags //@{ #define DBG_FLAG_REMOTE 0x00000001 ///< Remote debugger (requires remote host name unless #DBG_FLAG_NOHOST) #define DBG_FLAG_NOHOST 0x00000002 ///< Remote debugger with does not require network params (host/port/pass). ///< (a unique device connected to the machine) #define DBG_FLAG_FAKE_ATTACH 0x00000004 ///< ::PROCESS_ATTACH is a fake event ///< and does not suspend the execution #define DBG_FLAG_HWDATBPT_ONE 0x00000008 ///< Hardware data breakpoints are ///< one byte size by default #define DBG_FLAG_CAN_CONT_BPT 0x00000010 ///< Debugger knows to continue from a bpt. ///< This flag also means that the debugger module ///< hides breakpoints from ida upon read_memory #define DBG_FLAG_NEEDPORT 0x00000020 ///< Remote debugger requires port number (to be used with DBG_FLAG_NOHOST) #define DBG_FLAG_DONT_DISTURB 0x00000040 ///< Debugger can handle only ///< get_debug_event(), ///< prepare_to_pause_process(), ///< exit_process(). ///< when the debugged process is running. ///< The kernel may also call service functions ///< (file I/O, map_address, etc) #define DBG_FLAG_SAFE 0x00000080 ///< The debugger is safe (probably because it just emulates the application ///< without really running it) #define DBG_FLAG_CLEAN_EXIT 0x00000100 ///< IDA must suspend the application and remove ///< all breakpoints before terminating the application. ///< Usually this is not required because the application memory ///< disappears upon termination. #define DBG_FLAG_USE_SREGS 0x00000200 ///< Take segment register values into account (non flat memory) #define DBG_FLAG_NOSTARTDIR 0x00000400 ///< Debugger module doesn't use startup directory #define DBG_FLAG_NOPARAMETERS 0x00000800 ///< Debugger module doesn't use commandline parameters #define DBG_FLAG_NOPASSWORD 0x00001000 ///< Remote debugger doesn't use password #define DBG_FLAG_CONNSTRING 0x00002000 ///< Display "Connection string" instead of "Hostname" and hide the "Port" field #define DBG_FLAG_SMALLBLKS 0x00004000 ///< If set, IDA uses 256-byte blocks for caching memory contents. ///< Otherwise, 1024-byte blocks are used #define DBG_FLAG_MANMEMINFO 0x00008000 ///< If set, manual memory region manipulation commands ///< will be available. Use this bit for debugger modules ///< that can not return memory layout information #define DBG_FLAG_EXITSHOTOK 0x00010000 ///< IDA may take a memory snapshot at ::PROCESS_EXIT event #define DBG_FLAG_VIRTHREADS 0x00020000 ///< Thread IDs may be shuffled after each debug event. ///< (to be used for virtual threads that represent cpus for windbg kmode) #define DBG_FLAG_LOWCNDS 0x00040000 ///< Low level breakpoint conditions are supported. #define DBG_FLAG_DEBTHREAD 0x00080000 ///< Supports creation of a separate thread in ida ///< for the debugger (the debthread). ///< Most debugger functions will be called from debthread (exceptions are marked below) ///< The debugger module may directly call only #THREAD_SAFE functions. ///< To call other functions please use execute_sync(). ///< The debthread significantly increases debugging ///< speed, especially if debug events occur frequently (to be tested) #define DBG_FLAG_DEBUG_DLL 0x00100000 ///< Can debug standalone DLLs. ///< For example, Bochs debugger can debug any snippet of code #define DBG_FLAG_FAKE_MEMORY 0x00200000 ///< get_memory_info()/read_memory()/write_memory() work with the idb. ///< (there is no real process to read from, as for the replayer module) ///< the kernel will not call these functions if this flag is set. ///< however, third party plugins may call them, they must be implemented. #define DBG_FLAG_ANYSIZE_HWBPT 0x00400000 ///< The debugger supports arbitrary size hardware breakpoints. #define DBG_FLAG_TRACER_MODULE 0x00800000 ///< The module is a tracer, not a full featured debugger module #define DBG_FLAG_PREFER_SWBPTS 0x01000000 ///< Prefer to use software breakpoints //@} const char **register_classes; ///< Array of register class names int register_classes_default; ///< Mask of default printed register classes register_info_t *_registers; ///< Array of registers. Use registers() to access it int registers_size; ///< Number of registers int memory_page_size; ///< Size of a memory page const uchar *bpt_bytes; ///< Array of bytes for a breakpoint instruction uchar bpt_size; ///< Size of this array uchar filetype; ///< for miniidbs: use this value ///< for the file type after attaching ///< to a new process ushort resume_modes; ///< \ref DBG_RESMOD_ /// \defgroup DBG_RESMOD_ Resume modes /// Used by debugger_t::resume_modes //@{ #define DBG_RESMOD_STEP_INTO 0x0001 ///< ::RESMOD_INTO is available #define DBG_RESMOD_STEP_OVER 0x0002 ///< ::RESMOD_OVER is available #define DBG_RESMOD_STEP_OUT 0x0004 ///< ::RESMOD_OUT is available #define DBG_RESMOD_STEP_SRCINTO 0x0008 ///< ::RESMOD_SRCINTO is available #define DBG_RESMOD_STEP_SRCOVER 0x0010 ///< ::RESMOD_SRCOVER is available #define DBG_RESMOD_STEP_SRCOUT 0x0020 ///< ::RESMOD_SRCOUT is available #define DBG_RESMOD_STEP_USER 0x0040 ///< ::RESMOD_USER is available #define DBG_RESMOD_STEP_HANDLE 0x0080 ///< ::RESMOD_HANDLE is available //@} #if !defined(_MSC_VER) // this compiler complains :( static const int default_port_number = 23946; #define DEBUGGER_PORT_NUMBER debugger_t::default_port_number #else #define DEBUGGER_PORT_NUMBER 23946 #endif /// Initialize debugger. /// This function is called from the main thread. /// \return success bool (idaapi *init_debugger)(const char *hostname, int portnum, const char *password); /// Terminate debugger. /// This function is called from the main thread. /// \return success bool (idaapi *term_debugger)(void); /// Return information about the running processes. /// This function is called from the main thread. /// \retval 1 ok /// \retval 0 failed /// \retval -1 network error int (idaapi *get_processes)(procinfo_vec_t *procs); /// Start an executable to debug. /// This function is called from debthread. /// \param path path to executable /// \param args arguments to pass to executable /// \param startdir current directory of new process /// \param dbg_proc_flags \ref DBG_PROC_ /// \param input_path path to database input file. /// (not always the same as 'path' - eg if you're analyzing /// a dll and want to launch an executable that loads it) /// \param input_file_crc32 CRC value for 'input_path' /// \retval 1 ok /// \retval 0 failed /// \retval -2 file not found (ask for process options) /// \retval 1 | #CRC32_MISMATCH ok, but the input file crc does not match /// \retval -1 network error 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); /// \defgroup DBG_PROC_ Debug process flags /// Passed as 'dbg_proc_flags' parameter to debugger_t::start_process //@{ #define DBG_PROC_IS_DLL 0x01 ///< database contains a dll (not exe) #define DBG_PROC_IS_GUI 0x02 ///< using gui version of ida #define DBG_PROC_32BIT 0x04 ///< application is 32-bit #define DBG_PROC_64BIT 0x08 ///< application is 64-bit #define DBG_NO_TRACE 0x10 ///< do not trace the application (mac/linux) #define DBG_HIDE_WINDOW 0x20 ///< application should be hidden on startup (windows) //@} #define CRC32_MISMATCH 0x40000000 ///< crc32 mismatch bit (see return values for debugger_t::start_process) /// Attach to an existing running process. /// event_id should be equal to -1 if not attaching to a crashed process. /// This function is called from debthread. /// \param pid process id to attach /// \param event_id event to trigger upon attaching /// \param dbg_proc_flags \ref DBG_PROC_ /// \retval 1 ok /// \retval 0 failed /// \retval -1 network error int (idaapi *attach_process)(pid_t pid, int event_id, int dbg_proc_flags); /// Detach from the debugged process. /// May be called while the process is running or suspended. /// Must detach from the process in any case. /// The kernel will repeatedly call get_debug_event() and until ::PROCESS_DETACH. /// In this mode, all other events will be automatically handled and process will be resumed. /// This function is called from debthread. /// \retval 1 ok /// \retval 0 failed /// \retval -1 network error int (idaapi *detach_process)(void); /// Rebase database if the debugged program has been rebased by the system. /// This function is called from the main thread. void (idaapi *rebase_if_required_to)(ea_t new_base); /// Prepare to pause the process. /// Normally the next get_debug_event() will pause the process /// If the process is sleeping then the pause will not occur /// until the process wakes up. The interface should take care of /// this situation. /// If this function is absent, then it won't be possible to pause the program. /// This function is called from debthread. /// \retval 1 ok /// \retval 0 failed /// \retval -1 network error int (idaapi *prepare_to_pause_process)(void); /// Stop the process. /// May be called while the process is running or suspended. /// Must terminate the process in any case. /// The kernel will repeatedly call get_debug_event() and until ::PROCESS_EXIT. /// In this mode, all other events will be automatically handled and process will be resumed. /// This function is called from debthread. /// \retval 1 ok /// \retval 0 failed /// \retval -1 network error int (idaapi *exit_process)(void); /// Get a pending debug event and suspend the process. /// This function will be called regularly by IDA. /// This function is called from debthread. /// IMPORTANT: commdbg does not expect immediately after a BPT-related event /// any other event with the same thread/IP - this can cause erroneous /// restoring of a breakpoint before resume /// (the bug was encountered 24.02.2015 in pc_linux_upx.elf) gdecode_t (idaapi *get_debug_event)(debug_event_t *event, int timeout_ms); /// Continue after handling the event. /// This function is called from debthread. /// \retval 1 ok /// \retval 0 failed /// \retval -1 network error int (idaapi *continue_after_event)(const debug_event_t *event); /// Set exception handling. /// This function is called from debthread or the main thread. void (idaapi *set_exception_info)(const exception_info_t *info, int qty); /// This function will be called by the kernel each time /// it has stopped the debugger process and refreshed the database. /// The debugger module may add information to the database if it wants. /// /// The reason for introducing this function is that when an event line /// LOAD_DLL happens, the database does not reflect the memory state yet /// and therefore we can't add information about the dll into the database /// in the get_debug_event() function. /// Only when the kernel has adjusted the database we can do it. /// Example: for imported PE DLLs we will add the exported function /// names to the database. /// /// This function pointer may be absent, ie NULL. /// This function is called from the main thread. void (idaapi *stopped_at_debug_event)(bool dlls_added); /// \name Threads /// The following functions manipulate threads. /// These functions are called from debthread. /// \retval 1 ok /// \retval 0 failed /// \retval -1 network error //@{ int (idaapi *thread_suspend) (thid_t tid); ///< Suspend a running thread int (idaapi *thread_continue)(thid_t tid); ///< Resume a suspended thread int (idaapi *set_resume_mode)(thid_t tid, resume_mode_t resmod); ///< Specify resume action //@} /// Read thread registers. /// This function is called from debthread. /// \param tid thread id /// \param clsmask bitmask of register classes to read /// \param values pointer to vector of regvals for all registers. /// regval is assumed to have debugger_t::registers_size elements /// \retval 1 ok /// \retval 0 failed /// \retval -1 network error int (idaapi *read_registers)(thid_t tid, int clsmask, regval_t *values); /// Write one thread register. /// This function is called from debthread. /// \param tid thread id /// \param regidx register index /// \param value new value of the register /// \retval 1 ok /// \retval 0 failed /// \retval -1 network error int (idaapi *write_register)(thid_t tid, int regidx, const regval_t *value); /// Get information about the base of a segment register. /// Currently used by the IBM PC module to resolve references like fs:0. /// This function is called from debthread. /// \param answer pointer to the answer. can't be NULL. /// \param tid thread id /// \param sreg_value value of the segment register (returned by get_reg_val()) /// \retval 1 ok /// \retval 0 failed /// \retval -1 network error int (idaapi *thread_get_sreg_base)(ea_t *answer, thid_t tid, int sreg_value); /// \name Memory manipulation /// The following functions manipulate bytes in the memory. //@{ /// Get information on the memory ranges. /// The debugger module fills 'ranges'. The returned vector MUST be sorted. /// This function is called from debthread. /// \retval -3 use idb segmentation /// \retval -2 no changes /// \retval -1 the process does not exist anymore /// \retval 0 failed /// \retval 1 new memory layout is returned int (idaapi *get_memory_info)(meminfo_vec_t &ranges); /// Read process memory. /// Returns number of read bytes. /// This function is called from debthread. /// \retval 0 read error /// \retval -1 process does not exist anymore ssize_t (idaapi *read_memory)(ea_t ea, void *buffer, size_t size); /// Write process memory. /// This function is called from debthread. /// \return number of written bytes, -1 if fatal error ssize_t (idaapi *write_memory)(ea_t ea, const void *buffer, size_t size); //@} /// Is it possible to set breakpoint?. /// This function is called from debthread or from the main thread if debthread /// is not running yet. /// It is called to verify hardware breakpoints. /// \return ref BPT_ int (idaapi *is_ok_bpt)(bpttype_t type, ea_t ea, int len); /// \defgroup BPT_ Breakpoint verification codes /// Return values for debugger_t::is_ok_bpt //@{ #define BPT_OK 0 ///< breakpoint can be set #define BPT_INTERNAL_ERR 1 ///< interr occurred when verifying breakpoint #define BPT_BAD_TYPE 2 ///< bpt type is not supported #define BPT_BAD_ALIGN 3 ///< alignment is invalid #define BPT_BAD_ADDR 4 ///< ea is invalid #define BPT_BAD_LEN 5 ///< bpt len is invalid #define BPT_TOO_MANY 6 ///< reached max number of supported breakpoints #define BPT_READ_ERROR 7 ///< failed to read memory at bpt ea #define BPT_WRITE_ERROR 8 ///< failed to write memory at bpt ea #define BPT_SKIP 9 ///< update_bpts(): do not process bpt #define BPT_PAGE_OK 10 ///< update_bpts(): ok, added a page bpt //@} /// Add/del breakpoints. /// bpts array contains nadd bpts to add, followed by ndel bpts to del. /// This function is called from debthread. /// \return number of successfully modified bpts, -1 if network error int (idaapi *update_bpts)(update_bpt_info_t *bpts, int nadd, int ndel); /// Update low-level (server side) breakpoint conditions. /// This function is called from debthread. /// \return nlowcnds. -1-network error int (idaapi *update_lowcnds)(const lowcnd_t *lowcnds, int nlowcnds); /// \name Remote file /// Open/close/read a remote file. /// These functions are called from the main thread //@{ int (idaapi *open_file)(const char *file, uint64 *fsize, bool readonly); // -1-error void (idaapi *close_file)(int fn); ssize_t (idaapi *read_file)(int fn, qoff64_t off, void *buf, size_t size); //@} /// Map process address. /// This function may be absent. /// This function is called from debthread. /// \param off offset to map /// \param regs current register values. if regs == NULL, then perform /// global mapping, which is independent on used registers /// usually such a mapping is a trivial identity mapping /// \param regnum required mapping. maybe specified as a segment register number /// or a regular register number if the required mapping can be deduced /// from it. for example, esp implies that ss should be used. /// \return mapped address or #BADADDR ea_t (idaapi *map_address)(ea_t off, const regval_t *regs, int regnum); /// Set debugger options (parameters that are specific to the debugger module). /// See the definition of ::set_options_t for arguments. /// See the convenience function in dbg.hpp if you need to call it. /// The kernel will call this function after reading the debugger specific /// config file (arguments are: keyword="", type=#IDPOPT_STR, value="") /// This function is optional. /// This function is called from the main thread const char *(idaapi *set_dbg_options)( const char *keyword, int pri, int value_type, const void *value); /// Get pointer to debugger specific functions. /// This function returns a pointer to a structure that holds pointers to /// debugger module specific functions. For information on the structure /// layout, please check the corresponding debugger module. Most debugger /// modules return NULL because they do not have any extensions. Available /// extensions may be called from plugins. /// This function is called from the main thread. const void *(idaapi *get_debmod_extensions)(void); /// Calculate the call stack trace. /// This function is called when the process is suspended and should fill /// the 'trace' object with the information about the current call stack. /// If this function is missing or returns false, IDA will use the standard /// mechanism (based on the frame pointer chain) to calculate the stack trace /// This function is called from the main thread. /// \return success bool (idaapi *update_call_stack)(thid_t tid, call_stack_t *trace); /// Call application function. /// This function calls a function from the debugged application. /// This function is called from debthread /// \param func_ea address to call /// \param tid thread to use /// \param fti type information for the called function /// \param nargs number of actual arguments /// \param regargs information about register arguments /// \param stkargs memory blob to pass as stack arguments (usually contains pointed data) /// it must be relocated by the callback but not changed otherwise /// \param retregs function return registers. /// \param[out] errbuf the error message. if empty on failure, see 'event'. /// should not be filled if an appcall exception /// happened but #APPCALL_DEBEV is set /// \param[out] event the last debug event that occurred during appcall execution /// filled only if the appcall execution fails and #APPCALL_DEBEV is set /// \param options appcall options, usually taken from \inf{appcall_options}. /// possible values: combination of \ref APPCALL_ or 0 /// \return ea of stkargs blob, #BADADDR if failed and errbuf is filled ea_t (idaapi *appcall)( ea_t func_ea, thid_t tid, const struct func_type_data_t *fti, int nargs, const struct regobjs_t *regargs, struct relobj_t *stkargs, struct regobjs_t *retregs, qstring *errbuf, debug_event_t *event, int options); /// \defgroup APPCALL_ Appcall options /// Passed as 'options' parameter to debugger_t::appcall //@{ #define APPCALL_MANUAL 0x0001 ///< Only set up the appcall, do not run. ///< debugger_t::cleanup_appcall will not be called by ida! #define APPCALL_DEBEV 0x0002 ///< Return debug event information #define APPCALL_TIMEOUT 0x0004 ///< Appcall with timeout. ///< If timed out, errbuf will contain "timeout". ///< See #SET_APPCALL_TIMEOUT and #GET_APPCALL_TIMEOUT //@} /// Cleanup after appcall(). /// The debugger module must keep the stack blob in the memory until this function /// is called. It will be called by the kernel for each successful appcall(). /// There is an exception: if #APPCALL_MANUAL, IDA may not call cleanup_appcall. /// If the user selects to terminate a manual appcall, then cleanup_appcall will be called. /// Otherwise, the debugger module should terminate the appcall when the called /// function returns. /// This function is called from debthread. /// \retval 2 ok, there are pending events /// \retval 1 ok /// \retval 0 failed /// \retval -1 network error int (idaapi *cleanup_appcall)(thid_t tid); /// Evaluate a low level breakpoint condition at 'ea'. /// Other evaluation errors are displayed in a dialog box. /// This call is rarely used by IDA when the process has already been suspended /// for some reason and it has to decide whether the process should be resumed /// or definitely suspended because of a breakpoint with a low level condition. /// This function is called from debthread. /// \retval 1 condition is satisfied /// \retval 0 not satisfied /// \retval -1 network error int (idaapi *eval_lowcnd)(thid_t tid, ea_t ea); /// This function is called from main thread ssize_t (idaapi *write_file)(int fn, qoff64_t off, const void *buf, size_t size); /// Perform a debugger-specific function. /// This function is called from debthread int (idaapi *send_ioctl)(int fn, const void *buf, size_t size, void **poutbuf, ssize_t *poutsize); /// Enable/Disable tracing. /// "trace_flags" can be a set of STEP_TRACE, INSN_TRACE, BBLK_TRACE or FUNC_TRACE. /// See thread_t::trace_mode in debugger.h. /// This function is called from the main thread. bool (idaapi *dbg_enable_trace)(thid_t tid, bool enable, int trace_flags); /// Is tracing enabled? ONLY used for tracers. /// "trace_bit" can be one of the following: STEP_TRACE, INSN_TRACE, BBLK_TRACE or FUNC_TRACE bool (idaapi *is_tracing_enabled)(thid_t tid, int tracebit); /// Execute a command on the remote computer. /// \return exit code int (idaapi *rexec)(const char *cmdline); /// Get (store to out_pattrs) process/debugger-specific runtime attributes. /// This function is called from main thread. void (idaapi *get_debapp_attrs)(debapp_attrs_t *out_pattrs); /// Get the path to a file containing source debug info for the given module. /// This allows srcinfo providers to call into the debugger when looking for debug info. /// It is useful in certain cases like the iOS debugger, which is a remote debugger but /// the remote debugserver does not provide dwarf info. So, we allow the debugger client /// to decide where to look for debug info locally. /// \param path output path (file might not exist) /// \param base base address of a module in the target process /// \return success, result stored in 'path' bool (idaapi *get_srcinfo_path)(qstring *path, ea_t base); };
      
      





, , , .









はい、はい。 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; ///< Should be equal to #IDP_INTERFACE_VERSION int flags; ///< \ref PLUGIN_ /// \defgroup PLUGIN_ Plugin features /// Used by plugin_t::flags //@{ #define PLUGIN_MOD 0x0001 ///< Plugin changes the database. ///< IDA won't call the plugin if ///< the processor module prohibited any changes. #define PLUGIN_DRAW 0x0002 ///< IDA should redraw everything after calling the plugin. #define PLUGIN_SEG 0x0004 ///< Plugin may be applied only if the current address belongs to a segment #define PLUGIN_UNL 0x0008 ///< Unload the plugin immediately after calling 'run'. ///< This flag may be set anytime. ///< The kernel checks it after each call to 'run' ///< The main purpose of this flag is to ease ///< the debugging of new plugins. #define PLUGIN_HIDE 0x0010 ///< Plugin should not appear in the Edit, Plugins menu. ///< This flag is checked at the start. #define PLUGIN_DBG 0x0020 ///< A debugger plugin. init() should put ///< the address of ::debugger_t to dbg. #define PLUGIN_PROC 0x0040 ///< Load plugin when a processor module is loaded. (and keep it ///< until the processor module is unloaded) #define PLUGIN_FIX 0x0080 ///< Load plugin when IDA starts and keep it in the memory until IDA stops #define PLUGIN_SCRIPTED 0x8000 ///< Scripted plugin. Should not be used by plugins, ///< the kernel sets it automatically. //@} int (idaapi *init)(void); ///< Initialize plugin - returns \ref PLUGIN_INIT /// \defgroup PLUGIN_INIT Plugin initialization codes /// Return values for plugin_t::init() //@{ #define PLUGIN_SKIP 0 ///< Plugin doesn't want to be loaded #define PLUGIN_OK 1 ///< Plugin agrees to work with the current database. ///< It will be loaded as soon as the user presses the hotkey #define PLUGIN_KEEP 2 ///< Plugin agrees to work with the current database and wants to stay in the memory //@} void (idaapi *term)(void); ///< Terminate plugin. This function will be called ///< when the plugin is unloaded. May be NULL. bool (idaapi *run)(size_t arg); ///< Invoke plugin const char *comment; ///< Long comment about the plugin. ///< it could appear in the status line ///< or as a hint const char *help; ///< Multiline help about the plugin const char *wanted_name; ///< The preferred short name of the plugin const char *wanted_hotkey; ///< The preferred hotkey to run the plugin };
      
      





おそらくそれだけです。1つの記事にはたくさんのコードがありますが、これがおもしろいことを願っています。ご覧のように、IDA



非常に興味深いものの、何かを書くプロセスは完全な苦痛に変わります。







約束どおり、ソースリンク:

GPGXデバッガー

Smd IDAツール








All Articles