<寒江独钓>Windows内核安全编程__Ramdisk源码解读

这篇文章来介绍一下WDK中提供的一个案例源码--Ramdisk虚拟磁盘。这个例子实现了一个非分页内存做的磁盘储存空间,并将其以一个独立磁盘的形式暴露给用户,用户可以将它格式化成一个Windows能够使用卷,并且像操作一般的磁盘卷一样对它进行操作。由于使用了内存作为虚拟的存储介质,使这个磁盘具有一个显著的特点,性能的提高。这个例子所使用的微软WDF驱动框架。
入口函数
1.入口函数的定义
任何一个驱动程序,不论它是一个标准的WDM驱动程序,还是使用WDF驱动程序框架,都会有一个叫做DriverEntry的入口函数,就好像普通控制台程序中的main函数一样。这个函数是这样声明的:
NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    );
这个函数具有两个参数,其中一个是PDRIVER_OBJECT类型的指针。它代表了Windows系统为这个驱动程序所分配的一个驱动对象。这个驱动对象是Windows系统中对某个驱动的唯一标示。
DriverEntry的第二个参数是一个UNICODE字符串,它代表了驱动在注册表中的参数所存放的位置。由于每个驱动都是以一个类似服务的形式存在的,在系统注册表的HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services树下总有一个和驱动名字相同的子树用来描述驱动的一些基本信息,并提供一个可使用的存储空间供驱动存放自己的特有信息。
2.Ramdisk驱动的入口函数
在Ramdisk驱动代码的DriverEntry函数中只做了几件简单的事情,下面用代码加以说明:
NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
 )
{
    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);
}
DriverEntry做的第一件事情是声明了一个WDF_DRIVER_CONFIG类型的变量config,并且在两句无关痛痒的输出语句之后,很快的使用WDF_DRIVER_CONFIG_INT初始化config变量。WDF_DRIVER_CONFIG_INIT结构通常用来说明这个驱动程序的一些可配置项,其中包括了这个驱动程序的EvtDriverDeviceAdd和EvtDriverUnload回调函数的入口地址,这个驱动程序在初始化时的一些标志和这个驱动程序在分配内存时所使用的tag值,WDF_DRIVER_CONFIG_INIT这个宏在初始化WDF_DRIVER_CONFIG类型的变量时会把用户提供的EvtDriverDeviceAdd回调函数的入口地址存入其中,并且初始化这个变量的其他部分。EvtDriverDeviceAdd回调函数是WDF驱动框架中的一个重要的回调函数,它用来在当即插即用管理器发现一个新的设备的时候对这个设备进行初始化操作,在这里我们可以将自己编写的RamDiskEvtDeviceAdd函数提供给系统作为本驱动的EvtDriverDeviceAdd回调函数。
在设置好了config变量后,DriverEntry直接调用了WdfDriverCreate并返回。WdfDriverCreate函数是使用任何WDF框架提供的函数之前必须调用的一个函数,用来建立WDF驱动对象。WdfDriverCreate函数的前两个参数就是DriverEntry传入的驱动对象(DriverObject)和注册表路径(RegisterPath),第三个参数用来说明这个WDF驱动对象的属性,这里简单的用WDF_NO_OBJECT_ATTRIBUTES说明不需要特殊的属性。第四个变量是之前初始化过的WDF_DRIVER_CONFIG变量,第四个参数是一个输出结果。
调用这个函数之后,前面初始化过的config变量中的EvtDriverDeviceAdd回调函数--RamDiskEvtDeviceAdd就和这个驱动挂起钩来,在今后的系统运行中,一旦发现了此类设备,RamDiskEvtDeviceAdd就会被Windows的Pnp manager调用,这个驱动自己的处理流程也就要上演了。
EvtDriverDeviceAdd函数
这里所说的EvtDriverDeviceAdd函数是WDF驱动模型中的名词,对应到传统的WDM驱动模型中就是WDM中的AddDevice函数。在本驱动中RamDiskEvtDeviceAdd作为一EvtDriverDeviceAdd函数在DriverEntry中被注册,在DriverEntry函数执行完毕之后,这个驱动就只依靠RamDiskEvtDeviceAdd函数和系统保持联系了。正如上一节所说的,系统在运行过程中一旦发现了这种类型的设备,就会调用RamDiskEvtDeviceAdd函数。下面对这个函数进行分析。
首先来看RamDiskEvtDeviceAdd的定义
NTSTATUS RamDiskEvtDeviceAdd(
       IN WDFDRIVER Driver,
       IN PWDFDRIVER_INIT DeviceInit
);
这个函数的返回值是NTSTATUS类型,可以根据实际函数的执行结果选择返回表示正确的STATUS_SUCCESS或者其他代表错误的返回值。这个函数的第一个参数是一个WDFDRIVER类型的参数,在这个例子中不会使用这个参数;第二个参数是一个WDFDRIVER_INIT类型的指针,这个参数是WDF驱动模型自动分配出来的一个数据结构,专门传递给EvtDriverDeviceAdd类函数用来建立一个新设备。下面具体来看代码:
2.局部变脸的声明
    //将要建立的设备对象的属性描述变量
    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);

