首页 > 代码库 > WDF编程框架

WDF编程框架

微软的wdk开发包里自带了一些sample,这是些质量不错并且权威的学习资料,最好的学习驱动的方法就是阅读和修改这些代码。其中Ramdisk实现了一个虚拟磁盘,可以作为WDF编程的经典代码材料,《寒江独钓-Windows内核安全编程》第5章“磁盘的虚拟”便以此为例,这篇博客是一篇学习总结。

驱动的入口函数很简洁:

NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    )
{
    WDF_DRIVER_CONFIG config;
    KdPrint(("Windows Ramdisk Driver - Driver Framework Edition.\n"));
    WDF_DRIVER_CONFIG_INIT( &config, RamDiskEvtDeviceAdd );
    return WdfDriverCreate(DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, WDF_NO_HANDLE);
}

上面代码最主要的任务就是创建一个“驱动对象”并马上返回了,我们看WDF_DRIVER_CONFIG这个玩意:

typedef struct _WDF_DRIVER_CONFIG {
  ULONG                     Size;  //这个结构体的大小,以自己为单位;
  PFN_WDF_DRIVER_DEVICE_ADD EvtDriverDeviceAdd;  //Windows驱动的EvtDriverDeviceAdd回调函数指针;
  PFN_WDF_DRIVER_UNLOAD     EvtDriverUnload;  //Windows驱动的EvtDriverUnload回调函数指针;
  ULONG                     DriverInitFlags;  //驱动初始化标志位,由一个或者多个WDF_DRIVER_INIT_FLAGS类型值构成;
  ULONG                     DriverPoolTag;  //驱动定义的内存池标签,是Framework分配给驱动的内存标签。调试时可以显示这个标签。
} WDF_DRIVER_CONFIG, *PWDF_DRIVER_CONFIG;

上面的WDF_DRIVER_CONFIG_INIT( &config, RamDiskEvtDeviceAdd );其实就是把 RamDiskEvtDeviceAdd 赋给结构体的第2项。上面有注释,它是一个回调函数指针,注意这是一个非常重要的参数,因为创建完驱动对象后主函数就返回了,而这个回调函数是后面驱动和系统联系起来的唯一纽带,在今后系统运行过程中,一旦发现了此类设备, RamDiskEvtDeviceAdd就会被 Windows 的PnP manager调用,这个驱动的处理流程也将在此后上演。

接下来看  RamDiskEvtDeviceAdd 这个函数:

NTSTATUS
RamDiskEvtDeviceAdd(
    IN WDFDRIVER Driver,
    IN PWDFDEVICE_INIT DeviceInit
    )
{
    WDF_OBJECT_ATTRIBUTES   deviceAttributes;  //将建立的设备对象的属性描述变量
    NTSTATUS                status;
    WDFDEVICE               device;        //将建立的设备
    WDF_OBJECT_ATTRIBUTES   queueAttributes;   //将要建立的队列对象的属性描述变量
    WDF_IO_QUEUE_CONFIG     ioQueueConfig;    //将要建立的队列配置变量
    PDEVICE_EXTENSION       pDeviceExtension;  //此设备所对应的设备扩展域的指针
    PQUEUE_EXTENSION        pQueueContext = NULL;  //将要建立的队列扩展域的指针
    WDFQUEUE                queue;        //将要建立的队列
    DECLARE_CONST_UNICODE_STRING(ntDeviceName, NT_DEVICE_NAME);
    PAGED_CODE();
    UNREFERENCED_PARAMETER(Driver);  //此函数不使用Driver参数,加这句为避免编译告警
    status = WdfDeviceInitAssignName(DeviceInit, &ntDeviceName);  //1.为设备指定名字
    if (!NT_SUCCESS(status)) {
        return status;
    }
    //2.设置设备的一些属性
    WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_DISK);
    WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect);
    WdfDeviceInitSetExclusive(DeviceInit, FALSE);
    //指定设备的设备对象扩展
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_EXTENSION);
    deviceAttributes.EvtCleanupCallback = RamDiskEvtDeviceContextCleanup;  //建立设备的清除回调函数
    //3.创建设备
    status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
    if (!NT_SUCCESS(status)) {
        return status;
    }
    //声明一个指向设备扩展的指针
    pDeviceExtension = DeviceGetExtension(device);
    //将队列的配置变量初始化为默认值
    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE (
        &ioQueueConfig,
        WdfIoQueueDispatchSequential
        );
    //设置我们感兴趣的请求的处理函数,这里处理了Read/Write/DeviceIoControl请求
    ioQueueConfig.EvtIoDeviceControl = RamDiskEvtIoDeviceControl;
    ioQueueConfig.EvtIoRead          = RamDiskEvtIoRead;
    ioQueueConfig.EvtIoWrite         = RamDiskEvtIoWrite;
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&queueAttributes, QUEUE_EXTENSION);  //指定一下队列的队列对象扩展
    __analysis_assume(ioQueueConfig.EvtIoStop != 0);
    status = WdfIoQueueCreate( device,    //4.用初始化好的参数创建队列
                               &ioQueueConfig,
                               &queueAttributes,
                               &queue );
    __analysis_assume(ioQueueConfig.EvtIoStop == 0);
    if (!NT_SUCCESS(status)) {
        return status;
    }
    pQueueContext = QueueGetExtension(queue);  //指向队列设备扩展的指针
    pQueueContext->DeviceExtension = pDeviceExtension;  //队列扩展与设备扩展关联起来
    //设备扩展中几个值的初始化
    pDeviceExtension->DiskRegInfo.DriveLetter.Buffer =
        (PWSTR) &pDeviceExtension->DriveLetterBuffer;
    pDeviceExtension->DiskRegInfo.DriveLetter.MaximumLength =
        sizeof(pDeviceExtension->DriveLetterBuffer);
    //从本驱动提供的注册表键中取信息
    RamDiskQueryDiskRegParameters(
        WdfDriverGetRegistryPath(WdfDeviceGetDriver(device)),
        &pDeviceExtension->DiskRegInfo
        );
    //分配指定大小的非分页内存
    pDeviceExtension->DiskImage = ExAllocatePoolWithTag(
        NonPagedPool,
        pDeviceExtension->DiskRegInfo.DiskSize,
        RAMDISK_TAG
        );
    if (pDeviceExtension->DiskImage) {
        UNICODE_STRING deviceName;
        UNICODE_STRING win32Name;
      //初始化磁盘的函数,属于业务逻辑部分,这里不同的功能代码替换之
        RamDiskFormatDisk(pDeviceExtension);
        status = STATUS_SUCCESS;
        RtlInitUnicodeString(&win32Name, DOS_DEVICE_NAME);
        RtlInitUnicodeString(&deviceName, NT_DEVICE_NAME);
        pDeviceExtension->SymbolicLink.Buffer = (PWSTR)
            &pDeviceExtension->DosDeviceNameBuffer;
        pDeviceExtension->SymbolicLink.MaximumLength =
            sizeof(pDeviceExtension->DosDeviceNameBuffer);
        pDeviceExtension->SymbolicLink.Length = win32Name.Length;
        RtlCopyUnicodeString(&pDeviceExtension->SymbolicLink, &win32Name);
        RtlAppendUnicodeStringToString(&pDeviceExtension->SymbolicLink,
                                       &pDeviceExtension->DiskRegInfo.DriveLetter);
      //5.为设备建立符号链接,即DOS_DEVICE_NAME+注册表中读取的盘符
        status = WdfDeviceCreateSymbolicLink(device,
                                             &pDeviceExtension->SymbolicLink);
    }
    return status;
}

上面代码注释的已经比较清晰,再简要描述一下我们做了什么。

