2.5 Windows驱动开发:DRIVER_OBJECT对象结构

news2024/11/19 19:40:07

在Windows内核中,每个设备驱动程序都需要一个DRIVER_OBJECT对象,该对象由系统创建并传递给驱动程序的DriverEntry函数。驱动程序使用此对象来注册与设备对象和其他系统对象的交互,并在操作系统需要与驱动程序进行交互时使用此对象。DRIVER_OBJECT对象还包含了与驱动程序所管理的设备对象相关联的设备扩展结构,以及用于处理I/O请求的函数指针等信息。它是驱动程序与操作系统内核之间的桥梁,用于协调设备的操作和管理。

本章将探索驱动程序开发的基础部分,了解驱动对象DRIVER_OBJECT结构体的定义,一般来说驱动程序DriverEntry入口处都会存在这样一个驱动对象,该对象内所包含的就是当前所加载驱动自身的一些详细参数,例如驱动大小,驱动标志,驱动名,驱动节等等,每一个驱动程序都会存在这样的一个结构,首先来看一下微软对其的定义;

typedef struct _DRIVER_OBJECT {
    CSHORT Type;                                // 驱动类型
    CSHORT Size;                                // 驱动大小
    PDEVICE_OBJECT DeviceObject;                // 驱动对象
    ULONG Flags;                                // 驱动的标志
    PVOID DriverStart;                          // 驱动的起始位置
    ULONG DriverSize;                           // 驱动的大小
    PVOID DriverSection;                        // 指向驱动程序映像的内存区对象
    PDRIVER_EXTENSION DriverExtension;          // 驱动的扩展空间
    UNICODE_STRING DriverName;                  // 驱动名字
    PUNICODE_STRING HardwareDatabase;
    PFAST_IO_DISPATCH FastIoDispatch;
    PDRIVER_INITIALIZE DriverInit;
    PDRIVER_STARTIO DriverStartIo;
    PDRIVER_UNLOAD DriverUnload;                 // 驱动对象的卸载地址
    PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
} DRIVER_OBJECT;

那么如果我们想要遍历出当前自身驱动的一些基本信息,我们只需要在驱动的头部解析_DRIVER_OBJECT即可得到全部的数据,这段代码可以写成如下样子,其中的IRP_MJ_这一系列则是微软的调用号,不同的RIP代表着不同的涵义,但一般驱动也就会用到如下这几种调用号。

#include <ntifs.h>

