您的位置:首页 > 其它

Windows 驱动中获取指定的设备对象

2016-06-07 22:53 585 查看
转载自:http://tudian2007.blog.163.com/blog/static/3156641320137295338938/

Windows 驱动中获取指定的设备对象

  众所周知应用层要和驱动层通讯的话需要先打开驱动设备对象,因为驱动设备名只是对内核模式中的驱动所识别的,应用层是无法识别的,所以一般驱动都有对应的symboliclink供应用层用使用的,如”\\.\C:”,”\??\C:”表示C:分区,\\.\PhysicalDrive0表示磁盘0,如果你需要使用的话只要调用CreateFile就能获得句柄了,还有一种打开驱动设备方法,主要是针对WDM驱动的,就是调用设备接口,相对比较复杂,这里不多讲了; 我们详细来讲讲内核模式下,如何打开一个指定驱动的设备的。

1.根据设备名打开:

  驱动设备创建的时候,如果需要需要创建控制设备的话我们都会给该设备指定DeviceName和Symboliclink,SymbolicLink上面讲过这里不讲了,DeviceName格式是”\Device\Devicename”,如C:盘就是”\Device\HarddiskVolume1”,如果在创建设备的时候没有指定,系统会默认分配一个”\Device\0000000NUM”之类的;在获取到设备名之后,我们可以调用ZwCreateFIle打开该设备;具体代码如下:

//代码来源《windows驱动开发详解》第11章
#pragma PAGEDCODE
NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp)
{
KdPrint(("DriverB:Enter B HelloDDKRead\n"));
NTSTATUS ntStatus = STATUS_SUCCESS;

UNICODE_STRING DeviceName;
RtlInitUnicodeString( &DeviceName, L"\\Device\\MyDDKDeviceA" );

//初始化objectAttributes
OBJECT_ATTRIBUTES objectAttributes;
InitializeObjectAttributes(&objectAttributes,
&DeviceName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL );

HANDLE hDevice;
IO_STATUS_BLOCK status_block;
//同步打开设备
//设定了FILE_SYNCHRONOUS_IO_NONALERT或者FILE_SYNCHRONOUS_IO_ALERT为同步打开设备
ntStatus = ZwCreateFile(&hDevice,
FILE_READ_ATTRIBUTES|SYNCHRONIZE,
&objectAttributes,
&status_block,
NULL,FILE_ATTRIBUTE_NORMAL,FILE_SHARE_READ,
FILE_OPEN_IF,FILE_SYNCHRONOUS_IO_NONALERT,NULL,0);

if (NT_SUCCESS(ntStatus))
{
ZwReadFile(hDevice,NULL,NULL,NULL,&status_block,NULL,0,NULL,NULL);
}

ZwClose(hDevice);

// 完成IRP
pIrp->IoStatus.Status = ntStatus;
pIrp->IoStatus.Information = 0; // bytes xfered
IoCompleteRequest( pIrp, IO_NO_INCREMENT );
KdPrint(("DriverB:Leave B HelloDDKRead\n"));
return ntStatus;
}


2.通过符号链接打开设备

  SymbolicLink不仅仅对用户模式起作用,驱动中也可以使用,先调用ZwOpenSymbolicLinkObject 得到符号链接的句柄,然后使用ZwQuerySymbolicLinkObject根据句柄得到设备的名字,得到设备名字之后就可以按照方法一里面的办法打开设备对象了;具体代码如下:

//代码来源《windows驱动开发详解》第11章
#pragma PAGEDCODE
NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
IN PIRP pIrp)
{
KdPrint(("DriverB:Enter B HelloDDKRead\n"));
NTSTATUS ntStatus = STATUS_SUCCESS;

UNICODE_STRING DeviceSymbolicLinkName;
RtlInitUnicodeString( &DeviceSymbolicLinkName, L"\\??\\HelloDDKA" );

//初始化objectAttributes
OBJECT_ATTRIBUTES objectAttributes;
InitializeObjectAttributes(&objectAttributes,
&DeviceSymbolicLinkName,
OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
NULL,
NULL );

HANDLE hSymbolic;
//设定了FILE_SYNCHRONOUS_IO_NONALERT或者FILE_SYNCHRONOUS_IO_ALERT为同步打开设备
ntStatus = ZwOpenSymbolicLinkObject(&hSymbolic,FILE_ALL_ACCESS,&objectAttributes);
#define UNICODE_SIZE 50
UNICODE_STRING LinkTarget;
LinkTarget.Buffer = (PWSTR)ExAllocatePool(PagedPool,UNICODE_SIZE);
LinkTarget.Length = 0;
LinkTarget.MaximumLength = UNICODE_SIZE;

ULONG unicode_length;
ntStatus = ZwQuerySymbolicLinkObject(hSymbolic,&LinkTarget,&unicode_length);

KdPrint(("DriverB:The device name is %wZ\n",&LinkTarget));

InitializeObjectAttributes(&objectAttributes,
&LinkTarget,
OBJ_CASE_INSENSITIVE,
NULL,
NULL );

HANDLE hDevice;
IO_STATUS_BLOCK status_block;
//设定了FILE_SYNCHRONOUS_IO_NONALERT或者FILE_SYNCHRONOUS_IO_ALERT为同步打开设备
ntStatus = ZwCreateFile(&hDevice,
FILE_READ_ATTRIBUTES|SYNCHRONIZE,
&objectAttributes,
&status_block,
NULL,FILE_ATTRIBUTE_NORMAL,FILE_SHARE_READ,
FILE_OPEN_IF,FILE_SYNCHRONOUS_IO_NONALERT,NULL,0);

if (NT_SUCCESS(ntStatus))
{
ZwReadFile(hDevice,NULL,NULL,NULL,&status_block,NULL,0,NULL,NULL);
}

ZwClose(hDevice);
ZwClose(hSymbolic);
ExFreePool(LinkTarget.Buffer);

ntStatus = STATUS_SUCCESS;
// 完成IRP
pIrp->IoStatus.Status = ntStatus;
pIrp->IoStatus.Information = 0; // bytes xfered
IoCompleteRequest( pIrp, IO_NO_INCREMENT );
KdPrint(("DriverB:Leave B HelloDDKRead\n"));
return ntStatus;
}
f643