首先创建一个“驱动对象”,并设置一个 RamDiskEvtDeviceAdd 回调函数,这个回调函数很重要,它是驱动和系统相联系的纽带。在 RamDiskEvtDeviceAdd 函数里面,最重要的任务便是建立一个“设备对象”,在这之后各种请求就会纷至踏来。而如何处理这些请求呢,一种常用的方式就是使用队列,把接收到的请求放入一个或多个队列中,另一个线程去处理这些队列,这是一个典型的生产者——消费者模型。为了方便我们编程,微软实现了这个轮子,这显然比我们自己处理方便许多,这里用到了“队列对象”。

目前为止我们用到了3个内核对象,分别是“驱动对象”,“设备对象”,“队列对象”,现在来总结一下他们有没有共性。

1.“对象”都有可配置的属性,如“驱动对象”通过WDF_DRIVER_CONFIG变量,“设备对象”和“队列对象”的WDF_OBJECT_ATTRIBUTES变量,“队列对象”的WDF_IO_QUEUE_CONFIG变量。

2.正确处理这上述“对象”结构的各个成员,那么驱动就实现它的功能了,这是使用框架开发带来的便捷。我们的编程工作最终就变成了:创建一个个“对象”,并妥善处理好它们。

现在我们还有几个问题没有处理,一是初始化磁盘的业务逻辑部分,二是请求的处理部分,三是设备的清除回调函数部分,下面分别说明。

初始化磁盘属于业务逻辑,与我们想要说明的WDF编程框架并没有联系,所以不作代码注释,如下:

