日常驱动开发中文件读写操作十分频繁,经常写不少重复的轮子,这里把生产环境稳定的文件读写操作模版发出来,方便有需要的同学不必重复造轮子。注意,我这里驱动开发使用的是C++17或者更高,所以大部分都是c++代码或者Template模板代码,需要设置c++编译环境才能使用。(注意,这里的代码示例不能在MiniFilter中使用,会造成重入;MiniFilter中文件的读写会在另一个帖子中说明)
文件读操作模板
//读取文件内容(全部/指定偏移与大小),调用者需要释放返回的内存指针
template <typename T>
PVOID ReadFromFile(T* szFilePath, PLARGE_INTEGER uOffset, ULONG uReadLen)
{
WKD_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
UNREFERENCED_PARAMETER(uOffset);
PVOID pBufPtr = NULL;
BOOLEAN bMalloc = FALSE;
NTSTATUS Status = STATUS_SUCCESS;
HANDLE hFile = NULL;
ULONG uBufSize = 0;
OBJECT_ATTRIBUTES objAttrs = { 0 };
UNICODE_STRING uFilePath;
IO_STATUS_BLOCK ioStatus;
auto iModel = sizeof(T);
if (iModel == 1)
{
ANSI_STRING asiPath;
RtlInitAnsiString(&asiPath, (PCSZ)szFilePath);
RtlAnsiStringToUnicodeString(&uFilePath, &asiPath, TRUE);
bMalloc = TRUE;
}
else if (iModel == 2)
{
RtlInitUnicodeString(&uFilePath, (PCWSTR)szFilePath);
}
InitializeObjectAttributes(
&objAttrs, &uFilePath,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL
);
Status = ZwCreateFile(
&hFile, GENERIC_READ, &objAttrs,
&ioStatus, NULL, FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ, FILE_OPEN,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
NULL, 0);
if (NT_SUCCESS(Status))
{
FILE_STANDARD_INFORMATION fsi = { 0 };
Status = ZwQueryInformationFile(hFile,
&ioStatus,
&fsi,
sizeof(FILE_STANDARD_INFORMATION),
FileStandardInformation);
if (uReadLen == 0)//0:表示读取全部文件,>0 读取指定数量的字节
{
uBufSize = fsi.EndOfFile.LowPart;
}
else if (uReadLen > 0 && uReadLen <= fsi.EndOfFile.LowPart)
{
uBufSize = uReadLen;
}
DbgPrint("file size:[%d]\n", uBufSize);
uOffset->QuadPart = uBufSize;
pBufPtr = kMalloc(uBufSize + 2);
if (pBufPtr)
{
RtlZeroMemory(&ioStatus, sizeof(ioStatus));
Status = ZwReadFile(hFile, NULL, NULL, NULL, &ioStatus, pBufPtr, uBufSize, NULL, NULL);
if (!NT_SUCCESS(Status))
{
DbgPrint("ZwReadFile failed:[%08x]\n", Status);
kFree(pBufPtr);
pBufPtr = NULL;
}
}
ZwClose(hFile);
}
else
{
DbgPrint("ZwCreateFile failed:%s\n",szFilePath);
}
if (bMalloc)
{
RtlFreeUnicodeString(&uFilePath);
}
return pBufPtr;
}
调用示例:
//用到的工具函数
PVOID kMalloc(SIZE_T size)
{
PVOID pTmp = (PVOID)ExAllocatePoolWithTag(NonPagedPool, size, 'pmt9');
if (pTmp)
{
RtlZeroMemory(pTmp, size);
}
return pTmp;
}
VOID kFree(PVOID pMem)
{
if (pMem)
{
ExFreePoolWithTag(pMem, '0GAT');
pMem = 0;
}
}
LARGE_INTEGER offset = { 0 };
PVOID pExeBuf = ReadFromFile("\\??\\c:\\test.txt", &offset, 0);//全部读入
if (pExeBuf)
{
//业务逻辑
DbgPrint("read:%s\n", pExeBuf);
//释放内存
kFree(pExeBuf);
}
文件写操作模板
//写文件(覆盖写/追加写)
enum _FILE_WRITE_TYPE
{
_FILE_OVERWRITE, //覆盖写
_FILE_APPENDWRITE //追加写
};
template <typename T, typename C>
NTSTATUS WriteToFile(T* szFilePath, C szBuf, int nLen, _FILE_WRITE_TYPE mType)
{
WKD_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
BOOLEAN bMalloc = FALSE;
NTSTATUS Status = STATUS_SUCCESS;
HANDLE hFile = NULL;
LARGE_INTEGER offset;
OBJECT_ATTRIBUTES objAttrs = { 0 };
UNICODE_STRING uFilePath;
IO_STATUS_BLOCK ioStatus;
auto iModel = sizeof(T);
if (iModel == 1)
{
ANSI_STRING asiPath;
RtlInitAnsiString(&asiPath, (PCSZ)szFilePath);
RtlAnsiStringToUnicodeString(&uFilePath, &asiPath, TRUE);
bMalloc = TRUE;
}
else if (iModel == 2)
{
RtlInitUnicodeString(&uFilePath, (PCWSTR)szFilePath);
}
InitializeObjectAttributes(
&objAttrs, &uFilePath,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL
);
Status = ZwCreateFile(
&hFile, GENERIC_ALL, &objAttrs,
&ioStatus, NULL, FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ, (mType == _FILE_APPENDWRITE) ? FILE_OPEN_IF : FILE_SUPERSEDE,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
NULL, 0);
if (NT_SUCCESS(Status))
{
//设置指针偏移
switch (mType)
{
case _FILE_OVERWRITE:
{
offset.QuadPart = 0;
break;
}
case _FILE_APPENDWRITE:
{
offset.QuadPart = -1;
break;
}
}
Status = ZwWriteFile(
hFile, NULL, NULL, NULL,
&ioStatus, (PVOID)szBuf, nLen,
&offset, NULL);
ZwClose(hFile);
}
if (bMalloc)
{
RtlFreeUnicodeString(&uFilePath);
}
return Status;
}
调用示例:
char* fpath = "\\??\\c:\\test.lnk";//注意路径需要拼接"\\??\\"
char* pData = "this is 中文数据";
WriteToFile(fpath, pData, strlen(pData), _FILE_APPENDWRITE);