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

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. 大数据系列-CDH环境中SOLR入数据

    1       创建集合 SSH远程连接到安装了SOLR的CDH节点. 运行solrctl  instancedir  --generate  /solr/test/GX_SH_TL_TGRYXX_2 ...

  2. tp框架之模板继承

    模板继承是一项更加灵活的模板布局方式,模板继承不同于模板布局,甚至来说,应该在模板布局的上层.模板继承其实并不难理解,就好比类的继承一样,模板也可以定义一个基础模板(或者是布局),并且其中定义相关的区 ...

  3. 【Java EE 学习 83 上】【SpringMVC】【基本使用方法】

    一.SpringMVC框架概述 什么是SpringMVC?SpringMVC是一个和Struts2差不多的东西,他们的作用和性质几乎是相同的,甚至开发效率上也差不多,但是在运行效率上SpringMVC ...

  4. Javascript中数组的基本操作

    删除数组指定的某个元素 via首先可以给JS的数组对象定义一个函数,用于查找指定的元素在数组中的位置,即索引,代码为: Array.prototype.indexOf = function(val) ...

  5. C# 正则表达式总结

    正则表达式 是一种匹配输入文本的模式..Net 框架提供了允许这种匹配的正则表达式引擎.模式由一个或多个字符.运算符和结构组成. 下面列出了用于定义正则表达式的各种类别的字符.运算符和结构. 字符转义 ...

  6. centos添加和删除用户及 xxx is not in the sudoers file.This incident will be reported.的解决方法

    修改主机名:vim /etc/sysconfig/network 1.添加用户,首先用adduser命令添加一个普通用户,命令如下: #adduser tommy //添加一个名为tommy的用户 # ...

  7. [原创]Centos7 从零整合LNMP一体包

    按照前几章配置好后,我们就可以把这些工具打包啦.生成LNMP一体包. # export LD_LIBRARY_PATH=/package/libmemcached/lib:$LD_LIBRARY_PA ...

  8. 【NuGet】搭建自己团队或公司的NuGet

    昨天接到领导安排,要搭建自己的NuGet,归结原因是自己前段时间在NuGet中安装mongoDb driver时,发现访问不了,无奈领导找的运维解决,也是此次任务的“导火索”……,好了,还是干活吧. ...

  9. DP套DP HDOJ 4899 Hero meet devil(国王的子民的DNA)

    题目链接 题意: 给n长度的S串,对于0<=i<=|S|,有多少个长度为m的T串,使得LCS(S,T) = i. 思路: 理解的不是很透彻,先占个坑. #include <bits/ ...

  10. ubuntu 用apt-get 安装apache 和php 之后php不能解析的问题

    sudo apt-get install apache2 sudo apt-get install php7.0 sudo apt-get install libapache2-mod-php //关 ...