Windows驱动开发(二)

1. NT和WDM式驱动

1. NT式驱动

传统的Windows系统驱动类型。NT式驱动的启动/停止/安装/卸载只能由 服务控制管理程序组件(SCM) 来完成的。
包括最简单的hello world,以及目前常用的文件过滤框架 minifilter 都是基于NT式实现的。
NT式驱动的最大特点即完全不依赖硬件支持即可工作在内核模式中。

VOID FilterUnload(PDRIVER_OBJECT DriverObject) {
    UNREFERENCED_PARAMETER(DriverObject);
}

VOID Initialize(PDRIVER_OBJECT pDriverObject) {
    pDriverObject->DriverUnload = FilterUnload;
    for (int i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) {
        pDriverObject->MajorFunction[i] = DispatchAny;
    }
}

//
// DriverEntry
//
_Function_class_(DRIVER_INITIALIZE)
    _IRQL_requires_same_
    _IRQL_requires_(PASSIVE_LEVEL)
EXTERN_C NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT pDriverObject,
                              _In_ PUNICODE_STRING pusRegistryPath) {
    // Enable POOL_NX_OPTIN
    // Set NonPagedPool
    ExInitializeDriverRuntime(DrvRtPoolNxOptIn);

    //
    // Init global data
    //
    BOOLEAN fSuccess = FALSE;
    __try {
        Initialize(pDriverObject);
        LOGINFO3("Driver Load\n");
        fSuccess = true;

    } __finally {
        if (!fSuccess) {
            FilterUnload(pDriverObject);
        }
    }

    return STATUS_SUCCESS;
}

但是NT式驱动的缺点也正因如此,当需要用NT式驱动实现设备过滤时需要枚举每个需要的设备再附加到设备堆栈,例如早期的SFilter框架。

2.WDM驱动

支持即插即用(PNP)和电源管理功能的驱动类型,也是最常见的驱动类型,通常与硬件关联的驱动都是由WDM实现。
WDM驱动与NT式驱动的最大区别在于WDM驱动不支持SCM管理。虽然WDM驱动不支持SCM管理,但依然支持通过SCM启动,只是无法通过SCM停止。
在具体实现上,相比NT式驱动,WDM驱动必须额外注册AddDevice回调函数,此回调函数的作用是创建设备对象并由PNP管理器调用。
同时在IRP_MJ_POWERIRP_MJ_PNP中处理设备插拔和电源的IRP请求。

VOID FilterUnload(PDRIVER_OBJECT DriverObject) {
    UNREFERENCED_PARAMETER(DriverObject);
}

NTSTATUS DispatchPower(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
    NTSTATUS Status = STATUS_INVALID_DEVICE_OBJECT_PARAMETER;
    PDEVICE_EXTENSION DevExt = NULL;
    do {
        DevExt = (PDEVICE_EXTENSION)(DeviceObject->DeviceExtension);
        IoAcquireRemoveLock(&DevExt->RemoveLock, Irp);
#if (NTDDI_VERSION < NTDDI_VISTA)
        PoStartNextPowerIrp(Irp);
        IoSkipCurrentIrpStackLocation(Irp);
        Status = PoCallDriver(DevExt->TargetDevice, Irp);
#else
        IoSkipCurrentIrpStackLocation(Irp);
        Status = IoCallDriver(DevExt->TargetDevice, Irp);
#endif
        IoReleaseRemoveLock(&DevExt->RemoveLock, Irp);
    } while (false);
    return Status;
}