NTSTATUS
RamDiskFormatDisk(
    IN PDEVICE_EXTENSION devExt
    )
{
    PBOOT_SECTOR bootSector = (PBOOT_SECTOR) devExt->DiskImage;
    PUCHAR       firstFatSector;
    ULONG        rootDirEntries;
    ULONG        sectorsPerCluster;
    USHORT       fatType;        // Type FAT 12 or 16
    USHORT       fatEntries;     // Number of cluster entries in FAT
    USHORT       fatSectorCnt;   // Number of sectors for FAT
    PDIR_ENTRY   rootDir;        // Pointer to first entry in root dir
    PAGED_CODE();
    ASSERT(sizeof(BOOT_SECTOR) == 512);
    ASSERT(devExt->DiskImage != NULL);
    RtlZeroMemory(devExt->DiskImage, devExt->DiskRegInfo.DiskSize);
    devExt->DiskGeometry.BytesPerSector = 512;
    devExt->DiskGeometry.SectorsPerTrack = 32;     // Using Ramdisk value
    devExt->DiskGeometry.TracksPerCylinder = 2;    // Using Ramdisk value
    devExt->DiskGeometry.Cylinders.QuadPart = devExt->DiskRegInfo.DiskSize / 512 / 32 / 2;
    devExt->DiskGeometry.MediaType = RAMDISK_MEDIA_TYPE;
    KdPrint((
        "Cylinders: %I64d\n TracksPerCylinder: %lu\n SectorsPerTrack: %lu\n BytesPerSector: %lu\n",
        devExt->DiskGeometry.Cylinders.QuadPart, devExt->DiskGeometry.TracksPerCylinder,
        devExt->DiskGeometry.SectorsPerTrack, devExt->DiskGeometry.BytesPerSector
        ));
    rootDirEntries = devExt->DiskRegInfo.RootDirEntries;
    sectorsPerCluster = devExt->DiskRegInfo.SectorsPerCluster;
    if (rootDirEntries & (DIR_ENTRIES_PER_SECTOR - 1)) {
        rootDirEntries =
            (rootDirEntries + (DIR_ENTRIES_PER_SECTOR - 1)) &
                ~ (DIR_ENTRIES_PER_SECTOR - 1);
    }
    KdPrint((
        "Root dir entries: %lu\n Sectors/cluster: %lu\n",
        rootDirEntries, sectorsPerCluster
        ));
    bootSector->bsJump[0] = 0xeb;
    bootSector->bsJump[1] = 0x3c;
    bootSector->bsJump[2] = 0x90;
    bootSector->bsOemName[0] = ‘R‘;
    bootSector->bsOemName[1] = ‘a‘;
    bootSector->bsOemName[2] = ‘j‘;
    bootSector->bsOemName[3] = ‘u‘;
    bootSector->bsOemName[4] = ‘R‘;
    bootSector->bsOemName[5] = ‘a‘;
    bootSector->bsOemName[6] = ‘m‘;
    bootSector->bsOemName[7] = ‘ ‘;
    bootSector->bsBytesPerSec = (SHORT)devExt->DiskGeometry.BytesPerSector;
    bootSector->bsResSectors  = 1;
    bootSector->bsFATs        = 1;
    bootSector->bsRootDirEnts = (USHORT)rootDirEntries;
    bootSector->bsSectors     = (USHORT)(devExt->DiskRegInfo.DiskSize /
                                         devExt->DiskGeometry.BytesPerSector);
    bootSector->bsMedia       = (UCHAR)devExt->DiskGeometry.MediaType;
    bootSector->bsSecPerClus  = (UCHAR)sectorsPerCluster;
    fatEntries =
        (bootSector->bsSectors - bootSector->bsResSectors -
            bootSector->bsRootDirEnts / DIR_ENTRIES_PER_SECTOR) /
                bootSector->bsSecPerClus + 2;
    if (fatEntries > 4087) {
        fatType =  16;
        fatSectorCnt = (fatEntries * 2 + 511) / 512;
        fatEntries   = fatEntries + fatSectorCnt;
        fatSectorCnt = (fatEntries * 2 + 511) / 512;
    }
    else {
        fatType =  12;
        fatSectorCnt = (((fatEntries * 3 + 1) / 2) + 511) / 512;
        fatEntries   = fatEntries + fatSectorCnt;
        fatSectorCnt = (((fatEntries * 3 + 1) / 2) + 511) / 512;
    }
    bootSector->bsFATsecs       = fatSectorCnt;
    bootSector->bsSecPerTrack   = (USHORT)devExt->DiskGeometry.SectorsPerTrack;
    bootSector->bsHeads         = (USHORT)devExt->DiskGeometry.TracksPerCylinder;
    bootSector->bsBootSignature = 0x29;
    bootSector->bsVolumeID      = 0x12345678;
    bootSector->bsLabel[0]  = ‘R‘;
    bootSector->bsLabel[1]  = ‘a‘;
    bootSector->bsLabel[2]  = ‘m‘;
    bootSector->bsLabel[3]  = ‘D‘;
    bootSector->bsLabel[4]  = ‘i‘;
    bootSector->bsLabel[5]  = ‘s‘;
    bootSector->bsLabel[6]  = ‘k‘;
    bootSector->bsLabel[7]  = ‘ ‘;
    bootSector->bsLabel[8]  = ‘ ‘;
    bootSector->bsLabel[9]  = ‘ ‘;
    bootSector->bsLabel[10] = ‘ ‘;
    bootSector->bsFileSystemType[0] = ‘F‘;
    bootSector->bsFileSystemType[1] = ‘A‘;
    bootSector->bsFileSystemType[2] = ‘T‘;
    bootSector->bsFileSystemType[3] = ‘1‘;
    bootSector->bsFileSystemType[4] = ‘?‘;
    bootSector->bsFileSystemType[5] = ‘ ‘;
    bootSector->bsFileSystemType[6] = ‘ ‘;
    bootSector->bsFileSystemType[7] = ‘ ‘;
    bootSector->bsFileSystemType[4] = ( fatType == 16 ) ? ‘6‘ : ‘2‘;
    bootSector->bsSig2[0] = 0x55;
    bootSector->bsSig2[1] = 0xAA;
    firstFatSector    = (PUCHAR)(bootSector + 1);
    firstFatSector[0] = (UCHAR)devExt->DiskGeometry.MediaType;
    firstFatSector[1] = 0xFF;
    firstFatSector[2] = 0xFF;
    if (fatType == 16) {
        firstFatSector[3] = 0xFF;
    }
    rootDir = (PDIR_ENTRY)(bootSector + 1 + fatSectorCnt);
    rootDir->deName[0] = ‘M‘;
    rootDir->deName[1] = ‘S‘;
    rootDir->deName[2] = ‘-‘;
    rootDir->deName[3] = ‘R‘;
    rootDir->deName[4] = ‘A‘;
    rootDir->deName[5] = ‘M‘;
    rootDir->deName[6] = ‘D‘;
    rootDir->deName[7] = ‘R‘;
    rootDir->deExtension[0] = ‘I‘;
    rootDir->deExtension[1] = ‘V‘;
    rootDir->deExtension[2] = ‘E‘;
    rootDir->deAttributes = DIR_ATTR_VOLUME;
    return STATUS_SUCCESS;
}

