入口函数,即驱动加载函数

NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
) /*++ Routine Description: Installable driver initialization entry point.
This entry point is called directly by the I/O system. Arguments: DriverObject - pointer to the driver object RegistryPath - pointer to a unicode string representing the path
to driver-specific key in the registry Return Value: STATUS_SUCCESS if successful. --*/ {
WDF_DRIVER_CONFIG config; KdPrint(("Windows Ramdisk Driver - Driver Framework Edition.\n"));
KdPrint(("Built %s %s\n", __DATE__, __TIME__)); WDF_DRIVER_CONFIG_INIT( &config, RamDiskEvtDeviceAdd ); return WdfDriverCreate(DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, WDF_NO_HANDLE);
}

初始化可配置变量config,指定RamDiskEvtDeviceAdd的地址,这里的RamDiskEvtDeviceAdd相当于wdm中AddDevice回调函数,当即插即用管理器发现了一个新设备 ,则调用的函数。然后调用WdfDriverCreate返回,我的理解是WdfDriverCreate创建了WDF驱动框架。

接下来是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内存
PAGED_CODE();
//防止产生警告
UNREFERENCED_PARAMETER(Driver);
//初始化设备的名字
status = WdfDeviceInitAssignName(DeviceInit, &ntDeviceName);
if (!NT_SUCCESS(status)) {
return status;
}
//磁盘设备的类型 必须是FILE_DEVICE_DISK
WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_DISK);
//设备的io类型 WdfDeviceIoBuffered 使用缓冲区接受数据 WdfDeviceIoDirect 直接接收数据 IRP所携带的缓冲区 可以直接使用
//WdfDeviceIoBufferedOrDirect 前面两种方式 都可以使用
WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect);
//Exclusive:独占 设置设备为非独占的
WdfDeviceInitSetExclusive(DeviceInit, FALSE);
//设置属性描述变量 就是说 设备对象的扩展 使用什么样的数据结构存储数据
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_EXTENSION);
//制定设备的清理回调函数
deviceAttributes.EvtCleanupCallback = RamDiskEvtDeviceContextCleanup; //建立这个设备
status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
if (!NT_SUCCESS(status)) {
return status;
}
//将指针指向新建立设备的设备扩展
pDeviceExtension = DeviceGetExtension(device);
//将队列的配置对象初始化为默认值
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE (
&ioQueueConfig,
WdfIoQueueDispatchSequential
);
//wdf中我们需要将发往自己创建的设备的请求处理函数 在队列对象的设置对象中设置 //我们暂时只关心IoDeviceControl和读写事件
ioQueueConfig.EvtIoDeviceControl = RamDiskEvtIoDeviceControl;
ioQueueConfig.EvtIoRead = RamDiskEvtIoRead;
ioQueueConfig.EvtIoWrite = RamDiskEvtIoWrite;
//指定这个队列设备的属性描述对象
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&queueAttributes, QUEUE_EXTENSION); //创建这个队列对象 这里第一个参数是设备对象 说明这个队列在创建的时候 已经和设备绑定了
status = WdfIoQueueCreate( device,
&ioQueueConfig,
&queueAttributes,
&queue );
if (!NT_SUCCESS(status)) {
return status;
}
//获取队列设备的扩展指针
pQueueContext = QueueGetExtension(queue);
//设置队列对象的扩展的设备对象扩展
pQueueContext->DeviceExtension = pDeviceExtension; //设置进度条显示
//status = SetForwardProgressOnQueue(queue);
//if (!NT_SUCCESS(status)) {
// return status;
//} //初始化设备扩展中一些变量
pDeviceExtension->DiskRegInfo.DriveLetter.Buffer =
(PWSTR) &pDeviceExtension->DriveLetterBuffer;
pDeviceExtension->DiskRegInfo.DriveLetter.MaximumLength =
sizeof(pDeviceExtension->DriveLetterBuffer);
//从注册表中获取 这个注册表信息 其实是由inf文件指定
RamDiskQueryDiskRegParameters(
WdfDriverGetRegistryPath(WdfDeviceGetDriver(device)),
&pDeviceExtension->DiskRegInfo
);
//分配非分页内存给这个磁盘 DiskImage是磁盘镜像的意思
//分页内存和非分页内存 分页内存的存储介质有可能在内存 也有可能在硬盘
//非分页内存的存储介质一定是内存 所以分配非分页内存 不会引起复杂的换页操作和一些缺页中断
//RAMDISK_TAG 代表空间标识 便于调试
pDeviceExtension->DiskImage = ExAllocatePoolWithTag(
NonPagedPool,
pDeviceExtension->DiskRegInfo.DiskSize,
RAMDISK_TAG
); if (pDeviceExtension->DiskImage) { UNICODE_STRING deviceName;
UNICODE_STRING win32Name;
//初始化磁盘空间
RamDiskFormatDisk(pDeviceExtension); status = STATUS_SUCCESS; // /DosDevice/xxx windows下代表了磁盘设备
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); status = WdfDeviceCreateSymbolicLink(device,
&pDeviceExtension->SymbolicLink);
} return status; }

