// This file is included in the debugger stub that runs on the computer with IDA #include #include #include #include "dbg_rpc_client.h" #include "dbg_rpc_hlp.h" #include "debmod.h" //-------------------------------------------------------------------------- // check and send to the remote server the specified stub // do it only if its crc does not match the specified crc // this function runs on the local machine with ida interface static uchar *sync_stub(const char *fname, uint32 crc, size_t *psize) { bool complain = true; uchar *retval = NULL; char path[QMAXPATH]; if ( getsysfile(path, sizeof(path), fname, NULL) != NULL ) { linput_t *li = open_linput(path, false); if ( li != NULL ) { int64 size = qlsize(li); if ( size > 0 ) { bytevec_t buf; buf.resize(size); if ( qlread(li, buf.begin(), size) == size ) { complain = false; if ( calc_crc32(0, buf.begin(), size) != crc ) { *psize = size; retval = buf.extract(); } else { msg("Kernel debugger stub is up to date...\n"); *psize = 1; // signal ok } } } close_linput(li); } } if ( complain ) warning("AUTOHIDE NONE\nCould not find/read debugger stub %s", fname); return retval; } //-------------------------------------------------------------------------- dbg_rpc_client_t::dbg_rpc_client_t(idarpc_stream_t *_irs) : dbg_rpc_engine_t(/*is_client=*/ true), client_irs(_irs) { pending_event.clear_all(); verbose = false; recv_timeout = RECV_TIMEOUT_PERIOD; } //------------------------------------------------------------------------- void dbg_rpc_client_t::my_update_wait_dialog( const char *message, const rpc_packet_t *rp) { if ( send_request_data.wait_dialog_displayed ) { if ( rp->code != send_request_data.code ) replace_wait_box("%s", message); } else { show_wait_box("%s", message); send_request_data.wait_dialog_displayed = true; } send_request_data.code = rp->code; } //-------------------------------------------------------------------------- // requests received from the server. // here the client handles certain server -> client requests bytevec_t dbg_rpc_client_t::on_send_request_interrupt(const rpc_packet_t *rp) { memory_deserializer_t mmdsr(rp+1, rp->length); bytevec_t req = prepare_rpc_packet(RPC_OK); switch ( rp->code ) { // send_debug_names_to_ida() is thread safe case RPC_SET_DEBUG_NAMES: { my_update_wait_dialog("Downloading Symbols", rp); int qty = mmdsr.unpack_dd(); ea_t *addrs = OPERATOR_NEW(ea_t, qty); char **names = OPERATOR_NEW(char *, qty); qstring name; ea_t old = 0; for ( int i=0; i < qty; i++ ) { adiff_t o2 = mmdsr.unpack_ea64(); if ( mmdsr.unpack_dd() ) o2 = -o2; old += o2; addrs[i] = old; int oldlen = mmdsr.unpack_dd(); QASSERT(1203, oldlen >= 0 && oldlen <= name.length()); //keep the prefix name.resize(oldlen); if ( !mmdsr.unpack_str(&name) ) INTERR(1294); names[i] = qstrdup(name.c_str()); } int result = send_debug_names_to_ida(addrs, names, qty); verb(("set_debug_name(qty=%d) => %d\n", qty, result)); req.pack_dd(result); for ( int i=0; i < qty; i++ ) qfree(names[i]); delete [] addrs; delete [] names; } break; // import_dll() is thread safe case RPC_IMPORT_DLL: { my_update_wait_dialog("Importing DLLs", rp); ea_t base = mmdsr.unpack_ea64(); const char *path = mmdsr.unpack_str(); int n = mmdsr.unpack_dd(); const void *bytes = mmdsr.unpack_obj_inplace(n); bytevec_t uuid(bytes, n); int result = import_dll(import_request_t(base, path, uuid)); verb(("import_dll(base=%a, path=%s) => %d\n", base, path, result)); req.pack_dd(result); } break; // send_debug_event_to_ida() is thread safe case RPC_HANDLE_DEBUG_EVENT: { debug_event_t ev; extract_debug_event(&ev, mmdsr); int rqflags = mmdsr.unpack_dd(); int code = send_debug_event_to_ida(&ev, rqflags); req.pack_dd(code); } break; // sync_stub() is thread safe case RPC_SYNC_STUB: { const char *fname = mmdsr.unpack_str(); uint32 crc = mmdsr.unpack_dd(); // security problem: the debugger server should not be able to // read an arbitrary file on the local computer. therefore we completely // ignore the file name and use a hardcoded name. qnotused(fname); fname = "ida_kdstub.dll"; size_t size = 0; uchar *contents = sync_stub(fname, crc, &size); req.pack_dd((uint32)size); if ( contents != NULL ) { req.append(contents, size); qfree(contents); } } break; // msg/error/warning are thread safe case RPC_ERROR: case RPC_MSG: case RPC_WARNING: { const char *str = mmdsr.unpack_str(); if ( *str != '\0' ) { if ( rp->code == RPC_MSG ) msg("%s", str); else if ( rp->code == RPC_ERROR ) error("%s", str); else warning("%s", str); } } break; // no external functions are called case RPC_EVENT: { extract_debug_event(&pending_event, mmdsr); has_pending_event = true; req = prepare_rpc_packet(RPC_EVOK); verbev(("got event, storing it and sending RPC_EVOK\n")); } break; // i doubt that this code is used on the client side // ioctl_handler is NULL case RPC_IOCTL: { int code = handle_ioctl_packet(req, mmdsr.ptr, mmdsr.end); if ( code != RPC_OK ) return prepare_rpc_packet((uchar)code); } break; // report_idc_error() is thread safe case RPC_REPORT_IDC_ERROR: { ea_t ea = mmdsr.unpack_ea64(); error_t code = mmdsr.unpack_dd(); const char *errprm; ssize_t errval; if ( mmdsr.unpack_db() ) { errprm = mmdsr.unpack_str(); errval = (ssize_t)errprm; } else { errprm = NULL; errval = mmdsr.unpack_ea64(); } report_idc_error(NULL, ea, code, errval, errprm); } break; default: return prepare_rpc_packet(RPC_UNK); } return req; } //------------------------------------------------------------------------- void dbg_rpc_client_t::on_send_request_end(const rpc_packet_t *) { if ( send_request_data.wait_dialog_displayed ) hide_wait_box(); send_request_data.reset(); }