NTSTATUS DispatchPnp(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
    NTSTATUS Status = STATUS_INVALID_DEVICE_OBJECT_PARAMETER;
    PDEVICE_EXTENSION DevExt = NULL;
    PDEVICE_OBJECT TargetDevice = NULL;
    PIO_STACK_LOCATION IrpStack = NULL;
    bool LockState = true;
    do {
        DevExt = (PDEVICE_EXTENSION)(DeviceObject->DeviceExtension);
        IoAcquireRemoveLock(&DevExt->RemoveLock, Irp);
        TargetDevice = DevExt->TargetDevice;  //
        IrpStack = IoGetCurrentIrpStackLocation(Irp);
        switch (IrpStack->MinorFunction) {
            case IRP_MN_START_DEVICE:  // 处理设备启动请求
                break;
            // case IRP_MN_QUERY_REMOVE_DEVICE: // 安全移除
            // case IRP_MN_SURPRISE_REMOVAL: // 强制移除
            case IRP_MN_REMOVE_DEVICE:  // 处理设备移除请求
                IoReleaseRemoveLockAndWait(&DevExt->RemoveLock, Irp);
                IoDetachDevice(DevExt->TargetDevice);  //
                IoDeleteDevice(DeviceObject);          //
                LockState = false;
                break;
        }
        //
        IoSkipCurrentIrpStackLocation(Irp);
        Status = IoCallDriver(DevExt->TargetDevice, Irp);
        if (LockState) {
            IoReleaseRemoveLock(&DevExt->RemoveLock, Irp);
        }
    } while (false);
    return Status;
}

NTSTATUS AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT PDO) {
    NTSTATUS Status = STATUS_SUCCESS;
    PDEVICE_EXTENSION DevExt = NULL;
    PDEVICE_OBJECT FilterDevice = NULL;
    Status = IoCreateDevice(
        DriverObject, sizeof(DEVICE_EXTENSION),
        NULL, PDO->DeviceType,
        PDO->Characteristics, FALSE, &FilterDevice);
    if (!NT_SUCCESS(Status)) {
        LOGERROR(Status, "IoCreateDevice failed\n");
        return Status;
    }

    FilterDevice->Flags |= PDO->Flags;
    DevExt = (PDEVICE_EXTENSION)(FilterDevice->DeviceExtension);
    RtlZeroMemory(DevExt, sizeof(DEVICE_EXTENSION));
    IoInitializeRemoveLock(&DevExt->RemoveLock, c_nRmLockTag, 60, 0);
    DevExt->PDO = PDO;

    Status = IoAttachDeviceToDeviceStackSafe(
        FilterDevice,            //
        PDO,                     //
        &DevExt->TargetDevice);  //
    if (!NT_SUCCESS(Status)) {
        LOGERROR(Status, "IoAttachDeviceToDeviceStackSafe failed\n");
        IoDeleteDevice(FilterDevice);
        return Status;
    }

    FilterDevice->Flags |= DevExt->TargetDevice->Flags & (DO_DIRECT_IO | DO_BUFFERED_IO);
    FilterDevice->Flags &= ~DO_DEVICE_INITIALIZING;
    return STATUS_SUCCESS;
}

VOID Initialize(PDRIVER_OBJECT pDriverObject) {
    pDriverObject->DriverUnload = FilterUnload;
    for (int i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) {
        pDriverObject->MajorFunction[i] = DispatchAny;
    }

    pDriverObject->MajorFunction[IRP_MJ_POWER] = DispatchPower;
    pDriverObject->MajorFunction[IRP_MJ_PNP] = DispatchPnp;
    pDriverObject->DriverExtension->AddDevice = AddDevice;
}

//
// DriverEntry
//
_Function_class_(DRIVER_INITIALIZE)
    _IRQL_requires_same_
    _IRQL_requires_(PASSIVE_LEVEL)
EXTERN_C NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT pDriverObject,
                              _In_ PUNICODE_STRING pusRegistryPath) {
    // Enable POOL_NX_OPTIN
    // Set NonPagedPool
    ExInitializeDriverRuntime(DrvRtPoolNxOptIn);

    //
    // Init global data
    //
    BOOLEAN fSuccess = FALSE;
    __try {
        Initialize(pDriverObject);
        LOGINFO3("Driver Load\n");
        fSuccess = true;
    } __finally {
        if (!fSuccess) {
            FilterUnload(pDriverObject);
        }
    }

    return STATUS_SUCCESS;
}

