static int crack_hex_integer(QueryThread* query, const char** key, uint64_t* out) { uint64_t r = 0; const char* s = *key; for (;;) { char ch = toupper(*s); if (ch >= '0' && ch <= '9') { r *= 16; r += ch - '0'; } else if (ch >= 'A' && ch <= 'F') { r *= 16; r += ch - 'A' + 10; } else { break; } s++; } if (s == *key) { debugger_error(query, "bad.hex", "Expected hexadecimal character in %s", *key); return 0; } if (s[0] == '_') { s++; } *key = s; *out = r; return 1; }
static uintptr_t crack_offset(QueryThread* query, const char** key) { uint64_t offset; if (!crack_hex_integer(query, key, &offset)) return 0; if ((uintptr_t)offset != offset) { debugger_error(query, "bad.offset", "Debug object offset %lld is invalid", (long long)offset); } return (uintptr_t)offset; }
static DebugObject* crack_defining_object(QueryThread* query, const char** key) { uint64_t index; if (!crack_hex_integer(query, key, &index)) return NULL; if (index >= debug_object_count || !debug_objects[index].dwarf_obj) { debugger_error(query, "bad.debug.obj", "Debug object index %lld is invalid", (long long)index); } return &debug_objects[index]; }
static int crack_type_key(QueryThread* query, const char* key, DebugObject** defining_object, uintptr_t* type_offset) { if (key[0] != 'T') { debugger_error(query, "not.type.key", "typekey %s is not a type key", key); return 0; } ++key; *defining_object = crack_defining_object(query, &key); if (!*defining_object) return 0; *type_offset = crack_offset(query, &key); return *type_offset != 0; }
static int crack_value_key(QueryThread* query, const char* key, DebugObject** defining_object, uintptr_t* context_offset, uintptr_t* offset) { if (key[0] != 'V') { debugger_error(query, "not.value.key", "valkey %s is not a value key", key); return 0; } ++key; *defining_object = crack_defining_object(query, &key); if (!*defining_object) return 0; *context_offset = crack_offset(query, &key); if (!*context_offset != 0) return 0; *offset = crack_offset(query, &key); return *offset != 0; }
int dbg_read_reg(CH_DbgProgramState* state, uint8_t reg, uint8_t size, uint8_t* result) { ExaminationClosure* cl = state->closure; uint8_t* reg_sizes = get_register_byte_sizes(); ensure_registers_loaded(state); if (size > reg_sizes[reg]) { debugger_error(cl->query, "bad.debug.info.register.overrun", "Debug info requesting non-existent register bytes"); memset(result, 0, size); size = reg_sizes[reg]; } memcpy(result, cl->saved_registers[reg], size); if (cl->tracker) { CH_DbgValuePiece piece = { CH_PIECE_REGISTER, reg, 0, size*8 }; cl->tracker(cl->tracker_closure, &piece); } return 1; }
static int lookup_type_dwarf2(QueryThread* q, JSON_Builder* builder, DebugObject* container_object, uintptr_t type_offset) { CH_DbgDwarf2TypeInfo info; CH_DbgDwarf2Object* dwarf_obj = container_object->dwarf_obj; char const* kind; if (!container_object->dwarf_obj) { debugger_error(q, "bad.type.key", "Typekey %s refers to executable with no debug information"); return 0; } if (!dwarf2_lookup_type_info(q, dwarf_obj, type_offset, &info)) return 0; JSON_open_object(builder, NULL); append_type_key(builder, "typeKey", container_object, type_offset); if (info.inner_type_offset && info.kind != CH_TYPE_FUNCTION) { append_type_key(builder, "innerTypeKey", container_object, info.inner_type_offset); } if (info.is_dynamic) { JSON_append_simple(builder, "dynamic", JSON_TRUE); } if (info.is_declaration_only) { JSON_append_simple(builder, "partial", JSON_TRUE); } output_compilation_unit_info(&info.cu, builder); if (info.name) { JSON_append_stringdup(builder, "name", info.name); } if (info.namespace_prefix) { JSON_append_stringdup(builder, "namespacePrefix", info.namespace_prefix); safe_free(info.namespace_prefix); } if (info.container_prefix) { JSON_append_stringdup(builder, "containerPrefix", info.container_prefix); safe_free(info.container_prefix); } if (info.bytes_size >= 0) { JSON_append_int(builder, "byteSize", info.bytes_size); } switch (info.kind) { case CH_TYPE_UNKNOWN: debugger_error(q, "bad.type.key", "Typekey refers to unknown type"); return 0; case CH_TYPE_ANNOTATION: { char const* annotation_kind; switch (info.annotation_kind) { case CH_ANNOTATION_CONST: annotation_kind = "const"; break; case CH_ANNOTATION_VOLATILE: annotation_kind = "volatile"; break; case CH_ANNOTATION_RESTRICT: annotation_kind = "restrict"; break; default: debugger_error(q, "bad.type.key", "Typekey refers to annotation of unknown kind"); return 0; } JSON_append_string(builder, "annotation", annotation_kind); kind = "annotation"; break; } case CH_TYPE_ARRAY: if (info.array_length >= 0) { JSON_append_int(builder, "length", info.array_length); } kind = "array"; break; case CH_TYPE_ENUM: JSON_open_array(builder, "values"); if (!dwarf2_iterate_type_enum_values(q, dwarf_obj, type_offset, output_enum_value, builder)) return 0; JSON_close_array(builder); kind = "enum"; break; case CH_TYPE_FLOAT: kind = "float"; break; case CH_TYPE_INT: if (info.int_is_signed) { JSON_append_simple(builder, "signed", JSON_TRUE); } kind = "int"; break; case CH_TYPE_TYPEDEF: kind = "typedef"; break; case CH_TYPE_POINTER: if (info.pointer_is_reference) { JSON_append_simple(builder, "isReference", JSON_TRUE); } kind = "pointer"; break; case CH_TYPE_FUNCTION: { BuilderContainerClosure cl = { builder, container_object }; kind = "function"; if (info.inner_type_offset) { append_type_key(builder, "resultTypeKey", container_object, info.inner_type_offset); } JSON_open_array(builder, "parameters"); if (!dwarf2_iterate_type_function_parameters(q, dwarf_obj, type_offset, output_function_parameter, &cl)) return 0; JSON_close_array(builder); break; } case CH_TYPE_STRUCT: { char const* struct_kind; BuilderContainerClosure cl = { builder, container_object }; switch (info.struct_kind) { case CH_STRUCT_KIND_STRUCT: struct_kind = "struct"; break; case CH_STRUCT_KIND_UNION: struct_kind = "union"; break; case CH_STRUCT_KIND_CLASS: struct_kind = "class"; break; default: debugger_error(q, "bad.type.key", "Typekey refers to struct of unknown kind"); return 0; } JSON_append_string(builder, "structKind", struct_kind); JSON_open_array(builder, "fields"); if (!dwarf2_iterate_type_struct_fields(q, dwarf_obj, type_offset, output_struct_field, &cl)) return 0; JSON_close_array(builder); kind = "struct"; break; } default: debugger_error(q, "bad.type.key", "Typekey refers to type of unknown kind"); return 0; } JSON_append_string(builder, "kind", kind); JSON_close_object(builder); return 1; }
CH_DbgValuePiece* dbg_examine_value(QueryThread* q, CH_TStamp tstamp, const char* valkey, const char* typekey, CH_DbgDependencyCallback dependency_tracker, void* dependency_tracker_closure, CH_Range** output_valid_instruction_ranges) { ExaminationClosure cl; DebugObject* value_object; DebugObject* type_object; uintptr_t function_offset, variable_offset, type_offset; CH_MemMapInfo mmap_info; CH_Address file_pc_addr; CH_Address virtual_pc_addr = get_virtual_pc_addr(q, tstamp); CH_DbgProgramState state; CH_DbgValuePiece* pieces; if (!virtual_pc_addr) return NULL; translate_virtual_to_file_address(virtual_pc_addr, tstamp, &file_pc_addr, &mmap_info); if (!mmap_info.map_operation) return NULL; if (!crack_value_key(q, valkey, &value_object, &function_offset, &variable_offset)) return NULL; if (!crack_type_key(q, typekey, &type_object, &type_offset)) return NULL; if (value_object != type_object) { debugger_error(q, "valkey.typekey.mismatch", "Debug object mismatch between valkey %s and typekey %s", valkey, typekey); return NULL; } if (!type_object->dwarf_obj) { debugger_error(q, "bad.type.key", "Typekey %s refers to object with no debug information"); return NULL; } cl.query = q; cl.tracker = dependency_tracker; cl.tracker_closure = dependency_tracker_closure; cl.saved_registers_buf = NULL; state.closure = &cl; state.tstamp = tstamp; /* XXX should we set the size of the last piece, if necessary, from the type? */ pieces = dwarf2_examine_value(q, value_object->dwarf_obj, function_offset, file_pc_addr, variable_offset, &state, output_valid_instruction_ranges); /* translate validity addresses from dwarf file offsets to virtual addresses */ if (pieces && *output_valid_instruction_ranges) { int i; CH_DBAddrMapEntry* map_event = mmap_info.map_operation; for (i = 0; (*output_valid_instruction_ranges)[i].length > 0; ++i) { (*output_valid_instruction_ranges)[i].start += map_event->address - map_event->offset; } } safe_free(cl.saved_registers_buf); return pieces; }
TARGET_ADDRESS nub_setup_function_call (NUB nub, NUBTHREAD thread, TARGET_ADDRESS function, NUBINT arg_count, TARGET_ADDRESS *args, NUBHANDLE *context_cookie) { LPDBGPROCESS process = (LPDBGPROCESS) nub; LPDBGTHREAD threadC = (LPDBGTHREAD) thread; CONTEXT context; THREAD_MEMORY *saved_thread = (THREAD_MEMORY*) malloc (sizeof(THREAD_MEMORY)); BOOL status; DWORD stack_position; DWORD original_IP; DWORD i = 0; BOOL write_status; DWORD bytes_written; TARGET_ADDRESS address_to_break; // suspend_thread(threadC); // Now get context information. We need to know the return address // for our frame-to-be, and also the stack pointer + frame pointer // as is. context.ContextFlags = CONTEXT_FULL; status = get_thread_context(process, threadC, &context); dylan_debugger_message("nub_setup_function_call: Thread Context: %= : %=", threadC->ThreadHandle, status); dylan_debugger_message("Esp: %= Eip: %=", context.Esp, context.Eip); //print_context("Context pulled from thread state", &context); // Now remember everything about the debug state of this thread. saved_thread->ThreadState = threadC->ThreadState; saved_thread->WaitingForDebugger = threadC->WaitingForDebugger; saved_thread->SingleStepping = threadC->SingleStepping; saved_thread->NeedsBreakpointReplacement = threadC->NeedsBreakpointReplacement; saved_thread->BreakpointToReplace = threadC->BreakpointToReplace; saved_thread->StoppedState = threadC->StoppedState; saved_thread->LastReceivedEvent = threadC->LastReceivedEvent; saved_thread->NubCodeOfLastEvent = threadC->NubCodeOfLastEvent; saved_thread->ThreadContext = context; // Allocate enough space on the stack to hold the arguments to the // remote function, and the return address. stack_position = (DWORD) nub_allocate_stack_space (nub, thread, ((DWORD) (arg_count + 1)) * sizeof(DWORD)); if (stack_position == 0x0) { debugger_error("Serious Error: Failed to allocate stack in Spy call on Thread %=", (TARGET_ADDRESS)threadC->ThreadHandle, (TARGET_ADDRESS)NULL); // Internal error return(NULL); } // And get ready for the remote call. If the thread was stopped at a // breakpoint, we need to override that, because we are going to alter // the instruction pointer. if (saved_thread->NeedsBreakpointReplacement) { LPDEBUG_POINT breakpoint = saved_thread->BreakpointToReplace; drop_breakpoint(process, breakpoint); threadC->NeedsBreakpointReplacement = FALSE; if (!(saved_thread->SingleStepping)) { context.EFlags = context.EFlags & 0xFFFFFEFF; } // And resume those threads that will have been suspended. // resume_all_except(process, thread); } if (!status) { // Internal nub error. return (NULL); } //print_context("Context being saved", &(saved_thread->ThreadContext)); // Grab the return address so that the access path chappies can set a // breakpoint on it to clean up the stack. address_to_break = (TARGET_ADDRESS) context.Eip; original_IP = context.Eip; // DIY stack frame!!!! // We are using the C calling convention to bring about our remote // call. At the point of call, the new stack frame must have all the // arguments pushed, followed by the return address. context.Esp = stack_position; // And make the instruction pointer point to our function. context.Eip = (DWORD) function; // The stack should now be fooling this thread into thinking that // it has to execute our remote function, which it will go off and do // as soon as the application resumes. But we have to set the context. status = SetThreadContext (threadC->ThreadHandle, &context); if (!status) { // Internal nub error. return(NULL); } // print_context("Context set back to thread", &context); // Push the return address - ie, the next instruction that was going // to be executed, before we started messing about... write_status = ValidatedWriteProcessMemory (process->ProcessHandle, (LPVOID) stack_position, (LPVOID) &(original_IP), sizeof(TARGET_ADDRESS), &bytes_written); stack_position += sizeof(TARGET_ADDRESS); if ((!write_status) || (bytes_written != sizeof(TARGET_ADDRESS))) { // Internal nub error. return (NULL); } // Push the argument array. for (i = 0; i < (DWORD) arg_count; i++) { write_status = ValidatedWriteProcessMemory (process->ProcessHandle, (LPVOID) stack_position, (LPVOID) &(args[i]), sizeof(TARGET_ADDRESS), &bytes_written); if ((!write_status) || (bytes_written != sizeof(TARGET_ADDRESS))) { // Internal nub error. return (NULL); } else { //printf ("Wrote the argument %x at %x.\n", args[i], stack_position); } stack_position += sizeof(TARGET_ADDRESS); } //print_context("Context set back to thread", &context); (*context_cookie) = (NUBHANDLE) saved_thread; //resume_thread(threadC); return (address_to_break); }