3.用IoGetDeviceObjectPointer

  每一个内核态里的句柄都和一个内核对象关联在一起,这个函数可以获取到我们需要打开的驱动设备的设备堆栈最上层的FILE_OBJECT和DEVICE_OBJECT,之后我们只要往这个设备对象发需要的IRP就能通信了;需要注意的是,在调用完这个函数之后,设备对象的引用计数会加1,所以最后我们需要调用ObDereferenceObject来减引用,代码如下:

//根据汇编逆向出来的IoGetDeviceObjectPointer

NTSTATUS COSNVf::IoGetDeviceObjectPointNew(IN PUNICODE_STRING ObjectName,
IN ACCESS_MASK DesiredAccess,
OUT PFILE_OBJECT *FileObject,
OUT PDEVICE_OBJECT *DeviceObject)
{
IO_STATUS_BLOCK ioStatus;
OBJECT_ATTRIBUTES objectAttributes;
PFILE_OBJECT fileObject;
HANDLE fileHandle;
NTSTATUS status;

InitializeObjectAttributes(&objectAttributes,
ObjectName,
OBJ_KERNEL_HANDLE,
(HANDLE)NULL,
(PSECURITY_DESCRIPTOR)NULL);
//这里的Open权限我改过了,原版的Open只有exc权限;结果我执行的环境一直失败,所以我改了权限;
status = ZwOpenFile(&fileHandle,
DesiredAccess,
&objectAttributes,
&ioStatus,
FILE_SHARE_READ|FILE_SHARE_WRITE,
FILE_NON_DIRECTORY_FILE);

if(NT_SUCCESS(status))
{
status = ObReferenceObjectByHandle(fileHandle,
0,
*IoFileObjectType,
0,
(PVOID *)&fileObject,
0);
if(NT_SUCCESS(status))
{
*FileObject = fileObject;
*DeviceObject = IoGetRelatedDeviceObject(fileObject);
}

ZwClose(fileHandle);
}

return status;
}
//下面是在磁盘卷设备里获取磁盘signature的函数;

