Example #1
0
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;
}
Example #2
0
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;
}
Example #3
0
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];
}
Example #4
0
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;
}
Example #5
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;
}
Example #6
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;
}
Example #7
0
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;
}
Example #8
0
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);
}