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

转载自: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;
}

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的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值