码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 驱动开发:内核遍历文件或目录


    合集 - Windows 内核安全编程技术实践(97)
    1.驱动开发:运用VAD隐藏R3内存思路2021-07-162.驱动开发:应用DeviceIoContro模板精讲06-293.驱动开发:取进程模块的函数地址06-284.驱动开发:内核读写内存多级偏移06-275.驱动开发:内核物理内存寻址读写06-266.驱动开发:内核远程线程实现DLL注入06-257.驱动开发:摘除InlineHook内核钩子06-248.驱动开发:内核中进程与句柄互转06-239.驱动开发:内核注册表增删改查06-2110.驱动开发:基于事件同步的反向通信06-2011.驱动开发:文件微过滤驱动入门06-1912.驱动开发:内核RIP劫持实现DLL注入06-1613.驱动开发:内核解锁与强删文件06-1514.驱动开发:内核ShellCode线程注入06-1415.驱动开发:内核LoadLibrary实现DLL注入06-13
    16.驱动开发:内核遍历文件或目录06-12
    17.驱动开发:内核文件读写系列函数06-0918.驱动开发:内核封装WFP防火墙入门06-0819.驱动开发:PE导出函数与RVA转换06-0720.驱动开发:内核扫描SSDT挂钩状态06-0621.驱动开发:内核实现SSDT挂钩与摘钩06-0522.驱动开发:内核PE结构VA与FOA转换06-0223.驱动开发:内核解析PE结构节表06-0124.驱动开发:内核解析PE结构导出表05-3125.驱动开发:内核读写内存浮点数05-3026.驱动开发:内核解析内存四级页表05-2927.驱动开发:内核实现进程汇编与反汇编05-2328.驱动开发:通过应用堆实现多次通信05-1929.驱动开发:内核远程堆分配与销毁05-1530.驱动开发:通过MDL映射实现多次通信04-2931.驱动开发:内核使用IO/DPC定时器04-0432.驱动开发:探索DRIVER_OBJECT驱动对象04-0333.驱动开发:配置Visual Studio驱动开发环境03-1334.驱动开发:内核封装TDI网络通信接口2022-11-0335.驱动开发:内核封装WSK网络通信接口2022-11-0336.驱动开发:内核层InlineHook挂钩函数2022-10-3137.驱动开发:内核LDE64引擎计算汇编长度2022-10-3138.驱动开发:内核强制结束进程运行2022-10-2939.驱动开发:内核监控FileObject文件回调2022-10-2840.驱动开发:内核监控Register注册表回调2022-10-2741.驱动开发:内核运用LoadImage屏蔽驱动2022-10-2642.驱动开发:内核监视LoadImage映像回调2022-10-2543.驱动开发:内核无痕隐藏自身分析2022-10-2444.驱动开发:内核注册并监控对象回调2022-10-2445.驱动开发:内核监控进程与线程回调2022-10-2346.驱动开发:内核测试模式过DSE签名2022-10-2247.驱动开发:内核枚举进程与线程ObCall回调2022-10-2248.驱动开发:内核枚举Registry注册表回调2022-10-2149.驱动开发:内核枚举LoadImage映像回调2022-10-2050.驱动开发:内核枚举ShadowSSDT基址2022-10-2051.驱动开发:Win10枚举完整SSDT地址表2022-10-1952.驱动开发:Win10内核枚举SSDT表基址2022-10-1953.驱动开发:内核特征码扫描PE代码段2022-10-1854.驱动开发:内核枚举Minifilter微过滤驱动2022-10-1855.驱动开发:内核特征码搜索函数封装2022-10-1756.驱动开发:内核枚举驱动内线程(答疑篇)2022-10-1657.驱动开发:内核枚举PspCidTable句柄表2022-10-1658.驱动开发:内核枚举DpcTimer定时器2022-10-1659.驱动开发:如何枚举所有SSDT表地址2022-10-1460.驱动开发:内核枚举IoTimer定时器2022-10-1461.驱动开发:内核遍历进程VAD结构体2022-10-1362.驱动开发:内核中实现Dump进程转储2022-10-1163.驱动开发:内核R3与R0内存映射拷贝2022-10-1164.驱动开发:内核通过PEB得到进程参数2022-10-1065.驱动开发:内核取应用层模块基地址2022-10-0966.驱动开发:内核取ntoskrnl模块基地址2022-10-0967.驱动开发:判断自身是否加载成功2022-10-0868.驱动开发:应用DeviceIoContro开发模板2022-10-0369.驱动开发:通过Async反向与内核通信2022-10-0370.驱动开发:通过PIPE管道与内核层通信2022-10-0171.驱动通信:通过PIPE管道与内核层通信2022-10-0172.驱动开发:通过ReadFile与内核层通信2022-09-3073.驱动开发:内核字符串拷贝与比较2022-09-2974.驱动开发:内核字符串转换方法2022-09-2875.驱动开发:内核中的自旋锁结构2022-09-2876.驱动开发:内核CR3切换读写内存2022-09-2577.驱动开发:内核中的链表与结构体2022-09-2378.驱动开发:摘链DKOM进程隐藏2022-08-3179.驱动开发:WinDBG 枚举SSDT以及SSSDT地址2022-04-2880.驱动开发:实现驱动加载卸载工具2021-07-1681.驱动开发:断链隐藏驱动程序自身2021-07-1682.驱动开发:通过内存拷贝读写内存2021-07-1283.驱动开发:内核MDL读写进程内存2021-07-0584.驱动开发:内核监控进程与线程创建2021-05-0685.驱动开发:监控进程与线程对象操作2020-06-1486.驱动开发:WinDBG 常用调试命令总结2020-06-1087.驱动开发:通过SystemBuf与内核层通信2020-04-1388.驱动开发:对象回调监控文件访问2019-11-0189.驱动开发:内核中枚举进线程与模块2019-10-2190.驱动开发:DKOM 实现进程隐藏2019-10-1191.驱动开发:内核读取SSDT表基址2019-10-0992.驱动开发:驱动与应用的简单通信2019-09-2393.驱动开发:恢复SSDT内核钩子2019-09-2194.驱动开发:挂接SSDT内核钩子2019-09-2095.驱动开发:WinDBG 配置内核双机调试2019-09-1996.VS2013+WDK8.1 驱动开发环境配置2019-09-1897.驱动开发:派遣函数与设备对象2019-09-14
    收起

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

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

    在概述中提到过,目录遍历的核心是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代表是目录,反之则是文件,实现目录文件遍历完整代码如下所示;

    // 署名权
    // right to sign one's name on a piece of work
    // PowerBy: LyShark
    // Email: me@lyshark.com
    #include 
    #include 
    
    // 遍历文件夹和文件
    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.com \n");
    
    	// 遍历文件夹和文件
    	UNICODE_STRING ustrQueryFile;
    	RtlInitUnicodeString(&ustrQueryFile, L"\\??\\C:\\Windows");
    	MyQueryFileAndFileFolder(ustrQueryFile);
    
    	DbgPrint("驱动加载成功 \n");
    	Driver->DriverUnload = UnDriver;
    	return STATUS_SUCCESS;
    }
    

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

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

  • 相关阅读:
    2-35 基于matlab的四足液压机器人设计程序
    ⑥ 在vue中引入路由
    Java学习笔记(三十一)
    Redis的下载与安装Windows和Linux版
    python 随手写的堆排序
    【JavaEE初阶】前端篇:CSS(下篇)
    【RepVGG网络】
    【项目小tips】登录状态存储
    B. Box Fitting-CodeCraft-21 and Codeforces Round #711 (Div. 2)
    李廉洋:6.3黄金原油美盘尾盘分析及最新动向分析;
  • 原文地址:https://www.cnblogs.com/LyShark/p/17155325.html
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号