8.3 Windows驱动开发:内核遍历文件或目录

在笔者前一篇文章《内核文件读写系列函数》简单的介绍了内核中如何对文件进行基本的读写操作,本章我们将实现内核下遍历文件或目录这一功能,该功能的实现需要依赖于ZwQueryDirectoryFile这个内核API函数来实现,该函数可返回给定文件句柄指定的目录中文件的各种信息,此类信息会保存在PFILE_BOTH_DIR_INFORMATION结构下,通过遍历该目录即可获取到文件的详细参数,如下将具体分析并实现遍历目录功能。

该功能也是ARK工具的最基本功能,如下图是一款通用ARK工具的文件遍历功能的实现效果;

在概述中提到过,目录遍历的核心是ZwQueryDirectoryFile()系列函数,该函数可返回给定文件句柄指定的目录中文件的各种信息,其微软官方定义如下;

ZwQueryDirectoryFile是Windows操作系统中的一个系统调用函数,用于查询目录中的文件信息。具体而言,它可以用来枚举一个目录中的所有文件,并返回每个文件的名称、属性、时间戳等信息。

调用ZwQueryDirectoryFile函数需要指定以下参数:

  • 目录句柄:表示要查询的目录的句柄,可以通过调用ZwOpenFile函数打开目录获取。
  • 文件信息类:表示要返回的文件信息的类型,如文件名、文件大小、文件时间戳等。
  • 文件信息缓冲区:表示存放返回文件信息的缓冲区,其大小必须足够大以容纳查询结果。
  • 缓冲区大小:表示文件信息缓冲区的大小。
  • 是否遍历子目录:指定是否遍历目录中的子目录。
  • 文件名匹配模式:指定查询的文件名模式,支持通配符。
  • 是否返回长文件名:指定是否返回长文件名。
  • 函数执行成功时,将返回STATUS_SUCCESS,同时将文件信息写入文件信息缓冲区中。当返回STATUS_NO_MORE_FILES时,表示目录中没有更多的文件需要枚举。

需要注意的是,使用ZwQueryDirectoryFile函数需要具有足够的权限,并且应该对返回的文件信息进行适当的处理,以避免潜在的安全问题。

NTSYSAPI NTSTATUS ZwQueryDirectoryFile(
  [in]           HANDLE                 FileHandle,            // 返回的文件对象的句柄,表示要为其请求信息的目录。
  [in, optional] HANDLE                 Event,                 // 调用方创建的事件的可选句柄。 
  [in, optional] PIO_APC_ROUTINE        ApcRoutine,            // 请求的操作完成时要调用的可选调用方提供的 APC 例程的地址。
  [in, optional] PVOID                  ApcContext,            // 如果调用方提供 APC 或 I/O 完成对象与文件对象关联,则为调用方确定的上下文区域的可选指针。
  [out]          PIO_STATUS_BLOCK       IoStatusBlock,         // 指向 IO_STATUS_BLOCK 结构的指针,该结构接收最终完成状态和有关操作的信息。
  [out]          PVOID                  FileInformation,       // 指向接收有关文件的所需信息的输出缓冲区的指针。
  [in]           ULONG                  Length,                // FileInformation 指向的缓冲区的大小(以字节为单位)。
  [in]           FILE_INFORMATION_CLASS FileInformationClass,  // 要返回的有关目录中文件的信息类型。
  [in]           BOOLEAN                ReturnSingleEntry,     // 如果只应返回单个条目,则设置为 TRUE ,否则为 FALSE 。
  [in, optional] PUNICODE_STRING        FileName,              // 文件路径
  [in]           BOOLEAN                RestartScan            // 如果扫描是在目录中的第一个条目开始,则设置为 TRUE 。
);

该函数我们需要注意FileInformation参数,在本例中它被设定为了PFILE_BOTH_DIR_INFORMATION用于存储当前节点下文件或目录的一些属性,如文件名,文件时间,文件状态等,其次FileInformationClass参数也是有多种选择的,本例中我们需要遍历文件或目录则设置成FileBothDirectoryInformation就可以,在循环遍历文件时需要将当前目录.以及上一级目录…排除,而pDir->FileAttributes则用于判断当前节点是文件还是目录,属性FILE_ATTRIBUTE_DIRECTORY代表是目录,反之则是文件,实现目录文件遍历完整代码如下所示;

