#include "Common.h"
#include "VideoDevice.h"
// Ist ein Ersatz für den richtigen Konstruktor VideoDevice::VideoDevice, der nie aufgerufen
// wird, da wir keine C++-Runtime haben und daher der bekannte operator new schlicht nicht
// existiert, der dann den Konstruktor normalerweise aufruft. Ist aber nicht weiter tragisch
// da dieser ja auch nur eine simple Funktion ist wie diese hier.
bool VideoDevice::Constructor(uint32 id)
{
DbgPrint(__FUNCTION__);
m_signature = 'VDDV';
m_pdo = NULL;
m_devObj = NULL;
m_fileObj = NULL;
m_hDrvReg = NULL;
m_lastActionStatus = STATUS_SUCCESS;
WCHAR devString[50] = { 0 };
RtlStringCchPrintfW(devString, 50, L"\\Device\\Video%u", id);
RtlInitUnicodeString(&m_deviceName, devString);
return OpenDevice();
}
// Siehe Kommentar von VideoDevice::Constructur. Gilt auch hier, da delete fehlt
void VideoDevice::Destructor()
{
DbgPrint(__FUNCTION__);
if(m_fileObj != NULL)
ObDereferenceObject(m_fileObj);
}
// Versucht einen Zeiger auf das entsprechende Videodevice zu erhalten mit Read access.
// Falls das möglich war wird das physikalische Device Object (PDO) gesucht, dass wir
// für alle späteren Funktionen brauchen.
bool VideoDevice::OpenDevice()
{
DbgPrint(__FUNCTION__);
NTSTATUS status = IoGetDeviceObjectPointer(&m_deviceName, FILE_READ_DATA, &m_fileObj, &m_devObj);
if(NT_SUCCESS(status) == FALSE)
{
SetLastStatus(status);
return false;
}
return LoadPhysicalDevice();
}
// Sucht das PDO mit der major function IRP_MJ_PNP und der minor function
// IRP_MN_QUERY_DEVICE_RELATIONS. Wenn das Video device dies nicht unterstützt
// funktioniert das ganze entsprechend nicht.
bool VideoDevice::LoadPhysicalDevice()
{
DbgPrint(__FUNCTION__);
KEVENT hEvent;
IO_STATUS_BLOCK ioStatus;
KeInitializeEvent(&hEvent, NotificationEvent, FALSE);
PIRP irp = IoBuildSynchronousFsdRequest(
IRP_MJ_PNP,
m_devObj,
NULL,
0,
NULL,
&hEvent,
&ioStatus
);
if(irp == NULL)
{
SetLastStatus(STATUS_DATA_ERROR);
return false;
}
PIO_STACK_LOCATION irpLoc;
irpLoc = IoGetNextIrpStackLocation(irp);
irpLoc->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
irpLoc->MajorFunction = IRP_MJ_PNP;
irpLoc->Parameters.QueryDeviceRelations.Type = TargetDeviceRelation;
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
NTSTATUS stat = IofCallDriver(m_devObj, irp);
if(stat == STATUS_PENDING)
{
KeWaitForSingleObject(&hEvent, Executive, KernelMode, FALSE, NULL);
stat = ioStatus.Status;
}
if(NT_SUCCESS(stat) == FALSE)
{
SetLastStatus(stat);
return false;
}
PDEVICE_RELATIONS rel = (PDEVICE_RELATIONS)ioStatus.Information;
if(rel == NULL || rel->Count == 0)
{
SetLastStatus(STATUS_DATA_ERROR);
return false;
}
m_pdo = rel->Objects[0];
ExFreePool(rel);
SetLastStatus(STATUS_SUCCESS);
return true;
}
// Jeder Treiber für jedes Device für das er verwendet wird kann einen Pfad in der
// Registry haben in dem er seine Daten speichert. Dies ist in der Regel dann in
// HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Video\{TreiberGUID}\DeviceID\
// IoOpenDeviceRegistryKey gibt einen HANDLE zurück, der den entsprechenden Pfad
// geöffnet hat.
bool VideoDevice::LoadDriverRegistry()
{
DbgPrint(__FUNCTION__);
if(m_hDrvReg != NULL)
ZwClose(m_hDrvReg);
m_hDrvReg = NULL;
NTSTATUS status = IoOpenDeviceRegistryKey(m_pdo, PLUGPLAY_REGKEY_DRIVER, KEY_READ, &m_hDrvReg);
SetLastStatus(status);
return NT_SUCCESS(status);
}
// Versucht einen __STRING__ Wert aus dem Registryset des Devicetreibers zu laden.
bool VideoDevice::QueryDriverRegistryKey(WCHAR* keyName, PUNICODE_STRING dstString)
{
DbgPrint(__FUNCTION__);
if(m_hDrvReg == NULL || dstString == NULL || keyName == NULL)
{
SetLastStatus(STATUS_INVALID_PARAMETER);
return false;
}
UNICODE_STRING keyString;
RtlInitUnicodeString(&keyString, keyName);
ULONG fullLen = 0;
NTSTATUS status = ZwQueryValueKey(m_hDrvReg, &keyString, KeyValuePartialInformation, NULL, 0, &fullLen);
if((status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL) || fullLen == 0)
{
SetLastStatus(status);
return false;
}
PKEY_VALUE_PARTIAL_INFORMATION vpi = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePoolWithTag(NonPagedPool, fullLen, 'VDDC');
if(vpi == NULL)
{
SetLastStatus(STATUS_NO_MEMORY);
return false;
}
status = ZwQueryValueKey(m_hDrvReg, &keyString, KeyValuePartialInformation, vpi, fullLen, &fullLen);
if(NT_SUCCESS(status) == false || vpi->DataLength == 0)
{
SetLastStatus(status);
ExFreePool(vpi);
return false;
}
if(vpi->Type != REG_SZ && vpi->Type != REG_MULTI_SZ && vpi->Type != REG_EXPAND_SZ)
{
SetLastStatus(STATUS_DATA_ERROR);
ExFreePool(vpi);
return false;
}
RtlInitUnicodeString(&keyString, (WCHAR*)vpi->Data);
dstString->Length = (vpi->DataLength - sizeof(WCHAR)) / sizeof(WCHAR);
dstString->MaximumLength = dstString->Length + 1;
dstString->Buffer = (PWCHAR)ExAllocatePoolWithTag(NonPagedPool, dstString->MaximumLength * sizeof(WCHAR), 'VDDC');
RtlCopyUnicodeString(dstString, &keyString);
ExFreePool(vpi);
SetLastStatus(status);
return true;
}
// Ruft eine für den I/O-Control passende Funktion auf
bool VideoDevice::HandleIOControl(ULONG ioCode, PVOID inBuffer, ULONG inLen, PVOID outBuffer, ULONG outLen, PULONG bytesReturned)
{
DbgPrint(__FUNCTION__);
switch(ioCode)
{
case IOCTL_GET_UMD_DRIVER_NAME:
{
return VideoDevice::HandleGetUMDName(inBuffer, inLen, outBuffer, outLen, bytesReturned);
}
case IOCTL_OPEN_ADAPTER:
{
return VideoDevice::HandleOpenAdapter(inBuffer, inLen, outBuffer, outLen, bytesReturned);
}
case IOCTL_CLOSE_ADAPTER:
{
return VideoDevice::HandleCloseAdapter(inBuffer, inLen, outBuffer, outLen, bytesReturned);
}
default:
return false;
}
}
bool VideoDevice::IsValidIOCode(ULONG code)
{
return (code == IOCTL_GET_UMD_DRIVER_NAME || code == IOCTL_OPEN_ADAPTER || code == IOCTL_CLOSE_ADAPTER);
}
bool VideoDevice::HandleCloseAdapter(PVOID in, ULONG inLen, PVOID out, ULONG outLen, PULONG bytesReturned)
{
DbgPrint(__FUNCTION__);
*bytesReturned = 0;
if(inLen < sizeof(HANDLE*) || in == NULL)
return false;
VideoDevice* dev = *(VideoDevice**)in;
if(dev->m_signature != 'VDDV')
return false;
dev->Destructor();
ExFreePool(dev);
return true;
}
bool VideoDevice::HandleOpenAdapter(PVOID in, ULONG inLen, PVOID out, ULONG outLen, PULONG bytesReturned)
{
DbgPrint(__FUNCTION__);
*bytesReturned = 0;
if(outLen < sizeof(HANDLE) || out == NULL)
return false;
VideoDevice* retValue = (VideoDevice*)ExAllocatePoolWithTag(NonPagedPool, sizeof(VideoDevice), 'VDDC');
if(retValue == NULL)
{
*(HANDLE*)out = NULL;
return false;
}
if(!retValue->Constructor(6))
{
*(HANDLE*)out = NULL;
ExFreePool(retValue);
return true;
}
*(HANDLE*)out = (HANDLE)retValue;
*bytesReturned = sizeof(HANDLE);
return true;
}
bool VideoDevice::HandleGetUMDName(PVOID in, ULONG inLen, PVOID out, ULONG outLen, PULONG bytesReturned)
{
DbgPrint(__FUNCTION__);
*bytesReturned = 0;
if(inLen < sizeof(HANDLE*) || in == NULL || out == NULL || outLen == 0)
return false;
VideoDevice* dev = *(VideoDevice**)in;
if(dev->m_signature != 'VDDV')
return false;
if(!dev->LoadDriverRegistry())
return false;
UNICODE_STRING strDriver;
if(!dev->QueryDriverRegistryKey(L"UserModeDriverNameWoW", &strDriver))
return false;
RtlStringCchCopyNW((PWSTR)out, outLen, strDriver.Buffer, strDriver.MaximumLength);
ExFreePool(strDriver.Buffer);
*bytesReturned = outLen;
return true;
}