windows-nt/Source/XPSP1/NT/base/ntos/vdm/i386/vdminit.c

470 lines
13 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
#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;
}