VOID UnDriver(PDRIVER_OBJECT driver)
{
    DbgPrint(("Uninstall Driver Is OK \n"));
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("hello lyshark \n");

    Driver->DriverUnload = UnDriver;

    DbgPrint("驱动名字 = %wZ \n", Driver->DriverName);
    DbgPrint("驱动起始地址 = %p | 大小 = %x | 结束地址 %p \n",Driver->DriverStart,Driver->DriverSize,(ULONG64)Driver->DriverStart + Driver->DriverSize);

    DbgPrint("卸载地址 = %p\n", Driver->DriverUnload);
    DbgPrint("IRP_MJ_READ地址 = %p\n", Driver->MajorFunction[IRP_MJ_READ]);
    DbgPrint("IRP_MJ_WRITE地址 = %p\n", Driver->MajorFunction[IRP_MJ_WRITE]);
    DbgPrint("IRP_MJ_CREATE地址 = %p\n", Driver->MajorFunction[IRP_MJ_CREATE]);
    DbgPrint("IRP_MJ_CLOSE地址 = %p\n", Driver->MajorFunction[IRP_MJ_CLOSE]);
    DbgPrint("IRP_MJ_DEVICE_CONTROL地址 = %p\n", Driver->MajorFunction[IRP_MJ_DEVICE_CONTROL]);

    // 输出完整的调用号
    for (auto i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
    {
        DbgPrint("IRP_MJ调用号 = %d | 函数地址 = %p \r\n", i, Driver->MajorFunction[i]);
    }

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

编译这段程序,签名并运行,我们即可看到如下输出信息,此时当前自身驱动的详细参数都可以被输出;

当然运用_DRIVER_OBJECT对象中的DriverSection字段我们完全可以遍历输出当前系统下所有的驱动程序的具体信息,DriverSection结构指向了一个_LDR_DATA_TABLE_ENTRY结构,结构的微软定义如下;

typedef struct _LDR_DATA_TABLE_ENTRY {
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderLinks;
    LIST_ENTRY InInitializationOrderLinks;
    PVOID DllBase;
    PVOID EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
    ULONG Flags;
    USHORT LoadCount;
    USHORT TlsIndex;
    union {
        LIST_ENTRY HashLinks;
        struct {
            PVOID SectionPointer;
            ULONG CheckSum;
        };
    };
    union {
        struct {
            ULONG TimeDateStamp;
        };
        struct {
            PVOID LoadedImports;
        };
    };
}LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;

为了能够遍历出所有的系统驱动,我们需要得到pLdr结构,该结构可通过Driver->DriverSection的方式获取到,获取到之后通过pLdr->InLoadOrderLinks.Flink得到当前驱动的入口地址,而每一次调用pListEntry->Flink都将会指向下一个驱动对象,通过不断地循环CONTAINING_RECORD解析,即可输出当前系统内所有驱动的详细信息。这段程序的写法可以如下所示;

#include <ntifs.h>

typedef struct _LDR_DATA_TABLE_ENTRY {
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderLinks;
    LIST_ENTRY InInitializationOrderLinks;
    PVOID DllBase;
    PVOID EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
    ULONG Flags;
    USHORT LoadCount;
    USHORT TlsIndex;
    union {
        LIST_ENTRY HashLinks;
        struct {
            PVOID SectionPointer;
            ULONG CheckSum;
        };
    };
    union {
        struct {
            ULONG TimeDateStamp;
        };
        struct {
            PVOID LoadedImports;
        };
    };
}LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;

VOID UnDriver(PDRIVER_OBJECT driver)
{
    DbgPrint(("Uninstall Driver Is OK \n"));
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("hello lyshark \n");

    Driver->DriverUnload = UnDriver;

    PLDR_DATA_TABLE_ENTRY pLdr = NULL;
    PLIST_ENTRY pListEntry = NULL;
    PLIST_ENTRY pCurrentListEntry = NULL;

    PLDR_DATA_TABLE_ENTRY pCurrentModule = NULL;
    pLdr = (PLDR_DATA_TABLE_ENTRY)Driver->DriverSection;
    pListEntry = pLdr->InLoadOrderLinks.Flink;
    pCurrentListEntry = pListEntry->Flink;

    // 判断是否结束
    while (pCurrentListEntry != pListEntry)
    {
        // 获取LDR_DATA_TABLE_ENTRY结构
        pCurrentModule = CONTAINING_RECORD(pCurrentListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);

        if (pCurrentModule->BaseDllName.Buffer != 0)
        {
            DbgPrint("模块名 = %wZ | 模块基址 = %p | 模块入口 = %p | 模块时间戳 = %d \n",
                pCurrentModule->BaseDllName,
                pCurrentModule->DllBase,
                pCurrentModule->EntryPoint,
                pCurrentModule->TimeDateStamp);
        }
        pCurrentListEntry = pCurrentListEntry->Flink;
    }

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

编译这段程序,签名并运行,我们即可看到如下输出信息,此时当前自身驱动的详细参数都可以被输出;

通过使用上一篇文章《内核字符串拷贝与比较》中所介绍的的RtlCompareUnicodeString函数,还可用于对比与过滤特定结果,以此来实现通过驱动名返回驱动基址的功能。

LONGLONG GetModuleBaseByName(PDRIVER_OBJECT pDriverObj, UNICODE_STRING ModuleName)
{
    PLDR_DATA_TABLE_ENTRY pLdr = NULL;
    PLIST_ENTRY pListEntry = NULL;
    PLIST_ENTRY pCurrentListEntry = NULL;

    PLDR_DATA_TABLE_ENTRY pCurrentModule = NULL;
    pLdr = (PLDR_DATA_TABLE_ENTRY)pDriverObj->DriverSection;
    pListEntry = pLdr->InLoadOrderLinks.Flink;
    pCurrentListEntry = pListEntry->Flink;

    while (pCurrentListEntry != pListEntry)
    {
        // 获取LDR_DATA_TABLE_ENTRY结构
        pCurrentModule = CONTAINING_RECORD(pCurrentListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);

        if (pCurrentModule->BaseDllName.Buffer != 0)
        {
            // 对比模块名
            if (RtlCompareUnicodeString(&pCurrentModule->BaseDllName, &ModuleName, TRUE) == 0)
            {
                return (LONGLONG)pCurrentModule->DllBase;
            }
        }
        pCurrentListEntry = pCurrentListEntry->Flink;
    }
    return 0;
}

上这段代码的使用也非常简单,通过传入一个UNICODE_STRING类型的模块名,即可获取到模块基址并返回,至于如何初始化UNICODE_STRING则在《内核字符串转换方法》中有详细的介绍,此处你只需要这样来写。

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("hello lyshark \n");

    UNICODE_STRING unicode;

    // 获取WinDDK驱动基地址
    RtlUnicodeStringInit(&unicode, L"WinDDK.sys");
    LONGLONG winddk_address = GetModuleBaseByName(Driver, unicode);
    DbgPrint("WinDDK模块基址 = %p \n", winddk_address);

    // 获取ACPI驱动基地址
    RtlUnicodeStringInit(&unicode, L"ACPI.sys");
    LONGLONG acpi_address = GetModuleBaseByName(Driver, unicode);
    DbgPrint("ACPI模块基址 = %p \n", acpi_address);

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

运行这段驱动程序,即可分别输出WinDDK.sys以及ACPI.sys两个驱动模块的基地址;

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用PHP编写采集药品官方数据的程序

目录 一、引言 二、程序设计和实现 1、确定采集目标 2、使用PHP的cURL库进行数据采集 3、解析JSON数据 4、数据处理和存储 5、数据验证和清理 6、数据输出和可视化 7、数据分析和挖掘 三、注意事项 1、合法性原则 2、准确性原则 3、完整性原则 4、隐私保护原则 …

Mac笔记本打开Outlook提示:您需要最新的版本的Outlook才能使用此数据库

Mac笔记本打开Outlook提示&#xff1a;您需要最新的版本的Outlook才能使用此数据库 故障现象&#xff1a; 卸载旧的office安装新版的office&#xff0c;打开outlook提示&#xff1a;您需要最新的版本的outlook才能使用此数据库。 故障截图&#xff1a; 故障原因&#xff1a;…

3类主流的车道检测AI模型

2014年的一天&#xff0c;我舒舒服服地躺在沙发上&#xff0c;看着我和加拿大朋友租的豪华滑雪别墅的篝火营地&#xff0c;突然&#xff0c;一个东西出现在我的视野里&#xff1a; “着火了&#xff01;着火了&#xff01;着火了&#xff01;” 我大喊。 几秒钟之内&#xff…

Redis 事务是什么?又和MySQL事务有什么区别?

目录 1. Redis 事务的概念 2. Redis 事务和 MySQL事务的区别&#xff1f; 3. Redis 事务常用命令 1. Redis 事务的概念 下面是在 Redis 官网上找到的关于事务的解释&#xff0c;这里划重点&#xff0c;一组命令&#xff0c;一个步骤。 也就是说&#xff0c;在客户端与 Redi…

synchronized jvm实现思考

底层实现时&#xff0c;为什么使用了cxq队列和entryList双向链表&#xff1f;这里为什么不跟AQS中使用一个队列就行了&#xff0c;加了一个entryList的目的是为了什么&#xff1f; 个人理解这里多一个entryList&#xff0c;可能是用于减少频繁的cas操作。假设存在很多锁竞争时&…

vue项目修改字体为苹方

vue项目修改字体为苹方 在项目assets中创建字体文件夹fonts&#xff0c;在文件夹中添加字体文件 在fonts底下创建css文件 font.css font-face {font-family: PingFang;src: url(./PingFang.ttf);font-weight: normal;font-style: normal; }需要在全局样式中引入&#xff0c;a…

网络安全准入技术之MAC VLAN

网络准入控制作为主要保障企业网络基础设施的安全的措施&#xff0c;特别是对于中大型企业来说&#xff0c;终端类型多样数量激增、终端管理任务重难度大、成本高。 在这样的一个大背景下&#xff0c;拥有更灵活的动态识别、认证、访问控制等成为了企业网络安全的最核心诉求之…

自定义GPT已经出现,并将影响人工智能的一切,做好被挑战的准备了吗?

原创 | 文 BFT机器人 OpenAI凭借最新突破&#xff1a;定制GPT站在创新的最前沿。预示着个性化数字协助的新时代到来&#xff0c;ChatGPT以前所未有的精度来满足个人需求和专业需求。 从本质上讲&#xff0c;自定义GPT是之前的ChatGPT的高度专业化版本或代理&#xff0c;但自定…

方阵的施密特正交化与相似对角化

方阵的施密特正交化与相似对角化 施密特正交化 施密特正交化步骤 example 略 相似对角化 相似对角化步骤 step1: step2: step3: step4: example 注:特征值的个数与秩无关 A {{-3, 6}, {-10, 6}}; Eigenvalues[A] V Eigenvectors[A]; P {V[[1]], V[[2]]}; P Transpo…

做一个Springboot文件上传-阿里云

概述 这个模块是用来上传头像以及文章封面的&#xff0c;图片的值是一个地址字符串&#xff0c;一般存放在本地或阿里云服务中 1、本地文件上传 我们将文件保存在一个本地的文件夹下&#xff0c;由于可能两个人上传不同图片但是却同名的图片&#xff0c;那么就会一个人的图片就…

ArkUI实战,深入浅出OpenHarmony应用开发

前言 | 《ArkUI实战》《ArkUI实战》深入浅出的介绍了OpenHarmony开发框架ArkUI组件的使用和应用开发流程&#xff0c;是OpenHarmony应用开发的必备电子书。https://www.arkui.club/ OpenHarmony开发资料归档__南先森-Laval社区OpenHarmony入门看这里 _南先森 Laval社区https:/…

Vue.js的生命周期钩子

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

DAY53 1143.最长公共子序列 + 1035.不相交的线 + 53. 最大子序和

1143.最长公共子序列 题目要求&#xff1a;给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长公共子序列的长度。 一个字符串的 子序列 是指这样一个新的字符串&#xff1a;它是由原字符串在不改变字符的相对顺序的情况下删除某些字符&#xff08;也可以不删…

Android图片压缩插件

今天才发现这个还有插件&#xff0c;平时都是传网站上压缩完了又下载下来覆盖原文件。现在有这个了&#xff0c;开发好高效&#x1f601;&#xff01;分享给大家&#xff0c;可能对你们有用哈哈&#x1f606;。也可能你们早都知道了……

信息系统项目管理师(第四版)教材精读思维导图-第十五章到二十四章

请参阅我的另一篇文章&#xff0c;综合介绍软考高项&#xff1a; 信息系统项目管理师&#xff08;软考高项&#xff09;备考总结_计算机技术与软件专业技术_铭记北宸的博客-CSDN博客 ​ 思维导图源文件下载链接&#xff1a; 十五章风险管理 十六章采购管理 十七章干系人管理…

如何驾驭逻辑、形式逻辑与AI算法?

逻辑错误与逻辑形式错误是有区别的&#xff1a; 逻辑错误经常表现为没有逻辑因果&#xff0c;用辩证法、阴谋论和统计归纳替代因果演绎&#xff1b;而逻辑形式错误是&#xff1a;前提是形式和内容需要分离&#xff0c;就像数学与语文分开&#xff0c;数学代表形式&#xff0c;…

技术贴 | SQL 执行 - 执行器优化

本期技术贴主要介绍查询执行引擎的优化。查询执行引擎负责将 SQL 优化器生成的执行计划进行解释&#xff0c;通过任务调度执行从存储引擎里面把数据读取出来&#xff0c;计算出结果集&#xff0c;然后返回给客户。 在关系型数据库发展的早期&#xff0c;受制于计算机 IO 能力的…

SLF4J: Class path contains multiple SLF4J bindings.

问题截图 问题原因 这里就是由于hbase 安装路径下的一个文件和hadoop安装路径下的文件起冲突了 解决办法 我的路径&#xff1a; 这个一定要看自己电脑上的路径 /usr/local/hbase/lib/client-facing-thirdparty/ slf4j-log4j12-1.7.25.jar更名为&#xff1a; /usr/local/hb…

手把手带你学习 JavaScript 的 ES6 ~ ESn

文章目录 一、引言二、了解 ES6~ESn 的新特性三、掌握 ES6~ESn 的用法和实现原理四、深入挖掘和拓展《深入理解现代JavaScript》编辑推荐内容简介作者简介精彩书评目录 一、引言 JavaScript 是一种广泛使用的网络编程语言&#xff0c;它在前端开发中扮演着重要角色。随着时间的…

Python | 机器学习之聚类算法

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《人工智能奇遇记》&#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 目录结构 1. 机器学习之聚类算法概念 1.1 机器学习 1.2 聚类算法 2. 聚类算法 2.1 实验目的…