C語言驅動開發(fā)之內核文件的讀寫
在應用層下的文件操作只需要調用微軟應用層下的API
函數(shù)及C庫
標準函數(shù)即可,而如果在內核中讀寫文件則應用層的API顯然是無法被使用的,內核層需要使用內核專有API,某些應用層下的API只需要增加Zw開頭即可在內核中使用,例如本章要講解的文件與目錄操作相關函數(shù),多數(shù)ARK反內核工具都具有對文件的管理功能,實現(xiàn)對文件或目錄的基本操作功能也是非常有必要的。
首先無論在內核態(tài)還是在用戶態(tài),我們調用的文件操作函數(shù)其最終都會轉換為一個IRP請求,并發(fā)送到文件系統(tǒng)驅動上的IRP_MJ_READ
派遣函數(shù)里面,這個讀寫流程大體上可分為如下四步;
- 對于FAT32分區(qū)會默認分發(fā)到
FASTFAT.SYS
,而相對于NTFS分區(qū)則會分發(fā)到NTFS.SYS
驅動上。 - 文件系統(tǒng)驅動經(jīng)過處理后,就把IRP傳給磁盤類驅動的
IRP_MJ_READ
分發(fā)函數(shù)處理,當磁盤類驅動處理完畢后,又把IRP傳給磁盤小端口驅動。 - 在磁盤小端口驅動里,無論是讀還是寫,用的都是
IRP_MJ_SCSI
這個分發(fā)函數(shù)。 - IRP被磁盤小端口驅動處理完之后,就要依靠
HAL.DLL
進行端口IO,此時數(shù)據(jù)就真的從硬盤里讀取了出來。
創(chuàng)建文件或目錄
實現(xiàn)創(chuàng)建文件或目錄,創(chuàng)建文件或目錄都可調用ZwCreateFile()
這個內核函數(shù)來實現(xiàn),唯一不同的區(qū)別在于當用戶傳入?yún)?shù)中包含有FILE_SYNCHRONOUS_IO_NONALERT
屬性時則會默認創(chuàng)建文件,而如果包含有FILE_DIRECTORY_FILE
屬性則默認為創(chuàng)建目錄,該函數(shù)的微軟定義以及備注信息如下所示;
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結構的指針,該結構接收最終完成狀態(tài)和有關所請求操作的其他信息。 [in, optional] PLARGE_INTEGER AllocationSize, // 指向LARGE_INTEGER的指針,其中包含創(chuàng)建或覆蓋的文件的初始分配大小(以字節(jié)為單位)。 [in] ULONG FileAttributes, // 指定一個或多個FILE_ATTRIBUTE_XXX標志,這些標志表示在創(chuàng)建或覆蓋文件時要設置的文件屬性。 [in] ULONG ShareAccess, // 共享訪問的類型,指定為零或以下標志的任意組合。 [in] ULONG CreateDisposition, // 指定在文件存在或不存在時要執(zhí)行的操作。 [in] ULONG CreateOptions, // 指定要在驅動程序創(chuàng)建或打開文件時應用的選項。 [in, optional] PVOID EaBuffer, // 對于設備和中間驅動程序,此參數(shù)必須是NULL指針。 [in] ULONG EaLength // 對于設備和中間驅動程序,此參數(shù)必須為零。 );
參數(shù)DesiredAccess
用于指明對象訪問權限的,常用的權限有FILE_READ_DATA
讀取文件,FILE_WRITE_DATA
寫入文件,FILE_APPEND_DATA
追加文件,FILE_READ_ATTRIBUTES
讀取文件屬性,以及FILE_WRITE_ATTRIBUTES
寫入文件屬性。
參數(shù)ObjectAttributes
指向了一個OBJECT_ATTRIBUTES
指針,通常會通過InitializeObjectAttributes()
宏對其進行初始化,當一個例程打開對象時由此結構體指定目標對象的屬性。
參數(shù)ShareAccess
用于指定訪問屬性,通常屬性有FILE_SHARE_READ
讀取,FILE_SHARE_WRITE
寫入,FILE_SHARE_DELETE
刪除。
參數(shù)CreateDisposition
用于指定在文件存在或不存在時要執(zhí)行的操作,一般而言我們會指定為FILE_OPEN_IF
打開文件,或FILE_OVERWRITE_IF
打開文件并覆蓋,FILE_SUPERSEDE
替換文件。
參數(shù)CreateOptions
用于指定創(chuàng)建文件或目錄,一般FILE_SYNCHRONOUS_IO_NONALERT
代表創(chuàng)建文件,參數(shù)FILE_DIRECTORY_FILE
代表創(chuàng)建目錄。
相對于創(chuàng)建文件而言刪除文件或目錄只需要調用ZwDeleteFile()
系列函數(shù)即可,此類函數(shù)只需要傳遞一個OBJECT_ATTRIBUTES
參數(shù)即可,其微軟定義如下所示;
NTSYSAPI NTSTATUS ZwDeleteFile( [in] POBJECT_ATTRIBUTES ObjectAttributes );
接下來我們就封裝三個函數(shù)MyCreateFile()
用于創(chuàng)建文件,MyCreateFileFolder()
用于創(chuàng)建目錄,MyDeleteFileOrFileFolder()
用于刪除空目錄。
// 署名權 // right to sign one's name on a piece of work // PowerBy: LyShark // Email: me@lyshark.com #include <ntifs.h> #include <ntstrsafe.h> // 創(chuàng)建文件 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); // 創(chuàng)建文件 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; } // 創(chuàng)建目錄 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); // 創(chuàng)建目錄 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); // 執(zhí)行刪除操作 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; // 創(chuàng)建目錄 UNICODE_STRING ustrDirectory; RtlInitUnicodeString(&ustrDirectory, L"\\??\\C:\\LySharkFolder"); ref = MyCreateFileFolder(ustrDirectory); if (ref != FALSE) { DbgPrint("[LyShark] 創(chuàng)建目錄成功 \n"); } else { DbgPrint("[LyShark] 創(chuàng)建文件失敗 \n"); } // 創(chuàng)建文件 UNICODE_STRING ustrCreateFile; RtlInitUnicodeString(&ustrCreateFile, L"\\??\\C:\\LySharkFolder\\lyshark.txt"); ref = MyCreateFile(ustrCreateFile); if (ref != FALSE) { DbgPrint("[LyShark] 創(chuàng)建文件成功 \n"); } else { DbgPrint("[LyShark] 創(chuàng)建文件失敗 \n"); } Driver->DriverUnload = UnDriver; return STATUS_SUCCESS; }
運行如上代碼,分別創(chuàng)建LySharkFolder
目錄,并在其中創(chuàng)建lyshark.txt
最終再將其刪除,輸出效果如下;
重命名文·件或目錄
在內核中重命名文件或目錄核心功能的實現(xiàn)依賴于ZwSetInformationFile()
這個內核函數(shù),該函數(shù)可用于更改有關文件對象的各種信息,其微軟官方定義如下;
NTSYSAPI NTSTATUS ZwSetInformationFile( [in] HANDLE FileHandle, // 文件句柄 [out] PIO_STATUS_BLOCK IoStatusBlock, // 指向 IO_STATUS_BLOCK 結構的指針 [in] PVOID FileInformation, // 指向緩沖區(qū)的指針,該緩沖區(qū)包含要為文件設置的信息。 [in] ULONG Length, // 緩沖區(qū)的大?。ㄒ宰止?jié)為單位) [in] FILE_INFORMATION_CLASS FileInformationClass // 為文件設置的類型 );
這其中最重要的參數(shù)就是FileInformationClass
根據(jù)該參數(shù)的不同則對文件的操作方式也就不同,如果需要重命名文件則此處應使用FileRenameInformation
而如果需要修改文件的當前信息則應使用FilePositionInformation
創(chuàng)建鏈接文件則使用FileLinkInformation
即可,以重命名為例,首先我們需要定義一個FILE_RENAME_INFORMATION
結構并按照要求填充,最后直接使用ZwSetInformationFile()
并傳入相關信息后即可完成修改,其完整代碼流程如下;
// 署名權 // right to sign one's name on a piece of work // PowerBy: LyShark // Email: me@lyshark.com #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
,前提是該目錄與該文件必須存在;
那么如果你需要將文件設置為只讀模式或修改文件的創(chuàng)建日期,那么你就需要看一下微軟的定義FILE_BASIC_INFORMATION
結構,依次填充此結構體并調用ZwSetInformationFile()
即可實現(xiàn)修改,該結構的定義如下所示;
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()
這個內核函數(shù),該函數(shù)可以返回有關文件對象的各種信息,參數(shù)傳遞上與ZwSetInformationFile()
很相似,其FileInformationClass
都需要傳入一個文件類型結構,該函數(shù)的完整定義如下;
NTSYSAPI NTSTATUS ZwQueryInformationFile( [in] HANDLE FileHandle, // 文件句柄。 [out] PIO_STATUS_BLOCK IoStatusBlock, // 指向接收最終完成狀態(tài)和操作相關信息的 IO_STATUS_BLOCK 結構的指針。 [out] PVOID FileInformation, // 指向調用方分配的緩沖區(qū)的指針,例程將請求的有關文件對象的信息寫入其中。 [in] ULONG Length, // 長度。 [in] FILE_INFORMATION_CLASS FileInformationClass // 指定要在 FileInformation 指向的緩沖區(qū)中返回的有關文件的信息類型。 );
本例中我們需要讀入文件的所占字節(jié)數(shù),那么FileInformation
字段就需要傳入FileStandardInformation
來獲取文件的基本信息,獲取到的信息會被存儲到FILE_STANDARD_INFORMATION
結構內,用戶只需要解析該結構體fsi.EndOfFile.QuadPart
即可得到文件長度,其完整代碼如下所示;
// 署名權 // right to sign one's name on a piece of work // PowerBy: LyShark // Email: me@lyshark.com #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
程序的大小字節(jié)數(shù),如下圖所示;
內核文件讀寫
內核讀取文件可以使用ZwReadFile()
,內核寫入文件則可使用ZwWriteFile()
,這兩個函數(shù)的參數(shù)傳遞基本上一致,如下是讀寫兩個函數(shù)的對比參數(shù)。
NTSYSAPI NTSTATUS ZwReadFile( [in] HANDLE FileHandle, // 文件對象的句柄。 [in, optional] HANDLE Event, // (可選)事件對象的句柄,在讀取操作完成后設置為信號狀態(tài)。 [in, optional] PIO_APC_ROUTINE ApcRoutine, // 此參數(shù)為保留參數(shù)。 [in, optional] PVOID ApcContext, // 此參數(shù)為保留參數(shù)。 [out] PIO_STATUS_BLOCK IoStatusBlock, // 接收實際從文件讀取的字節(jié)數(shù)。 [out] PVOID Buffer, // 指向調用方分配的緩沖區(qū)的指針,該緩沖區(qū)接收從文件讀取的數(shù)據(jù)。 [in] ULONG Length, // 緩沖區(qū)指向的緩沖區(qū)的大?。ㄒ宰止?jié)為單位)。 [in, optional] PLARGE_INTEGER ByteOffset, // 指定將開始讀取操作的文件中的起始字節(jié)偏移量。 [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()
函數(shù),將數(shù)據(jù)讀入到pBuffer
并輸出,完整代碼如下所示;
// 署名權 // right to sign one's name on a piece of work // PowerBy: LyShark // Email: me@lyshark.com #include <ntifs.h> #include <ntstrsafe.h> // 讀取文件數(shù)據(jù) 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; } // 獲取實際讀取的數(shù)據(jù) *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個字節(jié) for (size_t i = 0; i < 16; i++) { DbgPrint("%02X \n", pBuffer[i]); } Driver->DriverUnload = UnDriver; return STATUS_SUCCESS; }
編譯并運行這段代碼,并循環(huán)輸出lyshark.exe
文件的頭16個字節(jié)的數(shù)據(jù),效果圖如下所示;
文件寫入MyWriteFile()
與讀取類似,如下通過運用文件讀寫實現(xiàn)了文件拷貝
功能,實現(xiàn)完整代碼如下所示;
// 署名權 // right to sign one's name on a piece of work // PowerBy: LyShark // Email: me@lyshark.com #include <ntifs.h> #include <ntstrsafe.h> // 讀取文件數(shù)據(jù) 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; } // 獲取實際讀取的數(shù)據(jù) *pulReadDataSize = iosb.Information; // 關閉句柄 ZwClose(hFile); return TRUE; } // 向文件寫入數(shù)據(jù) 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; } // 獲取實際寫入的數(shù)據(jù) *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); // 數(shù)據(jù)為空則讀取結束 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
目錄下,實現(xiàn)效果圖如下所示;
實現(xiàn)文件讀寫傳遞
通過如上學習相信你已經(jīng)掌握了如何使用文件讀寫系列函數(shù)了,接下來將封裝一個文件讀寫驅動,應用層接收,驅動層讀??;
此驅動部分完整代碼如下所示;
// 署名權 // right to sign one's name on a piece of work // PowerBy: LyShark // Email: me@lyshark.com #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; // 讀寫數(shù)據(jù)集 }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; } // 讀取文件數(shù)據(jù) 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; } // 獲取實際讀取的數(shù)據(jù) *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) { // 讀取內存函數(shù) 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); // 讀取內存到緩沖區(qū) BYTE* pBuffer = ExAllocatePool(NonPagedPool, ulBufferSize); MyReadFile(ustrFileSize, liOffset, pBuffer, &ulBufferSize); // 返回數(shù)據(jù) 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); // 創(chuàng)建設備 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); // 創(chuàng)建符號鏈接 status = IoCreateSymbolicLink(&SymbolName, &DeviceName); // 失敗則刪除設備 if (status != STATUS_SUCCESS) { IoDeleteDevice(device); } } // 派遣函數(shù)初始化 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: me@lyshark.com #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; // 讀寫數(shù)據(jù)集 }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字節(jié)數(shù)組 data.data = 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
的文件數(shù)據(jù),如下圖所示;
以上就是C語言驅動開發(fā)之內核文件的讀寫的詳細內容,更多關于C語言內核文件讀寫的資料請關注腳本之家其它相關文章!
相關文章
C++/STL實現(xiàn)判斷平面內兩條線段的位置關系代碼示例
這篇文章主要介紹了C++/STL實現(xiàn)判斷平面內兩條線段的位置關系代碼示例,具有一定參考價值,需要的朋友可以了解下。2017-11-11Java C++ 算法leetcode828統(tǒng)計子串中唯一字符乘法原理
這篇文章主要為大家介紹了Java C++ 算法leetcode828統(tǒng)計子串中唯一字符乘法原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09