从上述的示例代码中可以看出,WDM驱动与NT式驱动的区。

2. 设备堆栈和IRP派遣

上图是USB存储设备的设备堆栈示例图,我们从下往上来看。
最下层是USB控制控制总线,由pci驱动创建设备的PDO(Physical Device Object)。
上一层是USB根控制器,由usbuhci驱动创建PDO,同时附加到下层的控制总线上。
更上层则是USB存储设备,由usbhub驱动创建PDO,附加到USB控制器。
最上层即实际的USB磁盘设备,由usbstor驱动创建PDO,附加到下一层的存储设备。
继续向上追溯的话,还有由disk驱动创建的磁盘分区,这里就不一一赘述了。
而所有的IRP请求都是由系统分发给最顶层的驱动程序,然后调用IoCallDriver向下分发,等待下层的执行的结果。
当然,实际的IO请求实际上是从卷设备开始分发的,而具体如何由卷设备派遣给磁盘设备的逻辑下次再说了。

  • 如何由卷设备追溯顶级的USB设备

1)通过IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS请求获取物理磁盘的编号。

bool GetPhysicalDrive(PDEVICE_OBJECT DeviceObject, PWCH pPhysicalDrive, ULONG size) {
    VOLUME_DISK_EXTENTS vde;
    NTSTATUS status = IoControl(DeviceObject, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, &vde, sizeof(vde));
    swprintf_s(pPhysicalDrive, size, L"\\??\\PhysicalDrive%lu", vde.Extents[0].DiskNumber);
    return true;
}

2)由物理磁盘的编号获取文件系统设备对象

PFILE_OBJECT FileObj;
NTSTATUS status = IoGetDeviceObjectPointer(&usDiskWin32Name, FILE_READ_DATA, &FileObj, &DevObj);
ObDereferenceObject(FileObj);
// Find FileSystem From Device Stack
UNICODE_STRING usFileSystemDriver = RTL_CONSTANT_STRING(L"\\FileSystem\\RAW");
DevObj = GetTargetDeviceFromStackByDriver(DevObj, &usFileSystemDriver);

3)由文件系统设备对象获取磁盘设备对象

IoGetDiskDeviceObject(DevObj, &DiskObj);

4)由磁盘设备对象获取磁盘驱动器对象

PDEVICE_OBJECT GetTargetDeviceFromStackByDriver(PDEVICE_OBJECT DeviceObject, PUNICODE_STRING DriverName) {
    PDEVICE_OBJECT TargetDevice;
    TargetDevice = IoGetLowerDeviceObject(DeviceObject);
    while (TargetDevice) {
        if (0 == RtlCompareUnicodeString(&TargetDevice->DriverObject->DriverName, DriverName, TRUE)) {
            break;
        }

        DeviceObject = TargetDevice;
        TargetDevice = IoGetLowerDeviceObject(DeviceObject);
        ObDereferenceObject(DeviceObject);
    }
    return TargetDevice;
}

// Find USBSTOR From Device Stack
UNICODE_STRING usUsbstorDriver = RTL_CONSTANT_STRING(L"\\Driver\\USBSTOR");
DevObj = GetTargetDeviceFromStackByDriver(DiskObj, &usUsbstorDriver);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/888312.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于SpringBoot健身房管理系统【附源码】

效果如下&#xff1a; 系统首页界面 系统注册详细页面 健身课程详细页面 后台登录界面 管理员主页面 员工界面 健身教练界面 员工主页面 健身教练主页面 研究背景 随着生活水平的提高和健康意识的增强&#xff0c;现代人越来越注重健身。健身房作为一种专业的健身场所&#x…

前端工程化17-邂逅原生的ajax、跨域、JSONP