3.磁盘设备的创建
EvtDriverDeviceAdd类函数的一个重要任务是创建设备,而它的WDFDEVICE_INIT类型参数就是用来做这样的事情,在创建设备之前需要按照开发人员的思想对WDFDEVICE_INIT变量进行进一步的加工,使创建的设备能够达到想要的效果。由于这里的设备首先需要一个名字,这是因为这个设备将会通过这个名字暴露给应用层并且被应用层所使用,一个没有名字的设备是无法在应用层使用的。另外需要将这个设备的类型设置为FILE_DEVICE_DISK,这是因为所有的磁盘设备都需要使用这个设备类型。将这个设备的I/O类型设置为Direct方式,这样在将读,写和DeviceIoControl的IRP发送到这个设备时,IRP所携带的缓冲区将可以直接被使用。将Exclusive设置为FALSE这说明这个设备可以被多次打开。
    //首先需要为这个设备指定一个名称,这里使用刚才声明的UNICODE_STRING
    status = WdfDeviceInitAssignName(DeviceInit, &ntDeviceName);
    if (!NT_SUCCESS(status)) {
        return status;
    }
    //接下来需要对这个设备进行一些属性设置,包括设备类型,IO操作类型和设备的排他方式
    WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_DISK);
    WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect);
    WdfDeviceInitSetExclusive(DeviceInit, FALSE);
    //下面来指定这个设备的设备扩展
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_EXTENSION);
    //下面我们还将利用这个WDF_OBJECT_ATTRIBUTES类型的变量来指定这个设备的清除回调函数,
    //这个WDF_OBJECT_ATTRIBUTES类型的变量将会在下面建立设备时作为一个参数传进去
    deviceAttributes.EvtCleanupCallback = RamDiskEvtDeviceContextCleanup;
    //到这里所有的准备工作都已就绪,我们可以开始真正的建立这个设备了,
    //建立出的设备被保存在device这个局部变量中
    status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
    if (!NT_SUCCESS(status)) {
        return status;
    }
//这个pDeviceExtension是我们声明的一个局部指针变量,将其指向新建立的设备扩展
pDeviceExtension = DeviceGetExtension(device);