这里我们使用的内存来虚拟磁盘,所以对磁盘的读写等操作最后都映射到内存,以下是3种请求的处理函数部分:

VOID
RamDiskEvtIoRead(
    IN WDFQUEUE Queue,
    IN WDFREQUEST Request,
    IN size_t Length
    )
{
    PDEVICE_EXTENSION      devExt = QueueGetExtension(Queue)->DeviceExtension;
    NTSTATUS               Status = STATUS_INVALID_PARAMETER;
    WDF_REQUEST_PARAMETERS Parameters;
    LARGE_INTEGER          ByteOffset;
    WDFMEMORY              hMemory;
    _Analysis_assume_(Length > 0);
    WDF_REQUEST_PARAMETERS_INIT(&Parameters);
    WdfRequestGetParameters(Request, &Parameters);
    ByteOffset.QuadPart = Parameters.Parameters.Read.DeviceOffset;
    if (RamDiskCheckParameters(devExt, ByteOffset, Length)) {
        Status = WdfRequestRetrieveOutputMemory(Request, &hMemory);
        if(NT_SUCCESS(Status)){
            Status = WdfMemoryCopyFromBuffer(hMemory,   // Destination
                                             0,         // Offset into the destination
                                             devExt->DiskImage + ByteOffset.LowPart, // source
                                             Length);
        }
    }
    WdfRequestCompleteWithInformation(Request, Status, (ULONG_PTR)Length);
}
VOID
RamDiskEvtIoWrite(
    IN WDFQUEUE Queue,
    IN WDFREQUEST Request,
    IN size_t Length
    )
{
    PDEVICE_EXTENSION      devExt = QueueGetExtension(Queue)->DeviceExtension;
    NTSTATUS               Status = STATUS_INVALID_PARAMETER;
    WDF_REQUEST_PARAMETERS Parameters;
    LARGE_INTEGER          ByteOffset;
    WDFMEMORY              hMemory;
    _Analysis_assume_(Length > 0);
    WDF_REQUEST_PARAMETERS_INIT(&Parameters);
    WdfRequestGetParameters(Request, &Parameters);
    ByteOffset.QuadPart = Parameters.Parameters.Write.DeviceOffset;
    if (RamDiskCheckParameters(devExt, ByteOffset, Length)) {
        Status = WdfRequestRetrieveInputMemory(Request, &hMemory);
        if(NT_SUCCESS(Status)){
            Status = WdfMemoryCopyToBuffer(hMemory, // Source
                                    0,              // offset in Source memory where the copy has to start
                                    devExt->DiskImage + ByteOffset.LowPart, // destination
                                    Length);
        }
    }
    WdfRequestCompleteWithInformation(Request, Status, (ULONG_PTR)Length);
}
VOID
RamDiskEvtIoDeviceControl(
    IN WDFQUEUE Queue,
    IN WDFREQUEST Request,
    IN size_t OutputBufferLength,
    IN size_t InputBufferLength,
    IN ULONG IoControlCode
    )
{
    NTSTATUS          Status = STATUS_INVALID_DEVICE_REQUEST;
    ULONG_PTR         information = 0;
    size_t            bufSize;
    PDEVICE_EXTENSION devExt = QueueGetExtension(Queue)->DeviceExtension;
    UNREFERENCED_PARAMETER(OutputBufferLength);
    UNREFERENCED_PARAMETER(InputBufferLength);
    switch (IoControlCode) {
    case IOCTL_DISK_GET_PARTITION_INFO: {
            PPARTITION_INFORMATION outputBuffer;
            PBOOT_SECTOR bootSector = (PBOOT_SECTOR) devExt->DiskImage;
            information = sizeof(PARTITION_INFORMATION);
            Status = WdfRequestRetrieveOutputBuffer(Request, sizeof(PARTITION_INFORMATION), &outputBuffer, &bufSize);
            if(NT_SUCCESS(Status) ) {
                outputBuffer->PartitionType =
                    (bootSector->bsFileSystemType[4] == ‘6‘) ? PARTITION_FAT_16 : PARTITION_FAT_12;
                outputBuffer->BootIndicator       = FALSE;
                outputBuffer->RecognizedPartition = TRUE;
                outputBuffer->RewritePartition    = FALSE;
                outputBuffer->StartingOffset.QuadPart = 0;
                outputBuffer->PartitionLength.QuadPart = devExt->DiskRegInfo.DiskSize;
                outputBuffer->HiddenSectors       = (ULONG) (1L);
                outputBuffer->PartitionNumber     = (ULONG) (-1L);
                Status = STATUS_SUCCESS;
            }
        }
        break;
    case IOCTL_DISK_GET_DRIVE_GEOMETRY:  {
            PDISK_GEOMETRY outputBuffer;
            information = sizeof(DISK_GEOMETRY);
            Status = WdfRequestRetrieveOutputBuffer(Request, sizeof(DISK_GEOMETRY), &outputBuffer, &bufSize);
            if(NT_SUCCESS(Status) ) {
                RtlCopyMemory(outputBuffer, &(devExt->DiskGeometry), sizeof(DISK_GEOMETRY));
                Status = STATUS_SUCCESS;
            }
        }
        break;
    case IOCTL_DISK_CHECK_VERIFY:
    case IOCTL_DISK_IS_WRITABLE:
        Status = STATUS_SUCCESS;
        break;
    }
    WdfRequestCompleteWithInformation(Request, Status, information);
}