5、邂逅原生的ajax 5.1、什么是ajax AJAX 全称为Asynchronous Javascript And XML&#xff0c;就是异步的 JS 和 XML。通过AJAX可以在浏览器中向服务器发送异步请求&#xff0c;最大的优势&#xff1a;页面无刷新获取数据。AJAX 不是新的编程语言&#xff0c;而是一种将现有的…

windows配置C++编译环境和VScode C++配置(保姆级教程)

1.安装MinGW-w64 MinGW-w64是一个开源的编译器套件&#xff0c;适用于Windows平台&#xff0c;支持32位和64位应用程序的开发。它包含了GCC编译器、GDB调试器以及其他必要的工具&#xff0c;是C开发者在Windows环境下进行开发的重要工具。 我找到了一个下载比较快的链接&#…

Excel下拉菜单制作及选项修改

Excel下拉菜单 1、下拉菜单制作2、下拉菜单修改 下拉框&#xff08;选项菜单&#xff09;是十分常见的功能。Excel支持下拉框制作&#xff0c;通过预设选项进行菜单选择&#xff0c;可以避免手动输入错误和重复工作&#xff0c;提升数据输入的准确性和效率 1、下拉菜单制作 步…

硬盘数据恢复的方法有哪几种?9种妙招速览

在当今数字化时代&#xff0c;硬盘数据的安全至关重要。然而&#xff0c;数据丢失的情况时有发生&#xff0c;掌握硬盘数据恢复方法显得尤为重要。本文将详细介绍几种有效的硬盘数据恢复方法&#xff0c;帮助用户在遇到数据丢失问题时&#xff0c;能够迅速采取措施&#xff0c;…

LabVIEW提高开发效率技巧----使用动态事件

在LabVIEW开发过程中&#xff0c;用户交互行为可能是多样且不可预知的。为应对这些变化&#xff0c;使用动态事件是一种有效的策略。本文将从多个角度详细介绍动态事件的概念及其在LabVIEW开发中的应用技巧&#xff0c;并结合实际案例&#xff0c;说明如何通过动态事件提高程序…

github——指标统计

github——指标统计 它的作用特定项目统计首页展示 github-readme-stats是一个可以统计指定用户github指标的项目。可以使用此项目统计自己的github&#xff0c;用于首页展示。效果如图&#xff1a; 它的作用 它可以&#xff1a; 统计git操作统计账户编程语言构成比例解除githu…

sqli-labs less-13 post报错注入使用extractvalue

post提交报错注入 闭合方式及注入点 利用hackbar进行注入&#xff0c;构造post语句 unameaaa’passwdbbb&SubmitSubmit 页面报错&#xff0c;根据分析&#xff0c;闭合方式). 确定列数 构造 unameaaa’) or 11 # &passwdbbb&SubmitSubmit 确定存在注入 unameaaa’…

论文复现:Training on the Benchmark Is Not All You Need

文章目录 1 资料2 我的总结3 复现源码首先你需要有gpt的api接口安装&#xff1a;执行指令源码data_process.pyinference_logprobs.py 4 结果 1 资料 我复现的源码: https://github.com/Whiffe/Benchmark-leakage-detection/tree/main 官网源码&#xff1a;https://github.com…

【RAG】HiQA:一种用于多文档问答的层次化上下文增强RAG

前言 文档领域的RAG&#xff0c;之前的工作如ChatPDF等很多的RAG框架&#xff0c;文档数量一旦增加&#xff0c;将导致响应准确性下降&#xff0c;如下图&#xff1b;现有RAG方法在处理具有相似内容&#xff08;在面对大量难以区分的文档时&#xff09;和结构的文档时表现不佳…

【leetcode】125.验证回文串

思路&#xff1a; isPalindrome 函数&#xff1a; 使用两个指针 left 和 right 分别指向字符串的开始和结束。使用 isalnum 函数跳过非字母数字字符。使用 tolower 函数将字符转换为小写进行比较。如果在某一步比较中发现字符不相等&#xff0c;则返回 0&#xff08;false&…