调用PAGED_CODE()保证代码可以调用非分页内存,这个宏其实是判断IRQL级别不处于DISPATCH_LEVEL级别及以上,如果是,则触发断言。

使用WdfDeviceInitAssignName初始化设备的名字,设备名和符号链接是不一样的,设备名只能在内核态被其他驱动识别。而符号链接则可以在用户态被应用程序识别。

使用WdfDeviceInitSetIoType指定设备的IO类型,一共有三种类型可选,1.WdfDeviceIoBuffered 使用缓冲区接受数据  2.WdfDeviceIoDirect,直接接收数据,IRP所携带的缓冲区 ,可以直接使用

3.WdfDeviceIoBufferedOrDirect 前面两种方式都会使用

使用函数WdfDeviceInitSetExclusive设置设备为非独占

WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE指定设备扩展使用什么样的数据结构存储数据,DEVICE_EXTENSION为自定义的结构体

deviceAttributes.EvtCleanupCallback这里指定设备的清理函数为RamDiskEvtDeviceContextCleanup

准备完毕之后使用WdfDeviceCreate创建磁盘设备,参数都是前面处理过,其中device为传出参数

下面是创建队列对象的代码

ioQueueConfig为队列对象的配置对象,首先通过WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE初始化为默认值,然后指定IoDeviceControl和读写事件,通过WdfIoQueueCreate创建这个队列对象

注意的是队列对象的扩展结构里面包含了设备对象的扩展结构指针

RamDiskQueryDiskRegParameters取得注册表数据,系统在驱动加载之前,会首先提供访问注册表的功能,这里的数据其实是安装驱动时ramdisk.inf指定

pDeviceExtension->DiskImage这个指针是磁盘镜像,即磁盘位置的起始,然后为这个指针分配非分页内存,调用ExAllocatePoolWithTag传入第一个参数NonPagedPool。使用非分页内存的原因是,非分页内存的存储介质一定是内存,所以分配非分页内存 ,不会引起复杂的换页操作和一些缺页中断。

如果分配内存成功,则使用RamDiskFormatDisk函数初始化磁盘空间。接下来使用了win32Name存储符号链接,最终的符号链接为/DosDevice/Z: ,在用户态会默认显示Z作为盘符

刚开始我找到WdfControlFinishInitializing这个函数,以为可以通知用户层更新,从而显示出新建的磁盘对象,后来发现这个函数不能达到目的。在这里也求大神指教,如何不用重启,即可显示出新建的磁盘设备。

