Exemple #1
0
void dump_interesting_fields(u8 bus, u8 slot, u8 func) {
  u16 vendor_id = read_pci_config_16(bus, slot, func, PCI_VENDOR_ID);
  u16 device_id = read_pci_config_16(bus, slot, func, PCI_DEVICE_ID);

  WinDbgPrint("pci 0000:%02x:%02x.%d  ", bus, slot, func);
  WinDbgPrint("Id %04x:%04x config space:\n", vendor_id, device_id);
};
Exemple #2
0
void DumpConfigSpace(u8 bus, u8 slot, u8 func) {

  u8 i, j;

  for(i=0; i<0x80; i+=0x10) {
    WinDbgPrint("0x%02X ", i);
    for(j=i; j<i+0x10; j++) {
      u8 b = read_pci_config_byte(bus, slot, func, j);
      WinDbgPrint("%02X ", b);
    };
    WinDbgPrint("\n");
  }
};
Exemple #3
0
// Parse a 64 bit page table entry and print it.
static void print_pte_contents(PTE_MMAP_OBJ *self, PTE_LOGLEVEL loglevel,
                               PTE *pte) {
  WinDbgPrint("Virtual Address:%#016llx\n"
              "\tpresent:      %lld\n"
              "\trw:           %lld\n"
              "\tuser:         %lld\n"
              "\twrite_through:%lld\n"
              "\tcache_disable:%lld\n"
              "\taccessed:     %lld\n"
              "\tdirty:        %lld\n"
              "\tpat:          %lld\n"
              "\tglobal:       %lld\n"
              "\txd:           %lld\n"
              "\tpfn: %010llx",
              (pte_uint64)pte,
              (pte_uint64)pte->present,
              (pte_uint64)pte->rw,
              (pte_uint64)pte->user,
              (pte_uint64)pte->write_through,
              (pte_uint64)pte->cache_disable,
              (pte_uint64)pte->accessed,
              (pte_uint64)pte->dirty,
              (pte_uint64)pte->pat,
              (pte_uint64)pte->global,
              (pte_uint64)pte->xd,
              (pte_uint64)pte->page_frame);
}
Exemple #4
0
void DumpRuns(struct PmemMemoryInfo *info) {
  int i;
  for(i=0; i<info->NumberOfRuns.QuadPart; i++) {
    WinDbgPrint("0x%llx 0x%llx 0x%llx\n", info->Run[i].BaseAddress.QuadPart,
		info->Run[i].BaseAddress.QuadPart + info->Run[i].NumberOfBytes.QuadPart,
		info->Run[i].NumberOfBytes.QuadPart);
  };
};
Exemple #5
0
void dump_bar(u8 bus, u8 slot, u8 func) {
  u16 vendor_id = read_pci_config_16(bus, slot, func, PCI_VENDOR_ID);
  u16 device_id = read_pci_config_16(bus, slot, func, PCI_DEVICE_ID);
  u32 base0 = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_0);
  u32 base1 = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_1);
  u32 base2 = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_2);
  u32 base3 = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_3);
  u32 base4 = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_4);
  u32 base5 = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_5);


  WinDbgPrint("Base Addresses: %08X %08X %08X %08X %08X %08X\n",
	      base0, base1, base2, base3, base4, base5);

  WinDbgPrint("Masks: %08X %08X %08X %08X %08X %08X\n",
	      get_base_register_size(bus, slot, func, PCI_BASE_ADDRESS_0),
	      get_base_register_size(bus, slot, func, PCI_BASE_ADDRESS_1),
	      get_base_register_size(bus, slot, func, PCI_BASE_ADDRESS_2),
	      get_base_register_size(bus, slot, func, PCI_BASE_ADDRESS_3),
	      get_base_register_size(bus, slot, func, PCI_BASE_ADDRESS_4),
	      get_base_register_size(bus, slot, func, PCI_BASE_ADDRESS_5));
}
Exemple #6
0
static NTSTATUS InsertMemoryHole(struct PmemMemoryInfo *info, int len,
				 u64 start, u64 end) {
  int i;

#if WINPMEM_PCI_DEBUG
  WinDbgPrint("Inserting memory hole at Start %llX end %llx\n", start, end);
#endif

  // Round start and end to page boundaries.
  start = start & 0xFFFFFFFFFFFFF000;
  end = end | 0xFFF;

  if (start == end){
    goto exit;
  };

  for (i=0; i<info->NumberOfRuns.QuadPart; i++) {
    PHYSICAL_MEMORY_RANGE Run = info->Run[i];
    u64 run_start = Run.BaseAddress.QuadPart;
    u64 run_end = Run.BaseAddress.QuadPart + Run.NumberOfBytes.QuadPart;

    if (run_start < start && start < run_end) {
      // Make some room for a new entry.
      RtlMoveMemory(&info->Run[i+1], &info->Run[i],
		    sizeof(Run) * (info->NumberOfRuns.LowPart - i));

      info->NumberOfRuns.QuadPart++;

      info->Run[i].NumberOfBytes.QuadPart = start - run_start - 1;
      info->Run[i+1].BaseAddress.QuadPart = start;
      info->Run[i+1].NumberOfBytes.QuadPart = run_end - start - 1;
      continue;
    };

    if (run_start < end && end < run_end) {
      info->Run[i].BaseAddress.QuadPart = end + 1;
      info->Run[i].NumberOfBytes.QuadPart = run_end - end - 1;
      goto exit;
    };
  };

 exit:

#if WINPMEM_PCI_DEBUG
  DumpRuns(info);
#endif

  return STATUS_SUCCESS;
};
Exemple #7
0
// Edit the page tables to point a virtual address to a specific physical page.
//
// Args:
//  self: The this pointer to the object using this function.
//  target: The physical address to map to.
//
// Returns:
//  PTE_SUCCESS or PTE_ERROR
//
static PTE_STATUS pte_remap_rogue_page(PTE_MMAP_OBJ *self, PHYS_ADDR target) {
  // Can only remap pages, addresses must be page aligned.
  if (((!target) & PAGE_MASK) || self->rogue_page.offset) {
    WinDbgPrint("Failed to map %#016llx, "
                "only page aligned remapping is supported!",
                target);
    return PTE_ERROR;
  }

  WinDbgPrintDebug("Remapping pte at %p to %#016llx",
                   self->rogue_pte,
                   target);
  // Change the pte to point to the new offset.
  self->rogue_pte->page_frame = PAGE_TO_PFN(target);
  // Flush the old pte from the tlbs in the system.
  self->flush_tlbs_page_(self->rogue_page.pointer);

  return PTE_SUCCESS;
}
Exemple #8
0
// Get a free, non-paged page of memory. On windows we can not
// allocate from pool because pool storage is controlled by large page
// PTEs. So we just use a static page in the driver executable.
static void *pte_get_rogue_page(void) {
  if (page_aligned_space == NULL) {
    rogue_mdl = IoAllocateMdl(&rogue_page, sizeof(rogue_page),
			     FALSE, FALSE, NULL);
    if (!rogue_mdl) {
      return NULL;
    };

    try {
      MmProbeAndLockPages(rogue_mdl, KernelMode, IoWriteAccess);
    } except(EXCEPTION_EXECUTE_HANDLER) {
      NTSTATUS ntStatus = GetExceptionCode();

      WinDbgPrint("Exception while locking inBuf 0X%08X in METHOD_NEITHER\n",
		  ntStatus);
      IoFreeMdl(rogue_mdl);
      rogue_mdl = NULL;
      return NULL;
    }
    page_aligned_space = rogue_page;
    page_aligned_space += PAGE_SIZE - ((__int64)rogue_page) % PAGE_SIZE;
  };
Exemple #9
0
/*
  Uses direct PCI probing to add accessible physical memory ranges.
*/
NTSTATUS PCI_AddMemoryRanges(struct PmemMemoryInfo *info, int len) {
  int required_length = (sizeof(struct PmemMemoryInfo) +
			 sizeof(PHYSICAL_MEMORY_RANGE));
  unsigned int bus, slot, func;

  if (len < required_length) {
    return STATUS_INFO_LENGTH_MISMATCH;
  };

  // Initialize the physical memory range.
  info->NumberOfRuns.QuadPart = 1;
  info->Run[0].BaseAddress.QuadPart = 0;
  info->Run[0].NumberOfBytes.QuadPart = -1;

  for (bus = 0; bus < 256; bus++) {
    for (slot = 0; slot < 32; slot++) {
      for (func = 0; func < 8; func++) {
	u8 type;

	u16 vendor_id = read_pci_config_16((u8)bus, (u8)slot, (u8)func,
					   PCI_VENDOR_ID);

	// Device not present.
	if (vendor_id == 0xffff)
	  continue;

	type = read_pci_config_byte((u8)bus, (u8)slot, (u8)func,
				    PCI_HEADER_TYPE);

        // Standard header.
	if ((type & 0x1f) == 0) {

#if WINPMEM_PCI_DEBUG
          WinDbgPrint("PCI Type %X\n", type);
          dump_interesting_fields((u8)bus, (u8)slot, (u8)func);
          dump_bar((u8)bus, (u8)slot, (u8)func);
          DumpConfigSpace((u8)bus, (u8)slot, (u8)func);
#endif
	  DumpStandardHeader((u8)bus, (u8)slot, (u8)func, info, len);

	  // PCI-PCI bridge.
	} else if ((type & 0x1f) == 1) {

#if WINPMEM_PCI_DEBUG
          WinDbgPrint("PCI Type %X\n", type);
          dump_interesting_fields((u8)bus, (u8)slot, (u8)func);
          DumpConfigSpace((u8)bus, (u8)slot, (u8)func);
#endif

	  DumpPCIBridge((u8)bus, (u8)slot, (u8)func, info, len);

	} else {
	  WinDbgPrint("Unknown header PCI at 0000:%02x:%02x.%d type %d\n",
		      bus, slot, func, type);
	};

	// This is not a multi function device.
	if (func == 0 && (type & 0x80) == 0) {
	  break;
	};
      }
    }
  }

  return STATUS_SUCCESS;
};
Exemple #10
0
NTSTATUS DriverEntry (IN PDRIVER_OBJECT DriverObject,
                      IN PUNICODE_STRING RegistryPath)
{
  UNICODE_STRING DeviceName, DeviceLink;
  NTSTATUS NtStatus;
  PDEVICE_OBJECT DeviceObject = NULL;
  PDEVICE_EXTENSION extension;

  WinDbgPrint("WinPMEM - " PMEM_VERSION " - Physical memory acquisition\n");

#if PMEM_WRITE_ENABLED == 1
  WinDbgPrint("WinPMEM write support available!");
#endif

  WinDbgPrint("Copyright (c) 2017, Michael Cohen <*****@*****.**>\n");

  // Initialize import tables:
  if(PmemGetProcAddresses() != STATUS_SUCCESS) {
    WinDbgPrint("Failed to initialize import table. Aborting.\n");
    goto error;
  };

  RtlInitUnicodeString (&DeviceName, L"\\Device\\" PMEM_DEVICE_NAME);

  // We create our secure device.
  // http://msdn.microsoft.com/en-us/library/aa490540.aspx
  NtStatus = IoCreateDeviceSecure(DriverObject,
                                  sizeof(DEVICE_EXTENSION),
                                  &DeviceName,
                                  FILE_DEVICE_UNKNOWN,
                                  FILE_DEVICE_SECURE_OPEN,
                                  FALSE,
                                  &SDDL_DEVOBJ_SYS_ALL_ADM_ALL,
                                  &GUID_DEVCLASS_PMEM_DUMPER,
                                  &DeviceObject);

  if (!NT_SUCCESS(NtStatus)) {
    WinDbgPrint ("IoCreateDevice failed. => %08X\n", NtStatus);
    return NtStatus;
  }

  DriverObject->MajorFunction[IRP_MJ_CREATE] = wddCreate;
  DriverObject->MajorFunction[IRP_MJ_CLOSE] = wddClose;
  DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = wddDispatchDeviceControl;
  DriverObject->MajorFunction[IRP_MJ_READ] = PmemRead;

#if PMEM_WRITE_ENABLED == 1
  {
    // Make sure that the drivers with write support are clearly marked as such.
    static char TAG[] = "Write Supported";
  }

  // Support writing.
  DriverObject->MajorFunction[IRP_MJ_WRITE] = PmemWrite;
#endif
  DriverObject->DriverUnload = IoUnload;

  // Use buffered IO - a bit slower but simpler to implement, and more
  // efficient for small reads.
  SetFlag(DeviceObject->Flags, DO_BUFFERED_IO );
  ClearFlag(DeviceObject->Flags, DO_DIRECT_IO );
  ClearFlag(DeviceObject->Flags, DO_DEVICE_INITIALIZING);

  RtlInitUnicodeString (&DeviceLink, L"\\DosDevices\\" PMEM_DEVICE_NAME);

  NtStatus = IoCreateSymbolicLink (&DeviceLink, &DeviceName);

  if (!NT_SUCCESS(NtStatus)) {
    WinDbgPrint ("IoCreateSymbolicLink failed. => %08X\n", NtStatus);
    IoDeleteDevice (DeviceObject);
  }

  // Populate globals in kernel context.
  CR3.QuadPart = __readcr3();

  // Initialize the device extension with safe defaults.
  extension = DeviceObject->DeviceExtension;
  extension->mode = ACQUISITION_MODE_PHYSICAL_MEMORY;
  extension->MemoryHandle = 0;

#if _WIN64
  // Disable pte mapping for 32 bit systems.
  extension->pte_mmapper = pte_mmap_windows_new();
  extension->pte_mmapper->loglevel = PTE_ERR;
  extension->mode = ACQUISITION_MODE_PTE_MMAP;
#else
  extension->pte_mmapper = NULL;
#endif

  WinDbgPrint("Driver intialization completed.");
  return NtStatus;

 error:
  return STATUS_UNSUCCESSFUL;
}
Exemple #11
0
NTSTATUS wddDispatchDeviceControl(IN PDEVICE_OBJECT DeviceObject,
				  IN PIRP Irp)
{
  UNICODE_STRING DestinationPath;
  PIO_STACK_LOCATION IrpStack;
  NTSTATUS status = STATUS_INVALID_PARAMETER;
  ULONG IoControlCode;
  PVOID IoBuffer;
  PULONG OutputBuffer;
  PDEVICE_EXTENSION ext;
  ULONG InputLen, OutputLen;

  ULONG Level;
  ULONG Type;

  // We must be running in PASSIVE_LEVEL or we bluescreen here. We
  // theoretically should always be running at PASSIVE_LEVEL here, but
  // in case we ended up here at the wrong IRQL its better to bail
  // than to bluescreen.
  if(KeGetCurrentIrql() != PASSIVE_LEVEL) {
    status = STATUS_ABANDONED;
    goto exit;
  };

  ext = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;

  Irp->IoStatus.Information = 0;

  IrpStack = IoGetCurrentIrpStackLocation(Irp);

  IoBuffer = Irp->AssociatedIrp.SystemBuffer;
  OutputLen = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
  InputLen = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
  IoControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;

  switch ((IoControlCode & 0xFFFFFF0F)) {

  // The old deprecated ioctrl interface for backwards
  // compatibility. Do not use for new code.
  case IOCTL_GET_INFO_DEPRECATED: {
    char *buffer = ExAllocatePoolWithTag(NonPagedPoolNx, 0x1000, PMEM_POOL_TAG);

    if (buffer) {
      struct DeprecatedPmemMemoryInfo *info = (void *)IoBuffer;
      struct PmemMemoryInfo *memory_info = (void *)buffer;

      status = AddMemoryRanges(memory_info, 0x1000);
      if (status != STATUS_SUCCESS) {
        ExFreePoolWithTag(buffer, PMEM_POOL_TAG);
        goto exit;
      };

      info->CR3.QuadPart = CR3.QuadPart;
      info->NumberOfRuns = (unsigned long)memory_info->NumberOfRuns.QuadPart;

      // Is there enough space in the user supplied buffer?
      if (OutputLen < (info->NumberOfRuns * sizeof(PHYSICAL_MEMORY_RANGE) +
                       sizeof(struct DeprecatedPmemMemoryInfo))) {
        status = STATUS_INFO_LENGTH_MISMATCH;
        ExFreePoolWithTag(buffer, PMEM_POOL_TAG);
        goto exit;
      };

      // Copy the runs over.
      RtlCopyMemory(&info->Run[0], &memory_info->Run[0],
                    info->NumberOfRuns * sizeof(PHYSICAL_MEMORY_RANGE));

      // This is the total length of the response.
      Irp->IoStatus.Information =
        sizeof(struct DeprecatedPmemMemoryInfo) +
        info->NumberOfRuns * sizeof(PHYSICAL_MEMORY_RANGE);

      WinDbgPrint("Returning info on the system memory using deprecated "
		  "interface!\n");

      ExFreePoolWithTag(buffer, PMEM_POOL_TAG);
      status = STATUS_SUCCESS;
    };
  }; break;

    // Return information about memory layout etc through this ioctrl.
  case IOCTL_GET_INFO: {
    struct PmemMemoryInfo *info = (void *)IoBuffer;

    if (OutputLen < sizeof(struct PmemMemoryInfo)) {
        status = STATUS_INFO_LENGTH_MISMATCH;
        goto exit;
    };

    // Ensure we clear the buffer first.
    RtlZeroMemory(IoBuffer, sizeof(struct PmemMemoryInfo));

    // Get the memory ranges according to the mode.
    if (ext->mode == ACQUISITION_MODE_PTE_MMAP_WITH_PCI_PROBE) {
      status = PCI_AddMemoryRanges(info, OutputLen);
    } else {
      status = AddMemoryRanges(info, OutputLen);
    }

    if (status != STATUS_SUCCESS) {
      goto exit;
    };

    WinDbgPrint("Returning info on the system memory.\n");

    // We are currently running in user context which means __readcr3() will
    // return the process CR3. So we return the kernel CR3 we found
    // when loading.
    info->CR3.QuadPart = CR3.QuadPart;

    info->NtBuildNumber.QuadPart = *NtBuildNumber;
    info->NtBuildNumberAddr.QuadPart = (uintptr_t)NtBuildNumber;
    info->KernBase.QuadPart = (uintptr_t)KernelGetModuleBaseByPtr(
       NtBuildNumber, "NtBuildNumber");

    // Fill in KPCR.
    GetKPCR(info);

    // This is the length of the response.
    Irp->IoStatus.Information =
      sizeof(struct PmemMemoryInfo) +
      info->NumberOfRuns.LowPart * sizeof(PHYSICAL_MEMORY_RANGE);

    status = STATUS_SUCCESS;
  }; break;

  case IOCTL_SET_MODE: {
    WinDbgPrint("Setting Acquisition mode.\n");

    /* First u32 is the acquisition mode. */
    if (InputLen >= sizeof(u32)) {
      enum PMEM_ACQUISITION_MODE mode = *(u32 *)IoBuffer;

      switch(mode) {
      case ACQUISITION_MODE_PHYSICAL_MEMORY:
        // These are all the requirements for this method.
        if (!Pmem_KernelExports.MmGetPhysicalMemoryRanges) {
          WinDbgPrint("Kernel APIs required for this method are not "
                      "available.");
          status = STATUS_UNSUCCESSFUL;
        } else {
          WinDbgPrint("Using physical memory device for acquisition.\n");
          status = STATUS_SUCCESS;
	  ext->mode = mode;
        };
        break;

      case ACQUISITION_MODE_MAP_IO_SPACE:
        if (!Pmem_KernelExports.MmGetPhysicalMemoryRanges ||
            !Pmem_KernelExports.MmMapIoSpace ||
            !Pmem_KernelExports.MmUnmapIoSpace) {
          WinDbgPrint("Kernel APIs required for this method are not "
                      "available.");
          status = STATUS_UNSUCCESSFUL;
        } else {
          WinDbgPrint("Using MmMapIoSpace for acquisition.\n");
          status = STATUS_SUCCESS;
	  ext->mode = mode;
        };
        break;

      case ACQUISITION_MODE_PTE_MMAP:
        if (!Pmem_KernelExports.MmGetVirtualForPhysical ||
            !Pmem_KernelExports.MmGetPhysicalMemoryRanges ||
            !ext->pte_mmapper) {
          WinDbgPrint("Kernel APIs required for this method are not "
                      "available.");
          status = STATUS_UNSUCCESSFUL;
        } else {
          WinDbgPrint("Using PTE Remapping for acquisition.\n");
          status = STATUS_SUCCESS;
	  ext->mode = mode;
        };
        break;

      case ACQUISITION_MODE_PTE_MMAP_WITH_PCI_PROBE:
        if (!Pmem_KernelExports.MmGetVirtualForPhysical ||
            !ext->pte_mmapper) {
          WinDbgPrint("Kernel APIs required for this method are not "
                      "available.");
          status = STATUS_UNSUCCESSFUL;
        } else {
          WinDbgPrint("Using PTE Remapping with PCI probe for acquisition.\n");
          status = STATUS_SUCCESS;
	  ext->mode = mode;
        };
        break;

      default:
        WinDbgPrint("Invalid acquisition mode %d.\n", mode);
        status = STATUS_INVALID_PARAMETER;
      };

    } else {
      status = STATUS_INFO_LENGTH_MISMATCH;
    };
  }; break;

#if PMEM_WRITE_ENABLED == 1
  case IOCTL_WRITE_ENABLE: {
    ext->WriteEnabled = !ext->WriteEnabled;
    WinDbgPrint("Write mode is %d. Do you know what you are doing?\n",
		ext->WriteEnabled);
    status = STATUS_SUCCESS;
  }; break;
#endif

  default: {
    WinDbgPrint("Invalid IOCTRL %d\n", IoControlCode);
    status = STATUS_INVALID_PARAMETER;
  };
  }

 exit:
  Irp->IoStatus.Status = status;
  IoCompleteRequest(Irp,IO_NO_INCREMENT);
  return status;
}
Exemple #12
0
// Traverses the page tables to find the pte for a given virtual address.
//
// Args:
//  self: The this pointer for the object calling this function.
//  vaddr: The virtual address to resolve the pte for
//  pte: A pointer to a pointer which will be set with the address of the pte,
//       if found.
//
// Returns:
//  PTE_SUCCESS or PTE_ERROR
//
static PTE_STATUS virt_find_pte(PTE_MMAP_OBJ *self, void *addr,
                                PTE **pte) {
  PTE_CR3 cr3;
  PML4E *pml4;
  PML4E *pml4e;
  PDPTE *pdpt;
  PDPTE *pdpte;
  PDE *pd;
  PDE *pde;
  PTE *pt;
  VIRT_ADDR vaddr;
  PTE_STATUS status = PTE_ERROR;

  vaddr.pointer = addr;

  WinDbgPrint("Resolving PTE for Address:%#016llx", vaddr);

  // Get contents of cr3 register to get to the PML4
  cr3 = self->get_cr3_();

  WinDbgPrint("Kernel CR3 is %p", cr3);
  WinDbgPrint("Kernel PML4 is at %p physical",
                   PFN_TO_PAGE(cr3.pml4_p));

  // Resolve the PML4
  pml4 = (PML4E *)self->phys_to_virt_(PFN_TO_PAGE(cr3.pml4_p));
  WinDbgPrint("kernel PML4 is at %p virtual", pml4);

  // Resolve the PDPT
  pml4e = (pml4 + vaddr.pml4_index);

  WinDbgPrint("PML4 entry %d is at %p", vaddr.pml4_index,
                   pml4e);

  if (!pml4e->present) {

    WinDbgPrint("Error, address %#016llx has no valid mapping in PML4:",
                vaddr.value);
    self->print_pte_(self, PTE_ERR, (PTE *)pml4e);
    goto error;
  }
  WinDbgPrint("PML4[%#010x]: %p)", vaddr.pml4_index, pml4e);


  pdpt = (PDPTE *)self->phys_to_virt_(PFN_TO_PAGE(pml4e->pdpt_p));
  WinDbgPrintDebug("Points to PDPT:   %p)", pdpt);

  // Resolve the PDT
  pdpte = (pdpt + vaddr.pdpt_index);
  if (!pdpte->present) {
    WinDbgPrint("Error, address %#016llx has no valid mapping in PDPT:",
                vaddr.value);
    self->print_pte_(self, PTE_ERR, (PTE *)pdpte);
    goto error;
  }
  if (pdpte->page_size) {
    WinDbgPrint("Error, address %#016llx belongs to a 1GB page:",
                vaddr.value);
    self->print_pte_(self, PTE_ERR, (PTE *)pdpte);
    goto error;
  }
  WinDbgPrint("PDPT[%#010x]: %p)", vaddr.pdpt_index, pdpte);
  pd = (PDE *)self->phys_to_virt_(PFN_TO_PAGE(pdpte->pd_p));
  WinDbgPrint("Points to PD:     %p)", pd);

  // Resolve the PT
  pde = (pd + vaddr.pd_index);
  if (!pde->present) {
    WinDbgPrint("Error, address %#016llx has no valid mapping in PD:",
                vaddr.value);
    self->print_pte_(self, PTE_ERR, (PTE *)pde);
    goto error;
  }
  if (pde->page_size) {
    WinDbgPrint("Error, address %#016llx belongs to a 2MB page:",
                vaddr.value);
    self->print_pte_(self, PTE_ERR, (PTE *)pde);
    goto error;
  }

  WinDbgPrint("PD  [%#010x]: %p)", vaddr.pd_index, pde);
  pt = (PTE *)self->phys_to_virt_(PFN_TO_PAGE(pde->pt_p));
  WinDbgPrint("Points to PT:     %p)", pt);

  // Get the PTE and Page Frame
  *pte = (pt + vaddr.pt_index);
  if (! (*pte)->present) {
    WinDbgPrint("Error, address %#016llx has no valid mapping in PT:",
                vaddr.value);
    self->print_pte_(self, PTE_ERR, (*pte));
    goto error;
  }
  WinDbgPrint("PT  [%#010x]: %p)", vaddr.pt_index, *pte);

  status = PTE_SUCCESS;
error:
  return status;
}