在應用層下的檔案操作只需要呼叫微軟應用層下的API函式及C庫標準函式即可,而如果在內核中讀寫檔案則應用層的API顯然是無法被使用的,內核層需要使用內核專有API,某些應用層下的API只需要增加Zw開頭即可在內核中使用,例如本章要講解的檔案與目錄操作相關函式,多數ARK反內核工具都具有對檔案的管理功能,實作對檔案或目錄的基本操作功能也是非常有必要的,
首先無論在內核態還是在用戶態,我們呼叫的檔案操作函式其最終都會轉換為一個IRP請求,并發送到檔案系統驅動上的IRP_MJ_READ派遣函式里面,這個讀寫流程大體上可分為如下四步;
- 對于FAT32磁區會默認分發到
FASTFAT.SYS,而相對于NTFS磁區則會分發到NTFS.SYS驅動上, - 檔案系統驅動經過處理后,就把IRP傳給磁盤類驅動的
IRP_MJ_READ分發函式處理,當磁盤類驅動處理完畢后,又把IRP傳給磁盤小埠驅動, - 在磁盤小埠驅動里,無論是讀還是寫,用的都是
IRP_MJ_SCSI這個分發函式, - IRP被磁盤小埠驅動處理完之后,就要依靠
HAL.DLL進行埠IO,此時資料就真的從硬碟里讀取了出來,
創建檔案或目錄: 實作創建檔案或目錄,創建檔案或目錄都可呼叫ZwCreateFile()這個內核函式來實作,唯一不同的區別在于當用戶傳入引數中包含有FILE_SYNCHRONOUS_IO_NONALERT屬性時則會默認創建檔案,而如果包含有FILE_DIRECTORY_FILE屬性則默認為創建目錄,該函式的微軟定義以及備注資訊如下所示;
NTSYSAPI NTSTATUS ZwCreateFile(
[out] PHANDLE FileHandle, // 指向HANDLE變數的指標,該變數接收檔案的句柄,
[in] ACCESS_MASK DesiredAccess, // 指定一個ACCESS_MASK值,該值確定對物件的請求訪問權限,
[in] POBJECT_ATTRIBUTES ObjectAttributes, // 指向OBJECT_ATTRIBUTES結構的指標,該結構指定物件名稱和其他屬性,
[out] PIO_STATUS_BLOCK IoStatusBlock, // 指向IO_STATUS_BLOCK結構的指標,該結構接收最終完成狀態和有關所請求操作的其他資訊,
[in, optional] PLARGE_INTEGER AllocationSize, // 指向LARGE_INTEGER的指標,其中包含創建或覆寫的檔案的初始分配大小(以位元組為單位),
[in] ULONG FileAttributes, // 指定一個或多個FILE_ATTRIBUTE_XXX標志,這些標志表示在創建或覆寫檔案時要設定的檔案屬性,
[in] ULONG ShareAccess, // 共享訪問的型別,指定為零或以下標志的任意組合,
[in] ULONG CreateDisposition, // 指定在檔案存在或不存在時要執行的操作,
[in] ULONG CreateOptions, // 指定要在驅動程式創建或打開檔案時應用的選項,
[in, optional] PVOID EaBuffer, // 對于設備和中間驅動程式,此引數必須是NULL指標,
[in] ULONG EaLength // 對于設備和中間驅動程式,此引數必須為零,
);
引數DesiredAccess用于指明物件訪問權限的,常用的權限有FILE_READ_DATA讀取檔案,FILE_WRITE_DATA寫入檔案,FILE_APPEND_DATA追加檔案,FILE_READ_ATTRIBUTES讀取檔案屬性,以及FILE_WRITE_ATTRIBUTES寫入檔案屬性,
引數ObjectAttributes指向了一個OBJECT_ATTRIBUTES指標,通常會通過InitializeObjectAttributes()宏對其進行初始化,當一個例程打開物件時由此結構體指定目標物件的屬性,
引數ShareAccess用于指定訪問屬性,通常屬性有FILE_SHARE_READ讀取,FILE_SHARE_WRITE寫入,FILE_SHARE_DELETE洗掉,
引數CreateDisposition用于指定在檔案存在或不存在時要執行的操作,一般而言我們會指定為FILE_OPEN_IF打開檔案,或FILE_OVERWRITE_IF打開檔案并覆寫,FILE_SUPERSEDE替換檔案,
引數CreateOptions用于指定創建檔案或目錄,一般FILE_SYNCHRONOUS_IO_NONALERT代表創建檔案,引數FILE_DIRECTORY_FILE代表創建目錄,
相對于創建檔案而言洗掉檔案或目錄只需要呼叫ZwDeleteFile()系列函式即可,此類函式只需要傳遞一個OBJECT_ATTRIBUTES引數即可,其微軟定義如下所示;
NTSYSAPI NTSTATUS ZwDeleteFile(
[in] POBJECT_ATTRIBUTES ObjectAttributes
);
接下來我們就封裝三個函式MyCreateFile()用于創建檔案,MyCreateFileFolder()用于創建目錄,MyDeleteFileOrFileFolder()用于洗掉空目錄,
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include <ntifs.h>
#include <ntstrsafe.h>
// 創建檔案
BOOLEAN MyCreateFile(UNICODE_STRING ustrFilePath)
{
HANDLE hFile = NULL;
OBJECT_ATTRIBUTES objectAttributes = { 0 };
IO_STATUS_BLOCK iosb = { 0 };
NTSTATUS status = STATUS_SUCCESS;
// 初始化物件屬性結構體 FILE_SYNCHRONOUS_IO_NONALERT
InitializeObjectAttributes(&objectAttributes, &ustrFilePath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
// 創建檔案
status = ZwCreateFile(&hFile, GENERIC_READ, &objectAttributes, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
if (!NT_SUCCESS(status))
{
return FALSE;
}
// 關閉句柄
ZwClose(hFile);
return TRUE;
}
// 創建目錄
BOOLEAN MyCreateFileFolder(UNICODE_STRING ustrFileFolderPath)
{
HANDLE hFile = NULL;
OBJECT_ATTRIBUTES objectAttributes = { 0 };
IO_STATUS_BLOCK iosb = { 0 };
NTSTATUS status = STATUS_SUCCESS;
// 初始化物件屬性結構體
InitializeObjectAttributes(&objectAttributes, &ustrFileFolderPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
// 創建目錄 FILE_DIRECTORY_FILE
status = ZwCreateFile(&hFile, GENERIC_READ, &objectAttributes, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_CREATE, FILE_DIRECTORY_FILE, NULL, 0);
if (!NT_SUCCESS(status))
{
return FALSE;
}
// 關閉句柄
ZwClose(hFile);
return TRUE;
}
// 洗掉檔案或是空目錄
BOOLEAN MyDeleteFileOrFileFolder(UNICODE_STRING ustrFileName)
{
NTSTATUS status = STATUS_SUCCESS;
OBJECT_ATTRIBUTES objectAttributes = { 0 };
// 初始化屬性
InitializeObjectAttributes(&objectAttributes, &ustrFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
// 執行洗掉操作
status = ZwDeleteFile(&objectAttributes);
if (!NT_SUCCESS(status))
{
return FALSE;
}
return TRUE;
}
VOID UnDriver(PDRIVER_OBJECT driver)
{
BOOLEAN ref = FALSE;
// 洗掉檔案
UNICODE_STRING ustrDeleteFile;
RtlInitUnicodeString(&ustrDeleteFile, L"\\??\\C:\\LySharkFolder\\lyshark.txt");
ref = MyDeleteFileOrFileFolder(ustrDeleteFile);
if (ref != FALSE)
{
DbgPrint("[LyShark] 洗掉檔案成功 \n");
}
else
{
DbgPrint("[LyShark] 洗掉檔案失敗 \n");
}
// 洗掉空目錄
UNICODE_STRING ustrDeleteFilder;
RtlInitUnicodeString(&ustrDeleteFilder, L"\\??\\C:\\LySharkFolder");
ref = MyDeleteFileOrFileFolder(ustrDeleteFilder);
if (ref != FALSE)
{
DbgPrint("[LyShark] 洗掉空目錄成功 \n");
}
else
{
DbgPrint("[LyShark] 洗掉空目錄失敗 \n");
}
DbgPrint("驅動卸載 \n");
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("hello lyshark.com \n");
BOOLEAN ref = FALSE;
// 創建目錄
UNICODE_STRING ustrDirectory;
RtlInitUnicodeString(&ustrDirectory, L"\\??\\C:\\LySharkFolder");
ref = MyCreateFileFolder(ustrDirectory);
if (ref != FALSE)
{
DbgPrint("[LyShark] 創建目錄成功 \n");
}
else
{
DbgPrint("[LyShark] 創建檔案失敗 \n");
}
// 創建檔案
UNICODE_STRING ustrCreateFile;
RtlInitUnicodeString(&ustrCreateFile, L"\\??\\C:\\LySharkFolder\\lyshark.txt");
ref = MyCreateFile(ustrCreateFile);
if (ref != FALSE)
{
DbgPrint("[LyShark] 創建檔案成功 \n");
}
else
{
DbgPrint("[LyShark] 創建檔案失敗 \n");
}
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
運行如上代碼,分別創建LySharkFolder目錄,并在其中創建lyshark.txt最終再將其洗掉,輸出效果如下;

重命名檔案或目錄: 在內核中重命名檔案或目錄核心功能的實作依賴于ZwSetInformationFile()這個內核函式,該函式可用于更改有關檔案物件的各種資訊,其微軟官方定義如下;
NTSYSAPI NTSTATUS ZwSetInformationFile(
[in] HANDLE FileHandle, // 檔案句柄
[out] PIO_STATUS_BLOCK IoStatusBlock, // 指向 IO_STATUS_BLOCK 結構的指標
[in] PVOID FileInformation, // 指向緩沖區的指標,該緩沖區包含要為檔案設定的資訊,
[in] ULONG Length, // 緩沖區的大小(以位元組為單位)
[in] FILE_INFORMATION_CLASS FileInformationClass // 為檔案設定的型別
);
這其中最重要的引數就是FileInformationClass根據該引數的不同則對檔案的操作方式也就不同,如果需要重命名檔案則此處應使用FileRenameInformation而如果需要修改檔案的當前資訊則應使用FilePositionInformation創建鏈接檔案則使用FileLinkInformation即可,以重命名為例,首先我們需要定義一個FILE_RENAME_INFORMATION結構并按照要求填充,最后直接使用ZwSetInformationFile()并傳入相關資訊后即可完成修改,其完整代碼流程如下;
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include <ntifs.h>
#include <ntstrsafe.h>
// 重命名檔案或檔案夾
BOOLEAN MyRename(UNICODE_STRING ustrSrcFileName, UNICODE_STRING ustrDestFileName)
{
HANDLE hFile = NULL;
OBJECT_ATTRIBUTES objectAttributes = { 0 };
IO_STATUS_BLOCK iosb = { 0 };
NTSTATUS status = STATUS_SUCCESS;
PFILE_RENAME_INFORMATION pRenameInfo = NULL;
ULONG ulLength = (1024 + sizeof(FILE_RENAME_INFORMATION));
// 為PFILE_RENAME_INFORMATION結構申請記憶體
pRenameInfo = (PFILE_RENAME_INFORMATION)ExAllocatePool(NonPagedPool, ulLength);
if (NULL == pRenameInfo)
{
return FALSE;
}
// 設定重命名資訊
RtlZeroMemory(pRenameInfo, ulLength);
// 設定檔案名長度以及檔案名
pRenameInfo->FileNameLength = ustrDestFileName.Length;
wcscpy(pRenameInfo->FileName, ustrDestFileName.Buffer);
pRenameInfo->ReplaceIfExists = 0;
pRenameInfo->RootDirectory = NULL;
// 初始化結構
InitializeObjectAttributes(&objectAttributes, &ustrSrcFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
// 打開檔案
status = ZwCreateFile(&hFile, SYNCHRONIZE | DELETE, &objectAttributes, &iosb, NULL, 0, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NO_INTERMEDIATE_BUFFERING, NULL, 0);
if (!NT_SUCCESS(status))
{
ExFreePool(pRenameInfo);
return FALSE;
}
// 利用ZwSetInformationFile來設定檔案資訊
status = ZwSetInformationFile(hFile, &iosb, pRenameInfo, ulLength, FileRenameInformation);
if (!NT_SUCCESS(status))
{
ZwClose(hFile);
ExFreePool(pRenameInfo);
return FALSE;
}
// 釋放記憶體,關閉句柄
ExFreePool(pRenameInfo);
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 ustrOldFile, ustrNewFile;
RtlInitUnicodeString(&ustrOldFile, L"\\??\\C:\\MyCreateFolder\\lyshark.txt");
RtlInitUnicodeString(&ustrNewFile, L"\\??\\C:\\MyCreateFolder\\hello_lyshark.txt");
BOOLEAN ref = MyRename(ustrOldFile, ustrNewFile);
if (ref == TRUE)
{
DbgPrint("已完成改名 \n");
}
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
運行后將會把C:\\MyCreateFolder\\lyshark.txt目錄下的檔案改名為hello_lyshark.txt,前提是該目錄與該檔案必須存在;

那么如果你需要將檔案設定為只讀模式或修改檔案的創建日期,那么你就需要看一下微軟的定義FILE_BASIC_INFORMATION結構,依次填充此結構體并呼叫ZwSetInformationFile()即可實作修改,該結構的定義如下所示;
typedef struct _FILE_BASIC_INFORMATION {
LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime;
LARGE_INTEGER ChangeTime;
ULONG FileAttributes;
} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;
當然如果你要修改日期你還需要自行填充LARGE_INTEGER結構,該結構的微軟定義如下所示,分為高位和低位依次填充即可;
#if defined(MIDL_PASS)
typedef struct _LARGE_INTEGER {
#else // MIDL_PASS
typedef union _LARGE_INTEGER {
struct {
ULONG LowPart;
LONG HighPart;
} DUMMYSTRUCTNAME;
struct {
ULONG LowPart;
LONG HighPart;
} u;
#endif //MIDL_PASS
LONGLONG QuadPart;
} LARGE_INTEGER;
我們就以修改檔案屬性為只讀模式為例,其核心代碼可以被描述為如下樣子,相比于改名而言其唯一的變化就是更換了PFILE_BASIC_INFORMATION結構體,其他的基本一致;
HANDLE hFile = NULL;
OBJECT_ATTRIBUTES objectAttributes = { 0 };
IO_STATUS_BLOCK iosb = { 0 };
NTSTATUS status = STATUS_SUCCESS;
PFILE_BASIC_INFORMATION pReplaceInfo = NULL;
ULONG ulLength = (1024 + sizeof(FILE_BASIC_INFORMATION));
// 為FILE_POSITION_INFORMATION結構申請記憶體
pReplaceInfo = (PFILE_BASIC_INFORMATION)ExAllocatePool(NonPagedPool, ulLength);
if (NULL == pReplaceInfo)
{
return FALSE;
}
RtlZeroMemory(pReplaceInfo, ulLength);
// 設定檔案基礎資訊,將檔案設定為只讀模式
pReplaceInfo->FileAttributes |= FILE_ATTRIBUTE_READONLY;
讀取檔案大小: 讀取特定檔案的所占空間,核心原理是呼叫了ZwQueryInformationFile()這個內核函式,該函式可以回傳有關檔案物件的各種資訊,引數傳遞上與ZwSetInformationFile()很相似,其FileInformationClass都需要傳入一個檔案型別結構,該函式的完整定義如下;
NTSYSAPI NTSTATUS ZwQueryInformationFile(
[in] HANDLE FileHandle, // 檔案句柄,
[out] PIO_STATUS_BLOCK IoStatusBlock, // 指向接收最終完成狀態和操作相關資訊的 IO_STATUS_BLOCK 結構的指標,
[out] PVOID FileInformation, // 指向呼叫方分配的緩沖區的指標,例程將請求的有關檔案物件的資訊寫入其中,
[in] ULONG Length, // 長度,
[in] FILE_INFORMATION_CLASS FileInformationClass // 指定要在 FileInformation 指向的緩沖區中回傳的有關檔案的資訊型別,
);
本例中我們需要讀入檔案的所占位元組數,那么FileInformation欄位就需要傳入FileStandardInformation來獲取檔案的基本資訊,獲取到的資訊會被存盤到FILE_STANDARD_INFORMATION結構內,用戶只需要決議該結構體fsi.EndOfFile.QuadPart即可得到檔案長度,其完整代碼如下所示;
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include <ntifs.h>
#include <ntstrsafe.h>
// 獲取檔案大小
ULONG64 MyGetFileSize(UNICODE_STRING ustrFileName)
{
HANDLE hFile = NULL;
OBJECT_ATTRIBUTES objectAttributes = { 0 };
IO_STATUS_BLOCK iosb = { 0 };
NTSTATUS status = STATUS_SUCCESS;
FILE_STANDARD_INFORMATION fsi = { 0 };
// 初始化結構
InitializeObjectAttributes(&objectAttributes, &ustrFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
// 打開檔案
status = ZwCreateFile(&hFile, GENERIC_READ, &objectAttributes, &iosb, NULL, 0, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
if (!NT_SUCCESS(status))
{
return 0;
}
// 獲取檔案大小資訊
status = ZwQueryInformationFile(hFile, &iosb, &fsi, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation);
if (!NT_SUCCESS(status))
{
ZwClose(hFile);
return 0;
}
return fsi.EndOfFile.QuadPart;
}
VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint("驅動卸載 \n");
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("hello lyshark.com \n");
// 獲取檔案大小
UNICODE_STRING ustrFileSize;
RtlInitUnicodeString(&ustrFileSize, L"\\??\\C:\\lyshark.exe");
ULONG64 ullFileSize = MyGetFileSize(ustrFileSize);
DbgPrint("獲取檔案大小: %I64d Bytes \n", ullFileSize);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
編譯并運行如上程式,即可讀取到C盤下的lyshark.exe程式的大小位元組數,如下圖所示;

內核檔案讀寫: 內核讀取檔案可以使用ZwReadFile(),內核寫入檔案則可使用ZwWriteFile(),這兩個函式的引數傳遞基本上一致,如下是讀寫兩個函式的對比引數,
NTSYSAPI NTSTATUS ZwReadFile(
[in] HANDLE FileHandle, // 檔案物件的句柄,
[in, optional] HANDLE Event, // (可選)事件物件的句柄,在讀取操作完成后設定為信號狀態,
[in, optional] PIO_APC_ROUTINE ApcRoutine, // 此引數為保留引數,
[in, optional] PVOID ApcContext, // 此引數為保留引數,
[out] PIO_STATUS_BLOCK IoStatusBlock, // 接收實際從檔案讀取的位元組數,
[out] PVOID Buffer, // 指向呼叫方分配的緩沖區的指標,該緩沖區接收從檔案讀取的資料,
[in] ULONG Length, // 緩沖區指向的緩沖區的大小(以位元組為單位),
[in, optional] PLARGE_INTEGER ByteOffset, // 指定將開始讀取操作的檔案中的起始位元組偏移量,
[in, optional] PULONG Key
);
NTSYSAPI NTSTATUS ZwWriteFile(
[in] HANDLE FileHandle,
[in, optional] HANDLE Event,
[in, optional] PIO_APC_ROUTINE ApcRoutine,
[in, optional] PVOID ApcContext,
[out] PIO_STATUS_BLOCK IoStatusBlock,
[in] PVOID Buffer,
[in] ULONG Length,
[in, optional] PLARGE_INTEGER ByteOffset,
[in, optional] PULONG Key
);
讀取檔案的代碼如下所示,分配非分頁pBuffer記憶體,然后呼叫MyReadFile()函式,將資料讀入到pBuffer并輸出,完整代碼如下所示;
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include <ntifs.h>
#include <ntstrsafe.h>
// 讀取檔案資料
BOOLEAN MyReadFile(UNICODE_STRING ustrFileName, LARGE_INTEGER liOffset, PUCHAR pReadData, PULONG pulReadDataSize)
{
HANDLE hFile = NULL;
IO_STATUS_BLOCK iosb = { 0 };
OBJECT_ATTRIBUTES objectAttributes = { 0 };
NTSTATUS status = STATUS_SUCCESS;
// 初始化結構
InitializeObjectAttributes(&objectAttributes, &ustrFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
// 打開檔案
status = ZwCreateFile(&hFile, GENERIC_READ, &objectAttributes, &iosb, NULL,FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN,FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
if (!NT_SUCCESS(status))
{
return FALSE;
}
// 初始化
RtlZeroMemory(&iosb, sizeof(iosb));
// 讀入檔案
status = ZwReadFile(hFile, NULL, NULL, NULL, &iosb, pReadData, *pulReadDataSize, &liOffset, NULL);
if (!NT_SUCCESS(status))
{
*pulReadDataSize = iosb.Information;
ZwClose(hFile);
return FALSE;
}
// 獲取實際讀取的資料
*pulReadDataSize = iosb.Information;
// 關閉句柄
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 ustrScrFile;
ULONG ulBufferSize = 40960;
LARGE_INTEGER liOffset = { 0 };
// 初始化需要讀取的檔案名
RtlInitUnicodeString(&ustrScrFile, L"\\??\\C:\\lyshark.exe");
// 分配非分頁記憶體
PUCHAR pBuffer = ExAllocatePool(NonPagedPool, ulBufferSize);
// 讀取檔案
MyReadFile(ustrScrFile, liOffset, pBuffer, &ulBufferSize);
// 輸出檔案前16個位元組
for (size_t i = 0; i < 16; i++)
{
DbgPrint("%02X \n", pBuffer[i]);
}
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
編譯并運行這段代碼,并回圈輸出lyshark.exe檔案的頭16個位元組的資料,效果圖如下所示;

檔案寫入MyWriteFile()與讀取類似,如下通過運用檔案讀寫實作了檔案拷貝功能,實作完整代碼如下所示;
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include <ntifs.h>
#include <ntstrsafe.h>
// 讀取檔案資料
BOOLEAN MyReadFile(UNICODE_STRING ustrFileName, LARGE_INTEGER liOffset, PUCHAR pReadData, PULONG pulReadDataSize)
{
HANDLE hFile = NULL;
IO_STATUS_BLOCK iosb = { 0 };
OBJECT_ATTRIBUTES objectAttributes = { 0 };
NTSTATUS status = STATUS_SUCCESS;
// 初始化結構
InitializeObjectAttributes(&objectAttributes, &ustrFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
// 打開檔案
status = ZwCreateFile(&hFile, GENERIC_READ, &objectAttributes, &iosb, NULL,FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN,FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
if (!NT_SUCCESS(status))
{
return FALSE;
}
// 初始化
RtlZeroMemory(&iosb, sizeof(iosb));
// 讀入檔案
status = ZwReadFile(hFile, NULL, NULL, NULL, &iosb, pReadData, *pulReadDataSize, &liOffset, NULL);
if (!NT_SUCCESS(status))
{
*pulReadDataSize = iosb.Information;
ZwClose(hFile);
return FALSE;
}
// 獲取實際讀取的資料
*pulReadDataSize = iosb.Information;
// 關閉句柄
ZwClose(hFile);
return TRUE;
}
// 向檔案寫入資料
BOOLEAN MyWriteFile(UNICODE_STRING ustrFileName, LARGE_INTEGER liOffset, PUCHAR pWriteData, PULONG pulWriteDataSize)
{
HANDLE hFile = NULL;
IO_STATUS_BLOCK iosb = { 0 };
OBJECT_ATTRIBUTES objectAttributes = { 0 };
NTSTATUS status = STATUS_SUCCESS;
// 初始化結構
InitializeObjectAttributes(&objectAttributes, &ustrFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
// 打開檔案
status = ZwCreateFile(&hFile, GENERIC_WRITE, &objectAttributes, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_IF, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
if (!NT_SUCCESS(status))
{
return FALSE;
}
// 初始化
RtlZeroMemory(&iosb, sizeof(iosb));
// 寫出檔案
status = ZwWriteFile(hFile, NULL, NULL, NULL, &iosb, pWriteData, *pulWriteDataSize, &liOffset, NULL);
if (!NT_SUCCESS(status))
{
*pulWriteDataSize = iosb.Information;
ZwClose(hFile);
return FALSE;
}
// 獲取實際寫入的資料
*pulWriteDataSize = iosb.Information;
// 關閉句柄
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 ustrScrFile, ustrDestFile;
RtlInitUnicodeString(&ustrScrFile, L"\\??\\C:\\lyshark.exe");
RtlInitUnicodeString(&ustrDestFile, L"\\??\\C:\\LyShark\\new_lyshark.exe");
ULONG ulBufferSize = 40960;
ULONG ulReadDataSize = ulBufferSize;
LARGE_INTEGER liOffset = { 0 };
// 分配非分頁記憶體
PUCHAR pBuffer = ExAllocatePool(NonPagedPool, ulBufferSize);
do
{
// 讀取檔案
ulReadDataSize = ulBufferSize;
MyReadFile(ustrScrFile, liOffset, pBuffer, &ulReadDataSize);
// 資料為空則讀取結束
if (0 >= ulReadDataSize)
{
break;
}
// 寫入檔案
MyWriteFile(ustrDestFile, liOffset, pBuffer, &ulReadDataSize);
// 更新偏移
liOffset.QuadPart = liOffset.QuadPart + ulReadDataSize;
DbgPrint("[+] 更新偏移: %d \n", liOffset.QuadPart);
} while (TRUE);
// 釋放記憶體
ExFreePool(pBuffer);
DbgPrint("[*] 已將檔案復制到新目錄 \n");
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
編譯并運行這段程式,則自動將C:\\lyshark.exe盤符下的檔案拷貝到C:\\LyShark\\new_lyshark.exe目錄下,實作效果圖如下所示;

實作檔案讀寫傳遞: 通過如上學習相信你已經掌握了如何使用檔案讀寫系列函式了,接下來將封裝一個檔案讀寫驅動,應用層接收,驅動層讀取;
此驅動部分完整代碼如下所示;
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include <ntifs.h>
#include <windef.h>
#define READ_FILE_SIZE_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ALL_ACCESS)
#define READ_FILE_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_ALL_ACCESS)
#define DEVICENAME L"\\Device\\ReadWriteDevice"
#define SYMBOLNAME L"\\??\\ReadWriteSymbolName"
typedef struct
{
ULONG64 size; // 讀寫長度
BYTE* data; // 讀寫資料集
}FileData;
// 獲取檔案大小
ULONG64 MyGetFileSize(UNICODE_STRING ustrFileName)
{
HANDLE hFile = NULL;
OBJECT_ATTRIBUTES objectAttributes = { 0 };
IO_STATUS_BLOCK iosb = { 0 };
NTSTATUS status = STATUS_SUCCESS;
FILE_STANDARD_INFORMATION fsi = { 0 };
// 初始化結構
InitializeObjectAttributes(&objectAttributes, &ustrFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
// 打開檔案
status = ZwCreateFile(&hFile, GENERIC_READ, &objectAttributes, &iosb, NULL, 0, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
if (!NT_SUCCESS(status))
{
return 0;
}
// 獲取檔案大小資訊
status = ZwQueryInformationFile(hFile, &iosb, &fsi, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation);
if (!NT_SUCCESS(status))
{
ZwClose(hFile);
return 0;
}
return fsi.EndOfFile.QuadPart;
}
// 讀取檔案資料
BOOLEAN MyReadFile(UNICODE_STRING ustrFileName, LARGE_INTEGER liOffset, PUCHAR pReadData, PULONG pulReadDataSize)
{
HANDLE hFile = NULL;
IO_STATUS_BLOCK iosb = { 0 };
OBJECT_ATTRIBUTES objectAttributes = { 0 };
NTSTATUS status = STATUS_SUCCESS;
// 初始化結構
InitializeObjectAttributes(&objectAttributes, &ustrFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
// 打開檔案
status = ZwCreateFile(&hFile, GENERIC_READ, &objectAttributes, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
if (!NT_SUCCESS(status))
{
return FALSE;
}
// 初始化
RtlZeroMemory(&iosb, sizeof(iosb));
// 讀入檔案
status = ZwReadFile(hFile, NULL, NULL, NULL, &iosb, pReadData, *pulReadDataSize, &liOffset, NULL);
if (!NT_SUCCESS(status))
{
*pulReadDataSize = iosb.Information;
ZwClose(hFile);
return FALSE;
}
// 獲取實際讀取的資料
*pulReadDataSize = iosb.Information;
// 關閉句柄
ZwClose(hFile);
return TRUE;
}
NTSTATUS DriverIrpCtl(PDEVICE_OBJECT device, PIRP pirp)
{
PIO_STACK_LOCATION stack;
stack = IoGetCurrentIrpStackLocation(pirp);
FileData* FileDataPtr;
switch (stack->MajorFunction)
{
case IRP_MJ_CREATE:
{
break;
}
case IRP_MJ_CLOSE:
{
break;
}
case IRP_MJ_DEVICE_CONTROL:
{
// 獲取應用層傳值
FileDataPtr = pirp->AssociatedIrp.SystemBuffer;
switch (stack->Parameters.DeviceIoControl.IoControlCode)
{
// 讀取記憶體函式
case READ_FILE_SIZE_CODE:
{
LARGE_INTEGER liOffset = { 0 };
UNICODE_STRING ustrFileSize;
RtlInitUnicodeString(&ustrFileSize, L"\\??\\C:\\Windows\\system32\\ntoskrnl.exe");
// 獲取檔案長度
ULONG64 ulBufferSize = MyGetFileSize(ustrFileSize);
DbgPrint("獲取檔案大小: %I64d Bytes \n", ulBufferSize);
// 將長度回傳應用層
FileDataPtr->size = ulBufferSize;
break;
}
// 讀取檔案
case READ_FILE_CODE:
{
FileData ptr;
LARGE_INTEGER liOffset = { 0 };
UNICODE_STRING ustrFileSize;
RtlInitUnicodeString(&ustrFileSize, L"\\??\\C:\\Windows\\system32\\ntoskrnl.exe");
// 獲取檔案長度
ULONG64 ulBufferSize = MyGetFileSize(ustrFileSize);
DbgPrint("獲取檔案大小: %I64d Bytes \n", ulBufferSize);
// 讀取記憶體到緩沖區
BYTE* pBuffer = ExAllocatePool(NonPagedPool, ulBufferSize);
MyReadFile(ustrFileSize, liOffset, pBuffer, &ulBufferSize);
// 回傳資料
FileDataPtr->size = ulBufferSize;
RtlCopyMemory(FileDataPtr->data, pBuffer, FileDataPtr->size);
break;
}
}
pirp->IoStatus.Information = sizeof(FileDataPtr);
break;
}
}
pirp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(pirp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
VOID UnDriver(PDRIVER_OBJECT driver)
{
if (driver->DeviceObject)
{
UNICODE_STRING SymbolName;
RtlInitUnicodeString(&SymbolName, SYMBOLNAME);
// 洗掉符號鏈接
IoDeleteSymbolicLink(&SymbolName);
IoDeleteDevice(driver->DeviceObject);
}
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_OBJECT device = NULL;
UNICODE_STRING DeviceName;
DbgPrint("[LyShark] hello lyshark.com \n");
// 初始化設備名
RtlInitUnicodeString(&DeviceName, DEVICENAME);
// 創建設備
status = IoCreateDevice(Driver, sizeof(Driver->DriverExtension), &DeviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &device);
if (status == STATUS_SUCCESS)
{
UNICODE_STRING SymbolName;
RtlInitUnicodeString(&SymbolName, SYMBOLNAME);
// 創建符號鏈接
status = IoCreateSymbolicLink(&SymbolName, &DeviceName);
// 失敗則洗掉設備
if (status != STATUS_SUCCESS)
{
IoDeleteDevice(device);
}
}
// 派遣函式初始化
Driver->MajorFunction[IRP_MJ_CREATE] = DriverIrpCtl;
Driver->MajorFunction[IRP_MJ_CLOSE] = DriverIrpCtl;
Driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverIrpCtl;
// 卸載驅動
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
客戶端完整代碼如下所示;
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <iostream>
#define READ_FILE_SIZE_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ALL_ACCESS)
#define READ_FILE_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_ALL_ACCESS)
typedef struct
{
DWORD size; // 讀寫長度
BYTE* data; // 讀寫資料集
}FileData;
int main(int argc, char* argv[])
{
// 連接到驅動
HANDLE handle = CreateFileA("\\??\\ReadWriteSymbolName", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
FileData data;
DWORD dwSize = 0;
// 首先得到檔案長度
DeviceIoControl(handle, READ_FILE_SIZE_CODE, 0, 0, &data, sizeof(data), &dwSize, NULL);
printf("%d \n", data.size);
// 讀取機器碼到BYTE位元組陣列
data.data = https://www.cnblogs.com/LyShark/p/new BYTE[data.size];
DeviceIoControl(handle, READ_FILE_CODE, &data, sizeof(data), &data, sizeof(data), &dwSize, NULL);
for (int i = 0; i < data.size; i++)
{
printf("0x%02X ", data.data[i]);
}
printf("\n");
getchar();
CloseHandle(handle);
return 0;
}
通過驅動加載工具將WinDDK.sys拉起來,然后啟動客戶端行程,即可輸出ntoskrnl.exe的檔案資料,如下圖所示;

文章出處:https://www.cnblogs.com/LyShark/p/17154665.html
本博客中的文章,轉載請注明出處,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/554822.html
標籤:領域驅動設計
下一篇:返回列表