4.如何处理发往设备的请求
在设备创建好之后,如何处理所有可能发送给设备的请求是需要考虑的下一个问题。在以往的WDM开发中,常用的方式是设置这个设备各个请求的分发函数为自己实现回调函数,并且将特殊处理放置在这些函数中。例如在这个例子中,可以将所有读写请求都实现为读写内存,这就是最简单的内存盘。上面的方式说起来很简单,但是实现时还是需要一定的技巧的,一种常用的方式是建立一个或多个队列,将所有发送到这个设备的请求都插入队列中,由另一个线程去处理队列。这是一个典型的生产者-消费者问题,这样做的好处是有了一个小小的缓冲,同时还不用担心由于缓冲带来的同步问题,因为所有的请求都被队列排队了。而WDF驱动框架,微软直接提供了这种队列。
为了实现为驱动制作一个处理队列这一目标,在WDF驱动框架中需要初始化一个队列配置变量ioQueueConfig,这个变量会说明队列的各种属性。一个简单的初始化方法是将这个配置变量初始化为默认状态,之后再对一些具有特殊属性的请求注册回调函数,例如为请求注册回调函数等。在这样的初始化之后再为指定设备建立这个队列,WDF驱动框架会自动将所有发往这个指定设备的请求都放到这个队列中去处理,同时当请求符合感兴趣的属性是会调用之前注册过的处理函数去处理。对每个设备可以建立多个队列,但是在本例中只有一个队列。
//将队列的配置变量初始化为默认值
    WDF_IO_QUEUE_CONFIG_INI

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 《Windows内核编程码》是一本非常经典的内核编程书籍。该书主要介绍了Windows内核的结构和工作原理,以及如何进行内核编程。作者通过自己的实践经验,深入浅出地解释了内核编程的基本原理和技巧。 该书的主要内容包括:Windows内核架构、内核模式和用户模式编程、驱动程序开发、处理器管理、内存管理、进程和线程管理、设备驱动程序、文件系统等。通过学习这些内容,读者可以全面了解Windows内核的各个方面,并具备进行内核编程的基础知识和技能。 内核编程是一项非常高级和复杂的技术,需要读者具备扎实的操作系统和编程基础。《Windows内核编程码》提供了丰富的码示例和实践案例,帮助读者加深对内核编程的理解,并能够在实践中掌握内核编程的各种技巧和方法。 通过学习该书,读者可以深入了解Windows内核的工作原理和开发方法,提升自己的技术水平,并有可能在内核开发领域取得突破。同时,该书也适合作为操作系统课程的参考书籍,帮助读者更好地理解操作系统的底层原理和设计思想。 总之,《Windows内核编程码》是一本值得学习的经典书籍,通过学习该书,读者可以从理论和实践两个方面全面提升自己的内核编程能力。 ### 回答2: Windows内核编程码,这是指在Windows操作系统内核级别的编程过程中,个人自执的情景。在传统的Windows内核编程中,需要深入理解Windows操作系统的内部机制、数据结构和调度算法,并通过编写驱动程序或者修改系统核心码来实现特定的功能或解决某些问题。这是一项相当具有挑战性和专业性的技术领域。 者,指的是自一人默默鱼的情景。在这个比喻中,内核编程者是在深入学习和专研Windows内核编程的过程中,自探索和解决问题,就像在边的一个人默默地捕捞。 Windows内核编程码指的是Windows操作系统的核心代码。对这些码的学习和理解,是了解Windows操作系统内部工作原理的关键。通过研究内核编程码,我们可以了解系统如何管理进程、内存分配、文件系统等功能,以及与硬件的交互过程。 进行Windows内核编程码可以带来许多好处。首先,它可以深入了解Windows操作系统的底层工作原理,从而更好地进行系统调试和性能优化。其次,它可以为用户定制和开发高性能的驱动程序,提升系统的稳定性和响应能力。此外,通过研究内核编程码,可以了解Windows操作系统的安全性和漏洞,从而更好地防范和解决安全问题。 然而,Windows内核编程码并不是一项简单的事情,它需要对操作系统原理、编程语言和底层架构有深入的理解。同时,需要拥有扎实的计算机科学基础和编程技巧,以及耐心和毅力来解决各种挑战和问题。 总之,Windows内核编程码是一项有挑战性的技术活动,通过对Windows操作系统内核代码的深入研究和理解,可以提高对系统的控制和优化能力,从而为用户提供更好的系统性能和功能。 ### 回答3: 《Windows内核编程码》是一本关于Windows内核编程的书籍,作者以“”来形容自己在Windows内核编程领域的孤求败之意。这本书主要介绍了Windows操作系统的内核编程细节和原理。 在这本书中,作者首先介绍了Windows内核的基本概念和架构,包括进程和线程管理、内存管理、驱动程序开发等。接着,作者深入讲解了Windows内核的各个模块和重要组件的实现原理,如调度器、文件系统、网络协议栈等。读者可以通过学习这些码,深入理解Windows内核的工作原理和设计思想。 《Windows内核编程码》不仅仅是一本理论性的书籍,更重要的是它提供了丰富的代码示例和实践案例,读者可以通过实际的代码实现和调试来加深对内核编程的理解。同时,本书还特别注重实用性,提供了大量的编程技巧和调试技巧,帮助读者快速掌握Windows内核编程的方法和技巧。 总之,《Windows内核编程码》是一本全面而深入的Windows内核编程技术书籍,适合那些对Windows系统内核感兴趣的程序员和操作系统开发者阅读。通过学习这本书,读者可以系统地学习Windows内核编程的原理和实践,提升自己的技术水平。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值