#include #include #include #include #include // otherwise cannot compile win32_remote.bpr #include #include "server.h" //-------------------------------------------------------------------------- // another copy of this function (for local debugging) is defined in common_local_impl.cpp int send_ioctl( rpc_engine_t *srv, int fn, const void *buf, size_t size, void **poutbuf, ssize_t *poutsize) { return srv->send_ioctl(fn, buf, size, poutbuf, poutsize); } //-------------------------------------------------------------------------- AS_PRINTF(3, 0) ssize_t dvmsg(int code, rpc_engine_t *rpc, const char *format, va_list va) { if ( code == 0 ) code = RPC_MSG; else if ( code > 0 ) code = RPC_WARNING; else code = RPC_ERROR; bytevec_t req = prepare_rpc_packet((uchar)code); char buf[MAXSTR]; qvsnprintf(buf, sizeof(buf), format, va); req.pack_str(buf); qfree(rpc->send_request_and_receive_reply(req)); if ( code == RPC_ERROR ) exit(1); return strlen(buf); } //-------------------------------------------------------------------------- void report_idc_error(rpc_engine_t *rpc, ea_t ea, error_t code, ssize_t errval, const char *errprm) { if ( code == eOS ) { dbg_rpc_handler_t *h = (dbg_rpc_handler_t *)rpc; errval = h->get_debugger_instance()->get_system_specific_errno(); } bytevec_t req = prepare_rpc_packet(RPC_REPORT_IDC_ERROR); req.pack_ea64(ea); req.pack_dd(code); if ( (const char *)errval == errprm ) { req.pack_db(1); req.pack_str(errprm); } else { req.pack_db(0); req.pack_ea64(errval); } qfree(rpc->send_request_and_receive_reply(req)); } //-------------------------------------------------------------------------- debmod_t *dbg_rpc_handler_t::get_debugger_instance() { return dbg_mod; //lint !e1535 !e1536 exposes lower access member } //-------------------------------------------------------------------------- void dbg_rpc_handler_t::prepare_broken_connection(void) { if ( debmod_t::reuse_broken_connections ) { if ( !dbg_mod->dbg_prepare_broken_connection() ) dmsg("Error preparing debugger server to handle a broken connection\n"); } } //-------------------------------------------------------------------------- // dbg_rpc_handler_t //-------------------------------------------------------------------------- dbg_rpc_handler_t::dbg_rpc_handler_t( idarpc_stream_t *_irs, dbgsrv_dispatcher_t *_dispatcher) : client_handler_t(_irs, /*_verbose=*/ false), dbg_rpc_engine_t(/*is_client=*/ false), dbg_mod(NULL), dispatcher(_dispatcher) { clear_channels(); //lint -esym(1566,dbg_rpc_handler_t::channels) not inited struct ida_local lambda_t { static int ioctl(rpc_engine_t *rpc, int fn, const void *buf, size_t size, void **out, ssize_t *outsz) { dbg_rpc_handler_t *serv = (dbg_rpc_handler_t *) rpc; memory_deserializer_t mmdsr(buf, size); if ( fn >= MIN_SERVER_IOCTL ) return serv->handle_server_ioctl(fn, out, outsz, mmdsr); else return serv->get_debugger_instance()->handle_ioctl(fn, buf, size, out, outsz); } static progress_loop_ctrl_t recv_data_iter(bool, size_t, size_t, void *ud) { dbg_rpc_handler_t *eng = (dbg_rpc_handler_t *) ud; bool performed = false; int code = eng->on_recv_packet_progress(&performed); if ( performed ) return code == 0 ? plc_skip_iter : plc_cancel; else return plc_proceed; } }; set_ioctl_handler(lambda_t::ioctl); irs_set_progress_cb(irs, 100, lambda_t::recv_data_iter, this); } //-------------------------------------------------------------------------- dbg_rpc_handler_t::~dbg_rpc_handler_t() { //lint -e(1506) Call to virtual function 'dbg_rpc_handler_t::get_broken_connection(void)' within a constructor or destructor if ( !get_broken_connection() ) delete dbg_mod; // the connection is not broken, delete the debugger instance //lint -esym(1579,dbg_rpc_handler_t::dbg_mod) pointer member might have been freed by a separate function clear_channels(); dispatcher = NULL; } //------------------------------------------------------------------------ // Function safe against time slicing attack, comparing time depends only on str length static bool password_matches(const char *str, const char *pass) { if ( str == NULL ) return false; int str_length = strlen(str); int pass_length = strlen(pass); int res = str_length ^ pass_length; if ( pass_length != 0 ) { for ( int i = 0; i < str_length; i++ ) res |= (pass[i % pass_length] ^ str[i]); } return res == 0; } //------------------------------------------------------------------------- bool dbg_rpc_handler_t::handle() { bytevec_t req = prepare_rpc_packet(RPC_OPEN); req.pack_dd(IDD_INTERFACE_VERSION); req.pack_dd(DEBUGGER_ID); req.pack_dd(sizeof(ea_t)); bool send_response = false; rpc_packet_t *rp = send_request_and_receive_reply(req, PREQ_MUST_LOGIN); bool ok = rp != NULL; if ( ok ) { send_response = true; // Answer is after rpc_packet_t memory_deserializer_t mmdsr(rp+1, rp->length); ok = mmdsr.unpack_dd() != 0; if ( !ok ) { lprintf("[%d] Incompatible IDA version\n", session_id); send_response = false; } else if ( !dispatcher->server_password.empty() ) { const char *pass = mmdsr.unpack_str(); if ( !password_matches(pass, dispatcher->server_password.c_str()) ) { lprintf("[%d] Bad password\n", session_id); ok = false; } } logged_in = ok; qfree(rp); } else { lprintf("[%d] Could not establish the connection\n", session_id); } if ( send_response ) { req = prepare_rpc_packet(RPC_OK); req.pack_dd(ok); send_data(req); // remove reception timeout on the server side recv_timeout = -1; logged_in = true; if ( ok ) { // the main loop: handle client requests until it drops the connection // or sends us RPC_OK (see rpc_debmod_t::close_remote) bytevec_t empty; rpc_packet_t *packet = send_request_and_receive_reply(empty); if ( packet != NULL ) qfree(packet); } } network_error = false; bool preserve_server = false; if ( get_broken_connection() ) { if ( dispatcher->on_broken_conn == BCH_KEEP_DEBMOD ) { term_irs(); lprintf("[%d] Debugged session entered into sleeping mode\n", session_id); prepare_broken_connection(); preserve_server = true; } else { if ( dispatcher->on_broken_conn == BCH_KILL_PROCESS ) { int pid = get_debugger_instance()->pid; if ( pid > 0 ) { lprintf("[%d] Killing debugged process %d\n", session_id, get_debugger_instance()->pid); int code = kill_process(); if ( code != 0 ) lprintf("[%d] Failed to kill process after %d seconds. Giving up\n", session_id, code); } } goto TERM_DEBMOD; } } else { TERM_DEBMOD: get_debugger_instance()->dbg_term(); term_irs(); } return !preserve_server; } //-------------------------------------------------------------------------- void dbg_rpc_handler_t::set_debugger_instance(debmod_t *instance) { dbg_mod = instance; dbg_mod->rpc = this; } //-------------------------------------------------------------------------- #ifdef VERBOSE_ENABLED static const char *bptcode2str(uint code) { static const char *const strs[] = { "BPT_OK", "BPT_INTERNAL_ERR", "BPT_BAD_TYPE", "BPT_BAD_ALIGN", "BPT_BAD_ADDR", "BPT_BAD_LEN", "BPT_TOO_MANY", "BPT_READ_ERROR", "BPT_WRITE_ERROR", "BPT_SKIP", "BPT_PAGE_OK", }; if ( code >= qnumber(strs) ) return "?"; return strs[code]; } #endif //-------------------------------------------------------------------------- int dbg_rpc_handler_t::rpc_update_bpts(bytevec_t &req, memory_deserializer_t &mmdsr) { update_bpt_vec_t bpts; int nadd = mmdsr.unpack_dd(); int ndel = mmdsr.unpack_dd(); if ( nadd < 0 || ndel < 0 || INT_MAX - ndel < nadd ) { req.pack_dd(0); verb(("update_bpts(nadd=%d, ndel=%d) => 0 (incorrect values)\n", nadd, ndel)); return 0; } bpts.resize(nadd+ndel); ea_t ea = 0; update_bpt_vec_t::iterator b; update_bpt_vec_t::iterator bend = bpts.begin() + nadd; for ( b=bpts.begin(); b != bend; ++b ) { b->code = BPT_OK; b->ea = ea + mmdsr.unpack_ea64(); ea = b->ea; b->size = mmdsr.unpack_dd(); b->type = mmdsr.unpack_dd(); b->pid = mmdsr.unpack_dd(); b->tid = mmdsr.unpack_dd(); } ea = 0; bend += ndel; for ( ; b != bend; ++b ) { b->ea = ea + mmdsr.unpack_ea64(); ea = b->ea; uchar len = mmdsr.unpack_db(); if ( len > 0 ) { b->orgbytes.resize(len); mmdsr.unpack_obj(b->orgbytes.begin(), len); } b->type = mmdsr.unpack_dd(); b->pid = mmdsr.unpack_dd(); b->tid = mmdsr.unpack_dd(); } #ifdef VERBOSE_ENABLED for ( b=bpts.begin()+nadd; b != bend; ++b ) verb(("del_bpt(ea=%a, type=%d orgbytes.size=%" FMT_Z " size=%d)\n", b->ea, b->type, b->orgbytes.size(), b->type != BPT_SOFT ? b->size : 0)); #endif int nbpts; qstring errbuf; drc_t drc = dbg_mod->dbg_update_bpts(&nbpts, bpts.begin(), nadd, ndel, &errbuf); bend = bpts.begin() + nadd; #ifdef VERBOSE_ENABLED for ( b=bpts.begin(); b != bend; ++b ) verb(("add_bpt(ea=%a type=%d len=%d) => %s\n", b->ea, b->type, b->size, bptcode2str(b->code))); #endif req.pack_dd(drc); req.pack_dd(nbpts); for ( b=bpts.begin(); b != bend; ++b ) { req.pack_db(b->code); if ( b->code == BPT_OK && b->type == BPT_SOFT ) { req.pack_db(b->orgbytes.size()); req.append(b->orgbytes.begin(), b->orgbytes.size()); } } bend += ndel; for ( ; b != bend; ++b ) { req.pack_db(b->code); verb(("del_bpt(ea=%a) => %s\n", b->ea, bptcode2str(b->code))); } if ( drc != DRC_OK ) req.pack_str(errbuf); return drc; } //-------------------------------------------------------------------------- void dbg_rpc_handler_t::rpc_update_lowcnds( bytevec_t &req, memory_deserializer_t &mmdsr) { ea_t ea = 0; lowcnd_vec_t lowcnds; int nlowcnds = mmdsr.unpack_dd(); lowcnds.resize(nlowcnds); lowcnd_t *lc = lowcnds.begin(); for ( int i=0; i < nlowcnds; i++, lc++ ) { lc->compiled = false; lc->ea = ea + mmdsr.unpack_ea64(); ea = lc->ea; lc->cndbody = mmdsr.unpack_str(); if ( !lc->cndbody.empty() ) { lc->size = 0; lc->type = mmdsr.unpack_dd(); if ( lc->type != BPT_SOFT ) lc->size = mmdsr.unpack_dd(); int norg = mmdsr.unpack_db(); if ( norg > 0 ) { lc->orgbytes.resize(norg); mmdsr.unpack_obj(lc->orgbytes.begin(), norg); } lc->cmd.ea = mmdsr.unpack_ea64(); if ( lc->cmd.ea != BADADDR ) mmdsr.unpack_obj(&lc->cmd, sizeof(lc->cmd)); } verb(("update_lowcnd(ea=%a cnd=%s)\n", ea, lc->cndbody.c_str())); } int nupdated; qstring errbuf; drc_t drc = dbg_mod->dbg_update_lowcnds(&nupdated, lowcnds.begin(), nlowcnds, &errbuf); verb((" update_lowcnds => %d\n", drc)); req.pack_dd(drc); req.pack_dd(nupdated); if ( drc != DRC_OK ) req.pack_str(errbuf); } //-------------------------------------------------------------------------- bool dbg_rpc_handler_t::check_broken_connection(pid_t pid) { bool result = false; dispatcher->clients_list->lock(); client_handlers_list_t::storage_t::iterator p; for ( p = dispatcher->clients_list->storage.begin(); p != dispatcher->clients_list->storage.end(); ++p ) { dbg_rpc_handler_t *h = (dbg_rpc_handler_t *) p->first; if ( h == this ) continue; debmod_t *d = h->get_debugger_instance(); if ( d->broken_connection && d->pid == pid && d->dbg_continue_broken_connection(pid) ) { dbg_mod->dbg_term(); delete dbg_mod; dbg_mod = d; result = true; verb(("reusing previously broken debugging session\n")); #ifndef __SINGLE_THREADED_SERVER__ qthread_t thr = p->second; // free thread if ( thr != NULL ) qthread_free(thr); #endif h->term_irs(); dispatcher->clients_list->storage.erase(p); delete h; d->broken_connection = false; break; } } dispatcher->clients_list->unlock(); return result; } //------------------------------------------------------------------------- int dbg_rpc_handler_t::handle_server_ioctl( int fn, void **out, ssize_t *outsz, memory_deserializer_t &mmdsr) { int code = -1; verb(("handle_server_ioctl(fn=%d, bufsize=%" FMT_Z ").\n", fn, mmdsr.size())); return code; } //------------------------------------------------------------------------- int dbg_rpc_handler_t::on_recv_packet_progress(bool *performed) { *performed = poll_debug_events; return poll_debug_events ? poll_events(TIMEOUT) : 0; } //-------------------------------------------------------------------------- drc_t dbg_rpc_handler_t::rpc_attach_process( qstring *errbuf, memory_deserializer_t &mmdsr) { pid_t pid = mmdsr.unpack_dd(); int event_id = mmdsr.unpack_dd(); int flags = mmdsr.unpack_dd(); drc_t drc = check_broken_connection(pid) ? DRC_OK : dbg_mod->dbg_attach_process(pid, event_id, flags, errbuf); verb(("attach_process(pid=%d, evid=%d) => %d\n", pid, event_id, drc)); return drc; } //------------------------------------------------------------------------- void dbg_rpc_handler_t::append_start_or_attach(bytevec_t &req, drc_t drc, const qstring &errbuf) const { req.pack_dd(drc); if ( drc > DRC_NONE ) { debapp_attrs_t attrs; dbg_mod->dbg_get_debapp_attrs(&attrs); append_debapp_attrs(req, attrs); append_dynamic_register_set(req, dbg_mod->idaregs); } else { req.pack_str(errbuf); } } //------------------------------------------------------------------------- void dbg_rpc_handler_t::shutdown_gracefully(int /*signum*/) { debmod_t *d = get_debugger_instance(); if ( d != NULL ) d->dbg_exit_process(NULL); // kill the process instead of letting it run in wild } //-------------------------------------------------------------------------- // performs requests on behalf of a remote client // client -> server #ifdef __UNIX__ # define PERM_0755 (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) # define IS_SUBPATH strneq #else # define PERM_0755 0755 # define IS_SUBPATH strnieq #endif bytevec_t dbg_rpc_handler_t::on_send_request_interrupt(const rpc_packet_t *rp) { // While the server is performing a request, it should not poll // for debugger events bool saved_poll_mode = poll_debug_events; poll_debug_events = false; memory_deserializer_t mmdsr(rp+1, rp->length); bytevec_t req = prepare_rpc_packet(RPC_OK); #if defined(__EXCEPTIONS) || defined(__NT__) try #endif { switch ( rp->code ) { case RPC_INIT: { dbg_mod->debugger_flags = mmdsr.unpack_dd(); bool debug_debugger = mmdsr.unpack_dd() != 0; if ( debug_debugger ) verbose = true; dbg_mod->dbg_set_debugging(debug_debugger); qstring errbuf; uint32_t flags2 = 0; drc_t drc = dbg_mod->dbg_init(&flags2, &errbuf); verb(("init(debug_debugger=%d) => %d (flags2=%d)\n", debug_debugger, drc, flags2)); req.pack_dd(drc); req.pack_dd(flags2); if ( drc != DRC_OK ) req.pack_str(errbuf); } break; case RPC_TERM: // Do not dbg_term() here, as it will be called // at the end of server.cpp's handle_single_session(), // right after this. // dbg_mod->dbg_term(); // verb(("term()\n")); break; case RPC_GET_PROCESSES: { procinfo_vec_t procs; qstring errbuf; drc_t drc = dbg_mod->dbg_get_processes(&procs, &errbuf); req.pack_dd(drc); if ( drc == DRC_OK ) append_process_info_vec(req, &procs); else req.pack_str(errbuf); verb(("get_processes() => %d\n", drc)); } break; case RPC_DETACH_PROCESS: { drc_t drc = dbg_mod->dbg_detach_process(); req.pack_dd(drc); verb(("detach_process() => %d\n", drc)); } break; case RPC_START_PROCESS: { const char *path = mmdsr.unpack_str(); const char *args = mmdsr.unpack_str(); const char *sdir = mmdsr.unpack_str(); int flags = mmdsr.unpack_dd(); const char *input = mmdsr.unpack_str(); uint32 crc32 = mmdsr.unpack_dd(); qstring errbuf; drc_t drc = DRC_NOFILE; if ( path != NULL && sdir != NULL && input != NULL ) // protect against malicious ida { drc = dbg_mod->dbg_start_process(path, args, sdir, flags, input, crc32, &errbuf); verb(("start_process(path=%s args=%s flags=%s%s%s\n" " sdir=%s\n" " input=%s crc32=%x) => %d\n", path, args, flags & DBG_PROC_IS_DLL ? " is_dll" : "", flags & DBG_PROC_IS_GUI ? " under_gui" : "", flags & DBG_HIDE_WINDOW ? " hide_window" : "", sdir, input, crc32, drc)); } append_start_or_attach(req, drc, errbuf); } break; case RPC_GET_DEBUG_EVENT: { int timeout_ms = mmdsr.unpack_dd(); gdecode_t result = GDE_NO_EVENT; if ( !has_pending_event ) result = dbg_mod->dbg_get_debug_event(&ev, timeout_ms); req.pack_dd(result); if ( result >= GDE_ONE_EVENT ) { append_debug_event(req, &ev); verb(("got event: %s\n", debug_event_str(&ev))); } else if ( !has_pending_event ) { saved_poll_mode = true; } verbev(("get_debug_event(timeout=%d) => %d (has_pending=%d, willpoll=%d)\n", timeout_ms, result, has_pending_event, saved_poll_mode)); } break; case RPC_ATTACH_PROCESS: { qstring errbuf; append_start_or_attach(req, rpc_attach_process(&errbuf, mmdsr), errbuf); } break; case RPC_PREPARE_TO_PAUSE_PROCESS: { qstring errbuf; drc_t drc = dbg_mod->dbg_prepare_to_pause_process(&errbuf); verb(("prepare_to_pause_process() => %d\n", drc)); req.pack_dd(drc); if ( drc < DRC_NONE ) req.pack_str(errbuf); } break; case RPC_EXIT_PROCESS: { qstring errbuf; drc_t drc = dbg_mod->dbg_exit_process(&errbuf); verb(("exit_process() => %d\n", drc)); req.pack_dd(drc); if ( drc < DRC_NONE ) req.pack_str(errbuf); } break; case RPC_CONTINUE_AFTER_EVENT: { extract_debug_event(&ev, mmdsr); drc_t drc = dbg_mod->dbg_continue_after_event(&ev); verb(("continue_after_event(...) => %d\n", drc)); req.pack_dd(drc); } break; case RPC_STOPPED_AT_DEBUG_EVENT: { bool dlls_added = mmdsr.unpack_db() != 0; bool ask_thr_names = mmdsr.unpack_db() != 0; import_infos_t infos; thread_name_vec_t thr_names; dbg_mod->dbg_stopped_at_debug_event(&infos, dlls_added, ask_thr_names ? &thr_names : NULL); process_import_requests(infos); name_info_t *ni = dbg_mod->get_debug_names(); int err = RPC_OK; if ( ni != NULL ) { err = send_debug_names_to_ida(ni->addrs.begin(), ni->names.begin(), (int)ni->addrs.size()); dbg_mod->clear_debug_names(); } if ( ask_thr_names ) { uint32 n = thr_names.size(); req.pack_dd(n); for ( int i=0; i < n; ++i ) { thread_name_t &tn = thr_names[i]; req.pack_dd(tn.tid); req.pack_str(tn.name); } } } break; case RPC_TH_SUSPEND: { thid_t tid = mmdsr.unpack_dd(); drc_t drc = dbg_mod->dbg_thread_suspend(tid); verb(("thread_suspend(tid=%d) => %d\n", tid, drc)); req.pack_dd(drc); } break; case RPC_TH_CONTINUE: { thid_t tid = mmdsr.unpack_dd(); drc_t drc = dbg_mod->dbg_thread_continue(tid); verb(("thread_continue(tid=%d) => %d\n", tid, drc)); req.pack_dd(drc); } break; case RPC_SET_RESUME_MODE: { thid_t tid = mmdsr.unpack_dd(); resume_mode_t resmod = resume_mode_t(mmdsr.unpack_dd()); drc_t drc = dbg_mod->dbg_set_resume_mode(tid, resmod); verb(("set_resume_mode(tid=%d, resmod=%d) => %d\n", tid, resmod, drc)); req.pack_dd(drc); } break; case RPC_READ_REGS: { drc_t drc = DRC_NONE; qstring errbuf; bytevec_t regmap; regvals_t values; thid_t tid = mmdsr.unpack_dd(); int clsmask = mmdsr.unpack_dd(); int nregs = mmdsr.unpack_dd(); if ( nregs <= 0 || nregs > dbg_mod->nregs ) { errbuf.sprnt("read_regs(tid=%d, mask=%x, nregs=%d) => 0 " "(incorrect nregs, should be in range 0..%d)\n", tid, clsmask, nregs, dbg_mod->nregs); } else { regmap.resize((nregs+7)/8); mmdsr.unpack_obj(regmap.begin(), regmap.size()); values.resize(dbg_mod->nregs); drc = dbg_mod->dbg_read_registers(tid, clsmask, values.begin(), &errbuf); verb(("read_regs(tid=%d, mask=%x) => %d\n", tid, clsmask, drc)); } req.pack_dd(drc); if ( drc == DRC_OK ) append_regvals(req, values.begin(), nregs, regmap.begin()); else req.pack_str(errbuf); } break; case RPC_WRITE_REG: { thid_t tid = mmdsr.unpack_dd(); int reg_idx = mmdsr.unpack_dd(); regval_t value; unpack_regvals(&value, 1, NULL, mmdsr); qstring errbuf; drc_t drc = dbg_mod->dbg_write_register(tid, reg_idx, &value, &errbuf); verb(("write_reg(tid=%d) => %d\n", tid, drc)); req.pack_dd(drc); if ( drc < DRC_NONE ) req.pack_str(errbuf); } break; case RPC_GET_SREG_BASE: { thid_t tid = mmdsr.unpack_dd(); int sreg_value = mmdsr.unpack_dd(); ea_t ea; qstring errbuf; drc_t drc = dbg_mod->dbg_thread_get_sreg_base(&ea, tid, sreg_value, &errbuf); verb(("get_thread_sreg_base(tid=%d, %d) => %a\n", tid, sreg_value, drc == DRC_OK ? ea : BADADDR)); req.pack_dd(drc); if ( drc == DRC_OK ) req.pack_ea64(ea); else req.pack_str(errbuf); } break; case RPC_SET_EXCEPTION_INFO: { int qty = mmdsr.unpack_dd(); exception_info_t *extable = extract_exception_info(qty, mmdsr); dbg_mod->dbg_set_exception_info(extable, qty); delete [] extable; verb(("set_exception_info(qty=%d)\n", qty)); } break; case RPC_GET_MEMORY_INFO: { meminfo_vec_t areas; qstring errbuf; drc_t drc = dbg_mod->dbg_get_memory_info(areas, &errbuf); int qty = areas.size(); verb(("get_memory_info() => %d (qty=%d)\n", drc, qty)); req.pack_dd(drc + (-DRC_IDBSEG)); if ( drc == DRC_OK ) { req.pack_dd(qty); for ( int i=0; i < qty; i++ ) append_memory_info(req, &areas[i]); } else { req.pack_str(errbuf); } } break; case RPC_GET_SCATTERED_IMAGE: { ea_t base = mmdsr.unpack_ea64(); scattered_image_t si; int result = dbg_mod->dbg_get_scattered_image(si, base); int qty = si.size(); verb(("get_scattered_image(base=%a) => %d (qty=%d)\n", base, result, qty)); req.pack_dd(result+2); if ( result > 0 ) { req.pack_dd(qty); for ( int i=0; i < qty; i++ ) append_scattered_segm(req, &si[i]); } } break; case RPC_GET_IMAGE_UUID: { ea_t base = mmdsr.unpack_ea64(); bytevec_t uuid; bool result = dbg_mod->dbg_get_image_uuid(&uuid, base); int size = uuid.size(); verb(("get_image_uuid(base=%a) => %d (size=%d)\n", base, result, size)); req.pack_dd(result); if ( result ) req.pack_buf(uuid.begin(), size); } break; case RPC_GET_SEGM_START: { ea_t base = mmdsr.unpack_ea64(); const char *segname = mmdsr.unpack_str(); ea_t result = dbg_mod->dbg_get_segm_start(base, segname); verb(("get_segm_start(base=%a, segname=%s) => %a\n", base, segname, result)); req.pack_ea64(result); } break; case RPC_READ_MEMORY: { ea_t ea = mmdsr.unpack_ea64(); size_t size = mmdsr.unpack_dd(); uchar *buf = new uchar[size]; qstring errbuf; ssize_t result = dbg_mod->dbg_read_memory(ea, buf, size, &errbuf); verb(("read_memory(ea=%a size=%" FMT_Z ") => %" FMT_ZS, ea, size, result)); if ( result > 0 && size == 1 ) verb((" (0x%02X)\n", *buf)); else verb(("\n")); req.pack_dd(uint32(result)); if ( result > 0 ) req.append(buf, result); else req.pack_str(errbuf); delete[] buf; } break; case RPC_WRITE_MEMORY: { ea_t ea = mmdsr.unpack_ea64(); size_t size = mmdsr.unpack_dd(); uchar *buf = new uchar[size]; mmdsr.unpack_obj(buf, size); qstring errbuf; ssize_t result = dbg_mod->dbg_write_memory(ea, buf, size, &errbuf); verb(("write_memory(ea=%a size=%" FMT_Z ") => %" FMT_ZS, ea, size, result)); if ( result && size == 1 ) verb((" (0x%02X)\n", *buf)); else verb(("\n")); req.pack_dd(uint32(result)); if ( result <= 0 ) req.pack_str(errbuf); delete[] buf; } break; case RPC_ISOK_BPT: { bpttype_t type = mmdsr.unpack_dd(); ea_t ea = mmdsr.unpack_ea64(); int len = mmdsr.unpack_dd() - 1; int result = dbg_mod->dbg_is_ok_bpt(type, ea, len); verb(("isok_bpt(type=%d ea=%a len=%d) => %d\n", type, ea, len, result)); req.pack_dd(result); } break; case RPC_UPDATE_BPTS: { int ret = rpc_update_bpts(req, mmdsr); if ( ret == 0 ) verb(("rpc_update_bpts failed!\n")); } break; case RPC_UPDATE_LOWCNDS: rpc_update_lowcnds(req, mmdsr); break; case RPC_EVAL_LOWCND: { thid_t tid = mmdsr.unpack_dd(); ea_t ea = mmdsr.unpack_ea64(); qstring errbuf; drc_t drc = dbg_mod->dbg_eval_lowcnd(tid, ea, &errbuf); req.pack_dd(drc); if ( drc != DRC_OK ) req.pack_str(errbuf); verb(("eval_lowcnd(tid=%d, ea=%a) => %d\n", tid, ea, drc)); } break; case RPC_OPEN_FILE: { const char *path = mmdsr.unpack_str(); bool readonly = mmdsr.unpack_dd() != 0; int64 fsize = 0; int fn = find_free_channel(); if ( fn != -1 ) { if ( readonly ) { channels[fn] = fopenRB(path); } else { char dir[QMAXPATH]; if ( qdirname(dir, sizeof(dir), path) && !qisdir(dir) ) { char absdir[QMAXPATH]; qmake_full_path(absdir, sizeof(absdir), dir); char cwd[QMAXPATH]; qgetcwd(cwd, sizeof(cwd)); if ( IS_SUBPATH(absdir, cwd, qstrlen(cwd)) ) { qstrvec_t subpaths; while ( !qisdir(absdir) ) { subpaths.insert(subpaths.begin(), absdir); if ( !qdirname(absdir, sizeof(absdir), absdir) ) break; } for ( size_t i = 0, n = subpaths.size(); i < n; ++i ) { const char *subdir = subpaths[i].c_str(); verb(("open_file() creating directory %s\n", subdir)); if ( qmkdir(subdir, PERM_0755) != 0 ) break; } } } channels[fn] = fopenWB(path); } if ( channels[fn] == NULL ) fn = -1; else if ( readonly ) fsize = qfsize(channels[fn]); } verb(("open_file('%s', %d) => %d %" FMT_64 "d\n", path, readonly, fn, fsize)); req.pack_dd(fn); if ( fn != -1 ) req.pack_dq(fsize); else req.pack_dd(qerrcode()); } break; case RPC_CLOSE_FILE: { int fn = mmdsr.unpack_dd(); if ( fn >= 0 && fn < qnumber(channels) ) { FILE *fp = channels[fn]; if ( fp != NULL ) { #ifdef __UNIX__ fchmod(fileno(fp), PERM_0755); // set mode 0755 for unix applications #endif qfclose(fp); channels[fn] = NULL; } } verb(("close_file(%d)\n", fn)); } break; case RPC_READ_FILE: { char *buf = NULL; int fn = mmdsr.unpack_dd(); int64 off = mmdsr.unpack_dq(); int32 size = mmdsr.unpack_dd(); int32 s2 = 0; if ( size > 0 ) { buf = new char[size]; qfseek(channels[fn], off, SEEK_SET); s2 = qfread(channels[fn], buf, size); } req.pack_dd(s2); if ( size != s2 ) req.pack_dd(qerrcode()); else req.append(buf, s2); delete[] buf; verb(("read_file(%d, 0x%" FMT_64 "X, %d) => %d\n", fn, off, size, s2)); } break; case RPC_WRITE_FILE: { char *buf = NULL; int fn = mmdsr.unpack_dd(); uint64 off = mmdsr.unpack_dq(); uint32 size = mmdsr.unpack_dd(); if ( size > 0 ) { buf = new char[size]; mmdsr.unpack_obj(buf, size); } qfseek(channels[fn], off, SEEK_SET); uint32 s2 = buf == NULL ? 0 : qfwrite(channels[fn], buf, size); req.pack_dd(size); if ( size != s2 ) req.pack_dd(qerrcode()); delete [] buf; verb(("write_file(%d, 0x%" FMT_64 "X, %u) => %u\n", fn, off, size, s2)); } break; case RPC_EVOK: req.clear(); verbev(("got evok!\n")); break; case RPC_IOCTL: { int code = handle_ioctl_packet(req, mmdsr.ptr, mmdsr.end); if ( code != RPC_OK ) req = prepare_rpc_packet((uchar)code); } break; case RPC_UPDATE_CALL_STACK: { call_stack_t trace; thid_t tid = mmdsr.unpack_dd(); drc_t drc = dbg_mod->dbg_update_call_stack(tid, &trace); req.pack_dd(drc); if ( drc == DRC_OK ) append_call_stack(req, trace); } break; case RPC_APPCALL: { ea_t func_ea = mmdsr.unpack_ea64(); thid_t tid = mmdsr.unpack_dd(); int stkarg_nbytes = mmdsr.unpack_dd(); int flags = mmdsr.unpack_dd(); regobjs_t regargs, retregs; relobj_t stkargs; regobjs_t *rr = (flags & APPCALL_MANUAL) == 0 ? &retregs : NULL; extract_appcall(®args, &stkargs, rr, mmdsr); qstring errbuf; debug_event_t event; ea_t sp = dbg_mod->dbg_appcall(func_ea, tid, stkarg_nbytes, ®args, &stkargs, &retregs, &errbuf, &event, flags); req.pack_ea64(sp); if ( sp == BADADDR ) { if ( (flags & APPCALL_DEBEV) != 0 ) append_debug_event(req, &event); req.pack_str(errbuf); } else if ( (flags & APPCALL_MANUAL) == 0 ) { append_regobjs(req, retregs, true); } } break; case RPC_CLEANUP_APPCALL: { thid_t tid = mmdsr.unpack_dd(); drc_t drc = dbg_mod->dbg_cleanup_appcall(tid); req.pack_dd(drc); } break; case RPC_REXEC: { const char *cmdline = mmdsr.unpack_str(); int code = dbg_mod->dbg_rexec(cmdline); req.pack_dd(code); } break; case RPC_BIN_SEARCH: { ea_t start_ea = mmdsr.unpack_ea64(); ea_t end_ea = mmdsr.unpack_ea64(); int cnt = mmdsr.unpack_dd(); compiled_binpat_vec_t ptns; ptns.resize(cnt); for ( int i=0; i < cnt; ++i ) { compiled_binpat_t &p = ptns[i]; // bytes int sz = mmdsr.unpack_dd(); if ( sz != 0 ) { p.bytes.resize(sz); mmdsr.unpack_obj(p.bytes.begin(), sz); } // mask sz = mmdsr.unpack_dd(); if ( sz != 0 ) { p.mask.resize(sz); mmdsr.unpack_obj(p.mask.begin(), sz); } // strlits sz = mmdsr.unpack_dd(); p.strlits.resize(sz); for ( int j=0; j < sz; ++j ) { p.strlits[j].start_ea = mmdsr.unpack_ea64(); p.strlits[j].end_ea = mmdsr.unpack_ea64(); } // encidx p.encidx = mmdsr.unpack_dd(); } int srch_flags = mmdsr.unpack_dd(); ea_t srch_ea; qstring errbuf; drc_t drc = dbg_mod->dbg_bin_search(&srch_ea, start_ea, end_ea, ptns, srch_flags, &errbuf); req.pack_dd(drc); if ( drc == DRC_OK ) req.pack_ea64(srch_ea); else if ( drc != DRC_FAILED ) // DRC_FAILED means not found req.pack_str(errbuf); } break; default: req = prepare_rpc_packet(RPC_UNK); break; } } #if defined(__EXCEPTIONS) || defined(__NT__) catch ( const std::bad_alloc & ) { req = prepare_rpc_packet(RPC_MEM); } #endif if ( saved_poll_mode ) poll_debug_events = true; return req; } //-------------------------------------------------------------------------- // poll for events from the debugger module int dbg_rpc_handler_t::poll_events(int timeout_ms) { int code = 0; if ( !has_pending_event ) { // immediately set poll_debug_events to false to avoid recursive calls. poll_debug_events = false; has_pending_event = dbg_mod->dbg_get_debug_event(&pending_event, timeout_ms) >= GDE_ONE_EVENT; if ( has_pending_event ) { verbev(("got event, sending it, poll will be 0 now\n")); bytevec_t req = prepare_rpc_packet(RPC_EVENT); append_debug_event(req, &pending_event); code = send_data(req); has_pending_event = false; } else { // no event, continue to poll poll_debug_events = true; } } return code; } //-------------------------------------------------------------------------- // this function runs on the server side // a dbg_rpc_client sends an RPC_SYNC request and the server must give the stub to the client bool dbg_rpc_handler_t::rpc_sync_stub(const char *server_stub_name, const char *ida_stub_name) { bool ok = false; int32 crc32 = -1; linput_t *li = open_linput(server_stub_name, false); if ( li != NULL ) { crc32 = calc_file_crc32(li); close_linput(li); } bytevec_t stub = prepare_rpc_packet(RPC_SYNC_STUB); stub.pack_str(ida_stub_name); stub.pack_dd(crc32); rpc_packet_t *rp = send_request_and_receive_reply(stub); if ( rp == NULL ) return ok; const uchar *answer = (uchar *)(rp+1); const uchar *end = answer + rp->length; size_t size = unpack_dd(&answer, end); if ( size == 1 ) { ok = true; } else if ( size != 0 ) { FILE *fp = fopenWB(server_stub_name); if ( fp != NULL ) { ok = qfwrite(fp, answer, size) == size; dmsg("Updated kernel debugger stub: %s\n", ok ? "success" : "failed"); qfclose(fp); } else { dwarning("Could not update the kernel debugger stub.\n%s", qerrstr()); } } qfree(rp); return ok; } //-------------------------------------------------------------------------- //lint -e{818} 'addrs' could be declared as pointing to const int dbg_rpc_handler_t::send_debug_names_to_ida(ea_t *addrs, const char *const *names, int qty) { if ( qty == 0 ) return RPC_OK; bytevec_t buf; const size_t SZPACKET = 1300; // try not to go over the usual network MSS // (this number is slightly less that 1460 because // we stop the loop going over this number) while ( qty > 0 ) { buf.qclear(); ea_t old = 0; const char *optr = ""; // Start appending names and EAs int i = 0; while ( i < qty ) { adiff_t diff = *addrs - old; bool neg = diff < 0; if ( neg ) diff = -diff; buf.pack_ea64(diff); // send address deltas buf.pack_dd(neg); old = *addrs; const char *nptr = *names; int len = 0; // do not send repeating prefixes of names while ( nptr[len] != '\0' && nptr[len] == optr[len] ) //lint !e690 wrong access len++; buf.pack_dd(len); buf.pack_str(nptr+len); optr = nptr; addrs++; names++; i++; if ( buf.size() > SZPACKET ) break; } qty -= i; bytevec_t req = prepare_rpc_packet(RPC_SET_DEBUG_NAMES); req.pack_dd(i); req.append(buf.begin(), buf.size()); // should return a qty as much as sent...if not probably network error! if ( i != send_request_get_long_result(req) ) return RPC_UNK; } return RPC_OK; } //-------------------------------------------------------------------------- int dbg_rpc_handler_t::send_debug_event_to_ida(const debug_event_t *debev, int rqflags) { bytevec_t req = prepare_rpc_packet(RPC_HANDLE_DEBUG_EVENT); append_debug_event(req, debev); req.pack_dd(rqflags); return send_request_get_long_result(req); } //-------------------------------------------------------------------------- void dbg_rpc_handler_t::process_import_requests(const import_infos_t &infos) { // in an effort to avoid sending large amounts of symbol data over the network, // we attempt to import symbols for each dll on the client side. // if the client does not support such behavior, then we simply parse the symbols // on the server side and append to the list of debug names to send to IDA, as normal. for ( import_infos_t::const_iterator i = infos.begin(); i != infos.end(); ++i ) { ea_t base = i->base; const char *path = i->path.c_str(); const bytevec_t &uuid = i->uuid; bytevec_t req = prepare_rpc_packet(RPC_IMPORT_DLL); req.pack_ea64(base); req.pack_str(path); req.pack_buf(uuid.begin(), uuid.size()); int code = send_request_get_long_result(req); if ( code < 0 ) // cancelled or network error return; if ( code != 0 ) // request failed, fall back to parsing symbols server-side dbg_mod->import_dll(*i); } } //-------------------------------------------------------------------------- bool dbg_rpc_handler_t::get_broken_connection(void) { return get_debugger_instance()->broken_connection; } //-------------------------------------------------------------------------- void dbg_rpc_handler_t::set_broken_connection(void) { get_debugger_instance()->broken_connection = true; } //------------------------------------------------------------------------- int dbg_rpc_handler_t::kill_process(void) { const int NSEC = 5; dbg_mod->dbg_exit_process(NULL); // now, wait up to NSEC seconds until the process is gone qtime64_t wait_start = qtime64(); qtime64_t wait_threshold = make_qtime64( get_secs(wait_start) + NSEC, get_usecs(wait_start)); while ( qtime64() < wait_threshold ) { gdecode_t result = dbg_mod->dbg_get_debug_event(&ev, 100); if ( result >= GDE_ONE_EVENT ) { dbg_mod->dbg_continue_after_event(&ev); if ( ev.eid() == PROCESS_EXITED ) return 0; } } return NSEC; } //-------------------------------------------------------------------------- int debmod_t::send_debug_names_to_ida(ea_t *addrs, const char *const *names, int qty) { dbg_rpc_handler_t *s = (dbg_rpc_handler_t *)rpc; return s->send_debug_names_to_ida(addrs, names, qty); } //-------------------------------------------------------------------------- int debmod_t::send_debug_event_to_ida(const debug_event_t *ev, int rqflags) { dbg_rpc_handler_t *s = (dbg_rpc_handler_t *)rpc; return s->send_debug_event_to_ida(ev, rqflags); }