设备的清除回调函数比较简单,主要进行释放分配的内存操作:

VOID
RamDiskEvtDeviceContextCleanup(
    IN WDFOBJECT Device
    )
{
    PDEVICE_EXTENSION pDeviceExtension = DeviceGetExtension(Device);
    PAGED_CODE();
    if(pDeviceExtension->DiskImage) {
        ExFreePool(pDeviceExtension->DiskImage);
    }
}

从注册表中读取配置参数的代码如下:

VOID
RamDiskQueryDiskRegParameters(
    _In_ PWSTR RegistryPath,
    _In_ PDISK_INFO DiskRegInfo
    )
{
    RTL_QUERY_REGISTRY_TABLE rtlQueryRegTbl[5 + 1];  // Need 1 for NULL
    NTSTATUS                 Status;
    DISK_INFO                defDiskRegInfo;
    PAGED_CODE();
    ASSERT(RegistryPath != NULL);
    defDiskRegInfo.DiskSize          = DEFAULT_DISK_SIZE;
    defDiskRegInfo.RootDirEntries    = DEFAULT_ROOT_DIR_ENTRIES;
    defDiskRegInfo.SectorsPerCluster = DEFAULT_SECTORS_PER_CLUSTER;
    RtlInitUnicodeString(&defDiskRegInfo.DriveLetter, DEFAULT_DRIVE_LETTER);
    RtlZeroMemory(rtlQueryRegTbl, sizeof(rtlQueryRegTbl));
    rtlQueryRegTbl[0].Flags         = RTL_QUERY_REGISTRY_SUBKEY;
    rtlQueryRegTbl[0].Name          = L"Parameters";
    rtlQueryRegTbl[0].EntryContext  = NULL;
    rtlQueryRegTbl[0].DefaultType   = (ULONG_PTR)NULL;
    rtlQueryRegTbl[0].DefaultData   = NULL;
    rtlQueryRegTbl[0].DefaultLength = (ULONG_PTR)NULL;
    rtlQueryRegTbl[1].Flags         = RTL_QUERY_REGISTRY_DIRECT;
    rtlQueryRegTbl[1].Name          = L"DiskSize";
    rtlQueryRegTbl[1].EntryContext  = &DiskRegInfo->DiskSize;
    rtlQueryRegTbl[1].DefaultType   = REG_DWORD;
    rtlQueryRegTbl[1].DefaultData   = &defDiskRegInfo.DiskSize;
    rtlQueryRegTbl[1].DefaultLength = sizeof(ULONG);
    rtlQueryRegTbl[2].Flags         = RTL_QUERY_REGISTRY_DIRECT;
    rtlQueryRegTbl[2].Name          = L"RootDirEntries";
    rtlQueryRegTbl[2].EntryContext  = &DiskRegInfo->RootDirEntries;
    rtlQueryRegTbl[2].DefaultType   = REG_DWORD;
    rtlQueryRegTbl[2].DefaultData   = &defDiskRegInfo.RootDirEntries;
    rtlQueryRegTbl[2].DefaultLength = sizeof(ULONG);
    rtlQueryRegTbl[3].Flags         = RTL_QUERY_REGISTRY_DIRECT;
    rtlQueryRegTbl[3].Name          = L"SectorsPerCluster";
    rtlQueryRegTbl[3].EntryContext  = &DiskRegInfo->SectorsPerCluster;
    rtlQueryRegTbl[3].DefaultType   = REG_DWORD;
    rtlQueryRegTbl[3].DefaultData   = &defDiskRegInfo.SectorsPerCluster;
    rtlQueryRegTbl[3].DefaultLength = sizeof(ULONG);
    rtlQueryRegTbl[4].Flags         = RTL_QUERY_REGISTRY_DIRECT;
    rtlQueryRegTbl[4].Name          = L"DriveLetter";
    rtlQueryRegTbl[4].EntryContext  = &DiskRegInfo->DriveLetter;
    rtlQueryRegTbl[4].DefaultType   = REG_SZ;
    rtlQueryRegTbl[4].DefaultData   = defDiskRegInfo.DriveLetter.Buffer;
    rtlQueryRegTbl[4].DefaultLength = 0;
    Status = RtlQueryRegistryValues(
                 RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
                 RegistryPath,
                 rtlQueryRegTbl,
                 NULL,
                 NULL
             );
    if (NT_SUCCESS(Status) == FALSE) {
        DiskRegInfo->DiskSize          = defDiskRegInfo.DiskSize;
        DiskRegInfo->RootDirEntries    = defDiskRegInfo.RootDirEntries;
        DiskRegInfo->SectorsPerCluster = defDiskRegInfo.SectorsPerCluster;
        RtlCopyUnicodeString(&DiskRegInfo->DriveLetter, &defDiskRegInfo.DriveLetter);
    }
    KdPrint(("DiskSize          = 0x%lx\n", DiskRegInfo->DiskSize));
    KdPrint(("RootDirEntries    = 0x%lx\n", DiskRegInfo->RootDirEntries));
    KdPrint(("SectorsPerCluster = 0x%lx\n", DiskRegInfo->SectorsPerCluster));
    KdPrint(("DriveLetter       = %wZ\n",   &(DiskRegInfo->DriveLetter)));
    return;
}

磁盘虚拟的驱动代码在这里作为WDF编程方式的总结案例,它的应用场景也是非常诱人的,实现数据隐藏、Sandbox、网吧无盘系统...这个代码使用内存作为磁盘存储介质,其实这个介质还可以是文件、注册表、网络文件等,实现功能而定了。

最后分享几个网站

收录一些Undocumented Kernel Data Structure的网站 http://www.nirsoft.net/kernel_struct/vista/index.html

张银奎先生关于软件调试内容的网站 http://advdbg.org/default.aspx


WDF编程框架