Win64 驱动内核编程-5.内核里操作文件
内核里操作文件
RING0 操作文件和 RING3 操作文件在流程上没什么大的区别,也是“获得文件句柄->读/写/删/改->关闭文件句柄”的模式。当然了,只能用内核 API,不能用 WIN32API。在讲解具体的代码之前,先讲解一下文件系统的流程,让大家对整个文件系统有个大概的了解。
假设我们要读写一个文件,无论在 RING3 调用 ReadFile,还是在 RING0 调用 NtReadFile,它们最终会转换为 IRP,发送到 文件系统驱动(具体哪个驱动和分区类型相关,如果是 FAT32分区,则是 FASTFAT.SYS;如果是 NTFS 分区,则是 NTFS.SYS)的 IRP_MJ_READ 分发函数里。文件系统驱动经过一定处理后,就把 IRP 传给 磁盘类驱动(通常是 CLASSPNP.SYS,此驱动的源码在 WDK 里有)的 IRP_MJ_READ 分发函数处理。磁盘类驱动处理完毕后,又把 IRP 传给磁盘小端口驱动的 IRP_MJ_SCSI 分发函数处理。 磁盘小端口 驱动太多了,网上有人 用ATAPI.SYS 来指代 磁盘 小端口驱动,是极端错误的说法。ATAPI.SYS 是磁盘小端口驱动,但磁盘小端口驱动绝非只能是 ATAPI.SYS,常见的磁盘小端口驱动还有 LSI_SAS.SYS 等。如果安装了芯片组驱动,磁盘小端口驱动通常会被替换成主板厂商的驱动。比安装了英特尔 P67、HM77 的芯片组驱动后,磁盘小端口驱动就会变成 iaStroV.sys。在磁盘小端口驱动里,无论是读还是写,用的都是 IRP_MJ_SCSI 的分发函数。IRP 被磁盘小端口驱动处理完 之后 , 就要靠 依靠 HAL.DLL 进行口 端口 IO , 此时数据就真的从硬盘里读取了出来。接下来再按照相反的方向把数据返回到调用者。另外,在内核里,文件夹和文件没啥本质的区别。比如 ZwDeleteFile既可以删除文件,也可以删除文件夹。接下来举几个例子,让大家了解内核里读写、删除、重命名和枚举文件,以及获取文件信息。
1.文件拷贝
BOOLEAN ZwCopyFiles
(
IN PUNICODE_STRING ustrDestFile, // \??\c:\1.txt
IN PUNICODE_STRING ustrSrcFile // \??\c:\0.txt
)
{
DbgPrint("UnicodeString:%wZ\n", ustrDestFile);
DbgPrint("UnicodeString:%wZ\n", ustrSrcFile);
HANDLE hSrcFile = NULL, hDestFile = NULL;
PVOID buffer = NULL;
ULONG length = 0;
LARGE_INTEGER offset = { 0 };
IO_STATUS_BLOCK Io_Status_Block = { 0 };
OBJECT_ATTRIBUTES obj_attrib;
NTSTATUS status;
BOOLEAN bRet = FALSE;
do
{
// 打开源文件
InitializeObjectAttributes(&obj_attrib,
ustrSrcFile,
OBJ_CASE_INSENSITIVE |
OBJ_KERNEL_HANDLE,
NULL,
NULL);
status = ZwCreateFile(&hSrcFile,
GENERIC_READ,
&obj_attrib,
&Io_Status_Block,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE |
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
if (!NT_SUCCESS(status))
{
DbgPrint("[KrnlHW64]Yuan Wen Jian 2333333333333\n");
bRet = FALSE;
goto END;
}
// 打开目标文件
InitializeObjectAttributes(&obj_attrib,
ustrDestFile,
OBJ_CASE_INSENSITIVE |
OBJ_KERNEL_HANDLE,
NULL,
NULL);
status = ZwCreateFile(&hDestFile,
GENERIC_WRITE,
&obj_attrib,
&Io_Status_Block,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN_IF,
FILE_NON_DIRECTORY_FILE |
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
if (!NT_SUCCESS(status))
{
bRet = FALSE;
goto END;
}
// 为 buffer 分配 4KB 空间
buffer = ExAllocatePool(NonPagedPool, 1024 * 4);
if (buffer == NULL)
{
bRet = FALSE;
goto END;
}
// 复制文件
while (1)
{
length = 4 * 1024;
// 读取源文件
status = ZwReadFile(hSrcFile,
NULL,
NULL,
NULL,
&Io_Status_Block,
buffer,
length,
&offset,
NULL);
if (!NT_SUCCESS(status))
{
// 如果状态为 STATUS_END_OF_FILE,说明文件已经读取到末尾
if (status == STATUS_END_OF_FILE)
{
bRet = TRUE;
goto END;
}
}
// 获得实际读取的长度
length = (ULONG)Io_Status_Block.Information;
// 写入到目标文件
status = ZwWriteFile(hDestFile,
NULL,
NULL,
NULL,
&Io_Status_Block,
buffer,
length,
&offset,
NULL);
if (!NT_SUCCESS(status))
{
bRet = FALSE;
goto END;
}
// 移动文件指针
offset.QuadPart += length;
}
} while (0);
END:
if (hSrcFile)
{
ZwClose(hSrcFile);
}
if (hDestFile)
{
ZwClose(hDestFile);
}
if (buffer != NULL)
{
ExFreePool(buffer);
}
return bRet;
}
VOID Test() {
UNICODE_STRING UnicodeString1 = { 0 };
RtlInitUnicodeString(&UnicodeString1, L"\\??\\c:\\a.dat");
UNICODE_STRING UnicodeString2 = { 0 };
RtlInitUnicodeString(&UnicodeString2, L"\\??\\c:\\b.dat");
ZwCopyFiles(&UnicodeString1, &UnicodeString2);
}
2.删除文件/文件夹
void ZwDeleteFileFolder(WCHAR *wsFileName)
{
NTSTATUS st;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING UniFileName;
//把 WCHAR*转化为 UNICODE_STRING
RtlInitUnicodeString(&UniFileName, wsFileName);
//设置包 OBJECT 对象并使用 ZwDeleteFile 删除
InitializeObjectAttributes(&ObjectAttributes,
&UniFileName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
st = ZwDeleteFile(&ObjectAttributes);
}
3.文件/文件夹重命名
/**
typedef struct _FILE_RENAME_INFORMATION
{
BOOLEAN ReplaceIfExists;
HANDLE RootDirectory;
ULONG FileNameLength;
WCHAR FileName[1];
} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION;
*/
NTSTATUS
ZwRenameFile
(
IN PWSTR SrcFileName, // \??\x:\xxx\...\xxx.xxx
IN PWSTR DstFileName // \??\x:\xxx\...\xxx.xxx
)
{
#define RN_MAX_PATH 2048
#define SFLT_POOL_TAG 'fuck'
HANDLE FileHandle = NULL;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatus;
NTSTATUS Status;
PFILE_RENAME_INFORMATION RenameInfo = NULL;
UNICODE_STRING ObjectName;
//设置重命名的信息
RenameInfo = (PFILE_RENAME_INFORMATION)ExAllocatePoolWithTag(NonPagedPool,
sizeof(FILE_RENAME_INFORMATION) + RN_MAX_PATH * sizeof(WCHAR), SFLT_POOL_TAG);
if (RenameInfo == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(RenameInfo, sizeof(FILE_RENAME_INFORMATION) + RN_MAX_PATH *
sizeof(WCHAR));
RenameInfo->FileNameLength = wcslen(DstFileName) * sizeof(WCHAR);
wcscpy(RenameInfo->FileName, DstFileName);
RenameInfo->ReplaceIfExists = 0;
RenameInfo->RootDirectory = NULL;
//设置源文件信息并获得句柄
RtlInitUnicodeString(&ObjectName, SrcFileName);
InitializeObjectAttributes(&ObjectAttributes,
&ObjectName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = ZwCreateFile(&FileHandle,
SYNCHRONIZE | DELETE,
&ObjectAttributes,
&IoStatus,
NULL,
0,
FILE_SHARE_READ,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT |
FILE_NO_INTERMEDIATE_BUFFERING,
NULL,
0);
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(RenameInfo, SFLT_POOL_TAG);
return Status;
}
//最关键一步,利用 ZwSetInformationFile 来设置文件信息
Status = ZwSetInformationFile(FileHandle,
&IoStatus,
RenameInfo,
sizeof(FILE_RENAME_INFORMATION) +
RN_MAX_PATH * sizeof(WCHAR),
FileRenameInformation);
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(RenameInfo, SFLT_POOL_TAG);
ZwClose(FileHandle);
return Status;
}
ZwClose(FileHandle);
return Status;
}
4.获取文件大小
//这里传入的是文件句柄不是文件名,大家尝试把这里改成传入文件名
ULONG64 GetFileSize(HANDLE hfile)
{
IO_STATUS_BLOCK iostatus = { 0 };
NTSTATUS ntStatus = 0;
FILE_STANDARD_INFORMATION fsi = { 0 };
ntStatus = ZwQueryInformationFile(hfile,
&iostatus,
&fsi,
sizeof(FILE_STANDARD_INFORMATION),
FileStandardInformation);
if (!NT_SUCCESS(ntStatus))
return 0;
return fsi.EndOfFile.QuadPart;
}
5.枚举文件(RING3 的 FindFirstFile 和 FindNextFile 内部就是用 ZwQueryDirectoryFile 实现的,为了方便大家以后抄代码,我就把 ZwQueryDirectoryFile 封装成了 RING0 版的 FindFirstFile 和FindNextFile):
NTKERNELAPI NTSTATUS ZwQueryDirectoryFile //最关键的 API
(
HANDLE FileHandle,
HANDLE Event,
PIO_APC_ROUTINE ApcRoutine,
PVOID ApcContext,
PIO_STATUS_BLOCK IoStatusBlock,
PVOID FileInformation,
ULONG Length,
FILE_INFORMATION_CLASS FileInformationClass,
BOOLEAN ReturnSingleEntry,
PUNICODE_STRING FileName,
BOOLEAN RestartScan
);
//几个常量
#define INVALID_HANDLE_VALUE (HANDLE)-1
#define MAX_PATH2 4096
#define kmalloc(_s) ExAllocatePoolWithTag(NonPagedPool, _s, 'SYSQ')
#define kfree(_p) ExFreePool(_p)
/*
//枚举文件用到的结构体
typedef struct _FILE_BOTH_DIR_INFORMATION
{
ULONG NextEntryOffset;
ULONG FileIndex;
LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime;
LARGE_INTEGER ChangeTime;
LARGE_INTEGER EndOfFile;
LARGE_INTEGER AllocationSize;
ULONG FileAttributes;
ULONG FileNameLength;
ULONG EaSize;
CCHAR ShortNameLength;
WCHAR ShortName[12];
WCHAR FileName[1];
} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION;
*/
//山寨版 MyFindFirstFile
HANDLE MyFindFirstFile(LPSTR lpDirectory, PFILE_BOTH_DIR_INFORMATION pDir, ULONG
uLength)
{
char strFolder[MAX_PATH2] = { 0 };
STRING astrFolder;
UNICODE_STRING ustrFolder;
OBJECT_ATTRIBUTES oa;
IO_STATUS_BLOCK ioStatus;
NTSTATUS ntStatus;
HANDLE hFind = INVALID_HANDLE_VALUE;
memset(strFolder, 0, MAX_PATH2);
strcpy(strFolder, "\\??\\");
strcat(strFolder, lpDirectory);
RtlInitString(&astrFolder, strFolder);
if (RtlAnsiStringToUnicodeString(&ustrFolder, &astrFolder, TRUE) == 0)
{
InitializeObjectAttributes(&oa, &ustrFolder, OBJ_CASE_INSENSITIVE, NULL, NULL);
ntStatus = IoCreateFile(
&hFind,
FILE_LIST_DIRECTORY | SYNCHRONIZE | FILE_ANY_ACCESS,
&oa,
&ioStatus,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN, //FILE_OPEN
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT |
FILE_OPEN_FOR_BACKUP_INTENT,
NULL,
0,
CreateFileTypeNone,
NULL,
IO_NO_PARAMETER_CHECKING);
RtlFreeUnicodeString(&ustrFolder);
if (ntStatus == 0 && hFind != INVALID_HANDLE_VALUE)
{
ntStatus = ZwQueryDirectoryFile(
hFind, // File Handle
NULL, // Event
NULL, // Apc routine
NULL, // Apc context
&ioStatus, // IoStatusBlock
pDir, // FileInformation
uLength, // Length
FileBothDirectoryInformation, // FileInformationClass
TRUE, // ReturnSingleEntry
NULL, // FileName
FALSE //RestartScan
);
if (ntStatus != 0)
{
ZwClose(hFind);
hFind = INVALID_HANDLE_VALUE;
}
}
}
return hFind;
}
//山寨版 MyFindNextFile
BOOLEAN MyFindNextFile(HANDLE hFind, PFILE_BOTH_DIR_INFORMATION pDir, ULONG
uLength)
{
IO_STATUS_BLOCK ioStatus;
NTSTATUS ntStatus;
ntStatus = ZwQueryDirectoryFile(
hFind, // File Handle
NULL, // Event
NULL, // Apc routine
NULL, // Apc context
&ioStatus, // IoStatusBlock
pDir, // FileInformation
uLength, // Length
FileBothDirectoryInformation, // FileInformationClass
FALSE, // ReturnSingleEntry
NULL, // FileName
FALSE //RestartScan
);
if (ntStatus == 0)
return TRUE;
else
return FALSE;
}
//枚举文件夹内容的函数,输入路径,返回目录下的文件和文件夹数目
ULONG SearchDirectory(LPSTR lpPath)
{
ULONG muFileCount = 0;
HANDLE hFind = INVALID_HANDLE_VALUE;
PFILE_BOTH_DIR_INFORMATION pDir;
char *strBuffer = NULL, *lpTmp = NULL;
char strFileName[255 * 2];
ULONG uLength = MAX_PATH2 * 2 + sizeof(FILE_BOTH_DIR_INFORMATION);
strBuffer = (PCHAR)kmalloc(uLength);
pDir = (PFILE_BOTH_DIR_INFORMATION)strBuffer;
hFind = MyFindFirstFile(lpPath, pDir, uLength);
if (hFind != INVALID_HANDLE_VALUE)
{
kfree(strBuffer);
uLength = (MAX_PATH2 * 2 + sizeof(FILE_BOTH_DIR_INFORMATION)) * 0x2000;
strBuffer = (PCHAR)kmalloc(uLength);
pDir = (PFILE_BOTH_DIR_INFORMATION)strBuffer;
if (MyFindNextFile(hFind, pDir, uLength))
{
while (TRUE)
{
memset(strFileName, 0, 255 * 2);
memcpy(strFileName, pDir->FileName, pDir->FileNameLength);
if (strcmp(strFileName, "..") != 0 && strcmp(strFileName, ".") != 0)
{
if (pDir->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
DbgPrint("[目录]%S\n", strFileName);
}
else
{
DbgPrint("[文件]%S\n", strFileName);
}
muFileCount++;
}
if (pDir->NextEntryOffset == 0) break;
pDir = (PFILE_BOTH_DIR_INFORMATION)((char
*)pDir + pDir->NextEntryOffset);
}
kfree(strBuffer);
}
ZwClose(hFind);
}
return muFileCount;
}
6.创建文件夹(其实用 IoCreateFile 也能实现 ZwCreateFile 的功能,ZwCreateFile 不过是
IoCreateFile 的 stub 而已。下面利用 IoCreateFile 创建文件夹)
void ZwCreateFolder(char *FolderPath)
{
NTSTATUS st;
HANDLE FileHandle;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
UNICODE_STRING UniFileName;
WCHAR wsFileName[2048] = { 0 };
CharToWchar(FolderPath, wsFileName);
RtlInitUnicodeString(&UniFileName, wsFileName);
InitializeObjectAttributes(&ObjectAttributes,
&UniFileName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
st = IoCreateFile(&FileHandle,
GENERIC_READ,
&ObjectAttributes,
&IoStatusBlock,
0,
FILE_ATTRIBUTE_NORMAL,
0,
FILE_CREATE,
FILE_DIRECTORY_FILE,
NULL,
0,
0,
NULL,
IO_NO_PARAMETER_CHECKING);
if (NT_SUCCESS(st))
ZwClose(FileHandle);
}
最后总结一下几个常见的、和文件相关的 Zw 函数的功能:
Win64 驱动内核编程-5.内核里操作文件的更多相关文章
- Win64 驱动内核编程-7.内核里操作进程
在内核里操作进程 在内核里操作进程,相信是很多对 WINDOWS 内核编程感兴趣的朋友第一个学习的知识点.但在这里,我要让大家失望了,在内核里操作进程没什么特别的,就标准方法而言,还是调用那几个和进程 ...
- Win64 驱动内核编程-6.内核里操作注册表
内核里操作注册表 RING0 操作注册表和 RING3 的区别也不大,同样是"获得句柄->执行操作->关闭句柄"的模式,同样也只能使用内核 API 不能使用 WIN32 ...
- Win64 驱动内核编程-4.内核里操作字符串
内核里操作字符串 字符串本质上就是一段内存,之所以和内存使用分开讲,是因为内核里的字符串太有花 样了,细数下来竟然有 4 种字符串!这四种字符串,分别是:CHAR*.WCHAR*.ANSI_STRIN ...
- Win64 驱动内核编程-3.内核里使用内存
内核里使用内存 内存使用,无非就是申请.复制.设置.释放.在 C 语言里,它们对应的函数是:malloc.memcpy.memset.free:在内核编程里,他们分别对应 ExAllocatePool ...
- Win64 驱动内核编程-8.内核里的其他常用
内核里的其他常用 1.遍历链表.内核里有很多数据结构,但它们并不是孤立的,内核使用双向链表把它们像糖 葫芦一样给串了起来.所以遍历双向链表能获得很多重要的内核数据.举个简单的例子,驱 动对象 Driv ...
- destruct析构函数里操作文件出现的问题
这几天要给后台加一个记录操作日志的功能,可是项目已经开发完了不可能再去改以前的代码了,那有什么快捷的方法呢? 项目使用的ThinkPHP3.23 ,为了方便权限控制,后台控制器结构为:普通控制器 ex ...
- IO编程(2)-操作文件和目录
操作文件和目录 如果我们要操作文件.目录,可以在命令行下面输入操作系统提供的各种命令来完成.比如dir.cp等命令. 如果要在Python程序中执行这些目录和文件的操作怎么办?其实操作系统提供的命令只 ...
- bash编程 将一个目录里所有文件存为一个array 并分割为三等分——利用bash array切片
files=(a b c d e f g h i j k l m n o p)cnt="${#files[@]}"let cnt1="($cnt+2)/3"le ...
- WIN64内核编程-的基础知识
WIN64内核编程基础班(作者:胡文亮) https://www.dbgpro.com/x64driver 我们先从一份"简历"说起: 姓名:X86或80x86 性别:? 出生 ...
随机推荐
- golang——gRPC学习
1.获取gRPC 环境变量GOPATH的src目录下执行: git clone https://github.com/grpc/grpc-go.git google.golang.org/grpc g ...
- WAV16T VPX国产化千兆交换板
WAV16T是基于盛科CTC5160设计的国产化3U三层千兆VPX交换板,提供16路千兆电口,采用龙芯 2K1000处理器.支持常规的L2/L3协议,支持Telnet.SNMP.WEB,CLI等多 ...
- slickgrid ( nsunleo-slickgrid ) 4 解决区域选择和列选择冲突
slickgrid ( nsunleo-slickgrid ) 3 解决区域选择和列选择冲突 之前启用区域选择的时候,又启用了列选择(CheckboxSelectColumn),此时发现选择状态与区域 ...
- Python字典与集合
一 字典创建.访问.添加.删除.修改.内建函数.内建方法 创建,列表不能作为键,因为键不能变?字典也不能作为键 dict1 = {} dict2 = {'name':'qq','sex':'male' ...
- Android Studio配置colors.xml
•colors.xml <?xml version="1.0" encoding="utf-8"?> <resources> <! ...
- SpringBoot-11 扩展功能
SpringBoot-11 扩展功能 异步 同步就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成,这是一种可靠的任务序列.要么成功都成功,失败都失败,两个任务 ...
- JS实现鼠标点击爱心&绘制多边形&每日一言功能
本篇文章主要介绍我的个人博客 程序猿刘川枫 中页面使用的美化功能(基于JS实现): 1.鼠标点击出现不同颜色爱心特效 2.页面浮动多边形跟随鼠标移动 3.每日一言功能 1.鼠标点击出现爱心特效 经常在 ...
- [Fundamental of Power Electronics]-PART I-5.不连续导电模式-5.3 Boost变换器实例
5.3 Boost变换器实例 作为第二个示例,考虑图5.12的Boost变换器.让我们来确定不同模式的边界并且求解DCM下的电压变换比.此前在2.3节中分析了在CCM工作的Boost变换器的特性,并确 ...
- Html5新增了什么
h5新增了些什么 介绍 HTML5 是下一代的 HTML, 将成为 HTML.XHTML 以及 HTML DOM 的新标准. 起步 HTML5 是 W3C 与 WHATWG 合作的结果. 为 HTML ...
- 万字长文,带你彻底理解EF Core5的运行机制,让你成为团队中的EF Core专家
在EF Core 5中,有很多方式可以窥察工作流程中发生的事情,并与该信息进行交互.这些功能点包括日志记录,拦截,事件处理程序和一些超酷的最新出现的调试功能.EF团队甚至从Entity Framewo ...