#include <ntifs.h>
#include <ntstatus.h>

// 遍历文件夹和文件
BOOLEAN MyQueryFileAndFileFolder(UNICODE_STRING ustrPath)
{
    HANDLE hFile = NULL;
    OBJECT_ATTRIBUTES objectAttributes = { 0 };
    IO_STATUS_BLOCK iosb = { 0 };
    NTSTATUS status = STATUS_SUCCESS;

    // 初始化结构
    InitializeObjectAttributes(&objectAttributes, &ustrPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);

    // 打开文件得到句柄
    status = ZwCreateFile(&hFile, FILE_LIST_DIRECTORY | SYNCHRONIZE | FILE_ANY_ACCESS,
        &objectAttributes, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE,
        FILE_OPEN, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT,
        NULL, 0);
    if (!NT_SUCCESS(status))
    {
        return FALSE;
    }

    // 为节点分配足够的空间
    ULONG ulLength = (2 * 4096 + sizeof(FILE_BOTH_DIR_INFORMATION)) * 0x2000;
    PFILE_BOTH_DIR_INFORMATION pDir = ExAllocatePool(PagedPool, ulLength);

    // 保存pDir的首地址
    PFILE_BOTH_DIR_INFORMATION pBeginAddr = pDir;

    // 获取信息,返回给定文件句柄指定的目录中文件的各种信息
    status = ZwQueryDirectoryFile(hFile, NULL, NULL, NULL, &iosb, pDir, ulLength, FileBothDirectoryInformation, FALSE, NULL, FALSE);
    if (!NT_SUCCESS(status))
    {
        ExFreePool(pDir);
        ZwClose(hFile);
        return FALSE;
    }

    // 遍历
    UNICODE_STRING ustrTemp;
    UNICODE_STRING ustrOne;
    UNICODE_STRING ustrTwo;

    RtlInitUnicodeString(&ustrOne, L".");
    RtlInitUnicodeString(&ustrTwo, L"..");

    WCHAR wcFileName[1024] = { 0 };
    while (TRUE)
    {
        // 判断是否是..上级目录或是.本目录
        RtlZeroMemory(wcFileName, 1024);
        RtlCopyMemory(wcFileName, pDir->FileName, pDir->FileNameLength);

        RtlInitUnicodeString(&ustrTemp, wcFileName);

        // 是否是.或者是..目录
        if ((0 != RtlCompareUnicodeString(&ustrTemp, &ustrOne, TRUE)) && (0 != RtlCompareUnicodeString(&ustrTemp, &ustrTwo, TRUE)))
        {
            // 判断是文件还是目录
            if (pDir->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
            {
                // 目录
                DbgPrint("[目录] 创建时间: %u | 改变时间: %u 目录名: %wZ \n", pDir->CreationTime, &pDir->ChangeTime, &ustrTemp);
            }
            else
            {
                // 文件
                DbgPrint("[文件] 创建时间: %u | 改变时间: %u | 文件名: %wZ \n", pDir->CreationTime, &pDir->ChangeTime, &ustrTemp);
            }
        }

        // 遍历完毕直接跳出循环
        if (0 == pDir->NextEntryOffset)
        {
            break;
        }

        // 每次都要将pDir指向新的地址
        pDir = (PFILE_BOTH_DIR_INFORMATION)((PUCHAR)pDir + pDir->NextEntryOffset);
    }

    // 释放内存并关闭句柄
    ExFreePool(pBeginAddr);
    ZwClose(hFile);

    return TRUE;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    DbgPrint("驱动卸载成功 \n");
}

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

    // 遍历文件夹和文件
    UNICODE_STRING ustrQueryFile;
    RtlInitUnicodeString(&ustrQueryFile, L"\\??\\C:\\Windows");
    MyQueryFileAndFileFolder(ustrQueryFile);

    DbgPrint("驱动加载成功 \n");
    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

编译如上驱动程序并运行,则会输出C:\\Windows目录下的所有文件和目录,以及创建时间和修改时间,输出效果如下图所示;

你是否会觉得很失望,为什么不是递归枚举,这里为大家解释一下,通常情况下ARK工具并不会在内核层实现目录与文件的递归操作,而是将递归过程搬到了应用层,当用户点击一个新目录时,在应用层只需要拼接新的路径再次发送给驱动程序让其重新遍历一份即可,这样不仅可以提高效率而且还降低了蓝屏的风险,显然在应用层遍历是更合理的。

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

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

相关文章

足底筋膜炎症状及治疗方法

足底筋膜炎是一种常见的足部疾病&#xff0c;通常会引起足跟疼痛和不适。这种疼痛通常在早晨起床后或者长时间休息后更为明显&#xff0c;行走一段时间后可能会减轻。下面我们将详细介绍足底筋膜炎的症状及治疗方法。 一、足底筋膜炎的症状 足跟疼痛&#xff1a;这是足底筋膜…

Avalonia 实现简单的IM即时通讯、视频通话(源码,支持国产系统,统信、银河麒麟)

Avalonia 在跨平台上的表现非常出色&#xff0c;对信创国产操作系统&#xff08;像银河麒麟、统信UOS、Deepin等&#xff09;也很不错。现在&#xff0c;我们就来使用 Avalonia 实现一个跨平台的简单IM&#xff0c;除了文字聊天外&#xff0c;还可以语音视频通话。废话不多说&a…

B032-服务器 Tomcat JavaWeb项目 Servlet

目录 服务器服务器的认识 Tomcat服务器Tomcat服务器的介绍Tomcat的安装Tomcat报错的情况Tomcat要启动成功的条件 JavaWeb项目Web的项目结构发布项目的第一种方式发布项目的第二种方式 Eclipse中搭建动态Web项目eclipse安装Tomcat插件servletservlet示例servlet的执行流程servle…

MySql 计算同比、环比

一、理论 国家统计局同比、环比计算公式 增长速度是反映经济社会某一领域发展变化情况的重要数据&#xff0c;而同比和环比是反映增长速度最基础、最核心的数据指标&#xff0c;也是国际上通用的指标。在统计中&#xff0c; 同比和环比通常是同比变化率和环比变化率的简称&…

【bug 回顾】上传图片超时

测试 bug 问题分析 - 上传图片超时 最近在测试上遇到一个莫名奇妙的问题&#xff0c;最后也没有得到具体是哪块的原因&#xff0c;看各位大佬有没有思路&#xff1f;&#xff1f; 一 、背景 现在我们有三台服务器&#xff0c;用来布两套环境。其中另外一台服务器3配置的 tom…

打包项目报错:程序包javax.servlet不存在

背景&#xff1a; WebService项目在没有配置Tomcat的情况下重新打包&#xff0c;由于是直接导入别人写好的项目&#xff0c;没有配置其他环境&#xff0c;所以报错程序包javax.servlet不存在 解决方法&#xff1a; 找到servlet-api.jar包&#xff0c;导入到现有项目的SDK 重…

深圳锐科达SV-X7 sip话机与海康威视摄像头联动设置方法

深圳锐科达SV-X7 sip话机与海康威视摄像头联动设置方法 SIP对讲终端获取设备IP地址方式 通过长按速拨键3秒&#xff08;上电30秒后&#xff0c;即听到提示音后&#xff09;&#xff0c;待喇叭发出急促嘟嘟声&#xff0c;再快速按一下速拨键&#xff0c;设备自动语音播报本机的…

【开源】基于Vue.js的婚恋交友网站

项目编号&#xff1a; S 057 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S057&#xff0c;文末获取源码。} 项目编号&#xff1a;S057&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 会员管理模块2.3 新…

CentOS7 FTP服务创建

一、安装FTP sudo dnf install vsftpd 二、设置防火墙&#xff0c;允许访问FTP //给防火墙增加一个允许的ftp服务(--permanent永久生效,重启后依然生效) sudo firewall-cmd --permanent --zonepublic --add-serviceftp//修改完后需要重启防火墙 sudo firewall-cmd --reload …

CSS特效014: hover后左右开门的效果

CSS常用示例100专栏目录 本专栏记录的是经常使用的CSS示例与技巧&#xff0c;主要包含CSS布局&#xff0c;CSS特效&#xff0c;CSS花边信息三部分内容。其中CSS布局主要是列出一些常用的CSS布局信息点&#xff0c;CSS特效主要是一些动画示例&#xff0c;CSS花边是描述了一些CSS…

应对数据爆炸时代,揭秘向量数据库如何成为AI开发者的新宠,各数据库差异对比

项目设计集合&#xff08;人工智能方向&#xff09;&#xff1a;助力新人快速实战掌握技能、自主完成项目设计升级&#xff0c;提升自身的硬实力&#xff08;不仅限NLP、知识图谱、计算机视觉等领域&#xff09;&#xff1a;汇总有意义的项目设计集合&#xff0c;助力新人快速实…

【OpenCV实现图像:OpenCV利用Python创作热力图】

文章目录 概要读取图像图像灰度化**像素化效果**小结 概要 热力图是一种强大的统计图表&#xff0c;通过对数据进行色彩映射&#xff0c;直观展示了数据分布的热度和密度。在绘制热力图时&#xff0c;关键在于指定颜色映射的规则&#xff0c;这决定了图中不同数值的呈现方式。…

vue中为什么data属性是一个函数而不是一个对象

面试官&#xff1a;为什么data属性是一个函数而不是一个对象&#xff1f; 一、实例和组件定义data的区别 vue实例的时候定义data属性既可以是一个对象&#xff0c;也可以是一个函数 const app new Vue({el:"#app",// 对象格式data:{foo:"foo"},// 函数格…

NX二次开发UF_CAM_opt_ask_subtypes 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CAM_opt_ask_subtypes Defined in: uf_cam.h int UF_CAM_opt_ask_subtypes(const char * opt_type_name, UF_CAM_opt_stype_cls_t subtype_class, int * count, const char * * *…

CBC算法实践Demo

效果图 全部代码 package encryption001;import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.util.Base64;public class EncryptionDemo {// 加密算法private static final String ALGORITHM "AES";// 加密模式和填充方式private s…

软件工程--软件建模--结构化方法通俗语言总结(暴肝超详解)(包含数据流图、数据字典、ER图、结构化设计和优化......)

目录 结构化分析 数据流图DFD 定义数据字典 实体关系图&#xff08;E-R图&#xff09; 结构化设计 变换映射 事务映射 优化结构设计 实例分析 详细设计&#xff08;过程设计&#xff09; 结构化方法是一种系统化开发软件的方法&#xff0c;该方法基于模块化的思想&am…

使用vue-cli搭建vue项目

1&#xff1a;安装vue-cli 命令&#xff1a;npm install -g vue/cli 2&#xff1a;查看安装的版本 vue --version 或者 vue -V 3&#xff1a;创建项目 vue create 项目名称 名称小写 4&#xff1a;vue2框架中根据自己的需求选择&#xff08;我选择…

springboot+bootstrap+java农业电商服务商城系统_30249

本农业电商服务系统是为了提高用户查阅信息的效率和管理人员管理信息的工作效率&#xff0c;可以快速存储大量数据&#xff0c;还有信息检索功能&#xff0c;这大大的满足了管理员、会员和商家这三者的需求。操作简单易懂&#xff0c;合理分析各个模块的功能&#xff0c;尽可能…

系列二、为什么要使用ThreadLocal?

一、为什么要使用ThreadLocal&#xff1f; 1.1、概述 并发场景下&#xff0c;会存在多个线程同时修改一个共享变量的场景&#xff0c;这就有可能会出现线程安全的问题。为了解决线程安全问题&#xff0c;可以用加锁的方式&#xff0c;比如对核心代码使用synchronized或者Lock进…

排序算法--插入排序

实现逻辑 ① 从第一个元素开始&#xff0c;该元素可以认为已经被排序 ② 取出下一个元素&#xff0c;在已经排序的元素序列中从后向前扫描 ③如果该元素&#xff08;已排序&#xff09;大于新元素&#xff0c;将该元素移到下一位置 ④ 重复步骤③&#xff0c;直到找到已排序的元…