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

时间: 2024-10-28 19:40:35

WDF编程框架的相关文章

iOS端JSON转Model链式编程框架SuperKVC使用方法与原理

背景 在client编程中.字典转模型是一个极为常见的问题,苹果提供了KVC来实现NSDictionary到Model的注入,可是KVC仅仅能进行单层浅注入.且无法处理类型转换.key与属性名不正确应.深度注入等问题,笔者从Masonry得到启示,开发了一个通过链式配置注入器实现深度注入.类型转换.key-属性名映射等功能的轻量级注入框架SuperKVC.眼下已经开源到GitHub,点击这里前往.欢迎Star和Fork.欢迎和我一起完好这个框架! 本文将从应用和原理两个角度介绍SuperKVC

Linux模块编程框架

Linux模块编程框架 Linux是单内核系统,可通用计算平台的外围设备是频繁变化的,不可能将所有的(包括将来即将出现的)设备的驱动程序都一次性编译进内核,为了解决这个问题,Linux提出了可加载内核模块(Loadable Kernel Module,LKM)的概念,允许一个设备驱动通过模块加载的方式,在内核运行起来之后"融入"内核,加载进内核的模块和本身就编译进内核的模块一模一样.一个程序在编译的地址的相对关系就已经确定了,运行的时候只是进行简单的偏移,为了使模块加载进内核后能够被放

跨平台网络通信与server编程框架库(acl库)介绍

一.描写叙述 acl project是一个跨平台(支持LINUX,WIN32,Solaris,MacOS,FreeBSD)的网络通信库及server编程框架,同一时候提供很多其它的有用功能库.通过该库,用户能够很easy地编写支持多种模式(多线程.多进程.非堵塞.触发器.UDP方式)的server程序,WEB 应用程序,数据库应用程序.此外,该库还提供了常见应用的client通信库(如:HTTP.SMTP.ICMP.memcache.beanstalk),常见流式编解码库:XML/JSON/MI

React Native是一套使用 React 构建 Native app 的编程框架

React Native at first sight what is React Native? 跟据官方的描述, React Native是一套使用 React 构建 Native app 的编程框架. 推出不久便引发了广泛关注, 这也得益于 JavaScript 开放而活跃的技术社区和 React Native 完备的技术体系支持. 本文试图概括的介绍 React Native. React Native 主要是一套 Runtime, 包括一套 React.js 库使得开发可以用 Reac

acl 是一个跨平台的网络通信库及服务器编程框架

acl 工程是一个跨平台(支持LINUX,WIN32,Solaris,MacOS,FreeBSD)的网络通信库及服务器编程框架,同时提供更多的实用功能库.通过该库,用户可以非常容易地编写支持多种模式(多线程.多进程.非阻塞.触发器.UDP方式.协程方式)的服务器程序,WEB 应用程序,数据库应用程序.此外,该库还提供了常见应用的客户端通信库(如:HTTP.SMTP.ICMP.redis.memcache.beanstalk.handler socket),常见流式编解码库:XML/JSON/MI

高大上函数响应式编程框架ReactiveCocoa学习笔记1 简介

原创文章,转载请声明出处哈. ReactiveCocoa函数响应式编程 一.简介 ReactiveCocoa(其简称为RAC)是函数响应式编程框架.RAC具有函数式编程和响应式编程的特性.它主要吸取了.Net的 Reactive Extensions的设计和实现. 函数式编程 (Functional Programming) 函数式编程也可以写N篇,它是完全不同于OO的编程模式,这里主要讲一下这个框架使用到的函数式思想. 1) 高阶函数:在函数式编程中,把函数当参数来回传递,而这个,说成术语,我

跨平台网络通信与服务器编程框架库(acl库)介绍

一.描述 acl 工程是一个跨平台(支持LINUX,WIN32,Solaris,MacOS,FreeBSD)的网络通信库及服务器编程框架,同时提供更多的实用功能库.通过该库,用户可以非常容易地编写支持多种模式(多线程.多进程.非阻塞.触发器.UDP方式)的服务器程序,WEB 应用程序,数据库应用程序.此外,该库还提供了常见应用的客户端通信库(如:HTTP.SMTP.ICMP.memcache.beanstalk),常见流式编解码库:XML/JSON/MIME/BASE64/UUCODE/QPCO

OpenGL【3 MFC和OpenGL联合编程框架简述】

[需要将view的显示区域黑色背景所需的步骤] 1. 简历普通单文档MFC工程(自动关联了DOC VIEW 和Frame三个类) 2. 拷贝Test 工程中的几个函数到目标工程 一.PreCreateWindow[改变窗口类型] 二.OnCreate[调用初始化函数myInitOpenGL] 三.myInitOpenGL[建立DC 和RC并关联二者,其中调用mySetupPixelFormat初始化RC绘图环境] 四.mySetupPixelFormat[初始化RC绘图环境] 五.OnDraw里

(46)LINUX应用编程和网络编程之一Linux应用编程框架

3.1.1.应用编程框架介绍 3.1.1.1.什么是应用编程 (1)整个嵌入式linux核心课程包括5个点,按照学习顺序依次是:裸机.C高级.uboot和系统移植.linux应用编程和网络编程.驱动. (2)典型的嵌入式产品就是基于嵌入式linux操作系统来工作的.典型的嵌入式产品的研发过程就是:第一步让linux系统在硬件上跑起来(系统移植工作),第二步基于linux系统来开发应用程序实现产品功能. (3)基于linux去做应用编程,其实就是通过调用linux的[系统API]来实现应用需要完成