// // This file is included from other files, do not directly compile it. // It contains the debugger_t structure definition and a few other helper functions // #include #include #include bool plugin_inited; bool debugger_inited; #define IS_GDB_DEBUGGER (DEBUGGER_ID == DEBUGGER_ID_GDB_USER || DEBUGGER_ID == DEBUGGER_ID_ARM_IPHONE_USER || DEBUGGER_ID == DEBUGGER_ID_XNU_USER) #if TARGET_PROCESSOR == PLFM_386 #define REGISTERS x86_registers #define REGISTERS_SIZE qnumber(x86_registers) #define REGISTER_CLASSES x86_register_classes #define REGISTER_CLASSES_DEFAULT X86_RC_GENERAL #define READ_REGISTERS x86_read_registers #define WRITE_REGISTER x86_write_register #if !IS_GDB_DEBUGGER #define is_valid_bpt is_x86_valid_bpt #endif #define BPT_CODE X86_BPT_CODE #define BPT_CODE_SIZE X86_BPT_SIZE #elif TARGET_PROCESSOR == PLFM_ARM #define REGISTERS arm_registers #define REGISTERS_SIZE qnumber(arm_registers) #define REGISTER_CLASSES arm_register_classes #define REGISTER_CLASSES_DEFAULT ARM_RC_GENERAL #define READ_REGISTERS s_read_registers #define WRITE_REGISTER s_write_register #if !IS_GDB_DEBUGGER #define is_valid_bpt is_arm_valid_bpt #else #define is_valid_bpt gdb_valid_bpt #endif #define BPT_CODE ARM_BPT_CODE #define BPT_CODE_SIZE ARM_BPT_SIZE #elif TARGET_PROCESSOR == PLFM_DALVIK #define BPT_CODE { 0 } #define BPT_CODE_SIZE 0 #define READ_REGISTERS s_read_registers #define WRITE_REGISTER s_write_register #define is_valid_bpt is_dalvik_valid_bpt #elif IS_GDB_DEBUGGER #define REGISTERS NULL #define REGISTERS_SIZE 0 #define REGISTER_CLASSES NULL #define REGISTER_CLASSES_DEFAULT 0 #define READ_REGISTERS simple_read_registers #define WRITE_REGISTER simple_write_register #define is_valid_bpt gdb_valid_bpt #define BPT_CODE { 0 } #define BPT_CODE_SIZE 0 #else #error This processor is not supported yet #endif static const uchar bpt_code[] = BPT_CODE; //-------------------------------------------------------------------------- int idaapi is_ok_bpt(bpttype_t type, ea_t ea, int len) { int ret = is_valid_bpt(type, ea, len); if ( ret != BPT_OK ) return ret; else return g_dbgmod.dbg_is_ok_bpt(type, ea, len); } //-------------------------------------------------------------------------- // For ARM, we have to set the low bit of the address to 1 for thumb mode #if DEBUGGER_ID == DEBUGGER_ID_ARM_LINUX_USER static drc_t idaapi arm_update_bpts( int *nbpts, update_bpt_info_t *bpts, int nadd, int ndel, qstring *errbuf) { // This function is called from debthread, but to use get_sreg() we must // switch to the mainthread struct ida_local arm_bptea_fixer_t : public exec_request_t { update_bpt_info_t *bpts; update_bpt_info_t *e; qvector thumb_mode; virtual int idaapi execute(void) override { for ( update_bpt_info_t *b=bpts; b != e; b++ ) { if ( b->type == BPT_SOFT && get_sreg(b->ea, ARM_T) == 1 ) { b->ea++; // odd address means that thumb bpt must be set thumb_mode.push_back(&b->ea); } } return 0; } arm_bptea_fixer_t(update_bpt_info_t *p1, update_bpt_info_t *p2) : bpts(p1), e(p2) {} }; arm_bptea_fixer_t abf(bpts, bpts+nadd); execute_sync(abf, MFF_READ); drc_t drc = s_update_bpts(nbpts, bpts, nadd, ndel, errbuf); // reset the odd bit because the addresses are required by the caller for ( int i=0; i < abf.thumb_mode.size(); i++ ) (*abf.thumb_mode[i])--; return drc; } #define s_update_bpts arm_update_bpts #endif //-------------------------------------------------------------------------- static drc_t idaapi update_bpts( int *nbpts, update_bpt_info_t *bpts, int nadd, int ndel, qstring *errbuf) { bool valid_bpt_exists = false; update_bpt_info_t *e = bpts + nadd; for ( update_bpt_info_t *b=bpts; b != e; b++ ) { if ( b->code == BPT_SKIP ) continue; b->code = is_valid_bpt(b->type, b->ea, b->size); if ( b->code == BPT_OK ) valid_bpt_exists = true; } if ( !valid_bpt_exists && ndel == 0 ) { if ( nbpts != NULL ) *nbpts = 0; return DRC_OK; // none of bpts is writable } drc_t drc = s_update_bpts(nbpts, bpts, nadd, ndel, errbuf); return drc; } //-------------------------------------------------------------------------- #ifndef REMOTE_DEBUGGER // another copy of this function (for remote debugging) is defined in dbg_rpc_handler.cpp int send_ioctl( void *, int fn, const void *buf, size_t size, void **poutbuf, ssize_t *poutsize) { return g_dbgmod.handle_ioctl(fn, buf, size, poutbuf, poutsize); } #endif //-------------------------------------------------------------------------- THREAD_SAFE int debmod_t::send_debug_names_to_ida( ea_t *addrs, const char *const *names, int qty) { return ::send_debug_names_to_ida(addrs, names, qty); } //--------------------------------------------------------------------------- THREAD_SAFE int send_debug_names_to_ida( ea_t *addrs, const char *const *names, int qty) { struct debug_name_handler_t : public exec_request_t { ea_t *addrs; const char *const *names; int qty; debug_name_handler_t(ea_t *_addrs, const char *const *_names, int _qty) : addrs(_addrs), names(_names), qty(_qty) {} int idaapi execute(void) override { set_arm_thumb_modes(addrs, qty); return set_debug_names(addrs, names, qty); } }; debug_name_handler_t dnh(addrs, names, qty); return execute_sync(dnh, MFF_WRITE); } //-------------------------------------------------------------------------- THREAD_SAFE int debmod_t::send_debug_event_to_ida( const debug_event_t *ev, int rqflags) { return ::send_debug_event_to_ida(ev, rqflags); } //--------------------------------------------------------------------------- THREAD_SAFE int send_debug_event_to_ida( const debug_event_t *ev, int rqflags) { return handle_debug_event(ev, rqflags); } //-------------------------------------------------------------------------- THREAD_SAFE int import_dll(const import_request_t &req) { struct dll_importer_t : public exec_request_t { const import_request_t &req; dll_importer_t(const import_request_t &_req) : req(_req) {} int idaapi execute(void) override { return g_dbgmod.import_dll(req) ? 0 : 1; } }; dll_importer_t di(req); return execute_sync(di, MFF_WRITE); } //-------------------------------------------------------------------------- #if TARGET_PROCESSOR != PLFM_ARM void set_arm_thumb_modes(ea_t * /*addrs*/, int /*qty*/) { } #endif //-------------------------------------------------------------------------- // installs or uninstalls debugger specific idc functions bool add_idc_funcs(const ext_idcfunc_t efuncs[], size_t nfuncs, bool reg) { if ( reg ) { for ( int i=0; i < nfuncs; i++ ) if ( !add_idc_func(efuncs[i]) ) return false; } else { for ( int i=0; i < nfuncs; i++ ) if ( !del_idc_func(efuncs[i].name) ) return false; } return true; } //-------------------------------------------------------------------------- static drc_t init_debugger( const char *hostname, int port_num, const char *password, qstring *errbuf) { g_dbgmod.dbg_set_debugging((debug & IDA_DEBUG_DEBUGGER) != 0); if ( !s_open_remote(hostname, port_num, password, errbuf) ) return DRC_FAILED; uint32_t flags2 = 0; drc_t drc = s_init(&flags2, errbuf); if ( drc != DRC_OK ) { s_close_remote(); return drc; } debugger.flags2 |= flags2; #if defined(REMOTE_DEBUGGER) && !defined(NO_OPEN_FILE) setflag(debugger.flags2, DBG_HAS_OPEN_FILE, true); #endif #ifdef HAVE_UPDATE_CALL_STACK setflag(debugger.flags2, DBG_HAS_UPDATE_CALL_STACK, true); #endif #ifdef HAVE_APPCALL setflag(debugger.flags2, DBG_HAS_APPCALL, true); #endif #ifdef HAVE_MAP_ADDRESS setflag(debugger.flags2, DBG_HAS_MAP_ADDRESS, true); #endif debugger_inited = true; processor_specific_init(); register_idc_funcs(true); init_dbg_idcfuncs(true); #if DEBUGGER_ID == DEBUGGER_ID_X86_IA32_WIN32_USER || DEBUGGER_ID == DEBUGGER_ID_X86_IA32_BOCHS install_x86seh_menu(); #endif return DRC_OK; } //-------------------------------------------------------------------------- static drc_t term_debugger(void) { if ( debugger_inited ) { debugger_inited = false; #if DEBUGGER_ID == DEBUGGER_ID_X86_IA32_WIN32_USER || DEBUGGER_ID == DEBUGGER_ID_X86_IA32_BOCHS remove_x86seh_menu(); #endif init_dbg_idcfuncs(false); register_idc_funcs(false); processor_specific_term(); g_dbgmod.dbg_term(); return s_close_remote(); } return DRC_FAILED; } //-------------------------------------------------------------------------- static ssize_t idaapi idd_notify(void *, int msgid, va_list va) { int retcode = DRC_NONE; qstring *errbuf; switch ( msgid ) { case debugger_t::ev_init_debugger: { const char *hostname = va_arg(va, const char *); int portnum = va_arg(va, int); const char *password = va_arg(va, const char *); errbuf = va_arg(va, qstring *); QASSERT(1522, errbuf != NULL); retcode = init_debugger(hostname, portnum, password, errbuf); } break; case debugger_t::ev_term_debugger: retcode = term_debugger(); break; case debugger_t::ev_get_processes: { procinfo_vec_t *procs = va_arg(va, procinfo_vec_t *); errbuf = va_arg(va, qstring *); retcode = g_dbgmod.dbg_get_processes(procs, errbuf); } break; case debugger_t::ev_start_process: { const char *path = va_arg(va, const char *); const char *args = va_arg(va, const char *); const char *startdir = va_arg(va, const char *); uint32 dbg_proc_flags = va_arg(va, uint32); const char *input_path = va_arg(va, const char *); uint32 input_file_crc32 = va_arg(va, uint32); errbuf = va_arg(va, qstring *); retcode = s_start_process(path, args, startdir, dbg_proc_flags, input_path, input_file_crc32, errbuf); } break; case debugger_t::ev_attach_process: { pid_t pid = va_argi(va, pid_t); int event_id = va_arg(va, int); uint32 dbg_proc_flags = va_arg(va, uint32); errbuf = va_arg(va, qstring *); retcode = s_attach_process(pid, event_id, dbg_proc_flags, errbuf); } break; case debugger_t::ev_detach_process: retcode = g_dbgmod.dbg_detach_process(); break; case debugger_t::ev_get_debapp_attrs: { debapp_attrs_t *out_pattrs = va_arg(va, debapp_attrs_t *); g_dbgmod.dbg_get_debapp_attrs(out_pattrs); retcode = DRC_OK; } break; case debugger_t::ev_rebase_if_required_to: { ea_t new_base = va_arg(va, ea_t); rebase_if_required_to(new_base); retcode = DRC_OK; } break; case debugger_t::ev_request_pause: errbuf = va_arg(va, qstring *); retcode = g_dbgmod.dbg_prepare_to_pause_process(errbuf); break; case debugger_t::ev_exit_process: errbuf = va_arg(va, qstring *); retcode = g_dbgmod.dbg_exit_process(errbuf); break; case debugger_t::ev_get_debug_event: { gdecode_t *code = va_arg(va, gdecode_t *); debug_event_t *event = va_arg(va, debug_event_t *); int timeout_ms = va_arg(va, int); *code = g_dbgmod.dbg_get_debug_event(event, timeout_ms); retcode = DRC_OK; } break; case debugger_t::ev_resume: { debug_event_t *event = va_arg(va, debug_event_t *); retcode = g_dbgmod.dbg_continue_after_event(event); } break; case debugger_t::ev_set_exception_info: { exception_info_t *info = va_arg(va, exception_info_t *); int qty = va_arg(va, int); g_dbgmod.dbg_set_exception_info(info, qty); retcode = DRC_OK; } break; case debugger_t::ev_suspended: { bool dlls_added = va_argi(va, bool); thread_name_vec_t *thr_names = va_arg(va, thread_name_vec_t *); s_stopped_at_debug_event(thr_names, dlls_added); retcode = DRC_OK; } break; case debugger_t::ev_thread_suspend: { thid_t tid = va_argi(va, thid_t); retcode = g_dbgmod.dbg_thread_suspend(tid); } break; case debugger_t::ev_thread_continue: { thid_t tid = va_argi(va, thid_t); retcode = g_dbgmod.dbg_thread_continue(tid); } break; case debugger_t::ev_set_resume_mode: { thid_t tid = va_argi(va, thid_t); resume_mode_t resmod = va_argi(va, resume_mode_t); retcode = g_dbgmod.dbg_set_resume_mode(tid, resmod); } break; case debugger_t::ev_read_registers: { thid_t tid = va_argi(va, thid_t); int clsmask = va_arg(va, int); regval_t *values = va_arg(va, regval_t *); errbuf = va_arg(va, qstring *); retcode = READ_REGISTERS(tid, clsmask, values, errbuf); } break; case debugger_t::ev_write_register: { thid_t tid = va_argi(va, thid_t); int regidx = va_arg(va, int); const regval_t *value = va_arg(va, const regval_t *); errbuf = va_arg(va, qstring *); retcode = WRITE_REGISTER(tid, regidx, value, errbuf); } break; case debugger_t::ev_thread_get_sreg_base: { ea_t *answer = va_arg(va, ea_t *); thid_t tid = va_argi(va, thid_t); int sreg_value = va_arg(va, int); errbuf = va_arg(va, qstring *); retcode = g_dbgmod.dbg_thread_get_sreg_base(answer, tid, sreg_value, errbuf); } break; case debugger_t::ev_get_memory_info: { meminfo_vec_t *ranges = va_arg(va, meminfo_vec_t *); errbuf = va_arg(va, qstring *); retcode = g_dbgmod.dbg_get_memory_info(*ranges, errbuf); } break; case debugger_t::ev_read_memory: { size_t *nbytes = va_arg(va, size_t *); ea_t ea = va_arg(va, ea_t); void *buffer = va_arg(va, void *); size_t size = va_arg(va, size_t); errbuf = va_arg(va, qstring *); ssize_t code = g_dbgmod.dbg_read_memory(ea, buffer, size, errbuf); *nbytes = code >= 0 ? code : 0; retcode = code >= 0 ? DRC_OK : DRC_NOPROC; } break; case debugger_t::ev_write_memory: { size_t *nbytes = va_arg(va, size_t *); ea_t ea = va_arg(va, ea_t); const void *buffer = va_arg(va, void *); size_t size = va_arg(va, size_t); errbuf = va_arg(va, qstring *); ssize_t code = g_dbgmod.dbg_write_memory(ea, buffer, size, errbuf); *nbytes = code >= 0 ? code : 0; retcode = code >= 0 ? DRC_OK : DRC_NOPROC; } break; case debugger_t::ev_check_bpt: { int *bptvc = va_arg(va, int *); bpttype_t type = va_argi(va, bpttype_t); ea_t ea = va_arg(va, ea_t); int len = va_arg(va, int); *bptvc = is_ok_bpt(type, ea, len); retcode = DRC_OK; } break; case debugger_t::ev_update_bpts: { int *nbpts = va_arg(va, int *); update_bpt_info_t *bpts = va_arg(va, update_bpt_info_t *); int nadd = va_arg(va, int); int ndel = va_arg(va, int); errbuf = va_arg(va, qstring *); retcode = update_bpts(nbpts, bpts, nadd, ndel, errbuf); } break; case debugger_t::ev_update_lowcnds: { int *nupdated = va_arg(va, int *); const lowcnd_t *lowcnds = va_arg(va, const lowcnd_t *); int nlowcnds = va_arg(va, int); errbuf = va_arg(va, qstring *); retcode = g_dbgmod.dbg_update_lowcnds(nupdated, lowcnds, nlowcnds, errbuf); } break; case debugger_t::ev_open_file: { const char *file = va_arg(va, const char *); uint64 *fsize = va_arg(va, uint64 *); bool readonly = va_argi(va, bool); retcode = g_dbgmod.dbg_open_file(file, fsize, readonly); } break; case debugger_t::ev_close_file: { int fn = va_arg(va, int); g_dbgmod.dbg_close_file(fn); retcode = DRC_OK; } break; case debugger_t::ev_read_file: { int fn = va_arg(va, int); qoff64_t off = va_arg(va, qoff64_t); void *buf = va_arg(va, void *); size_t size = va_arg(va, size_t); retcode = g_dbgmod.dbg_read_file(fn, off, buf, size); } break; case debugger_t::ev_write_file: { int fn = va_arg(va, int); qoff64_t off = va_arg(va, qoff64_t); const void *buf = va_arg(va, const void *); size_t size = va_arg(va, size_t); retcode = g_dbgmod.dbg_write_file(fn, off, buf, size); } break; case debugger_t::ev_map_address: { ea_t *mapped = va_arg(va, ea_t *); ea_t ea = va_arg(va, ea_t); const regval_t *regs = va_arg(va, const regval_t *); int regnum = va_arg(va, int); *mapped = g_dbgmod.map_address(ea, regs, regnum); return DRC_OK; } break; #ifdef GET_DEBMOD_EXTS case debugger_t::ev_get_debmod_extensions: { const void **ext = va_arg(va, const void **); *ext = GET_DEBMOD_EXTS(); retcode = DRC_OK; } break; #endif #ifdef HAVE_UPDATE_CALL_STACK case debugger_t::ev_update_call_stack: { thid_t tid = va_argi(va, thid_t); call_stack_t *trace = va_arg(va, call_stack_t *); retcode = g_dbgmod.dbg_update_call_stack(tid, trace); } break; #endif #ifdef HAVE_APPCALL case debugger_t::ev_appcall: { ea_t *blob_ea = va_arg(va, ea_t *); ea_t func_ea = va_arg(va, ea_t); thid_t tid = va_arg(va, thid_t); const func_type_data_t *fti = va_arg(va, const func_type_data_t *); int nargs = va_arg(va, int); const regobjs_t *regargs = va_arg(va, const regobjs_t *); relobj_t *stkargs = va_arg(va, relobj_t *); regobjs_t *retregs = va_arg(va, regobjs_t *); errbuf = va_arg(va, qstring *); debug_event_t *event = va_arg(va, debug_event_t *); int opts = va_arg(va, int); qnotused(nargs); *blob_ea = g_dbgmod.dbg_appcall(func_ea, tid, fti->stkargs, regargs, stkargs, retregs, errbuf, event, opts); retcode = DRC_OK; } break; case debugger_t::ev_cleanup_appcall: { thid_t tid = va_argi(va, thid_t); retcode = g_dbgmod.dbg_cleanup_appcall(tid); } break; #endif case debugger_t::ev_eval_lowcnd: { thid_t tid = va_argi(va, thid_t); ea_t ea = va_arg(va, ea_t); errbuf = va_arg(va, qstring *); retcode = g_dbgmod.dbg_eval_lowcnd(tid, ea, errbuf); } break; case debugger_t::ev_send_ioctl: { int fn = va_arg(va, int); const void *buf = va_arg(va, const void *); size_t size = va_arg(va, size_t); void **poutbuf = va_arg(va, void **); ssize_t *poutsize = va_arg(va, ssize_t *); retcode = g_dbgmod.handle_ioctl(fn, buf, size, poutbuf, poutsize); } break; case debugger_t::ev_dbg_enable_trace: { thid_t tid = va_arg(va, thid_t); bool enable = va_argi(va, bool); int trace_flags = va_arg(va, int); retcode = g_dbgmod.dbg_enable_trace(tid, enable, trace_flags) ? DRC_OK : DRC_NONE; } break; case debugger_t::ev_is_tracing_enabled: { thid_t tid = va_arg(va, thid_t); int tracebit = va_arg(va, int); retcode = g_dbgmod.dbg_is_tracing_enabled(tid, tracebit) ? DRC_OK : DRC_NONE; } break; case debugger_t::ev_rexec: { const char *cmdline = va_arg(va, const char *); retcode = g_dbgmod.dbg_rexec(cmdline); } break; #ifdef HAVE_GET_SRCINFO_PATH case debugger_t::ev_get_srcinfo_path: { qstring *path = va_arg(va, qstring *); ea_t base = va_arg(va, ea_t); bool ok = g_dbgmod.dbg_get_srcinfo_path(path, base); retcode = ok ? DRC_OK : DRC_NONE; } break; #endif case debugger_t::ev_bin_search: { ea_t *ea = va_arg(va, ea_t *); ea_t start_ea = va_arg(va, ea_t); ea_t end_ea = va_arg(va, ea_t); const compiled_binpat_vec_t *ptns = va_arg(va, const compiled_binpat_vec_t *); int srch_flags = va_arg(va, int); errbuf = va_arg(va, qstring *); if ( ptns != NULL ) retcode = g_dbgmod.dbg_bin_search(ea, start_ea, end_ea, *ptns, srch_flags, errbuf); } break; } return retcode; } //-------------------------------------------------------------------------- // Initialize debugger plugin static plugmod_t *idaapi init(void) { // copy of the definitions from loader.hpp // we will delete them after improving the debuggers to use PLUGIN_MULTI. #define PLUGIN_SKIP nullptr #define PLUGIN_KEEP ((plugmod_t *)2) if ( init_plugin() ) { update_idd_registers(); dbg = &debugger; plugin_inited = true; return PLUGIN_KEEP; } return PLUGIN_SKIP; } //-------------------------------------------------------------------------- // Terminate debugger plugin static void idaapi term(void) { if ( plugin_inited ) { term_plugin(); plugin_inited = false; } // we're being unloaded, clear the 'dbg' pointer if it's ours if ( dbg == &debugger ) dbg = NULL; } //-------------------------------------------------------------------------- // The plugin method - usually is not used for debugger plugins static bool idaapi run(size_t arg) { #ifdef HAVE_PLUGIN_RUN plugin_run(int(arg)); #else qnotused(arg); #endif return true; } //-------------------------------------------------------------------------- // // DEBUGGER DESCRIPTION BLOCK // //-------------------------------------------------------------------------- #ifdef SET_DBG_OPTIONS # define S_SET_DBG_OPTIONS s_set_dbg_options #else # define S_SET_DBG_OPTIONS NULL # define SET_DBG_OPTIONS NULL #endif #ifndef S_FILETYPE # define S_FILETYPE 0 #endif // arm has no single step mechanism // DBG_HAS_SET_RESUME_MODE must be set before init_debugger #if TARGET_PROCESSOR == PLFM_ARM # define S_DBG_HAS_SET_RESUME_MODE 0 #else # define S_DBG_HAS_SET_RESUME_MODE DBG_HAS_SET_RESUME_MODE #endif #ifndef DEBUGGER_RESMOD # define DEBUGGER_RESMOD 0 #endif debugger_t debugger = { IDD_INTERFACE_VERSION, DEBUGGER_NAME, DEBUGGER_ID, PROCESSOR_NAME, DEBUGGER_FLAGS, // flags DBG_HAS_ATTACH_PROCESS | DBG_HAS_REQUEST_PAUSE | DBG_HAS_SET_EXCEPTION_INFO | DBG_HAS_THREAD_SUSPEND | DBG_HAS_THREAD_CONTINUE | S_DBG_HAS_SET_RESUME_MODE | DBG_HAS_THREAD_GET_SREG_BASE | DBG_HAS_CHECK_BPT | DBG_HAS_REXEC, // flags2 REGISTER_CLASSES, REGISTER_CLASSES_DEFAULT, REGISTERS, REGISTERS_SIZE, MEMORY_PAGE_SIZE, bpt_code, sizeof(bpt_code), S_FILETYPE, DEBUGGER_RESMOD, S_SET_DBG_OPTIONS, idd_notify, }; //-------------------------------------------------------------------------- // // PLUGIN DESCRIPTION BLOCK // //-------------------------------------------------------------------------- plugin_t PLUGIN = { IDP_INTERFACE_VERSION, PLUGIN_HIDE|PLUGIN_DBG, // 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 comment, // multiline help about the plugin wanted_name, // the preferred short name of the plugin "" // the preferred hotkey to run the plugin };