Example #1
0
// MOV to / from DRx
_Use_decl_annotations_ static void VmmpHandleDrAccess(
    GuestContext *guest_context) {
  HYPERPLATFORM_PERFORMANCE_MEASURE_THIS_SCOPE();
  const MovDrQualification exit_qualification = {
      UtilVmRead(VmcsField::kExitQualification)};
  const auto register_used =
      VmmpSelectRegister(exit_qualification.fields.gp_register, guest_context);

  // Emulate the instruction
  switch (static_cast<MovDrDirection>(exit_qualification.fields.direction)) {
    case MovDrDirection::kMoveToDr:
      // clang-format off
      switch (exit_qualification.fields.debugl_register) {
        case 0: __writedr(0, *register_used); break;
        case 1: __writedr(1, *register_used); break;
        case 2: __writedr(2, *register_used); break;
        case 3: __writedr(3, *register_used); break;
        case 4: __writedr(4, *register_used); break;
        case 5: __writedr(5, *register_used); break;
        case 6: __writedr(6, *register_used); break;
        case 7: __writedr(7, *register_used); break;
        default: break;
      }
      // clang-format on
      break;
    case MovDrDirection::kMoveFromDr:
      // clang-format off
      switch (exit_qualification.fields.debugl_register) {
        case 0: *register_used = __readdr(0); break;
        case 1: *register_used = __readdr(1); break;
        case 2: *register_used = __readdr(2); break;
        case 3: *register_used = __readdr(3); break;
        case 4: *register_used = __readdr(4); break;
        case 5: *register_used = __readdr(5); break;
        case 6: *register_used = __readdr(6); break;
        case 7: *register_used = __readdr(7); break;
        default: break;
      }
      // clang-format on
      break;
    default:
      HYPERPLATFORM_COMMON_BUG_CHECK(HyperPlatformBugCheck::kUnspecified, 0, 0,
                                     0);
      break;
  }

  VmmpAdjustGuestInstructionPointer(guest_context->ip);
}
Example #2
0
// MOV to / from CRx
_Use_decl_annotations_ static void VmmpHandleCrAccess(
    GuestContext *guest_context) {
  HYPERPLATFORM_PERFORMANCE_MEASURE_THIS_SCOPE();
  const MovCrQualification exit_qualification = {
      UtilVmRead(VmcsField::kExitQualification)};

  const auto register_used =
      VmmpSelectRegister(exit_qualification.fields.gp_register, guest_context);

  switch (static_cast<MovCrAccessType>(exit_qualification.fields.access_type)) {
    case MovCrAccessType::kMoveToCr: {
      switch (exit_qualification.fields.control_register) {
        // CR0 <- Reg
        case 0:
          if (UtilIsX86Pae()) {
            UtilLoadPdptes(UtilVmRead(VmcsField::kGuestCr3));
          }
          UtilVmWrite(VmcsField::kGuestCr0, *register_used);
          UtilVmWrite(VmcsField::kCr0ReadShadow, *register_used);
          break;

        // CR3 <- Reg
        case 3:
          if (UtilIsX86Pae()) {
            UtilLoadPdptes(*register_used);
          }
          UtilVmWrite(VmcsField::kGuestCr3, *register_used);
          break;

        // CR4 <- Reg
        case 4:
          if (UtilIsX86Pae()) {
            UtilLoadPdptes(UtilVmRead(VmcsField::kGuestCr3));
          }
          UtilVmWrite(VmcsField::kGuestCr4, *register_used);
          UtilVmWrite(VmcsField::kCr4ReadShadow, *register_used);
          break;

        // CR8 <- Reg
        case 8:
          guest_context->cr8 = *register_used;
          break;

        default:
          HYPERPLATFORM_COMMON_BUG_CHECK(HyperPlatformBugCheck::kUnknown, 0, 0,
                                         0);
          break;
      }
    } break;

    // Note that MOV from CRx should never cause VM-exit with the current
    // settings. This is just for case when you enable it.
    case MovCrAccessType::kMoveFromCr: {
      switch (exit_qualification.fields.control_register) {
        // Reg <- CR3
        case 3:
          *register_used = UtilVmRead(VmcsField::kGuestCr3);
          break;

        // Reg <- CR8
        case 8:
          *register_used = guest_context->cr8;
          break;

        default:
          HYPERPLATFORM_COMMON_BUG_CHECK(HyperPlatformBugCheck::kUnknown, 0, 0,
                                         0);
          break;
      }
    } break;

    // Unimplemented
    case MovCrAccessType::kClts:
    case MovCrAccessType::kLmsw:
    default:
      HYPERPLATFORM_COMMON_DBG_BREAK();
      break;
  }

  VmmpAdjustGuestInstructionPointer(guest_context->ip);
}
Example #3
0
// Interrupt
_Use_decl_annotations_ static void VmmpHandleException(
    GuestContext *guest_context) {
  HYPERPLATFORM_PERFORMANCE_MEASURE_THIS_SCOPE();
  const VmExitInterruptionInformationField exception = {
      static_cast<ULONG32>(UtilVmRead(VmcsField::kVmExitIntrInfo))};

  if (static_cast<interruption_type>(exception.fields.interruption_type) ==
      interruption_type::kHardwareException) {
    // Hardware exception
    if (static_cast<InterruptionVector>(exception.fields.vector) ==
        InterruptionVector::kPageFaultException) {
      // #PF
      const PageFaultErrorCode fault_code = {
          static_cast<ULONG32>(UtilVmRead(VmcsField::kVmExitIntrErrorCode))};
      const auto fault_address = UtilVmRead(VmcsField::kExitQualification);

      VmEntryInterruptionInformationField inject = {};
      inject.fields.interruption_type = exception.fields.interruption_type;
      inject.fields.vector = exception.fields.vector;
      inject.fields.deliver_error_code = true;
      inject.fields.valid = true;
      AsmWriteCR2(fault_address);
      UtilVmWrite(VmcsField::kVmEntryExceptionErrorCode, fault_code.all);
      UtilVmWrite(VmcsField::kVmEntryIntrInfoField, inject.all);
      HYPERPLATFORM_LOG_INFO_SAFE("GuestIp= %p, #PF Fault= %p Code= 0x%2x",
                                  guest_context->ip, fault_address, fault_code);

    } else if (static_cast<InterruptionVector>(exception.fields.vector) ==
               InterruptionVector::kGeneralProtectionException) {
      // # GP
      const auto error_code =
          static_cast<ULONG32>(UtilVmRead(VmcsField::kVmExitIntrErrorCode));

      VmEntryInterruptionInformationField inject = {};
      inject.fields.interruption_type = exception.fields.interruption_type;
      inject.fields.vector = exception.fields.vector;
      inject.fields.deliver_error_code = true;
      inject.fields.valid = true;
      UtilVmWrite(VmcsField::kVmEntryExceptionErrorCode, error_code);
      UtilVmWrite(VmcsField::kVmEntryIntrInfoField, inject.all);
      HYPERPLATFORM_LOG_INFO_SAFE("GuestIp= %p, #GP Code= 0x%2x",
                                  guest_context->ip, error_code);

    } else {
      HYPERPLATFORM_COMMON_BUG_CHECK(HyperPlatformBugCheck::kUnknown, 0, 0, 0);
    }

  } else if (static_cast<interruption_type>(
                 exception.fields.interruption_type) ==
             interruption_type::kSoftwareException) {
    // Software exception
    if (static_cast<InterruptionVector>(exception.fields.vector) ==
        InterruptionVector::kBreakpointException) {
      // #BP
      VmEntryInterruptionInformationField inject = {};
      inject.fields.interruption_type = exception.fields.interruption_type;
      inject.fields.vector = exception.fields.vector;
      inject.fields.deliver_error_code = false;
      inject.fields.valid = true;
      UtilVmWrite(VmcsField::kVmEntryIntrInfoField, inject.all);
      UtilVmWrite(VmcsField::kVmEntryInstructionLen, 1);
      HYPERPLATFORM_LOG_INFO_SAFE("GuestIp= %p, #BP ", guest_context->ip);

    } else {
      HYPERPLATFORM_COMMON_BUG_CHECK(HyperPlatformBugCheck::kUnknown, 0, 0, 0);
    }
  } else {
    HYPERPLATFORM_COMMON_BUG_CHECK(HyperPlatformBugCheck::kUnknown, 0, 0, 0);
  }
}
Example #4
0
// Handles #BP. Determinas if the #BP is caused by a shadow breakpoint, and if
// so, runs its handler, switchs a page view to read/write shadow page and sets
// the monitor trap flag to execute only one instruction where is located on the
// read/write shadow page. Then saves the breakpoint info as the last event.
_Use_decl_annotations_ bool SbpHandleBreakpoint(EptData* ept_data,
                                                void* guest_ip,
                                                GpRegisters* gp_regs) {
  if (!SbppIsSbpActive()) {
    return false;
  }

  const auto info = SbppFindPatchInfoByAddress(guest_ip);
  if (!info) {
    return false;
  }

  if (!SbppIsShadowBreakpoint(*info)) {
    return false;
  }

  // DdiMon is unable to handle it
  if (KeGetCurrentIrql() > DISPATCH_LEVEL) {
    HYPERPLATFORM_COMMON_BUG_CHECK(HyperPlatformBugCheck::kUnspecified, 0, 0,
                                   0);
  }

  // VMM has to change the current CR3 to a guest's CR3 in order to access
  // memory address because VMM runs with System's CR3 saved in and restored
  // from
  // VmcsField::kHostCr3, while a guest's CR3 is depends on thread contexts.
  // Without using guest's CR3, it is likely that any use-address space is
  // inaccessible from a VMM ending up with a bug check.
  const auto guest_cr3 = UtilVmRead(VmcsField::kGuestCr3);
  const auto vmm_cr3 = __readcr3();

  if (info->type == BreakpointType::kPre) {
    // Pre breakpoint
    __writecr3(guest_cr3);
    info->handler(*info, ept_data, gp_regs, UtilVmRead(VmcsField::kGuestRsp));
    __writecr3(vmm_cr3);
    SbppEnablePageShadowingForRW(*info, ept_data);
    SbppSetMonitorTrapFlag(true);
    SbppSaveLastPatchInfo(*info);

  } else {
    // Post breakpoint
    if (info->target_tid == PsGetCurrentThreadId()) {
      // It is a target thread. Execute the post handler and let it continue
      // subsequence instructions.
      __writecr3(guest_cr3);
      info->handler(*info, ept_data, gp_regs, UtilVmRead(VmcsField::kGuestRsp));
      __writecr3(vmm_cr3);
      SbppDisablePageShadowing(*info, ept_data);
      SbppDeleteBreakpointFromList(*info);
    } else {
      // It is not. Let it allow to run one instruction without breakpoint
      SbppEnablePageShadowingForRW(*info, ept_data);
      SbppSetMonitorTrapFlag(true);
      SbppSaveLastPatchInfo(*info);
    }
  }

  // Yes, it was caused by shadow breakpoint. Do not deliver the #BP to a guest.
  return true;
}
Example #5
0
// MOV to / from CRx
_Use_decl_annotations_ static void VmmpHandleCrAccess(
    GuestContext *guest_context) {
  HYPERPLATFORM_PERFORMANCE_MEASURE_THIS_SCOPE();
  const MovCrQualification exit_qualification = {
      UtilVmRead(VmcsField::kExitQualification)};

  const auto register_used =
      VmmpSelectRegister(exit_qualification.fields.gp_register, guest_context);

  switch (static_cast<MovCrAccessType>(exit_qualification.fields.access_type)) {
    case MovCrAccessType::kMoveToCr:
      switch (exit_qualification.fields.control_register) {
        // CR0 <- Reg
        case 0: {
          HYPERPLATFORM_PERFORMANCE_MEASURE_THIS_SCOPE();
          if (UtilIsX86Pae()) {
            UtilLoadPdptes(UtilVmRead(VmcsField::kGuestCr3));
          }
          UtilVmWrite(VmcsField::kGuestCr0, *register_used);
          UtilVmWrite(VmcsField::kCr0ReadShadow, *register_used);
          break;
        }

        // CR3 <- Reg
        case 3: {
          HYPERPLATFORM_PERFORMANCE_MEASURE_THIS_SCOPE();
          if (UtilIsX86Pae()) {
            UtilLoadPdptes(*register_used);
          }
          UtilVmWrite(VmcsField::kGuestCr3, *register_used);
          break;
        }

        // CR4 <- Reg
        case 4: {
          HYPERPLATFORM_PERFORMANCE_MEASURE_THIS_SCOPE();
          if (UtilIsX86Pae()) {
            UtilLoadPdptes(UtilVmRead(VmcsField::kGuestCr3));
          }
          UtilVmWrite(VmcsField::kGuestCr4, *register_used);
          UtilVmWrite(VmcsField::kCr4ReadShadow, *register_used);
          break;
        }

        // CR8 <- Reg
        case 8: {
          HYPERPLATFORM_PERFORMANCE_MEASURE_THIS_SCOPE();
          guest_context->cr8 = *register_used;
          break;
        }

        default:
          HYPERPLATFORM_COMMON_BUG_CHECK(HyperPlatformBugCheck::kUnspecified, 0,
                                         0, 0);
          break;
      }
      break;

    case MovCrAccessType::kMoveFromCr:
      switch (exit_qualification.fields.control_register) {
        // Reg <- CR3
        case 3: {
          HYPERPLATFORM_PERFORMANCE_MEASURE_THIS_SCOPE();
          *register_used = UtilVmRead(VmcsField::kGuestCr3);
          break;
        }

        // Reg <- CR8
        case 8: {
          HYPERPLATFORM_PERFORMANCE_MEASURE_THIS_SCOPE();
          *register_used = guest_context->cr8;
          break;
        }

        default:
          HYPERPLATFORM_COMMON_BUG_CHECK(HyperPlatformBugCheck::kUnspecified, 0,
                                         0, 0);
          break;
      }
      break;

    // Unimplemented
    case MovCrAccessType::kClts:
    case MovCrAccessType::kLmsw:
    default:
      HYPERPLATFORM_COMMON_DBG_BREAK();
      break;
  }

  VmmpAdjustGuestInstructionPointer(guest_context->ip);
}