//-------------------------------------------------------------------------- ssize_t idaapi rpc_debmod_t::dbg_read_file(int fn, uint32 off, void *buf, size_t size) { bytevec_t req = prepare_rpc_packet(RPC_READ_FILE); append_dd(req, fn); append_dd(req, off); append_dd(req, (uint32)size); rpc_packet_t *rp = process_request(req); if ( rp == NULL ) return -1; const uchar *answer = (uchar *)(rp+1); const uchar *end = answer + rp->length; int32 rsize = extract_long(&answer, end); if ( size != rsize ) qerrcode(extract_long(&answer, end)); if ( rsize > 0 ) { if ( rsize > size ) error("rpc_read_file: protocol error"); extract_memory(&answer, end, buf, rsize); } qfree(rp); return rsize; }
//-------------------------------------------------------------------------- int idaapi rpc_debmod_t::dbg_attach_process(pid_t _pid, int event_id) { bytevec_t req = prepare_rpc_packet(RPC_ATTACH_PROCESS); append_dd(req, _pid); append_dd(req, event_id); return process_start_or_attach(req); }
//-------------------------------------------------------------------------- int idaapi rpc_debmod_t::dbg_write_register(thid_t tid, int reg_idx, const regval_t *value) { bytevec_t req = prepare_rpc_packet(RPC_WRITE_REG); append_dd(req, tid); append_dd(req, reg_idx); append_regvals(req, value, 1, NULL); return process_long(req); }
//-------------------------------------------------------------------------- int idaapi rpc_debmod_t::dbg_is_ok_bpt(bpttype_t type, ea_t ea, int len) { bytevec_t req = prepare_rpc_packet(RPC_ISOK_BPT); append_dd(req, type); append_ea64(req, ea); append_dd(req, len+1); return process_long(req); }
//-------------------------------------------------------------------------- int idaapi rpc_debmod_t::dbg_init(bool _debug_debugger) { has_pending_event = false; poll_debug_events = false; bytevec_t req = prepare_rpc_packet(RPC_INIT); append_dd(req, debugger.flags); append_dd(req, _debug_debugger); return process_long(req); }
//-------------------------------------------------------------------------- int rpc_debmod_t::getint2(uchar code, int x) { bytevec_t req = prepare_rpc_packet(code); append_dd(req, x); return process_long(req); }
//-------------------------------------------------------------------------- void idaapi rpc_debmod_t::dbg_close_file(int fn) { bytevec_t req = prepare_rpc_packet(RPC_CLOSE_FILE); append_dd(req, fn); qfree(process_request(req)); }
//-------------------------------------------------------------------------- int idaapi rpc_debmod_t::dbg_open_file(const char *file, uint32 *fsize, bool readonly) { bytevec_t req = prepare_rpc_packet(RPC_OPEN_FILE); append_str(req, file); append_dd(req, readonly); rpc_packet_t *rp = process_request(req); if ( rp == NULL ) return -1; const uchar *answer = (uchar *)(rp+1); const uchar *end = answer + rp->length; int fn = extract_long(&answer, end); if ( fn != -1 ) { if ( fsize != NULL && readonly ) *fsize = extract_long(&answer, end); } else { qerrcode(extract_long(&answer, end)); } qfree(rp); return fn; }
//-------------------------------------------------------------------------- int idaapi rpc_debmod_t::dbg_eval_lowcnd(thid_t tid, ea_t ea) { bytevec_t req = prepare_rpc_packet(RPC_EVAL_LOWCND); append_dd(req, tid); append_ea64(req, ea); return process_long(req); }
//-------------------------------------------------------------------------- void idaapi rpc_debmod_t::dbg_set_exception_info(const exception_info_t *table, int qty) { bytevec_t req = prepare_rpc_packet(RPC_SET_EXCEPTION_INFO); append_dd(req, qty); append_exception_info(req, table, qty); qfree(process_request(req)); }
//-------------------------------------------------------------------------- ssize_t idaapi rpc_debmod_t::dbg_write_memory(ea_t ea, const void *buffer, size_t size) { bytevec_t req = prepare_rpc_packet(RPC_WRITE_MEMORY); append_ea64(req, ea); append_dd(req, (uint32)size); append_memory(req, buffer, size); return process_long(req); }
//-------------------------------------------------------------------------- int idaapi rpc_debmod_t::dbg_start_process( const char *path, const char *args, const char *startdir, int flags, const char *input_path, uint32 input_file_crc32) { bytevec_t req = prepare_rpc_packet(RPC_START_PROCESS); append_str(req, path); append_str(req, args); append_str(req, startdir); append_dd(req, flags); append_str(req, input_path); append_dd(req, input_file_crc32); return process_start_or_attach(req); }
//-------------------------------------------------------------------------- ea_t idaapi rpc_debmod_t::dbg_appcall( ea_t func_ea, thid_t tid, int stkarg_nbytes, const struct regobjs_t *regargs, struct relobj_t *stkargs, struct regobjs_t *retregs, qstring *errbuf, debug_event_t *event, int flags) { bytevec_t req = prepare_rpc_packet(RPC_APPCALL); append_ea64(req, func_ea); append_dd(req, tid); append_dd(req, stkarg_nbytes); append_dd(req, flags); regobjs_t *rr = (flags & APPCALL_MANUAL) == 0 ? retregs : NULL; append_appcall(req, *regargs, *stkargs, rr); rpc_packet_t *rp = process_request(req); if ( rp == NULL ) return BADADDR; const uchar *answer = (uchar *)(rp+1); const uchar *end = answer + rp->length; ea_t sp = extract_ea64(&answer, end); if ( sp == BADADDR ) { if ( (flags & APPCALL_DEBEV) != 0 ) extract_debug_event(&answer, end, event); if ( errbuf != NULL ) *errbuf = extract_str(&answer, end); } else if ( (flags & APPCALL_MANUAL) == 0 ) { if ( retregs != NULL ) extract_regobjs(&answer, end, retregs, true); } qfree(rp); return sp; }
bool save_class(class_t * clas) { bytevec_t buffer; append_ea(buffer, clas->virt_table_ea); append_dd(buffer, clas->flags); append_eavec(buffer, 0, clas->functions_ea); append_eavec(buffer, 0, clas->parents_tid); netnode n = netnode(clas->tid); n.setblob(&buffer.front(), buffer.size(), 0, 'm'); return true; }
//-------------------------------------------------------------------------- int idaapi rpc_debmod_t::dbg_thread_get_sreg_base(thid_t tid, int sreg_value, ea_t *ea) { bytevec_t req = prepare_rpc_packet(RPC_GET_SREG_BASE); append_dd(req, tid); append_dd(req, sreg_value); rpc_packet_t *rp = process_request(req); if ( rp == NULL ) return -1; const uchar *answer = (uchar *)(rp+1); const uchar *end = answer + rp->length; bool result = extract_long(&answer, end) != 0; if ( result ) *ea = extract_ea64(&answer, end); qfree(rp); return result; }
//-------------------------------------------------------------------------- ssize_t idaapi rpc_debmod_t::dbg_write_file(int fn, uint32 off, const void *buf, size_t size) { bytevec_t req = prepare_rpc_packet(RPC_WRITE_FILE); append_dd(req, fn); append_dd(req, off); append_dd(req, (uint32)size); append_memory(req, buf, size); rpc_packet_t *rp = process_request(req); if ( rp == NULL ) return -1; const uchar *answer = (uchar *)(rp+1); const uchar *end = answer + rp->length; int32 rsize = extract_long(&answer, end); if ( size != rsize ) qerrcode(extract_long(&answer, end)); qfree(rp); return rsize; }
//-------------------------------------------------------------------------- gdecode_t idaapi rpc_debmod_t::dbg_get_debug_event(debug_event_t *event, int timeout_ms) { if ( has_pending_event ) { verbev(("get_debug_event => has pending event, returning it\n")); *event = pending_event; has_pending_event = false; poll_debug_events = false; return GDE_ONE_EVENT; } gdecode_t result = GDE_NO_EVENT; if ( poll_debug_events ) { // do we have something waiting? if ( irs_ready(irs, timeout_ms) != 0 ) { verbev(("get_debug_event => remote has an event for us\n")); // get the packet - it should be RPC_EVENT (nothing else can be) bytevec_t empty; rpc_packet_t *rp = process_request(empty); verbev(("get_debug_event => processed remote event, has=%d\n", has_pending_event)); if ( rp != NULL || !has_pending_event ) { warning("rpc: event protocol error (rp=%p has_event=%d)", rp, has_pending_event); return GDE_ERROR; } } } else { verbev(("get_debug_event => first time, send GET_DEBUG_EVENT\n")); bytevec_t req = prepare_rpc_packet(RPC_GET_DEBUG_EVENT); append_dd(req, timeout_ms); rpc_packet_t *rp = process_request(req); if ( rp == NULL ) return GDE_ERROR; const uchar *answer = (uchar *)(rp+1); const uchar *end = answer + rp->length; result = gdecode_t(extract_long(&answer, end)); if ( result >= GDE_ONE_EVENT ) extract_debug_event(&answer, end, event); else poll_debug_events = true; verbev(("get_debug_event => remote said %d, poll=%d now\n", result, poll_debug_events)); qfree(rp); } return result; }
//-------------------------------------------------------------------------- int idaapi rpc_debmod_t::dbg_read_registers(thid_t tid, int clsmask, regval_t *values) { bytevec_t req = prepare_rpc_packet(RPC_READ_REGS); append_dd(req, tid); append_dd(req, clsmask); // append additional information about the class structure bytevec_t regmap; int n_regs = calc_regmap(®map, clsmask); append_dd(req, n_regs); append_memory(req, regmap.begin(), regmap.size()); rpc_packet_t *rp = process_request(req); if ( rp == NULL ) return -1; const uchar *answer = (uchar *)(rp+1); const uchar *end = answer + rp->length; int result = extract_long(&answer, end); if ( result ) extract_regvals(&answer, end, values, n_regs, regmap.begin()); qfree(rp); return result; }
//-------------------------------------------------------------------------- int idaapi rpc_debmod_t::dbg_update_lowcnds(const lowcnd_t *lowcnds, int nlowcnds) { ea_t ea = 0; bytevec_t req = prepare_rpc_packet(RPC_UPDATE_LOWCNDS); append_dd(req, nlowcnds); const lowcnd_t *lc = lowcnds; for ( int i=0; i < nlowcnds; i++, lc++ ) { append_ea64(req, lc->ea-ea); ea = lc->ea; append_str(req, lc->cndbody); if ( !lc->cndbody.empty() ) { append_dd(req, lc->type); if ( lc->type != BPT_SOFT ) append_dd(req, lc->size); append_db(req, lc->orgbytes.size()); append_memory(req, lc->orgbytes.begin(), lc->orgbytes.size()); append_ea64(req, lc->cmd.ea); if ( lc->cmd.ea != BADADDR ) append_memory(req, &lc->cmd, sizeof(lc->cmd)); } } return process_long(req); }
//-------------------------------------------------------------------------- bool idaapi rpc_debmod_t::dbg_update_call_stack(thid_t tid, call_stack_t *trace) { bytevec_t req = prepare_rpc_packet(RPC_UPDATE_CALL_STACK); append_dd(req, tid); rpc_packet_t *rp = process_request(req); if ( rp == NULL ) return false; const uchar *answer = (uchar *)(rp+1); const uchar *end = answer + rp->length; bool result = extract_long(&answer, end) != 0; if ( result ) extract_call_stack(&answer, end, trace); qfree(rp); return result; }
//-------------------------------------------------------------------------- ssize_t idaapi rpc_debmod_t::dbg_read_memory(ea_t ea, void *buffer, size_t size) { bytevec_t req = prepare_rpc_packet(RPC_READ_MEMORY); append_ea64(req, ea); append_dd(req, (uint32)size); rpc_packet_t *rp = process_request(req); if ( rp == NULL ) return -1; const uchar *answer = (uchar *)(rp+1); const uchar *end = answer + rp->length; int result = extract_long(&answer, end); if ( result > 0 ) extract_memory(&answer, end, buffer, result); qfree(rp); return result; }
//-------------------------------------------------------------------------- // input is valid only if n==0 int idaapi rpc_debmod_t::dbg_process_get_info(int n, const char *input, process_info_t *procinf) { bytevec_t req = prepare_rpc_packet(RPC_GET_PROCESS_INFO); append_dd(req, n); if ( n == 0 ) append_str(req, input); rpc_packet_t *rp = process_request(req); if ( rp == NULL ) return -1; const uchar *answer = (uchar *)(rp+1); const uchar *end = answer + rp->length; bool result = extract_long(&answer, end) != 0; if ( result ) extract_process_info(&answer, end, procinf); qfree(rp); return result; }
//---------------------------------------------------------- main thread --- void win32_debmod_t::handle_pdb_request() { if ( pdbthread.req_kind == 1 ) { // read input file bytevec_t cmd; append_dq(cmd, pdbthread.off_ea); append_dd(cmd, pdbthread.count); void *outbuf = NULL; ssize_t outsize = 0; // send request to IDA int rc = send_ioctl(WIN32_IOCTL_READFILE, &cmd[0], cmd.size(), &outbuf, &outsize); if ( rc == 1 && outbuf != NULL ) { // OK size_t copylen = qmin(pdbthread.count, outsize); memcpy(pdbthread.buffer, outbuf, copylen); pdbthread.count = copylen; pdbthread.req_result = true; } else { pdbthread.req_result = false; } if ( outbuf != NULL ) qfree(outbuf); } else if ( pdbthread.req_kind == 2 ) { // read memory ssize_t rc = _read_memory(ea_t(pdbthread.off_ea), pdbthread.buffer, pdbthread.count); if ( rc >= 0 ) pdbthread.count = rc; pdbthread.req_result = rc >= 0; } else { // unknown request pdbthread.req_result = false; } }
//-------------------------------------------------------------------------- static void handle_single_session(rpc_server_t *server) { static int s_sess_id = 1; int sid = s_sess_id++; char peername[MAXSTR]; if ( !irs_peername(server->irs, peername, sizeof(peername), false) ) qstrncpy(peername, "(unknown)", sizeof(peername)); lprintf("=========================================================\n" "[%d] Accepting connection from %s...\n", sid, peername); bytevec_t req = prepare_rpc_packet(RPC_OPEN); append_dd(req, IDD_INTERFACE_VERSION); append_dd(req, DEBUGGER_ID); append_dd(req, sizeof(ea_t)); rpc_packet_t *rp = server->process_request(req, true); bool handle_request = true; bool send_response = true; bool ok; if ( rp == NULL ) { lprintf("[%d] Could not establish the connection\n", sid); handle_request = false; send_response = false; } if ( handle_request ) { // Answer is beyond the rpc_packet_t buffer const uchar *answer = (uchar *)(rp+1); const uchar *end = answer + rp->length; ok = extract_long(&answer, end) != 0; if ( !ok ) { lprintf("[%d] Incompatible IDA version\n", sid); send_response = false; } else if ( server_password != NULL ) { char *pass = extract_str(&answer, end); if ( strcmp(pass, server_password) != '\0' ) { lprintf("[%d] Bad password\n", sid); ok = false; } } qfree(rp); } if ( send_response ) { req = prepare_rpc_packet(RPC_OK); append_dd(req, ok); server->send_request(req); 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 = server->process_request(empty); if ( packet != NULL ) qfree(packet); } } server->network_error_code = 0; lprintf("[%d] Closing connection from %s...\n", sid, peername); bool preserve_server = keep_broken_connections && server->get_broken_connection(); if ( !preserve_server ) { // Terminate dedicated debugger instance. server->get_debugger_instance()->dbg_term(); server->term_irs(); } else { server->term_irs(); lprintf("[%d] Debugged session entered into sleeping mode\n", sid); server->prepare_broken_connection(); } if ( !preserve_server ) { // Remove the session from the list srv_lock_begin(); for (rpc_server_list_t::iterator it = clients_list.begin(); it != clients_list.end();++it) { if ( it->first != server ) continue; #ifndef __SINGLE_THREADED_SERVER__ // free the thread resources qthread_free(it->second); #endif // remove client from the list clients_list.erase(it); break; } srv_lock_end(); // Free the debug session delete server; } }
//-------------------------------------------------------------------------- int idaapi rpc_debmod_t::dbg_update_bpts(update_bpt_info_t *ubpts, int nadd, int ndel) { int skipped = 0; update_bpt_info_t *b; update_bpt_info_t *bend = ubpts + nadd; for ( b=ubpts; b != bend; b++ ) if ( b->code != BPT_OK ) skipped++; if ( skipped == nadd && ndel == 0 ) return 0; // no bpts to update bytevec_t req = prepare_rpc_packet(RPC_UPDATE_BPTS); append_dd(req, nadd-skipped); append_dd(req, ndel); ea_t ea = 0; for ( b=ubpts; b != bend; b++ ) { if ( b->code == BPT_OK ) { append_ea64(req, b->ea-ea); ea = b->ea; append_dd(req, b->size); append_dd(req, b->type); } } ea = 0; bend += ndel; for ( ; b != bend; b++ ) { append_ea64(req, b->ea-ea); ea = b->ea; append_db(req, b->orgbytes.size()); append_memory(req, b->orgbytes.begin(), b->orgbytes.size()); append_dd(req, b->type); } rpc_packet_t *rp = process_request(req); if ( rp == NULL ) return -1; const uchar *ptr = (uchar *)(rp+1); const uchar *end = ptr + rp->length; int ret = extract_long(&ptr, end); bend = ubpts + nadd; for ( b=ubpts; b != bend; b++ ) { if ( b->code == BPT_OK ) { b->code = extract_byte(&ptr, end); if ( b->code == BPT_OK && b->type == BPT_SOFT ) { uchar len = extract_byte(&ptr, end); b->orgbytes.resize(len); extract_memory(&ptr, end, b->orgbytes.begin(), len); } } } bend += ndel; for ( ; b != bend; b++ ) b->code = extract_byte(&ptr, end); return ret; }
//------------------------------------------------------------------------- bool varser_t::serialize(bytevec_t &out, const VARIANT &var) { append_dw(out, var.vt); if ( (var.vt & VT_BYREF) == VT_BYREF || (var.vt & VT_ARRAY) == VT_ARRAY ) { return false; } const size_t sz_before = out.size(); switch ( var.vt ) { case VT_EMPTY: // = 0x0000, case VT_NULL: // = 0x0001, break; case VT_I2: // = 0x0002, case VT_UI2: // = 0x0012, append_dw(out, var.uiVal); break; case VT_I4: // = 0x0003, case VT_UI4: // = 0x0013, append_dd(out, var.ulVal); break; case VT_R4: // = 0x0004, append_dd(out, *(uint32*)&var.fltVal); break; case VT_R8: // = 0x0005, append_dq(out, *(uint64*)&var.dblVal); break; case VT_CY: // = 0x0006, case VT_DATE: // = 0x0007, break; case VT_BSTR: // = 0x0008, { uint8 *ptr = (uint8*) var.bstrVal; ptr -= 4; uint32 bcnt = * (uint32*) ptr; append_dd(out, bcnt); append_memory(out, ptr + 4, bcnt); } break; case VT_DISPATCH: // = 0x0009, case VT_ERROR: // = 0x000A, case VT_BOOL: // = 0x000B, case VT_VARIANT: // = 0x000C, case VT_UNKNOWN: // = 0x000D, case VT_DECIMAL: // = 0x000E, case VT_I1: // = 0x0010, case VT_UI1: // = 0x0011, append_db(out, var.bVal); break; case VT_I8: // = 0x0014, case VT_UI8: // = 0x0015, append_dq(out, var.ullVal); break; case VT_INT: // = 0x0016, case VT_UINT: // = 0x0017, case VT_HRESULT: // = 0x0019, append_dd(out, var.uintVal); break; case VT_VOID: // = 0x0018, case VT_PTR: // = 0x001A, case VT_SAFEARRAY: // = 0x001B, case VT_CARRAY: // = 0x001C, case VT_USERDEFINED: // = 0x001D, case VT_LPSTR: // = 0x001E, case VT_LPWSTR: // = 0x001F, case VT_RECORD: // = 0x0024, case VT_INT_PTR: // = 0x0025, case VT_UINT_PTR: // = 0x0026, break; default: break; } return out.size() > sz_before; }
//-------------------------------------------------------------------------- int idaapi rpc_debmod_t::dbg_cleanup_appcall(thid_t tid) { bytevec_t req = prepare_rpc_packet(RPC_CLEANUP_APPCALL); append_dd(req, tid); return process_long(req); }
//-------------------------------------------------------------------------- bool rpc_debmod_t::open_remote( const char *hostname, int port_number, const char *password) { rpc_packet_t *rp = NULL; network_error_code = 0; irs = init_client_irs(hostname, port_number); if ( irs == NULL ) { FAILURE: if ( rp != NULL ) qfree(rp); term_irs(); return false; } rp = recv_request(); if ( rp == NULL || rp->code != RPC_OPEN ) // is this an ida debugger server? { rpc_client_t::dwarning("ICON ERROR\nAUTOHIDE NONE\n" "Bogus or irresponsive remote server"); goto FAILURE; } const uchar *answer = (uchar *)(rp+1); const uchar *end = answer + rp->length; int version = extract_long(&answer, end); int remote_debugger_id = extract_long(&answer, end); int easize = extract_long(&answer, end); qstring errstr; if ( version != IDD_INTERFACE_VERSION ) errstr.sprnt("protocol version is %d, expected %d", version, IDD_INTERFACE_VERSION); else if ( remote_debugger_id != debugger.id ) errstr.sprnt("debugger id is %d, expected %d (%s)", remote_debugger_id, debugger.id, debugger.name); else if ( easize != get_expected_addrsize() ) errstr.sprnt("address size is %d bytes, expected %d", easize, inf.is_64bit() ? 8 : 4); if ( !errstr.empty() ) { bytevec_t req = prepare_rpc_packet(RPC_OK); append_dd(req, false); send_request(req); warning("ICON ERROR\nAUTOHIDE NONE\n" "Incompatible debugging server:\n" "%s\n", errstr.c_str()); goto FAILURE; } qfree(rp); bytevec_t req = prepare_rpc_packet(RPC_OK); append_dd(req, true); append_str(req, password); send_request(req); rp = recv_request(); if ( rp == NULL || rp->code != RPC_OK ) goto FAILURE; answer = (uchar *)(rp+1); end = answer + rp->length; bool password_ok = extract_long(&answer, end) != 0; if ( !password_ok ) // is this an ida debugger server? { warning("ICON ERROR\nAUTOHIDE NONE\n" "Bad password"); goto FAILURE; } qfree(rp); return true; }