mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
469 lines
13 KiB
469 lines
13 KiB
|
|
#include "vdmp.h"
|
|
|
|
#include <ntos.h>
|
|
#include <zwapi.h>
|
|
#include <ntconfig.h>
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, VdmpInitialize)
|
|
#endif
|
|
|
|
#define KEY_VALUE_BUFFER_SIZE 1024
|
|
|
|
#if DEVL
|
|
ULONG VdmBopCount;
|
|
#endif
|
|
|
|
NTSTATUS
|
|
VdmpInitialize(
|
|
PVDM_INITIALIZE_DATA VdmInitData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the address space of a VDM.
|
|
|
|
Arguments:
|
|
|
|
VdmInitData - Supplies the captured initialization data.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
|
|
{
|
|
PETHREAD CurrentThread;
|
|
PVOID OriginalVdmObjects;
|
|
NTSTATUS Status, StatusCopy;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING SectionName;
|
|
UNICODE_STRING WorkString;
|
|
ULONG ViewSize;
|
|
LARGE_INTEGER ViewBase;
|
|
PVOID BaseAddress;
|
|
PVOID destination;
|
|
HANDLE SectionHandle, RegistryHandle;
|
|
PEPROCESS Process = PsGetCurrentProcess();
|
|
ULONG ResultLength;
|
|
ULONG Index;
|
|
PCM_FULL_RESOURCE_DESCRIPTOR ResourceDescriptor;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialResourceDescriptor;
|
|
PKEY_VALUE_FULL_INFORMATION KeyValueBuffer;
|
|
PCM_ROM_BLOCK BiosBlock;
|
|
ULONG LastMappedAddress;
|
|
PVDM_PROCESS_OBJECTS pVdmObjects;
|
|
HANDLE hThread;
|
|
PVDMICAUSERDATA pIcaUserData;
|
|
PVOID TrapcHandler;
|
|
|
|
PAGED_CODE();
|
|
|
|
NtCurrentTeb()->Vdm = NULL;
|
|
|
|
//
|
|
// Simple check to sure it is not already initialized. A final synchronized
|
|
// check is made farther down.
|
|
//
|
|
|
|
if (Process->VdmObjects) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
RtlInitUnicodeString (&SectionName, L"\\Device\\PhysicalMemory");
|
|
|
|
InitializeObjectAttributes (&ObjectAttributes,
|
|
&SectionName,
|
|
OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
|
|
(HANDLE) NULL,
|
|
(PSECURITY_DESCRIPTOR) NULL);
|
|
|
|
Status = ZwOpenSection (&SectionHandle,
|
|
SECTION_ALL_ACCESS,
|
|
&ObjectAttributes);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
pIcaUserData = VdmInitData->IcaUserData;
|
|
TrapcHandler = VdmInitData->TrapcHandler;
|
|
|
|
//
|
|
// Copy the first page of memory into the VDM's address space
|
|
//
|
|
|
|
BaseAddress = 0;
|
|
destination = 0;
|
|
ViewSize = 0x1000;
|
|
ViewBase.LowPart = 0;
|
|
ViewBase.HighPart = 0;
|
|
|
|
Status = ZwMapViewOfSection (SectionHandle,
|
|
NtCurrentProcess(),
|
|
&BaseAddress,
|
|
0,
|
|
ViewSize,
|
|
&ViewBase,
|
|
&ViewSize,
|
|
ViewUnmap,
|
|
0,
|
|
PAGE_READWRITE);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ZwClose (SectionHandle);
|
|
return Status;
|
|
}
|
|
|
|
StatusCopy = STATUS_SUCCESS;
|
|
|
|
try {
|
|
RtlCopyMemory (destination, BaseAddress, ViewSize);
|
|
}
|
|
except(ExSystemExceptionFilter()) {
|
|
StatusCopy = GetExceptionCode ();
|
|
}
|
|
|
|
Status = ZwUnmapViewOfSection (NtCurrentProcess(), BaseAddress);
|
|
|
|
if (!NT_SUCCESS(Status) || !NT_SUCCESS(StatusCopy)) {
|
|
ZwClose (SectionHandle);
|
|
return (NT_SUCCESS(Status) ? StatusCopy : Status);
|
|
}
|
|
|
|
//
|
|
// Map Rom into address space
|
|
//
|
|
|
|
BaseAddress = (PVOID) 0x000C0000;
|
|
ViewSize = 0x40000;
|
|
ViewBase.LowPart = 0x000C0000;
|
|
ViewBase.HighPart = 0;
|
|
|
|
//
|
|
// First unmap the reserved memory. This must be done here to prevent
|
|
// the virtual memory in question from being consumed by some other
|
|
// alloc vm call.
|
|
//
|
|
|
|
Status = ZwFreeVirtualMemory (NtCurrentProcess(),
|
|
&BaseAddress,
|
|
&ViewSize,
|
|
MEM_RELEASE);
|
|
|
|
//
|
|
// N.B. This should probably take into account the fact that there are
|
|
// a handful of error conditions that are ok (such as no memory to
|
|
// release).
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ZwClose (SectionHandle);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Set up and open KeyPath
|
|
//
|
|
|
|
InitializeObjectAttributes (&ObjectAttributes,
|
|
&CmRegistryMachineHardwareDescriptionSystemName,
|
|
OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
|
|
(HANDLE)NULL,
|
|
NULL);
|
|
|
|
Status = ZwOpenKey (&RegistryHandle, KEY_READ, &ObjectAttributes);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ZwClose(SectionHandle);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Allocate space for the data
|
|
//
|
|
|
|
KeyValueBuffer = ExAllocatePoolWithTag (PagedPool,
|
|
KEY_VALUE_BUFFER_SIZE,
|
|
' MDV');
|
|
|
|
if (KeyValueBuffer == NULL) {
|
|
ZwClose(RegistryHandle);
|
|
ZwClose(SectionHandle);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
//
|
|
// Get the data for the rom information
|
|
//
|
|
|
|
RtlInitUnicodeString (&WorkString, L"Configuration Data");
|
|
|
|
Status = ZwQueryValueKey (RegistryHandle,
|
|
&WorkString,
|
|
KeyValueFullInformation,
|
|
KeyValueBuffer,
|
|
KEY_VALUE_BUFFER_SIZE,
|
|
&ResultLength);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ExFreePool(KeyValueBuffer);
|
|
ZwClose(RegistryHandle);
|
|
ZwClose(SectionHandle);
|
|
return Status;
|
|
}
|
|
|
|
ResourceDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)
|
|
((PUCHAR) KeyValueBuffer + KeyValueBuffer->DataOffset);
|
|
|
|
if ((KeyValueBuffer->DataLength < sizeof(CM_FULL_RESOURCE_DESCRIPTOR)) ||
|
|
(ResourceDescriptor->PartialResourceList.Count < 2)) {
|
|
|
|
//
|
|
// No rom blocks.
|
|
//
|
|
|
|
ExFreePool(KeyValueBuffer);
|
|
ZwClose(RegistryHandle);
|
|
ZwClose(SectionHandle);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
PartialResourceDescriptor = (PCM_PARTIAL_RESOURCE_DESCRIPTOR)
|
|
((PUCHAR)ResourceDescriptor +
|
|
sizeof(CM_FULL_RESOURCE_DESCRIPTOR) +
|
|
ResourceDescriptor->PartialResourceList.PartialDescriptors[0]
|
|
.u.DeviceSpecificData.DataSize);
|
|
|
|
|
|
if (KeyValueBuffer->DataLength < ((PUCHAR)PartialResourceDescriptor -
|
|
(PUCHAR)ResourceDescriptor + sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR)
|
|
+ sizeof(CM_ROM_BLOCK))) {
|
|
|
|
ExFreePool(KeyValueBuffer);
|
|
ZwClose(RegistryHandle);
|
|
ZwClose(SectionHandle);
|
|
return STATUS_ILL_FORMED_SERVICE_ENTRY;
|
|
}
|
|
|
|
BiosBlock = (PCM_ROM_BLOCK)((PUCHAR)PartialResourceDescriptor +
|
|
sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR));
|
|
|
|
Index = PartialResourceDescriptor->u.DeviceSpecificData.DataSize /
|
|
sizeof(CM_ROM_BLOCK);
|
|
|
|
//
|
|
// N.B. Rom blocks begin on 2K (not necessarily page) boundaries
|
|
// They end on 512 byte boundaries. This means that we have
|
|
// to keep track of the last page mapped, and round the next
|
|
// Rom block up to the next page boundary if necessary.
|
|
//
|
|
|
|
LastMappedAddress = 0xC0000;
|
|
|
|
while (Index) {
|
|
|
|
#if 0
|
|
DbgPrint ("Bios Block, PhysAddr = %lx, size = %lx\n",
|
|
BiosBlock->Address,
|
|
BiosBlock->Size);
|
|
#endif
|
|
|
|
if ((Index > 1) &&
|
|
((BiosBlock->Address + BiosBlock->Size) == BiosBlock[1].Address)) {
|
|
|
|
//
|
|
// Coalesce adjacent blocks
|
|
//
|
|
|
|
BiosBlock[1].Address = BiosBlock[0].Address;
|
|
BiosBlock[1].Size += BiosBlock[0].Size;
|
|
Index -= 1;
|
|
BiosBlock += 1;
|
|
continue;
|
|
}
|
|
|
|
BaseAddress = (PVOID)(BiosBlock->Address);
|
|
ViewSize = BiosBlock->Size;
|
|
|
|
if ((ULONG)BaseAddress < LastMappedAddress) {
|
|
if (ViewSize > (LastMappedAddress - (ULONG)BaseAddress)) {
|
|
ViewSize = ViewSize - (LastMappedAddress - (ULONG)BaseAddress);
|
|
BaseAddress = (PVOID)LastMappedAddress;
|
|
} else {
|
|
ViewSize = 0;
|
|
}
|
|
}
|
|
|
|
ViewBase.LowPart = (ULONG)BaseAddress;
|
|
|
|
if (ViewSize > 0) {
|
|
|
|
Status = ZwMapViewOfSection (SectionHandle,
|
|
NtCurrentProcess(),
|
|
&BaseAddress,
|
|
0,
|
|
ViewSize,
|
|
&ViewBase,
|
|
&ViewSize,
|
|
ViewUnmap,
|
|
MEM_DOS_LIM,
|
|
PAGE_READWRITE);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
|
|
LastMappedAddress = (ULONG)BaseAddress + ViewSize;
|
|
}
|
|
|
|
Index -= 1;
|
|
BiosBlock += 1;
|
|
}
|
|
|
|
//
|
|
// Free up the handles
|
|
//
|
|
|
|
ExFreePool(KeyValueBuffer);
|
|
ZwClose(SectionHandle);
|
|
ZwClose(RegistryHandle);
|
|
|
|
//
|
|
// Create VdmObjects structure
|
|
//
|
|
// N.B. We don't use ExAllocatePoolWithQuota because it
|
|
// takes a reference to the process (which ExFreePool
|
|
// dereferences). Since we expect to clean up on
|
|
// process deletion, we don't need or want the reference
|
|
// (which will prevent the process from being deleted)
|
|
//
|
|
|
|
pVdmObjects = ExAllocatePoolWithTag (NonPagedPool,
|
|
sizeof(VDM_PROCESS_OBJECTS),
|
|
' MDV');
|
|
|
|
if (pVdmObjects == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
Status = PsChargeProcessPoolQuota (Process,
|
|
NonPagedPool,
|
|
sizeof(VDM_PROCESS_OBJECTS));
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
ExFreePool (pVdmObjects);
|
|
return Status;
|
|
}
|
|
|
|
RtlZeroMemory (pVdmObjects, sizeof(VDM_PROCESS_OBJECTS));
|
|
|
|
ExInitializeFastMutex (&pVdmObjects->DelayIntFastMutex);
|
|
KeInitializeSpinLock (&pVdmObjects->DelayIntSpinLock);
|
|
InitializeListHead (&pVdmObjects->DelayIntListHead);
|
|
|
|
pVdmObjects->pIcaUserData = ExAllocatePoolWithTag (PagedPool,
|
|
sizeof(VDMICAUSERDATA),
|
|
' MDV');
|
|
|
|
if (pVdmObjects->pIcaUserData == NULL) {
|
|
PsReturnPoolQuota (Process, NonPagedPool, sizeof(VDM_PROCESS_OBJECTS));
|
|
ExFreePool (pVdmObjects);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
Status = PsChargeProcessPoolQuota (Process,
|
|
PagedPool,
|
|
sizeof(VDMICAUSERDATA));
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
PsReturnPoolQuota (Process, NonPagedPool, sizeof(VDM_PROCESS_OBJECTS));
|
|
ExFreePool (pVdmObjects->pIcaUserData);
|
|
ExFreePool (pVdmObjects);
|
|
return Status;
|
|
}
|
|
|
|
try {
|
|
|
|
//
|
|
// Copy Ica addresses from service data (in user space) into
|
|
// pVdmObjects->pIcaUserData
|
|
//
|
|
|
|
ProbeForRead(pIcaUserData, sizeof(VDMICAUSERDATA), sizeof(UCHAR));
|
|
*pVdmObjects->pIcaUserData = *pIcaUserData;
|
|
|
|
//
|
|
// Probe static addresses in IcaUserData.
|
|
//
|
|
|
|
pIcaUserData = pVdmObjects->pIcaUserData;
|
|
|
|
ProbeForWriteHandle (pIcaUserData->phWowIdleEvent);
|
|
|
|
ProbeForWrite (pIcaUserData->pIcaLock,
|
|
sizeof(RTL_CRITICAL_SECTION),
|
|
sizeof(UCHAR));
|
|
|
|
ProbeForWrite (pIcaUserData->pIcaMaster,
|
|
sizeof(VDMVIRTUALICA),
|
|
sizeof(UCHAR));
|
|
|
|
ProbeForWrite (pIcaUserData->pIcaSlave,
|
|
sizeof(VDMVIRTUALICA),
|
|
sizeof(UCHAR));
|
|
|
|
ProbeForWriteUlong(pIcaUserData->pIretHooked);
|
|
ProbeForWriteUlong(pIcaUserData->pDelayIrq);
|
|
ProbeForWriteUlong(pIcaUserData->pUndelayIrq);
|
|
ProbeForWriteUlong(pIcaUserData->pDelayIret);
|
|
|
|
} except(ExSystemExceptionFilter()) {
|
|
Status = GetExceptionCode();
|
|
PsReturnPoolQuota (Process, NonPagedPool, sizeof(VDM_PROCESS_OBJECTS));
|
|
PsReturnPoolQuota(Process, PagedPool, sizeof(VDMICAUSERDATA));
|
|
ExFreePool (pVdmObjects->pIcaUserData);
|
|
ExFreePool (pVdmObjects);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Save a pointer to the main thread for the delayed interrupt DPC routine.
|
|
// To keep the pointer to the main thread valid, reference the thread
|
|
// and don't dereference it until process exit.
|
|
//
|
|
|
|
CurrentThread = PsGetCurrentThread ();
|
|
|
|
ObReferenceObject (CurrentThread);
|
|
|
|
pVdmObjects->MainThread = CurrentThread;
|
|
|
|
ASSERT (pVdmObjects->VdmTib == NULL);
|
|
|
|
//
|
|
// Carefully mark the process as a vdm (as other threads may be racing to
|
|
// do the same marking).
|
|
//
|
|
|
|
OriginalVdmObjects = InterlockedCompareExchangePointer (&Process->VdmObjects, pVdmObjects, NULL);
|
|
|
|
if (OriginalVdmObjects != NULL) {
|
|
PsReturnPoolQuota (Process, NonPagedPool, sizeof(VDM_PROCESS_OBJECTS));
|
|
PsReturnPoolQuota(Process, PagedPool, sizeof(VDMICAUSERDATA));
|
|
ExFreePool (pVdmObjects->pIcaUserData);
|
|
ExFreePool (pVdmObjects);
|
|
ObDereferenceObject (CurrentThread);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
ASSERT (Process->VdmObjects == pVdmObjects);
|
|
|
|
Process->Pcb.VdmTrapcHandler = TrapcHandler;
|
|
|
|
return Status;
|
|
}
|