//初始化磁盘结构的一些数据
NTSTATUS
RamDiskFormatDisk(
IN PDEVICE_EXTENSION devExt
) {
//将分配的非分页内存的首地址 转化为DBR结构的指针
PBOOT_SECTOR bootSector = (PBOOT_SECTOR) devExt->DiskImage;
//指向FAT表的指针
PUCHAR firstFatSector;
//根目录入口点个数
ULONG rootDirEntries;
//一个簇由多少扇区组成
ULONG sectorsPerCluster;
//fat文件系统类型
USHORT fatType; // Type FAT 12 or 16
//FAT表里面有多少表项
USHORT fatEntries; // Number of cluster entries in FAT
//一个FAT表需要占多少簇存储
USHORT fatSectorCnt; // Number of sectors for FAT
//第一个根目录入口点
PDIR_ENTRY rootDir; // Pointer to first entry in root dir
//保证这个函数可以操作paged内存
PAGED_CODE(); ASSERT(sizeof(BOOT_SECTOR) == );
ASSERT(devExt->DiskImage != NULL);
//清空磁盘镜像
RtlZeroMemory(devExt->DiskImage, devExt->DiskRegInfo.DiskSize);
//每个扇区有512个字节
devExt->DiskGeometry.BytesPerSector = ;
//每个磁道有32个扇区
devExt->DiskGeometry.SectorsPerTrack = ; // Using Ramdisk value
//每个柱面有两个磁道
devExt->DiskGeometry.TracksPerCylinder = ; // Using Ramdisk value
//计算得出磁柱面数
devExt->DiskGeometry.Cylinders.QuadPart = devExt->DiskRegInfo.DiskSize / / / ;
devExt->DiskGeometry.MediaType = RAMDISK_MEDIA_TYPE;
KdPrint((
"Cylinders: %ld\n TracksPerCylinder: %ld\n SectorsPerTrack: %ld\n BytesPerSector: %ld\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 - )) { rootDirEntries =
(rootDirEntries + (DIR_ENTRIES_PER_SECTOR - )) &
~ (DIR_ENTRIES_PER_SECTOR - );
} KdPrint((
"Root dir entries: %ld\n Sectors/cluster: %ld\n",
rootDirEntries, sectorsPerCluster
));
//硬编码写入跳转指令
bootSector->bsJump[] = 0xeb;
bootSector->bsJump[] = 0x3c;
bootSector->bsJump[] = 0x90;
//oem名字
bootSector->bsOemName[] = 'R';
bootSector->bsOemName[] = 'a';
bootSector->bsOemName[] = 'j';
bootSector->bsOemName[] = 'u';
bootSector->bsOemName[] = 'R';
bootSector->bsOemName[] = 'a';
bootSector->bsOemName[] = 'm';
bootSector->bsOemName[] = ' ';
//每个扇区有多少字节
bootSector->bsBytesPerSec = (SHORT)devExt->DiskGeometry.BytesPerSector;
//指定这个磁盘卷保留扇区 仅DBR这一个扇区为保留扇区
bootSector->bsResSectors = ;
//fat表一般一式两份 但这里就创建一份就可以
bootSector->bsFATs = ;
//指定根目录入口点个数
bootSector->bsRootDirEnts = (USHORT)rootDirEntries;
//磁盘总扇区数 磁盘大小除以扇区大小
bootSector->bsSectors = (USHORT)(devExt->DiskRegInfo.DiskSize /
devExt->DiskGeometry.BytesPerSector);
//磁盘介质类型
bootSector->bsMedia = (UCHAR)devExt->DiskGeometry.MediaType;
//每个簇有多少个扇区
bootSector->bsSecPerClus = (UCHAR)sectorsPerCluster;
//fat表项数 总扇区数-保留扇区数
//这里很疑惑 如何得到FAT表项数
fatEntries =
(bootSector->bsSectors - bootSector->bsResSectors -
bootSector->bsRootDirEnts / DIR_ENTRIES_PER_SECTOR) /
bootSector->bsSecPerClus + ; //如果表项数 大于2的12次方 则使用FAT16
if (fatEntries > ) {
fatType = ;
//这一步的调整 我很不理解
fatSectorCnt = (fatEntries * + ) / ;
fatEntries = fatEntries + fatSectorCnt;
fatSectorCnt = (fatEntries * + ) / ;
}
else {
fatType = ;
fatSectorCnt = (((fatEntries * + ) / ) + ) / ;
fatEntries = fatEntries + fatSectorCnt;
fatSectorCnt = (((fatEntries * + ) / ) + ) / ;
}
//初始化FAT表所占的扇区数
bootSector->bsFATsecs = fatSectorCnt;
//初始化DBR每个磁道的扇区数
bootSector->bsSecPerTrack = (USHORT)devExt->DiskGeometry.SectorsPerTrack;
//初始化每个柱面的磁道数
bootSector->bsHeads = (USHORT)devExt->DiskGeometry.TracksPerCylinder;
//初始化启动签名 windows要求必须是0x29或者0x28
bootSector->bsBootSignature = 0x29;
//卷ID 随便填写
bootSector->bsVolumeID = 0x12345678;
//初始化卷标
bootSector->bsLabel[] = 'R';
bootSector->bsLabel[] = 'a';
bootSector->bsLabel[] = 'm';
bootSector->bsLabel[] = 'D';
bootSector->bsLabel[] = 'i';
bootSector->bsLabel[] = 's';
bootSector->bsLabel[] = 'k';
bootSector->bsLabel[] = ' ';
bootSector->bsLabel[] = ' ';
bootSector->bsLabel[] = ' ';
bootSector->bsLabel[] = ' ';
//设置磁盘文件类型
bootSector->bsFileSystemType[] = 'F';
bootSector->bsFileSystemType[] = 'A';
bootSector->bsFileSystemType[] = 'T';
bootSector->bsFileSystemType[] = '';
bootSector->bsFileSystemType[] = '?';
bootSector->bsFileSystemType[] = ' ';
bootSector->bsFileSystemType[] = ' ';
bootSector->bsFileSystemType[] = ' '; bootSector->bsFileSystemType[] = ( fatType == ) ? '' : '';
//设置DBR结束标识
bootSector->bsSig2[] = 0x55;
bootSector->bsSig2[] = 0xAA;
//初始化FAT表结构
firstFatSector = (PUCHAR)(bootSector + );
firstFatSector[] = (UCHAR)devExt->DiskGeometry.MediaType;
firstFatSector[] = 0xFF;
firstFatSector[] = 0xFF; if (fatType == ) {
firstFatSector[] = 0xFF;
} rootDir = (PDIR_ENTRY)(bootSector + + fatSectorCnt); //接下来 初始化根目录入口点信息
rootDir->deName[] = 'M';
rootDir->deName[] = 'S';
rootDir->deName[] = '-';
rootDir->deName[] = 'R';
rootDir->deName[] = 'A';
rootDir->deName[] = 'M';
rootDir->deName[] = 'D';
rootDir->deName[] = 'R'; //
// Set device extension name to "IVE"
// NOTE: Fill all 3 characters, eg. sizeof(rootDir->deExtension);
//
rootDir->deExtension[] = 'I';
rootDir->deExtension[] = 'V';
rootDir->deExtension[] = 'E'; rootDir->deAttributes = DIR_ATTR_VOLUME; return STATUS_SUCCESS; }

RamDiskFormatDisk函数用来初始化硬盘空间,如果是引导盘,那么硬盘镜像的第一个结构是MBR(主引导扇区),但是ramdisk虚拟的硬盘没有引导系统的作用,所以,第一个结构是DBR,结构如下

typedef struct  _BOOT_SECTOR
{
UCHAR bsJump[]; // x86 jmp instruction, checked by FS
CCHAR bsOemName[]; // OEM name of formatter
USHORT bsBytesPerSec; // Bytes per Sector
UCHAR bsSecPerClus; // Sectors per Cluster
USHORT bsResSectors; // Reserved Sectors
UCHAR bsFATs; // Number of FATs - we always use 1
USHORT bsRootDirEnts; // Number of Root Dir Entries
USHORT bsSectors; // Number of Sectors
UCHAR bsMedia; // Media type - we use RAMDISK_MEDIA_TYPE
USHORT bsFATsecs; // Number of FAT sectors
USHORT bsSecPerTrack; // Sectors per Track - we use 32
USHORT bsHeads; // Number of Heads - we use 2
ULONG bsHiddenSecs; // Hidden Sectors - we set to 0
ULONG bsHugeSectors; // Number of Sectors if > 32 MB size
UCHAR bsDriveNumber; // Drive Number - not used
UCHAR bsReserved1; // Reserved
UCHAR bsBootSignature; // New Format Boot Signature - 0x29
ULONG bsVolumeID; // VolumeID - set to 0x12345678
CCHAR bsLabel[]; // Label - set to RamDisk
CCHAR bsFileSystemType[];// File System Type - FAT12 or FAT16
CCHAR bsReserved2[]; // Reserved
UCHAR bsSig2[]; // Originial Boot Signature - 0x55, 0xAA
} BOOT_SECTOR, *PBOOT_SECTOR;

DBR结构如下图

wdk中ramdisk代码解读的更多相关文章

  1. Android MVP模式 谷歌官方代码解读

    Google官方MVP Sample代码解读 关于Android程序的构架, 当前(2016.10)最流行的模式即为MVP模式, Google官方提供了Sample代码来展示这种模式的用法. Repo ...

  2. 优秀开源代码解读之JS与iOS Native Code互调的优雅实现方案

    简介 本篇为大家介绍一个优秀的开源小项目:WebViewJavascriptBridge. 它优雅地实现了在使用UIWebView时JS与ios 的ObjC nativecode之间的互调,支持消息发 ...

  3. SoftmaxLayer and SoftmaxwithLossLayer 代码解读

    SoftmaxLayer and SoftmaxwithLossLayer 代码解读 Wang Xiao 先来看看 SoftmaxWithLoss 在prototext文件中的定义: layer { ...

  4. Hybrid----优秀开源代码解读之JS与iOS Native Code互调的优雅实现方案-备

    本篇为大家介绍一个优秀的开源小项目:WebViewJavascriptBridge. 它优雅地实现了在使用UIWebView时JS与ios 的ObjC nativecode之间的互调,支持消息发送.接 ...

  5. Jsoup代码解读之六-防御XSS攻击

    Jsoup代码解读之八-防御XSS攻击 防御XSS攻击的一般原理 cleaner是Jsoup的重要功能之一,我们常用它来进行富文本输入中的XSS防御. 我们知道,XSS攻击的一般方式是,通过在页面输入 ...

  6. Jsoup代码解读之五-实现一个CSS Selector

    Jsoup代码解读之七-实现一个CSS Selector 当当当!终于来到了Jsoup的特色:CSS Selector部分.selector也是我写的爬虫框架webmagic开发的一个重点.附上一张s ...

  7. Jsoup代码解读之四-parser

    Jsoup代码解读之四-parser 作为Java世界最好的HTML 解析库,Jsoup的parser实现非常具有代表性.这部分也是Jsoup最复杂的部分,需要一些数据结构.状态机乃至编译器的知识.好 ...

  8. Jsoup代码解读之三-Document的输出

    Jsoup代码解读之三-Document的输出   Jsoup官方说明里,一个重要的功能就是output tidy HTML.这里我们看看Jsoup是如何输出HTML的. HTML相关知识 分析代码前 ...

  9. Jsoup代码解读之二-DOM相关对象

    Jsoup代码解读之二-DOM相关对象   之前在文章中说到,Jsoup使用了一套自己的DOM对象体系,和Java XML API互不兼容.这样做的好处是从XML的API里解脱出来,使得代码精炼了很多 ...

随机推荐

  1. Dcloud HTML5 监听蓝牙设备 调用 原生安卓实现

    最近一直搞Dcloud ,这是HTML5版本的开发,打包时候,可以打包成 apk 和ipa 分别运行在安卓和ios 机器上面, 但是这里面的资料很少,遇到问题,之后只能自己钻研总结, 现在有这么一个需 ...

  2. Mahout之数据承载

    转载自:https://www.douban.com/note/204399134/ 推荐数据的处理是大规模的,在集群环境下一次要处理的数据可能是数GB,所以Mahout针对推荐数据进行了优化. Pr ...

  3. 【修改 UITextField 中 placeholder 的顏色】

    第一种方法: [textfeild setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"]; ...

  4. wap支付宝接口的问题

    今天在支付宝接口开发时,遇到的两个坑 第一个: https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.8nHr4i& ...

  5. Jenkins配置自动发送邮件,成功!

    Jenkins自动发送邮件配置: 打开"系统管理"--"系统设置" 在"Jenkins Location"设置系统管理员地址(重要:不能省略 ...

  6. nodejs学习

    转自于网络: ubuntu 下面安装 vim 的问题 1.输入vim时,显示: 程序"vim"已包含在以下软件包中: * vim * vim-gnome * vim-tiny * ...

  7. swift 命令

    http://blog.chinaunix.net/uid-15063109-id-5144658.html http://www.cnblogs.com/fczjuever/p/3224022.ht ...

  8. TMS320F28027/26/23/22/21/20芯片解密单片机破解原理!

    TMS320F28027/26/23/22/21/20芯片解密单片机破解 TMS320F2802系列芯片解密型号: TMS320F28027F.TMS320F280270.TMS320F28027.T ...

  9. 从外部浏览开启app

    先描述一下需求:从浏览器中点击某个按钮,如果手机上装有相应的app,则直接开启app,并且到相对的页面.如果没有装该app,则会到相应的下载app的界面. 我这里主要用的是第三方的东西,就是魔窗中的m ...

  10. ASP.NET MVC3中Controller与View之间的数据传递

    在ASP.NET MVC中,经常会在Controller与View之间传递数据,因此,熟练.灵活的掌握这两层之间的数据传递方法就非常重要.本文从两个方面进行探讨: 一.  Controller向Vie ...