글을 쓰기 앞서 IoGetDeviceObjectPointer라는 유용한 커널 함수가 존재하나 해당 함수는
내부적으로 IoGetRelatedDeviceObject 함수를 호출함으로 이름에 해당하는 PDEVICE_OBJECT를
리턴하는것이 아니라 이름에 해당하는 PDEVICE_OBJECT에 최상위 PDEVICE_OBJECT를 리턴합니다.
예를 들어 atapi 드라이버에 다음과 같이 사용중인 디바이스 오브젝트가 3개가 있을 경우
"\\Device\\Ide\\IdeDeviceP1T0L0-17"는 시디롬 장치의 디바이스 오브젝트인데
IoGetDeviceObjectPointer 함수를 사용하면 0x817E4030 주소를 가진 PDEVICE_OBJECT를 리턴하는것이 아니라
Attach 되어 있는 최상위 디바이스 오브젝트인 \\Driver\redbook ( unnamed - 디바이스 이름없음 ) 하게 됩니다.
그러므로 디바이스 이름으로부터 정확한 포인터를 얻으려면 다음과 같은 방법으로 얻을 수 있습니다.
#include <Wdm.h>
PDEVICE_OBJECT MyIoGetDeviceObjectPointer(__in WCHAR *DeviceObjectName, __in BOOLEAN bRelated)
{
NTSTATUS ntStatus;
PFILE_OBJECT FileObject;
PDEVICE_OBJECT DeviceObject;
UNICODE_STRING Name;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE FileHandle;
IO_STATUS_BLOCK IoStatus;
DeviceObject = NULL;
RtlInitUnicodeString(&Name, DeviceObjectName);
if (bRelated == TRUE)
{
// IoGetDeviceObjectPointer는 내부적으로 IoGetRelatedDeviceObject 함수를 호출합니다.
// 따라서 DeviceObjectName 에 해당하는 디바이스 오브젝트 포인터를 얻어오는게 아니라
// 해당 디바이스 오브젝트에 대한 최상위 디바이스 오브젝트를 얻어옵니다.
ntStatus = IoGetDeviceObjectPointer(&Name, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject);
if (NT_SUCCESS(ntStatus) == TRUE)
{
return DeviceObject;
}
else
{
return NULL;
}
}
else
{
// 실제 해당 이름을 가진 디바이스 오브젝트를 얻옵니다.
InitializeObjectAttributes(&ObjectAttributes,
&Name,
0,
(HANDLE) NULL,
(PSECURITY_DESCRIPTOR)NULL);
ntStatus = ZwOpenFile(&FileHandle,
FILE_READ_ATTRIBUTES,
&ObjectAttributes,
&IoStatus,
0,
FILE_NON_DIRECTORY_FILE);
if (NT_SUCCESS(ntStatus) == FALSE)
{
// 해당 디바이스 오브젝트를 열 수 없습니다.
return NULL;
}
ntStatus = ObReferenceObjectByHandle(FileHandle,
0,
*IoFileObjectType,
KernelMode,
(PVOID *)&FileObject,
NULL);
if (NT_SUCCESS(ntStatus) == TRUE)
{
// Windows 2000 소스코드 참고
// 실제 내부 구현도 이와 비슷하게 되어 있습니다.
//
// If the file object was taken out against the mounted file system, it
// will have a Vpb. Traverse it to get to the DeviceObject. Note that in
// this case we should never follow FileObject->DeviceObject, as that
// mapping may be invalid after a forced dismount.
//
if (FileObject->Vpb != NULL && FileObject->Vpb->DeviceObject != NULL)
{
DeviceObject = FileObject->Vpb->DeviceObject;
//
// If a driver opened a disk device using direct device open and
// later on it uses IoGetRelatedTargetDeviceObject to find the
// device object it wants to send an IRP then it should not get the
// filesystem device object. This is so that if the device object is in the
// process of being mounted then vpb is not stable.
//
}
else if (!(FileObject->Flags & FO_DIRECT_DEVICE_OPEN) &&
FileObject->DeviceObject->Vpb != NULL &&
FileObject->DeviceObject->Vpb->DeviceObject != NULL)
{
DeviceObject = FileObject->DeviceObject->Vpb->DeviceObject;
//
// This is a direct open against the device stack (and there is no mounted
// file system to strain the IRPs through).
//
}
else
{
DeviceObject = FileObject->DeviceObject;
}
ZwClose(FileHandle);
return DeviceObject;
}
else
{
ZwClose(FileHandle);
return NULL;
}
}
}
VOID DriverUnload(__in PDRIVER_OBJECT DriverObject)
{
KdPrint(("Driver unload.\n"));
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
NTSTATUS ntStatus;
PDEVICE_OBJECT DeviceObject;
// 드라이버가 언로드 될 수 있도록 합니다.
DriverObject->DriverUnload = DriverUnload;
DeviceObject = MyIoGetDeviceObjectPointer(L"\\Device\\Ide\\IdeDeviceP1T0L0-17", FALSE);
KdPrint(("DeviceObject : %p\n", DeviceObject));
return STATUS_SUCCESS;
}
MyIoGetDeviceObjectPointer 에 두 번째 인자가 TRUE을 경우에는 기존 IoGetDeviceObjectPointer 함수를
사용하며 FALSE인 경우에는 이름에 해당하는 PDEVICE_OBJECT를 바로 얻어옵니다.