内核里操作文件

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.内核里操作文件相关推荐

  1. linux内核编程_内核线程kthread_run

    linux内核编程_内核线程kthread_run 1. 简述: 2. 使用示例: 3. 详述: 1. 简述: 头文件: include/linux/kthread.h 数据类型: struct ta ...

  2. Win64 驱动内核编程-6.内核里操作注册表

    内核里操作注册表 RING0 操作注册表和 RING3 的区别也不大,同样是"获得句柄->执行操作->关闭句柄"的模式,同样也只能使用内核 API 不能使用 WIN32 ...

  3. Win64 驱动内核编程-4.内核里操作字符串

    内核里操作字符串 字符串本质上就是一段内存,之所以和内存使用分开讲,是因为内核里的字符串太有花 样了,细数下来竟然有 4 种字符串!这四种字符串,分别是:CHAR*.WCHAR*.ANSI_STRIN ...

  4. Win64 驱动内核编程-3.内核里使用内存

    内核里使用内存 内存使用,无非就是申请.复制.设置.释放.在 C 语言里,它们对应的函数是:malloc.memcpy.memset.free:在内核编程里,他们分别对应 ExAllocatePool ...

  5. Win64 驱动内核编程-8.内核里的其他常用

    内核里的其他常用 1.遍历链表.内核里有很多数据结构,但它们并不是孤立的,内核使用双向链表把它们像糖 葫芦一样给串了起来.所以遍历双向链表能获得很多重要的内核数据.举个简单的例子,驱 动对象 Driv ...

  6. 国嵌linux内核编程,linux内核--那些年看国嵌视频学习

    1.linux系统构成 由用户空间和内核空间构成.其中用户空间由应用程序和C库:内核空间由系统调用接口.kernel.架构代码.硬件设备平台 为什么linux系统会被划分为用户空间和内核空间?处于安全 ...

  7. Windows内核编程(三)-内核驱动运行与调试

    内核驱动运行与调试 驱动的运行 驱动的运行通过服务来实现. 微软规定,驱动文件必须经过微软的数字签名后,才可以运行在64位系统上,如果把没有经过签名的驱动直接放在64位操作系统中运行,结果是驱动加载失 ...

  8. linux内核编程,实现内核之间的调用

    Linux 内核模块编写三个模块文件mainmod.c,lenmod.c,summod.c,在mainmod模块调用summod模块对数组进行求和运算,调用lenmod模块求数组中元素 1 先看自己的 ...

  9. Windows驱动开发学习笔记(二)—— 驱动调试内核编程基础

    Windows驱动开发学习笔记(二)-- 驱动调试&内核编程基础 基础知识 驱动调试 PDB(Program Debug Database) WinDbg 加载 PDB 实验:调试 .sys ...

最新文章

  1. access数据库拆分的用途_聊聊数据库设计一些经验 条条都是干货
  2. 米老鼠想吞并喜羊羊,不是赤裸裸的文化侵略是什么
  3. 让使用MSN就像访问网页一样容易!
  4. 扫盲了,RC,RTM,Beta等各个版本的意思
  5. 计算机工程与应用 格式,计算机工程与应用模板.doc
  6. Runner站立会议04
  7. 蘑菇车联打造一体化解决方案,致力产业协同发展
  8. 大工20秋《计算机原理》在线作业2答案,奥鹏大工15秋《计算机组成原理》在线作业2满分答案...
  9. 让华泰浮盈10亿美金的AssetMark-统包资产管理平台价值几何?
  10. 关闭Linux 内存地址随机化机制
  11. java 计算限行尾号(北京)
  12. 100个最常用的PHP函数(建议收藏)
  13. UVA167【The Sultan‘s Successors】(递归与回溯、8皇后问题)
  14. Data Binding 用户指南(Android)
  15. Spring Ioc原理解析
  16. WIN键 锁定、解锁
  17. PHPMyWind编辑器支持一键粘贴
  18. linux分区如何4k对齐,SSD使用之如何分区4K对齐
  19. 融合实体描述与路径信息的知识图谱表示学习模型
  20. 目标检测(3)—— 如何使用PyTorch加载COCO类型的数据集

热门文章

  1. Data_Structure03-栈和队列
  2. 使用jquery文件上传控件Uploadify来异步上传图片
  3. Noriben沙箱:分分钟搞定恶意软件
  4. MYSQL事务隔离级别详解附加实验
  5. 理解DataSet的数据缓存机制
  6. 敏捷个人架构图 V1.3
  7. 登录账号,玩猜数字游戏
  8. flink on yarn部分源码解析 (FLIP-6 new mode)
  9. 网络流24题之魔术球问题
  10. react-native 原生組件封裝與原生模塊和js的交互