Python进阶--函数进阶

目录 1. 函数多返回值 2. 函数多种传参方式 (1). 位置参数 (2). 关键字参数 (3). 缺省参数 (4). 不定长参数 3. 匿名函数 (1). 函数作为参数传递 (2). lambda匿名函数 1. 函数多返回值 def return_num():return 1# 返回1之后就不会再向下继续执行函数体return 2 resu…

gstreamer 内存 alloctor 介绍

文章目录 前言一、gstreamer 默认的内存 alloctor1. gstreamer 中默认的内存 allocator 为 GST_ALLOCATOR_SYSMEM (即SystemMemory)2. GST_ALLOCATOR_SYSMEM 申请内存实例二、gstreamer 目前支持的几种内存 alloctor1.GstDmaBufAllocator1.1 GstDmaBufAllocator 介绍1.2 GstDma…

Vue3轻松实现前端打印功能

文章目录 1.前言2.安装配置2.1 下载安装2.2 main.js 全局配置3.综合案例3.1 设置打印区域3.2 绑定打印事件3.3 完整代码4.避坑4.1 打印表格无边框4.2 单选框复选框打印不选中4.3 去除页脚页眉4.4 打印內容不自动换行1.前言 vue3 前端打印功能主要通过插件来实现。 市面上常用的…

基于springboot vue地方废物回收机构管理系统设计与实现

博主介绍&#xff1a;专注于Java&#xff08;springboot ssm 等开发框架&#xff09; vue .net php phython node.js uniapp 微信小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不…

GeoCue与Xer Technologies合作推动无人机测绘技术革新

GeoCue与Xer Technologies合作推动无人机测绘技术革新 近期,LiDAR测绘硬件和软件开发商GeoCue与瑞士长航时混合动力无人机制造商Xer Technologies AG携手合作,成功将GeoCue的TrueView 720 LiDAR和图像传感器集成至Xer X8无人机平台。这一里程碑式的合作不仅标志着无人机测绘技…

基于STM32的智能门锁控制系统设计

引言 本项目基于STM32微控制器设计了一个智能门锁控制系统&#xff0c;用户可以通过密码输入或指纹识别来控制门锁的开关。该系统集成了键盘、指纹传感器、舵机等外设&#xff0c;实现了门锁的安全、便捷控制&#xff0c;同时也具备了较强的扩展性。该项目展示了STM32在安防领…

某个应用的CPU使用率居然达到100%,我该怎么办?

摘至https://learn.lianglianglee.com/ CPU使用率 Linux 作为一个多任务操作系统&#xff0c;将每个 CPU 的时间划分为很短的时间片&#xff0c;再通过调度器轮流分配给各个任务使用&#xff0c;因此造成多任务同时运行的错觉。 为了维护 CPU 时间&#xff0c;Linux 通过事先定…

七、InnoDB数据存储结构

文章目录 1. 数据库的存储结构:页1.1 磁盘与内存交互基本单位:页1.2 页结构概述1.3 页的大小1.4 页的上层结构2. 页的内部结构2.1 第1部分:File Header(文件头部)和 File Trailer(文件尾部)2.1.1 File Header(文件头部)2.1.2 File tRAILER(文件尾部)2.2 第2部分:Fr…

【AIGC】通过OpenAi Canvas修改论文(附40条论文优化指令)

目录 1、用ChatGPT优化论文大纲和逻辑2、用ChatGPT充实论文内容3、用ChatGPT寻找案例和数据4、用ChatGPT检查语法和字词错误5、如何直接使用ChatGPT4o、o1、OpenAI Canvas6、OpenAI Canvas增强了啥&#xff1f;7、编程功能增强 在刚开始撰写学术论文时&#xff0c;很多小伙伴感…