NTSTATUS
COSNFilter::GetDiskSignature(ULONG diskIndex, PULONG pDiskSignature)
{

NTSTATUS        status;
PWSTR           ntNameBuffer;

UNICODE_STRING  ntNameUnicodeString;

PFILE_OBJECT    pDiskFileObject;
PDEVICE_OBJECT  pDiskDeviceObject, pDiskDeviceObjecttemp;

ntNameUnicodeString.MaximumLength = 128 * sizeof(WCHAR);
ntNameBuffer = (PWSTR) ExAllocatePoolWithTag(NonPagedPool, ntNameUnicodeString.MaximumLength, OSNTAG);
if(!ntNameBuffer)
{
return STATUS_INSUFFICIENT_RESOURCES;
}

//所需的设备名
swprintf(ntNameBuffer, L"\\Device\\Harddisk%d\\Partition0", diskIndex);

RtlInitUnicodeString(&ntNameUnicodeString, ntNameBuffer);

status = IoGetDeviceObjectPointer(&ntNameUnicodeString,
FILE_READ_DATA,
&pDiskFileObject,
&pDiskDeviceObject);

if(!NT_SUCCESS(status))
{
status = IoGetDeviceObjectPointer(&ntNameUnicodeString,
FILE_READ_ATTRIBUTES,
&pDiskFileObject,
&pDiskDeviceObject);
}

ExFreePool(ntNameBuffer);

if (!NT_SUCCESS(status))
{
return status;
}

PDEVICE_OBJECT pDevice = IoGetRelatedDeviceObject(pDiskFileObject);

ULONG                       size = (128 * sizeof(PARTITION_INFORMATION) + 4);
PDRIVE_LAYOUT_INFORMATION   pDriveLayout = (PDRIVE_LAYOUT_INFORMATION) ExAllocatePoolWithTag(NonPagedPool,size, OSNTAG);
if (!pDriveLayout)
{
ObDereferenceObject(pDiskFileObject);
return STATUS_INSUFFICIENT_RESOURCES;
}

//往下层设备同步下发,调用的是IoBuildDeviceIoControlRequest函数创建IRP
status = COSNFilter::OSNDeviceIoctl(pDevice,
IOCTL_DISK_GET_DRIVE_LAYOUT,
NULL,
0,
pDriveLayout,
size);

if(NT_SUCCESS(status))
{
*pDiskSignature = pDriveLayout->Signature;
ExFreePool(pDriveLayout);
}
else
{
ExFreePool(pDriveLayout);

size = (64 * sizeof(PARTITION_INFORMATION_EX) + 4);
PDRIVE_LAYOUT_INFORMATION_EX    pDriveLayoutEx = (PDRIVE_LAYOUT_INFORMATION_EX) ExAllocatePoolWithTag(NonPagedPool,size,OSNTAG);
if(!pDriveLayoutEx)
{
ObDereferenceObject(pDiskFileObject);
return STATUS_INSUFFICIENT_RESOURCES;
}

status = COSNFilter::OSNDeviceIoctl(pDevice,
IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
NULL,
0,
pDriveLayoutEx,
size);

if(NT_SUCCESS(status))
{
if(pDriveLayoutEx->PartitionStyle == PARTITION_STYLE_MBR)
{
*pDiskSignature = pDriveLayoutEx->Mbr.Signature;
}
else
{
RtlCopyMemory(pDiskSignature, &pDriveLayoutEx->Gpt.DiskId, sizeof(ULONG));
}
}

ExFreePool(pDriveLayoutEx);

}

ObDereferenceObject(pDiskFileObject);
KdPrint(("OSNVf:GetDiskSignature %X  sig %X", status, pDriveLayout->Signature));
ExFreePool(pDriveLayout);
return status;

}


4.ObReferenceObjectByName

  这个函数微软是没有公开的,在所有WDK的.h文件里都无法找到,但用Dependency工具是可以查到ntoskrnl.exe是可以导出ObReferenceObjectByName这个函数的。在使用这个函数的时候,我么需要自己声明下这个函数以及函数中也未公开的参数类型;如果是C++编译的话还需要extern “C”。ObReferenceObjectByName和IoGetDeviceObjectPointer 区别在于后者只能获取设备对象的对象指针;而ObReferenceObjectByName还可以获取其他内核对象的指针,如Event,Mutex之类的;同样ObReferenceObjectByName使用完之后,也需要ObDereferenceObjec减引用计数;具体代码如下:

//对ObReferenceObjectByName和IoDriverObjectType 的声明
#ifdef __cplusplus
extern "C"
{
#endif  __cplusplus
extern POBJECT_TYPE IoDeviceObjectType;
NTSYSAPI
NTSTATUS
NTAPI ObReferenceObjectByName( IN PUNICODE_STRING ObjectName,
IN ULONG Attributes,
IN PACCESS_STATE AccessState OPTIONAL,
IN ACCESS_MASK DesiredAccess OPTIONAL,
IN POBJECT_TYPE ObjectType,
IN KPROCESSOR_MODE AccessMode,
IN OUT PVOID ParseContext OPTIONAL,
OUT PVOID* Object );

#ifdef __cplusplus
}
#endif __cplusplus


//调用方法

NTSTATUS GetDeviceName()
{
UNICODE_STRING DeviceName;

NTSTATUS         ntSattus;
RtlInitUnicodeString( &DeviceName, L"\\device\\deviceA" );

PDEVICE_OBJECT pDeviceObject = NULL;
PFILE_OBJECT FileObject = NULL;
ntStatus = ObReferenceObjectByName(&DeviceName,
OBJ_CASE_INSENSITIVE,
NULL,
FILE_ALL_ACCESS,
IoDeviceObjectType,
KernelMode,
NULL,
(PVOID*)&pDeviceObject );

if(!NT_SUCCESS(ntStatus))
{
KdPrint(("FAILED TO GET OBJECT NAME \n"));
}
}


//我们还可以用ObReferenceObjectByName先根据驱动对象名获取驱动设备,然后再获取驱动设备中的设备对象;

5.IoGetDeviceAttachmentBaseRef

  获取设备驱动堆栈或文件系统最底层的设备对象,获取最底层驱动方法比较多,比如设备AddDevice的时候传进来的参数就是PDO最底层设备对象。如果你是上层过滤驱动,可以DEVICEOBJECT->AttachedDevice 一层一层往下找,当然如果你没有attach的话是找不到的,这个和IoGetDeviceAttachmentBaseRef 类似,如果没有attach的话这个函数也会返回NULL的
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息