SKY外语计算机学习
标题:
(试发贴2) 驱动程序 -虚拟硬盘,将内存虚拟成硬盘[RamDisk](WDF驱动)
[打印本页]
作者:
wangbeacon
时间:
2012-6-10 22:46
标题:
(试发贴2) 驱动程序 -虚拟硬盘,将内存虚拟成硬盘[RamDisk](WDF驱动)
本帖最后由 sky_yx 于 2015-12-30 14:17 编辑
// 编译以后用第一个 INF 文件 保存为 INF 文件
// 将 INF 文件和生成的SYS文件放在一起
// 在控制面板里添加硬件,选择这个INF文件
// 重启就会有个 0x100000 B 大小的虚拟硬盘 R: 盘
///////////////////SYS文件///////////////////
///////////使用C编译////////////
#include <NTDDK.h>
#include <ntdddisk.h>
#define NTSTRSAFE_LIB
#include <ntstrsafe.h>
#include "ntintsafe.h"
#include <WDF.h>
#define NT_DEVICE_NAME L"\\Device\\Ramdisk"
#define DOS_DEVICE_NAME L"\\DosDevices\\"
#define RAMDISK_TAG 'DmaR'
#define DOS_DEVNAME_LENGTH (sizeof(DOS_DEVICE_NAME) + sizeof(WCHAR) * 10)
#define DRIVE_LETTER_LENGTH (sizeof(WCHAR) * 10)
#define DRIVE_LETTER_BUFFER_SIZE 10
#define DOS_DEVNAME_BUFFER_SIZE (sizeof(DOS_DEVICE_NAME) / 2) + 10
#define RAMDISK_MEDIA_TYPE 0xF8
#define DIR_ENTRIES_PER_SECTOR 16
#define DEFAULT_DISK_SIZE (0x100000) //1MB
#define DEFAULT_ROOT_DIR_ENTRIES 0x200
#define DEFAULT_SECTORS_PER_CLUSTER 2
#define DEFAULT_DRIVE_LETTER L"Z:"
typedef struct _DISK_INFO {
ULONG DiskSize; // 磁盘大小,以Byte计算
ULONG RootDirEntries; // 磁盘上根文件系统的进入节点
ULONG SectorsPerCluster; // 磁盘每个簇由多少个扇区组成
UNICODE_STRING DriveLetter; // 磁盘的盘符
} DISK_INFO, *PDISK_INFO;
/*
typedef struct _DISK_GEOMETRY {
LARGE_INTEGER Cylinders; //有多少个柱面
MEDIA_TYPE MediaType; //磁盘介质的类型
ULONG TracksPerCylinder; //每个柱面有多少个磁道,也就是有多少个磁盘面
ULONG SectorsPerTrack; //每个磁道有多少个扇区
ULONG BytesPerSector; //每个扇区有多少个字节
} DISK_GEOMETRY, *PDISK_GEOMETRY;
*/
typedef struct _DEVICE_EXTENSION {
PUCHAR DiskImage; // 指向磁盘镜像的指针
DISK_GEOMETRY DiskGeometry; // 磁盘特性
DISK_INFO DiskRegInfo; // 自定义的磁盘信息结构,安装时存放在注册表中
UNICODE_STRING SymbolicLink; // 这个盘的符号链接名,这是真正的符号链接名
//DiskRegInfo 中 DriverLetter 的存储空间,这是用户有注册表中指定的盘符
WCHAR DriveLetterBuffer[DRIVE_LETTER_BUFFER_SIZE];
//SymbolicLink的存储空间
WCHAR DosDeviceNameBuffer[DOS_DEVNAME_BUFFER_SIZE];
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_EXTENSION, DeviceGetExtension)
typedef struct _QUEUE_EXTENSION {
PDEVICE_EXTENSION DeviceExtension;
} QUEUE_EXTENSION, *PQUEUE_EXTENSION;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(QUEUE_EXTENSION, QueueGetExtension)
#pragma pack(1)
typedef struct _BOOT_SECTOR
{
UCHAR bsJump[3]; // X86跳转指令,跳转到 DBR 中的引导程序
CCHAR bsOemName[8]; // 这个卷的 OEM 名称
USHORT bsBytesPerSec; // 每个扇区有多少个字节
UCHAR bsSecPerClus; // 每个簇有多少个扇区
USHORT bsResSectors; // 保留扇区数目,指的是第一个 FAT 表开始前的扇区数,包括 DBT 本身
UCHAR bsFATs; // 这个卷有多少个 FAT 表,在这里为 1
USHORT bsRootDirEnts; // 这个卷的根入口点有几个
USHORT bsSectors; // 这个卷一共有多少个扇区,对大于 65535 个扇区的卷,这个字段为 0
UCHAR bsMedia; // 这个卷的介质类型,在这里为 RAMDISK_MEDIA_TYPE
USHORT bsFATsecs; // 每个 FAT 表占用多少个扇区
USHORT bsSecPerTrack; // 每个磁道有多少个扇区,在这里为 32
USHORT bsHeads; // 有多少个磁头,在这里为 2
ULONG bsHiddenSecs; // 有多少个隐藏扇区,在这里设置为 0
ULONG bsHugeSectors; // 一个卷超过 65535 个扇区,会使用这个字段来说明总扇区数
UCHAR bsDriveNumber; // 驱动器编号,无用
UCHAR bsReserved1; // 保留字段
UCHAR bsBootSignature; // 磁盘扩展引导区标签,Windows 要求这个标签为 0x28 或 0x29
ULONG bsVolumeID; // 磁盘卷ID,在这里设置为 0x12345678
CCHAR bsLabel[11]; // 磁盘卷标
CCHAR bsFileSystemType[8];// 磁盘上的文件系统类型
CCHAR bsReserved2[448]; // 保留字段
UCHAR bsSig2[2]; // DBR 结束签名 - 0x55, 0xAA
} BOOT_SECTOR, *PBOOT_SECTOR;
typedef struct _DIR_ENTRY
{
UCHAR deName[8]; // 文件名
UCHAR deExtension[3]; // 文件扩展名
UCHAR deAttributes; // 文件属性
UCHAR deReserved; // 系统保留
USHORT deTime; // 文件建立时间
USHORT deDate; // 文件建立日期
USHORT deStartCluster; // 文件的第一个簇的编号
ULONG deFileSize; // 文件大小
} DIR_ENTRY, *PDIR_ENTRY;
#pragma pack()
#define DIR_ATTR_READONLY 0x01
#define DIR_ATTR_HIDDEN 0x02
#define DIR_ATTR_SYSTEM 0x04
#define DIR_ATTR_VOLUME 0x08
#define DIR_ATTR_DIRECTORY 0x10
#define DIR_ATTR_ARCHIVE 0x20
BOOLEAN CheckParameters(IN PDEVICE_EXTENSION pDeviceExtension,
IN LARGE_INTEGER liByteOffset,
IN size_t Length)
{
if(pDeviceExtension->DiskRegInfo.DiskSize < Length ||
liByteOffset.QuadPart < 0 ||
((ULONGLONG)liByteOffset.QuadPart > (pDeviceExtension->DiskRegInfo.DiskSize - Length)) ||
(Length & (pDeviceExtension->DiskGeometry.BytesPerSector - 1)))
{
KdPrint(("参数错误\n 偏移:[%X]\n 长度:[%d]\n",
liByteOffset,Length));
return FALSE;
}
return TRUE;
}
VOID DeviceRead(IN WDFQUEUE WdfQueue,
IN WDFREQUEST WdfRequest,
IN size_t Length)
{
//从队列的扩展域中获取到对应的磁盘设备的设备扩展
PDEVICE_EXTENSION pDeviceExtension = QueueGetExtension(WdfQueue)->DeviceExtension;
//用于各种函数返回值的状态变量
NTSTATUS ntStatus = STATUS_INVALID_PARAMETER;
//用于获取请求参数的变量
WDF_REQUEST_PARAMETERS WdfRequestParameters;
//用于获取读请求起始地址的变量
LARGE_INTEGER liByteOffset;
//这是一个用于获取读缓冲区的内存句柄
WDFMEMORY hMemory;
//初始化参数变量,为之后从请求参数中获取各种信息做准备
WDF_REQUEST_PARAMETERS_INIT(&WdfRequestParameters);
//从请求参数中获取信息
WdfRequestGetParameters(WdfRequest,&WdfRequestParameters);
//将请求参数中读的起始位置取出来
liByteOffset.QuadPart = WdfRequestParameters.Parameters.Read.DeviceOffset;
//这里是自己实现一个参数检查函数.由于读取范围不能超过磁盘镜像大小
//且必须是扇区对齐,所以这里需要有一个参数检查,如果检查失败,
//则直接将这个错误参数(STATUS_INVALID_PARAMETER)为返回值结束
if(CheckParameters(pDeviceExtension,liByteOffset,Length))
{
//从请求参数中获取读缓冲区的内存句柄
ntStatus = WdfRequestRetrieveOutputMemory(WdfRequest,&hMemory);
if(NT_SUCCESS(ntStatus))
//根据之前获取到的参数进行内存拷贝,填写这个读请求缓冲区
//从而完成这个读请求的操作
ntStatus = WdfMemoryCopyFromBuffer(hMemory,
0,pDeviceExtension->DiskImage + liByteOffset.LowPart,Length);
}
//结束这个读请求,这里要注意的是,需要将读取的长度作为返回的信息一并返回
WdfRequestCompleteWithInformation(WdfRequest,ntStatus,(ULONG_PTR)Length);
}
VOID DeviceWrite(IN WDFQUEUE WdfQueue,
IN WDFREQUEST WdfRequest,
IN size_t Length)
{
//从队列的扩展域中获取到对应的磁盘设备的设备扩展
PDEVICE_EXTENSION pDeviceExtension = QueueGetExtension(WdfQueue)->DeviceExtension;
//用于各种函数返回值的状态变量
NTSTATUS ntStatus = STATUS_INVALID_PARAMETER;
//用于获取请求参数的变量
WDF_REQUEST_PARAMETERS WdfRequestParameters;
//用于获取写请求起始地址的变量
LARGE_INTEGER liByteOffset;
//这是一个用于获取写缓冲区的内存句柄
WDFMEMORY hMemory;
//初始化参数变量,为之后从请求参数中获取各种信息做准备
WDF_REQUEST_PARAMETERS_INIT(&WdfRequestParameters);
//从请求参数中获取信息
WdfRequestGetParameters(WdfRequest,&WdfRequestParameters);
//将请求参数中写的起始位置取出来
liByteOffset.QuadPart = WdfRequestParameters.Parameters.Write.DeviceOffset;
//这里是自己实现一个参数检查函数.由于写入范围不能超过磁盘镜像大小
//且必须是扇区对齐,所以这里需要有一个参数检查,如果检查失败,
//则直接将这个错误参数(STATUS_INVALID_PARAMETER)为返回值结束
if(CheckParameters(pDeviceExtension,liByteOffset,Length))
{
//从请求参数中获写入缓冲区的内存句柄
ntStatus = WdfRequestRetrieveInputMemory(WdfRequest,&hMemory);
if(NT_SUCCESS(ntStatus))
//根据之前获取到的参数进行内存拷贝,写入内存磁盘数据
//从而完成这个写请求的操作
ntStatus = WdfMemoryCopyToBuffer(hMemory,
0,pDeviceExtension->DiskImage + liByteOffset.LowPart,Length);
}
//结束这个写请求,这里要注意的是,需要将写入的长度作为返回的信息一并返回
WdfRequestCompleteWithInformation(WdfRequest,ntStatus,(ULONG_PTR)Length);
}
VOID DeviceControl(IN WDFQUEUE WdfQueue,
IN WDFREQUEST WdfRequest,
IN size_t OutputBufferLength,
IN size_t InputBufferlength,
IN ULONG IoControlCode)
{
//初始化返回状态为非法的设备请求,这样在其它无关紧要的,不需要处理的
//DeviceIoControl 请求到来时,可以直接返回这个状态
NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST;
//用来存放返回的DeviceIoControl所要求的长度
ULONG_PTR ulInformation = 0;
//中间变量
size_t sBufferSize;
//和读/写回调函数相同,也通过队列的扩展来获取设备的扩展
PDEVICE_EXTENSION pDeviceExtension = QueueGetExtension(WdfQueue)->DeviceExtension;
//由于我们对发过来的请求长度很有信心(因为是Windows标准请求),
//所以这里不需要输入和输出缓冲区的长度
UNREFERENCED_PARAMETER(OutputBufferLength);
UNREFERENCED_PARAMETER(InputBufferlength);
//判断是哪个DeviceIoControl请求
switch(IoControlCode)
{
//这是一个获取当前分区信息的DeviceIoControl请求,需要处理
case IOCTL_DISK_GET_PARTITION_INFO:
{
//首先声明一个输出缓冲区指针
PPARTITION_INFORMATION pOutputBuffer;
//由于这个DeviceIoControl请求所需的信息大部分是从DBR中获取的
//所以需要一个指针DBR的指针
PBOOT_SECTOR pBootSector = (PBOOT_SECTOR)pDeviceExtension->DiskImage;
//这是将要返回的信息长度,它会被上层发出DeviceIoControl请求的设备收到
ulInformation = sizeof(PARTITION_INFORMATION);
//通过框架函数来获取这个DeviceIoControl请求所携带的输出缓冲区
ntStatus = WdfRequestRetrieveOutputBuffer(WdfRequest,
sizeof(PARTITION_INFORMATION),(PVOID*)&pOutputBuffer,&sBufferSize);
//在获取缓冲区成功的情况下,将DBR中的相关信息填入缓冲区
if(NT_SUCCESS(ntStatus))
{
pOutputBuffer->PartitionType =
(pBootSector->bsFileSystemType[4] == '6') ? PARTITION_FAT_16 :
PARTITION_FAT_12;
//还需要根据这个驱动的现实情况来"编造"一些数据
pOutputBuffer->BootIndicator = FALSE;
pOutputBuffer->RecognizedPartition = TRUE;
pOutputBuffer->RewritePartition = FALSE;
pOutputBuffer->StartingOffset.QuadPart = 0;
pOutputBuffer->PartitionLength.QuadPart = pDeviceExtension->DiskRegInfo.DiskSize;
pOutputBuffer->HiddenSectors = (ULONG)(1L);
pOutputBuffer->PartitionNumber = (ULONG)(-1L);
//最后由于成功地填充了缓冲区,因此将这个请求的状态设为成功
ntStatus = STATUS_SUCCESS;
}
}
break;
case IOCTL_DISK_GET_DRIVE_GEOMETRY:
{
//首先声明一个输出缓冲区指针
PDISK_GEOMETRY pOutputBuffer;
//这是将要返回的信息的长度,它会被上层发出 DeviceIoControl 请求的设备收到
ulInformation = sizeof(DISK_GEOMETRY);
//通过框架函数来获取这个DeviceIoControl请求所携带的输出缓冲区
ntStatus = WdfRequestRetrieveOutputBuffer(WdfRequest,
sizeof(DISK_GEOMETRY),(PVOID*)&pOutputBuffer,&sBufferSize);
//在获取缓冲区成功的情况下,将相关信息填入缓冲区
if(NT_SUCCESS(ntStatus))
{
//这里实际上就是填入之前初始化好的磁盘几何信息
RtlCopyMemory(pOutputBuffer,
&(pDeviceExtension->DiskGeometry),
sizeof(DISK_GEOMETRY));
ntStatus = STATUS_SUCCESS;
}
}
break;
//对于这两个 DeviceIoControl 请求,直接返回成功,因为这两个请求是不需要其它信息的
case IOCTL_DISK_CHECK_VERIFY:
case IOCTL_DISK_IS_WRITABLE:
ntStatus = STATUS_SUCCESS;
break;
}
//结束这个DeviceIoControl请求,需要将读取的长度作为返回的信息一并返回
WdfRequestCompleteWithInformation(WdfRequest,ntStatus,ulInformation);
}
NTSTATUS FormatDisk(IN PDEVICE_EXTENSION pDeviceExtension)
{
PBOOT_SECTOR pBootSector = (PBOOT_SECTOR) pDeviceExtension->DiskImage;
//一个指向第一个FAT表的指针
PUCHAR pFirstFatSector;
//用于记录有多少个根目录入口点
ULONG RootDirEntries;
//用于记录每个族由多少个扇区组成
ULONG SectorsPerCluster;
//用于记录FAT文件系统的类型,是FAT12还是FAT16
USHORT FatType;
//用于记录在FAT表里面一共有多少个表项
USHORT FatEntries;
//用于记录一个FAT表需要占用多少个扇区来存储
USHORT FatSectorCnt;
//用于指向第一个根目录入口点
PDIR_ENTRY pRootDir;
//用于确定这个函数是可以存取分页内存的
PAGED_CODE();
//用于确定这个盘的引导扇区的大小确实是一个扇区
ASSERT(sizeof(BOOT_SECTOR) == 512);
//用于确定我们操作的磁盘镜像不是一个不可用的指针
ASSERT(pDeviceExtension->DiskImage != NULL);
//清空磁盘镜像
RtlZeroMemory(pDeviceExtension->DiskImage,pDeviceExtension->DiskRegInfo.DiskSize);
//每个扇区有512字节
pDeviceExtension->DiskGeometry.BytesPerSector = 0x200;
//每个磁道有32个扇区
pDeviceExtension->DiskGeometry.SectorsPerTrack = 0x20;
//每个柱面有两个磁道
pDeviceExtension->DiskGeometry.TracksPerCylinder = 0x02;
//柱面数目由磁盘总容量计算得到
pDeviceExtension->DiskGeometry.Cylinders.QuadPart =
pDeviceExtension->DiskRegInfo.DiskSize / 0x200 / 0x20 / 0x02;
//磁盘的介质类型是我们自己定义的 RAMDISK_MEDIA_TYPE
pDeviceExtension->DiskGeometry.MediaType = (MEDIA_TYPE)RAMDISK_MEDIA_TYPE;
KdPrint(("柱面数:[%ld]\n 每柱面磁道数:[%ld]\n 每磁道扇区数:[%ld]\n 每扇区字节数:[%ld]\n",
pDeviceExtension->DiskGeometry.Cylinders.QuadPart,
pDeviceExtension->DiskGeometry.TracksPerCylinder,
pDeviceExtension->DiskGeometry.SectorsPerTrack,
pDeviceExtension->DiskGeometry.BytesPerSector));
//根据用户的指定值对根目录项的数目进行初使化
RootDirEntries = pDeviceExtension->DiskRegInfo.RootDirEntries;
//根据用户的指定值对每个簇有多少个扇区进行初使化
SectorsPerCluster = pDeviceExtension->DiskRegInfo.SectorsPerCluster;
//由于根目录入口点只使用 32 字节,但是最少占用一个扇区,这里是为了充分利用空间
//在用户指定的数目不合适时,会修正这个数目,以使扇区空间得到充分的利用
if(RootDirEntries & (DIR_ENTRIES_PER_SECTOR - 1))
RootDirEntries = (RootDirEntries + (DIR_ENTRIES_PER_SECTOR - 1)) &
~(DIR_ENTRIES_PER_SECTOR -1);
KdPrint(("根目录入口点数:[%ld]\n 每个簇扇区数:[%ld]\n",
RootDirEntries,SectorsPerCluster));
//对于一开始的跳转指令成员填入硬编码的指令,这是Windows指定的
pBootSector->bsJump[0] = 0xEB;
pBootSector->bsJump[1] = 0x3C;
pBootSector->bsJump[2] = 0x90;
//OEM 名称成员
pBootSector->bsOemName[0] = 'B';
pBootSector->bsOemName[1] = 'e';
pBootSector->bsOemName[2] = 'a';
pBootSector->bsOemName[3] = 'c';
pBootSector->bsOemName[4] = 'o';
pBootSector->bsOemName[5] = 'n';
pBootSector->bsOemName[6] = ' ';
pBootSector->bsOemName[7] = ' ';
//每个扇区有多少字节,这个成员的数值直接取自之前初始化的磁盘信息数据结构
pBootSector->bsBytesPerSec = (USHORT)pDeviceExtension->DiskGeometry.BytesPerSector;
//这个卷只有一个保留扇区,即 DBR 本身
pBootSector->bsResSectors = 1;
//和正常的卷不同,为了节省空间,我们只存放一份 FAT 表,而不是通常的两份
pBootSector->bsFATs = 1;
//根目录入口点数目由之前的计算得知
pBootSector->bsRootDirEnts = (USHORT)RootDirEntries;
//这个磁盘的总扇区数由磁盘总大小和每个扇区的字节数计算得到
pBootSector->bsSectors =
(USHORT)(pDeviceExtension->DiskRegInfo.DiskSize /
pDeviceExtension->DiskGeometry.BytesPerSector);
//这个磁盘介质类型由之前初始化的磁盘信息得到
pBootSector->bsMedia = (UCHAR)pDeviceExtension->DiskGeometry.MediaType;
//每个簇有多少个扇区,由之前的计算初始化得到
pBootSector->bsSecPerClus = (UCHAR)SectorsPerCluster;
//FAT表的表项数目是总扇区数减去保留扇区数,再减去根目录入口点所占用的扇区数
//然后除以每个簇的扇区数.最后的结果需要加 2,因为FAT表中第0项和第1项是保留的
FatEntries = (pBootSector->bsSectors -
pBootSector->bsResSectors -
pBootSector->bsRootDirEnts /
DIR_ENTRIES_PER_SECTOR) /
pBootSector->bsSecPerClus + 2;
//如果FAT表的表项数大于 4087 ,就使用 FAT16 文件系统,反之使用 FAT12 文件系统
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;
}
//初始化FAT表所占用的分区数
pBootSector->bsFATsecs = FatSectorCnt;
//初始化DBR中每个磁道的扇区数
pBootSector->bsSecPerTrack = (USHORT)pDeviceExtension->DiskGeometry.SectorsPerTrack;
//初始化磁头数,也就是每个柱面的磁道数
pBootSector->bsHeads = (USHORT)pDeviceExtension->DiskGeometry.TracksPerCylinder;
//初始化启动签名,Windows要求是 0x28 或 0x29
pBootSector->bsBootSignature = 0x29;
//随便写一个卷的ID
pBootSector->bsVolumeID = 0x12345678;
//将卷标设置成 "RamDisk"
pBootSector->bsLabel[0] = 'R';
pBootSector->bsLabel[1] = 'a';
pBootSector->bsLabel[2] = 'm';
pBootSector->bsLabel[3] = 'D';
pBootSector->bsLabel[4] = 'i';
pBootSector->bsLabel[5] = 's';
pBootSector->bsLabel[6] = 'k';
pBootSector->bsLabel[7] = ' ';
pBootSector->bsLabel[8] = ' ';
pBootSector->bsLabel[9] = ' ';
pBootSector->bsLabel[10] = ' ';
//根据我们之前计算得出的结果来选择到底是 FAT12 还是 FAT16 文件系统
pBootSector->bsFileSystemType[0] = 'F';
pBootSector->bsFileSystemType[1] = 'A';
pBootSector->bsFileSystemType[2] = 'T';
pBootSector->bsFileSystemType[3] = '1';
pBootSector->bsFileSystemType[4] = '?';
pBootSector->bsFileSystemType[5] = ' ';
pBootSector->bsFileSystemType[6] = ' ';
pBootSector->bsFileSystemType[7] = ' ';
pBootSector->bsFileSystemType[4] = (FatType == 16) ? '6' : '2';
//签署DBR最后的标志, 0x55AA
pBootSector->bsSig2[0] = 0x55;
pBootSector->bsSig2[1] = 0xAA;
//定位到FAT表的起始点,这里的定位方式是利用了DBR只有一个扇区这个条件
pFirstFatSector = (PUCHAR)(pBootSector + 1);
//填写介质类型标识
pFirstFatSector[0] = (UCHAR)pDeviceExtension->DiskGeometry.MediaType;
pFirstFatSector[1] = 0xFF;
pFirstFatSector[2] = 0xFF;
//注意:如果是FAT16,那么每个FAT表的表项是 4 字节
if(FatType == 16) pFirstFatSector[3] = 0xFF;
//由于紧跟着FAT表,所以根目录入口点的表的起始位置很容易定位
pRootDir = (PDIR_ENTRY)(pBootSector + 1 + FatSectorCnt);
//初使化卷标
pRootDir->deName[0] = 'M';
pRootDir->deName[1] = 'S';
pRootDir->deName[2] = '-';
pRootDir->deName[3] = 'R';
pRootDir->deName[4] = 'A';
pRootDir->deName[5] = 'M';
pRootDir->deName[6] = 'D';
pRootDir->deName[7] = 'R';
pRootDir->deExtension[0] = 'I';
pRootDir->deExtension[1] = 'V';
pRootDir->deExtension[2] = 'E';
//将这个入口点的属性设置为卷标属性
pRootDir->deAttributes = DIR_ATTR_VOLUME;
return STATUS_SUCCESS;
}
VOID DeviceCleanup(IN WDFOBJECT WdfObject)
{
PDEVICE_EXTENSION pDeviceExtension = DeviceGetExtension(WdfObject);
PAGED_CODE();
if(pDeviceExtension->DiskImage)
ExFreePool(pDeviceExtension->DiskImage);
}
VOID QueryParameters(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);
// Set the default values
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));
//
// Setup the query table
//
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;
//
// Disk paramters
//
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;
}
NTSTATUS DeviceAdd(IN WDFDRIVER WDFDriver,IN PWDFDEVICE_INIT pWDFDeviceInit)
{
//将要建立的设备对象的属性描述变量
WDF_OBJECT_ATTRIBUTES WdfDeviceAttributes;
//将要调用的各种函数的状态返回值
NTSTATUS ntStatus;
//将要建立的设备
WDFDEVICE WdfDevice;
//将要建立的队列对象属性描述变量
WDF_OBJECT_ATTRIBUTES WdfQueueAttributes;
//将要建立的队列配置变量
WDF_IO_QUEUE_CONFIG WdfIoQueueConfig;
//这个设备所对应的设备扩展域的指针
PDEVICE_EXTENSION pDeviceExtension;
//将要建立的队列扩展域的指针
PQUEUE_EXTENSION pQueueExtension = NULL;
//将要建立的队列
WDFQUEUE WdfQueue;
//声明一个UNICODE_STRING类型的变量 ntDeviceName,并且将它初始化为
//NT_DEVICE_ANEM 宏所说明的字符串,这里实际上是 L"\\Device\\Ramdisk"
DECLARE_CONST_UNICODE_STRING(ntDeviceName, NT_DEVICE_NAME);
//保证这个函数可以操作分页内存
PAGED_CODE();
//由于我们不使用WdfDriver这个参数,为了避免编译警告,加入下面这句
UNREFERENCED_PARAMETER(WDFDriver);
//首先需要为这个设备指定一个名称,这里使用刚才声明的 UNICODE_STRING 变量
ntStatus = WdfDeviceInitAssignName(pWDFDeviceInit,&ntDeviceName);
if(!NT_SUCCESS(ntStatus))return ntStatus;
//接下来需要对这个设备进行一些属性的设置
//包括设备类型,IO操作类型和设置的排它方式
WdfDeviceInitSetDeviceType(pWDFDeviceInit,FILE_DEVICE_DISK);
WdfDeviceInitSetIoType(pWDFDeviceInit,WdfDeviceIoDirect);
WdfDeviceInitSetExclusive(pWDFDeviceInit,FALSE);
//下面来指定这个设备的设备对象扩展,这里的 DEVICE_EXTENSION 是一个在前面声明好的结构体数据类型
//我们使用一个WDF_OBJECT_ATTRIBUTES类型的变量并用其设置好DEVICE_EXTENSION
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&WdfDeviceAttributes,DEVICE_EXTENSION);
//下面还要将用这个WDF_OBJECT_ATTRIBUTES类型的变量来指定这个设备的清除回调函数
//这个WDF_OBJECT_ATTRIBUTES类型的变量将会在下面建立设备时作为参数传进去
WdfDeviceAttributes.EvtCleanupCallback = DeviceCleanup;
//到这里所有的准备工作都已经就绪,可以开始真正建立这个设备了
//建立出的设备保存在WdfDevice这个局部变量中
ntStatus = WdfDeviceCreate(&pWDFDeviceInit,&WdfDeviceAttributes,&WdfDevice);
if(!NT_SUCCESS(ntStatus))return ntStatus;
//这个 pDeviceExtension 是之前声明的一个局部指针变量,将其指向新建立的设备的设备扩展域
pDeviceExtension = DeviceGetExtension(WdfDevice);
//将队列的配置变量初始化为默认值
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&WdfIoQueueConfig,WdfIoQueueDispatchSequential);
//由于我们对发往这个设备的 DeviceIoControl 请求和读/写请求感兴趣
//所以将这三个请求的处理函数设置为自己的函数,其余的请求使用默认值
WdfIoQueueConfig.EvtIoDeviceControl = DeviceControl;
WdfIoQueueConfig.EvtIoRead = DeviceRead;
WdfIoQueueConfig.EvtIoWrite = DeviceWrite;
//指定这个队列的队列对象扩展,这里的 QUEUE_EXTENSION 是之前声明好的结构体数据类型
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&WdfQueueAttributes,QUEUE_EXTENSION);
//创建这个队列,将之前创建的设备作为队列的父对象
//这样在这个设备被销毁的同时这个队列也会被销毁
//这样就不用担心队列的结束问题
ntStatus = WdfIoQueueCreate(WdfDevice,&WdfIoQueueConfig,&WdfQueueAttributes,&WdfQueue);
if(!NT_SUCCESS(ntStatus))return ntStatus;
//将指针 pQueueExtension 指向刚生成的队列的队列扩展
pQueueExtension = QueueGetExtension(WdfQueue);
//这里初始化队列扩展里的 DeviceExtension 项,并将其设置为刚建立的设备的设备扩展指针
//这样以后在有队列的地方,可以轻松获取到这个队列所对应的设备的设备扩展
pQueueExtension->DeviceExtension = pDeviceExtension;
//将生成的设备的设备扩展中相应的 UNICODE_STRING 初使化
pDeviceExtension->DiskRegInfo.DriveLetter.Buffer =
(PWSTR)&pDeviceExtension->DriveLetterBuffer;
pDeviceExtension->DiskRegInfo.DriveLetter.MaximumLength =
sizeof(pDeviceExtension->DriveLetterBuffer);
//从系统为本驱动提供的注册表键中获取我们需要的信息
QueryParameters(WdfDriverGetRegistryPath(WdfDeviceGetDriver(WdfDevice)),
&pDeviceExtension->DiskRegInfo);
//分配用户指定大小的非分页内存,并使用我们自己的内存Tag值
pDeviceExtension->DiskImage = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool,
pDeviceExtension->DiskRegInfo.DiskSize,
RAMDISK_TAG);
//下面的代码只有在内存分配成功时才会运行
if(pDeviceExtension->DiskImage)
{
UNICODE_STRING usDeviceName;
UNICODE_STRING usWin32Name;
//在这里调用我们自己实现的函数去初始化磁盘
FormatDisk(pDeviceExtension);
ntStatus = STATUS_SUCCESS;
RtlInitUnicodeString(&usWin32Name,DOS_DEVICE_NAME);
RtlInitUnicodeString(&usDeviceName,NT_DEVICE_NAME);
//这里准备好用来存储符号链接名的UNICODE_STRING变量
pDeviceExtension->SymbolicLink.Buffer = (PWSTR)
&pDeviceExtension->DosDeviceNameBuffer;
pDeviceExtension->SymbolicLink.MaximumLength =
sizeof(pDeviceExtension->DosDeviceNameBuffer);
pDeviceExtension->SymbolicLink.Length = usWin32Name.Length;
//将符号链接名一开始设置为 "\\DosDevices\\",这是所有符号链接有的前缀
RtlCopyUnicodeString(&pDeviceExtension->SymbolicLink,&usWin32Name);
//在上面赋值好前缀后面连接我们从用户配置中读出来的用户指定盘符
RtlAppendUnicodeStringToString(&pDeviceExtension->SymbolicLink,
&pDeviceExtension->DiskRegInfo.DriveLetter);
//现在符号链接名已经准备好,调用WDF驱动框架模型提供的函数来为之前生成的设备建立符号链接
ntStatus = WdfDeviceCreateSymbolicLink(WdfDevice,&pDeviceExtension->SymbolicLink);
}
//最后返回状态,函数结束
return ntStatus;
}
EXTERN_C NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING pRegistryPath)
{
WDF_DRIVER_CONFIG WDFDriverConfig;
WDF_DRIVER_CONFIG_INIT(&WDFDriverConfig,DeviceAdd);
KdPrint(("DriverEntry"));
return WdfDriverCreate(pDriverObject,pRegistryPath,
WDF_NO_OBJECT_ATTRIBUTES,
&WDFDriverConfig,WDF_NO_HANDLE);
}
复制代码
作者:
wangbeacon
时间:
2012-6-10 22:48
本帖最后由 sky_yx 于 2015-12-30 14:17 编辑
//////////////////////INF文件////////////////////////
[Version]
Signature="$WINDOWS NT$"
Class=Sample
ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171}
Provider=%MSFT%
DriverVer=12/20/2009,6.1.7600.16385
CatalogFile=KmdfSamples.cat
[DestinationDirs]
DefaultDestDir = 12
[ClassInstall32]
Addreg=SampleClassReg
[SampleClassReg]
HKR,,,0,%ClassName%
HKR,,Icon,,-5
[DiskCopyfiles]
WdfRamdisk.sys ;驱动程序文件名
[SourceDisksNames]
1=%InstDisk%,
[SourceDisksFiles]
WdfRamdisk.sys=1 ;驱动程序文件名
[Manufacturer]
%MSFT% = DiskDevice,NTx86
; For Win2K
[DiskDevice]
%DiskDevDesc% = DiskInstall, Ramdisk
; For XP and later
[DiskDevice.NTx86]
%DiskDevDesc% = DiskInstall, Ramdisk
[DiskInstall.NT]
CopyFiles = DiskCopyfiles
[DiskInstall.NT.Services]
AddService = Ramdisk, %SPSVCINST_ASSOCSERVICE%, DiskServiceInst
[DiskServiceInst]
ServiceType = %SERVICE_KERNEL_DRIVER%
StartType = %SERVICE_DEMAND_START%
ErrorControl = %SERVICE_ERROR_NORMAL%
DisplayName = %DiskServiceDesc%
ServiceBinary = %12%\WdfRamdisk.sys
AddReg = DiskAddReg
[DiskAddReg]
HKR, "Parameters", "BreakOnEntry", %REG_DWORD%, 0x00000000
HKR, "Parameters", "DebugLevel", %REG_DWORD%, 0x00000000
HKR, "Parameters", "DebugComp", %REG_DWORD%, 0xFFFFFFFF
HKR, "Parameters", "DiskSize", %REG_DWORD%, 0x00100000
HKR, "Parameters", "DriveLetter", %REG_SZ%, "R:"
HKR, "Parameters", "RootDirEntries", %REG_DWORD%, 0x00000200
HKR, "Parameters", "SectorsPerCluster", %REG_DWORD%, 0x00000002
;-------------- Coinstaller installation
[DestinationDirs]
CoInstaller_CopyFiles = 11
[DiskInstall.NT.CoInstallers]
AddReg=CoInstaller_AddReg
CopyFiles=CoInstaller_CopyFiles
[CoInstaller_CopyFiles]
WdfCoInstaller01009.dll
[SourceDisksFiles]
WdfCoInstaller01009.dll=1 ; make sure the number matches with SourceDisksNames
[CoInstaller_AddReg]
HKR,,CoInstallers32,0x00010000, "WdfCoInstaller01009.dll,WdfCoInstaller"
[DiskInstall.NT.Wdf]
KmdfService = Ramdisk, Ramdisk_wdfsect
[Ramdisk_wdfsect]
KmdfLibraryVersion = 1.9
[Strings]
MSFT = "Microsoft" ;制造商
ClassName = "Sample Device" ;设备类型名
DiskDevDesc = "WDF Sample RAM disk Driver" ;设备描述
DiskServiceDesc = "Ramdisk Driver" ;服务描述
InstDisk = "Ramdisk Install Disk" ;
;*******************************************
;Handy macro substitutions (non-localizable)
SPSVCINST_ASSOCSERVICE = 0x00000002
SERVICE_KERNEL_DRIVER = 1
SERVICE_DEMAND_START = 3
SERVICE_ERROR_NORMAL = 1
REG_DWORD = 0x00010001
REG_SZ = 0x00000000
复制代码
作者:
admin
时间:
2012-6-11 01:10
真正的高手+真正的技术帖子,非顶不可!
欢迎光临 SKY外语计算机学习 (http://skywj.com/)
Powered by Discuz! X2.5