Windows驅動開發技術詳解__Windows內核函數

這篇文章主要複習Windows內核中字符串處理函數,文件讀寫函數,註冊表讀寫函數。


內核模式下的字符串操作:


1.ASCII字符串和寬字符串

在應用程序中,往往使用兩種字符:一種是char型字符串,負責記錄ANSI字符集。它是指向一個char數組的指針,每個char型變量的大小爲一個字節,字符串是以0標誌結尾。還有一種是wchar_t型的寬字符,負責描述unicode字符集的字符串,他是一個指向wchar_t數組的指針,wchar_t字符串大小爲兩個字節字符串以0標誌字符串結束。

ANSI字符串構造如下:

char * str1 = "abc";

UNICODE字符串的構造如下:

wchar_t *str2 = L"abc";


2.ANSI_STRING字符串和UNICODE_STRING字符串

DDK鼓勵程序員使用DDK自定義的字符串,這種數據格式定義如下:

typedef struct _STRING
{
	USHORT Length;
	USHORT MaximumLength;
	PCHAR Buffer;
}STRING;

typedef STRING ANSI_STRING;
typedef PSTRING PANSI_STRING;

typedef STRING OEM_STRING;
typedef PSTRING POEM_STRING;

這個數據結構對ASCII字符串進行了封裝:

Length:字符串的長度

MaximumLength:整個字符串緩衝區的最大長度

Buffer:緩衝區的指針。


與ANSI_STRING相對應,DDK將寬字符封裝成UNICODE_STRING數據結構:

typedef struct _UNICODE_STRING
{
	USHORT Length;
	USHORT MaximumLength;
	PWSTR Buffer;
}UNICODE_STRING;

KdPrint函數提供了打印log方法
ANSI_STRING ansiString;
//省去對ansiString初始化
KdPrint("%Z\n",&ansiString);

UNICODE_STRING uniString;
//省去對uniString的初始化
KdPrint("%wZ\n",&uniString);

3.字符串的初始化與銷燬

ANSI_STRIN字符串和UNICODE_STRING字符串使用前需要進行初始化。有兩種辦法構造這個數據結構。

(1)使用DDK提供的函數

初始化ANSI_STRING字符串:

VOID
 RtlInitAnsiString(
            IN PANSI_STRING DestinationString,
	    IN PCSZ SourceString 
	 );
DestinationString:要初始化的ANSI_STRING字符串

SourceString:字符串的內容

初始化UNICODE_STRING字符串:

VOID 
 RtlInitUnicodeString( 
            IN PUNICODE_STRING DestinationString,
	    IN PCWSTR SourceString
	);

DestinationString:要初始化的UNICODE_STRING字符串

SourceString:字符串的內容


看下面一段代碼:

	//用RtlInitAnsiString初始化字符串
	ANSI_STRING AnsiString1;
	CHAR * string1 = "hello";
	//初始化ANSI_STRING字符串
	RtlInitAnsiString(&AnsiString1,string1);
	KdPrint(("AnsiString1:%Z\n",&AnsiString1));//打印hello

	string1[0] = 'H';
	string1[1] = 'E';
	string1[2] = 'L';
	string1[3] = 'L';
	string1[4] = 'O';
	//改變string1,AnsiString1同樣會導致變化
	KdPrint(("AnsiString1:%Z\n",&AnsiString1));//打印HELLO

(2)另一種方法是程序員自己申請內存,並初始化內存,當不用字符串時,需要回收字符串佔用的內存

//程序員自己初始化字符串
#define  BUFFER_SIZE 1024
	UNICODE_STRING UnicodeString1 = {0};
	//設置緩衝區大小
	UnicodeString1.MaximumLength = BUFFER_SIZE;
	//分配內存
	UnicodeString1.Buffer = (PWSTR)ExAllocatePool(PagedPool,BUFFER_SIZE);
	WCHAR * wideString = L"hello";

	//設置字符串長度,因爲是寬字符,所以是字符長度的2倍
	UnicodeString1.Length = 2*wcslen(wideString);
    
	//保證緩衝區足夠大,否則程序終止
	ASSERT(UnicodeString1.MaximumLength >= UnicodeString1.Length);
	//內存拷貝
	RtlCopyMemory(UnicodeString1.Buffer,wideString,UnicodeString1.Length);
	//設置字符長度
	UnicodeString1.Length = 2*wcslen(wideString);

	KdPrint(("UnicodeString:%wZ\n",&UnicodeString1));

	//清理內存
	ExFreePool(UnicodeString1.Buffer);
	UnicodeString1.Buffer = NULL;
	UnicodeString1.Length = UnicodeString1.MaximumLength = 0;

4.字符串複製

DDK提供了針對ANSI_STRING和UNICODE_STRING字符串的複製字符串命令,分別是:

ANSI_STRING:

VOID
 RtlCopyString(
                 IN PSTRING DestinationString,
		 IN PSTRING SourceString 
		);


UNICODE_STRING:

VOID 
 RtlCopyUnicodeString(
          IN PUNICODE_STRING DestinationString, 
	  IN PUNICODE_STRING SourceString
	 );

DestinationString:目的字符串

SourceString:源字符串

請看如下代碼:

	//初始化Unicodestring1
	UNICODE_STRING UnicodeString1;
	RtlInitUnicodeString(&UnicodeString1,L"Hello World");

	//初始化UnicodeString2
	UNICODE_STRING UnicodeString2={0};
	UnicodeString2.Buffer = (PWSTR)ExAllocatePool(PagedPool,BUFFER_SIZE);
	UnicodeString2.MaximumLength = BUFFER_SIZE;

	//初始化Unicodestring2拷貝到UnicodeString1
	RtlCopyUnicodeString(&UnicodeString2,&UnicodeString1);

	//分別顯示UnicodeString1和UnicodeString2
	KdPrint(("UnicodeString1:%wZ\n",&UnicodeString1));
	KdPrint(("UnicodeString2:%wZ\n",&UnicodeString2));

	//銷燬UnicodeString2
	//注意!!UnicodeString1不用銷燬
	RtlFreeUnicodeString(&UnicodeString2);

5.字符串的比較

DDK提供了對ANSI_STRING和UNICODE_STRING字符串的相關字符串比較操作。

ANSI_STRING:

LONG 
  RtlCompareString( 
        IN PSTRING String1, 
	IN PSTRING String2,
	 BOOLEAN CaseInSensitive
	);
UNICODE_STRING:

LONG 
  RtlCompareUnicodeString( 
         IN PUNICODE_STRING String1, 
	 IN PUNICODE_STRING String2,
         BOOLEAN CaseInSensitive 
	);
String1:要比較的第一個字符串

String2:要比較的第二個字符串

CaseInSensitive:是否對大小寫敏感

下面代碼演示如何使用RtlCompareUnicodeString函數:

        if (RtlEqualUnicodeString(&UnicodeString1,&UnicodeString2,TRUE))
	{
		KdPrint(("UnicodeString1 and UnicodeString2 are equal\n"));
	}
	else
	{
		KdPrint(("UnicodeString1 and UnicodeString2 are NOT equal\n"));
	}

6.字符串轉化成大寫

DDK提供了對ANSI_STRING和UNICODE_STRING字符串的相關字符串大小寫轉化的函數。

(1)ANSI_STRING字符串轉化成大寫

VOID  
  RtlUpperString( 
       OUT  PSTRING DestinationString,
       OUT  PSTRING SourceString
	);

DestinationString:目的字符串

SourceString:源字符串


(2)UNICODE_STRING字符串轉化成大寫

NTSTATUS 
  RtlUpcaseUnicodeString(
           IN OUT PUNICODE_STRING DestinationString,
	   IN PCUNICODE_STRING SourceString,
	   IN BOOLEAN AllocateDestinationString 
	 );

DestinationString:目的字符串

SourceString:源字符串

AllocateDestinationString:是否爲目的字符串分配內存

返回值:返回轉換是否成功


DDK雖然提供了轉化成大寫的函數,但卻沒有提供轉化成小寫的函數。下面代碼演示瞭如何使用RtlUpcaseUnicodeString函數:

//初始化UnicodeString1
	UNICODE_STRING UnicodeString1;
	RtlInitUnicodeString(&UnicodeString1,L"Hello World");

    //變化前
	KdPrint(("UnicodeString1:%wZ\n",&UnicodeString1));
	//變大寫
	RtlUpcaseUnicodeString(&UnicodeString1,&UnicodeString1,FALSE);
	//變化後
	KdPrint(("UnicodeString1:%wZ\n",&UnicodeString1));

7.字符串與整形數字相互轉換

DDK提供了UNICODE_STRING字符串與整數相互轉換的內核函數。

(1)將UNICODE_STRING字符串轉換成整數

這個函數是RtlUnicodeStringToInteger,其聲明是:

NTSTATUS
 RtlUnicodeStringToInteger(
             IN PUNICODE_STRING String, 
	     IN ULONG Base, 
	     OUT PULONG Value 
	);

String:需要轉換的字符串

Base:轉換的數的進制

Vlaue:需要轉換的數字

返回值:指明是否轉換成功


(2)將整數轉換成UNICODE_STRING字符串

這個函數RtlIntegerToUnicodeString,其聲明時:

NTSTATUS
 RtlIntegerToUnicodeString( 
           IN ULONG Value,
	   IN ULONG Base, 
	   IN OUT PUNICODE_STRING String 
		);

Value:需要轉換的數字

Base:轉換的數的進制

String:需要轉換的字符串

返回值:是否轉換成功

以下是字符串和數字之間的相互轉換:

//字符串轉換成數字
	//初始化UnicodeString1
	UNICODE_STRING UnicodeString1;
	RtlInitUnicodeString(&UnicodeString1,L"-100");

	ULONG lNumber;
    NTSTATUS nStatus = RtlUnicodeStringToInteger(&UnicodeString1,10,&lNumber);
	if (NT_SUCCESS(nStatus))
	{
		KdPrint(("Convert to integer successfully\n"));
	}
	else
	{
		KdPrint(("Convert to integer unsuccessfully\n"));
	}

	//數字轉換成字符串
	UNICODE_STRING UnicodeString2 = {0};
    UnicodeString2.Buffer = (PWSTR)ExAllocatePool(PagedPool,BUFFER_SIZE);
    UnicodeString2.MaximumLength = BUFFER_SIZE;
	nStatus = RtlIntegerToUnicodeString(200,10,&UnicodeString2);
    if (NT_SUCCESS(nStatus))
    {
		KdPrint(("Conver to string successfully!\n"));
		KdPrint(("Result: %wZ\n",&UnicodeString2));
    }
	else
	{
		KdPrint(("Convert to string unsuccessfully!\n"));
	}
	//銷燬UnicodeString2
	//注意!!UnicodeString1不用銷燬
	RtlFreeUnicodeString(&UnicodeString2);

ANSI_STRING字符串和UNICODE_STRING字符串相互轉換

DDK提供了ANSI_STRING字符串和UNICODE_STRING字符串相互轉換的相關函數

(1)將UNICODE_STRING字符串轉換爲ANSI_STRING字符串

DDK提供了函數:RtlUnicodeStringToAnsiString,其聲明是:

NTSTATUS
  RtlUnicodeStringToAnsiString( 
            IN OUT  PANSI_STRING DestinationString, 
	    IN  PUNICODE_STRING SourceString,
	    IN  BOOLEAN AllocateDestinationString
		);

DestinationString:需要被轉換的字符串

SourceString:需要轉換的源字符串

AllocateDestinationString:是否需要對被轉換字符分配內存

返回值:指明是否分配成功


(2)將ANSI_STRING字符串轉換爲UNICODE_STRING字符串

DDK提供函數:RtlAnsiStringToUnicodeString,其聲明:

NTSTATUS
 RtlAnsiStringToUnicodeString( 
        IN OUT PUNICODE_STRING DestinationString, 
	IN PANSI_STRING SourceString, 
	IN BOOLEAN AllocateDestinationString
	);

DestinationString:需要被轉換的字符串

SourceString:需要轉換的源字符串

AllocateDestinationString:是否需要對被轉換字符分配內存

返回值:指明是否分配成功


下面代碼演示了ANSI_STRING和UNICODE_STRING之間是如何轉換的:

//將UNICODE_STRING字符串轉換成ANSI_STRING字符串
	//初始化UnicodeString1
	UNICODE_STRING UnicodeString1;
	RtlInitUnicodeString(&UnicodeString1,L"Hello World");

	ANSI_STRING AnsiString1;
	NTSTATUS nStatus = RtlUnicodeStringToAnsiString(&AnsiString1,&UnicodeString1,TRUE);

	if (NT_SUCCESS(nStatus))
	{
		KdPrint(("Conver successfully!\n"));
		KdPrint(("Result:%Z\n",&AnsiString1));
	}
	else
	{
		KdPrint(("Conver unsuccessfully!\n"));
	}

	//銷燬AnsiString1
    RtlFreeAnsiString(&AnsiString1);

	//將ANSI_STRING字符串轉換成UNICODE_STRING字符串
	//初始化AnsiString2
	ANSI_STRING AnsiString2;
	RtlInitAnsiString(&AnsiString2,"Hello World");

	UNICODE_STRING UnicodeString2;
	nStatus = RtlAnsiStringToUnicodeString(&UnicodeString2,&AnsiString2,TRUE);

	if (NT_SUCCESS(nStatus))
	{
		KdPrint(("Conver succussfully!\n"));
		KdPrint(("Result:%wZ\n",&UnicodeString2));
	}
	else
	{
		KdPrint(("Conver unsuccessfully!\n"));
	}
	//銷燬UnicodeString2
	RtlFreeUnicodeString(&UnicodeString2);

內核模式下的文件操作:

1.文件的創建

對文件的創建或者打開都是通過ZwCreateFile實現的。和Windows API類似,這個內核函數返回一個文件句柄,文件的所有操作都是依靠這個句柄進行操作的。在操作完畢後,需要關閉這個句柄。

NTSTATUS
 ZwCreateFile(
          OUT PHANDLE FileHandle, 
	  IN ACCESS_MASK DesiredAccess, 
	  IN POBJECT_ATTRIBUTES ObjectAttributes,
	  OUT PIO_STATUS_BLOCK IoStatusBlock,
	  IN PLARGE_INTEGER AllocationSize OPTIONAL, 
	  IN ULONG FileAttributes,
	  IN ULONG ShareAccess,
	  IN ULONG CreateDisposition,
	  IN ULONG CreateOptions, 
	  IN PVOID EaBuffer OPTIONAL,
	  IN ULONG EaLength
	);

FileHanlde:返回打開文件的句柄

DesireAccess:對打開文件操作的描述

ObjectAttributes:是OBJECT_ATTRIBUTES結構的地址,該結構包含要打開的文件名

IoStatuesBlock:指向一個IO_STATUS_BLOCK結構,該結構接收ZwCreateFile操作的結果狀態

AllocateSize:是一個指針,指向一個64位整數,指定文件初始分配的大小

FileAttributes:0或FILE_ATTRIBUTES_NORMAL,指定新創建文件的屬性

ShareAccess:FILE_OPEN_READ或0,指定文件的共享方式

CreateDisposition:FILE_OPEN或FILE_OVERWRITE_IF,表明當文件存在或不存在時應如何處理

CreateOptions:FILE_SYNCHRONOUS_IO_NONALERT,指定控制文件打開操作和句柄使用的附加標誌位

EaBuffer:一個指針,指向可選的擴展屬性區

EaLength:擴展屬性區的長度

這個函數需要填寫CreateDisposition參數。如果想打開文件,CreateDisposition參數設置成FILE_OPEN。如果想創建文件,CreateDisposition參數設置成FILE_OVERWRITE_IF。此時無論文件是否存在,都會創建新文件。

文件名的指定是通過設定第三個參數ObjectAttributes。這個參數是一個OBJECT_ATTRIBUTES結構。DDK提供對OBJECT_ATTRIBUTES結構初始化的宏InitializeObjectAttributes.

VOID 
  InitializeObjectAttributes( 
          OUT POBJECT_ATTRIBUTES InitializedAttributes,
	  IN PUNICODE_STRING ObjectName,
	  IN ULONG Attributes,
	  IN HANDLE RootDirectory,
	  IN PSECURITY_DESCRIPTOR SecurityDescriptor
  );

InitializedAttributes:返回OBJECT_ATTRIBUTES結構

ObjectName:對象名稱,用UNICODE_STRING描述,這裏設置的是文件名

Attributes:一般設置爲OBJ_CASE_INSENSITIVE,對大小寫敏感

後兩個參數一般設置爲NULL

下面代碼演示如何在驅動程序中創建和打開文件

(1)創建文件

VOID CreateFileTest()
{
	OBJECT_ATTRIBUTES objectAtrributes;
	IO_STATUS_BLOCK iostatus;
	HANDLE hfile;
	UNICODE_STRING logFileUnicodeString;

	//初始化UNICODE_STRING字符串
	RtlInitUnicodeString(&logFileUnicodeString,
		L"\\??\\C:\\1.log");
	//初始化objectAtrributes
	InitializeObjectAttributes(&objectAtrributes,
		                        &logFileUnicodeString,
								OBJ_CASE_INSENSITIVE,
								NULL,
								NULL);
	//創建文件
	NTSTATUS ntStatus = ZwCreateFile(&hfile,
		                             GENERIC_WRITE,
									 &objectAtrributes,
									 &iostatus,
									 NULL,
									 FILE_ATTRIBUTE_NORMAL,
									 FILE_SHARE_READ,
									 FILE_OPEN_IF,
									 FILE_SYNCHRONOUS_IO_NONALERT,
									 NULL,
									 0);
	if (NT_SUCCESS(ntStatus))
	{
		KdPrint(("Create file successfully!\n"));
	}
	else
	{
		KdPrint(("Create file unseccessfully!\n"));
	}

	//文件操作
	//。。。。。。。。

	//關閉文件句柄
	ZwClose(hfile);
}

(2)打開文件
#pragma INITCODE
VOID OpenFileTest1()
{
	OBJECT_ATTRIBUTES objectAttributes;
	IO_STATUS_BLOCK iostatus;
	HANDLE hfile;
	UNICODE_STRING logFileUnicodeString;

	//初始化UNICODE_STRING字符串
	RtlInitUnicodeString(&logFileUnicodeString,
		 L"\\??\\C:\\1.log");

	//初始化objectAttributes
	InitializeObjectAttributes(&objectAttributes,
		                        &logFileUnicodeString,
								OBJ_CASE_INSENSITIVE,
								NULL,
								NULL);
	//創建文件
	NTSTATUS ntStatus = ZwCreateFile(&hfile,
		                              GENERIC_READ,
									  &objectAttributes,
									  &iostatus,
									  NULL,
									  FILE_ATTRIBUTE_NORMAL,
									  FILE_SHARE_WRITE,
									  FILE_OPEN,
									  FILE_SYNCHRONOUS_IO_NONALERT,
									  NULL,
									  0);
	if (NT_SUCCESS(ntStatus))
	{
		KdPrint(("Open file succssfully!\n"));
	}
	else
	{
		KdPrint(("Open file unsuccssfully!\n"));
	}

	//文件操作
	//。。。。

	//關閉文件句柄
	ZwClose(hfile);

}

2.文件的打開

除了使用ZwCreateFile函數可以打開文件,DDK還提供了一個內核函數ZwOpenFile。ZwOpenFile內核函數的參數比ZwCreateFile的參數簡化,方便程序員打開文件。

該函數聲明如下:

NTSTATUS
  ZwOpenFile( 
           OUT PHANDLE FileHandle, 
	   IN ACCESS_MASK DesiredAccess,
	   IN POBJECT_ATTRIBUTES ObjectAttributes,
	   OUT PIO_STATUS_BLOCK IoStatusBlock, 
	   IN ULONG ShareAccess,
	   IN ULONG OpenOptions 
	 );

FileHandle:返回打開文件的句柄

DesireAccess:打開的權限,一般設置爲GENERIC_ALL

ObjectAttributes:OBJECT_ATTRIBUTES結構體

IoStatusBlock:指向一個結構體的指針,該結構體指明打開文件的狀態

ShareAccess:共享的權限。可以是FILE_SYNCHRONOUS_IO_NONALERT

返回值:指明文件是否被成功打開

下面代碼演示瞭如何使用ZwOpenFile打開文件:

OBJECT_ATTRIBUTES objectAttributes;
	IO_STATUS_BLOCK iostatus;
	HANDLE hfile;
	UNICODE_STRING logFileUnicodeString;

	//初始化UNICODE_STRING字符串
	RtlInitUnicodeString(&logFileUnicodeString,
		L"\\??\\C:\\1.log");
	//初始化objectAttributes
	InitializeObjectAttributes(&objectAttributes,
		                         &logFileUnicodeString,
								 OBJ_CASE_INSENSITIVE,
								 NULL,
								 NULL);
	//創建文件
    NTSTATUS ntStatus = ZwOpenFile(&hfile,
		                            GENERIC_ALL,
									&objectAttributes,
									&iostatus,
									FILE_SHARE_READ | FILE_SHARE_WRITE,
									FILE_SYNCHRONOUS_IO_NONALERT);
	if (NT_SUCCESS(ntStatus))
	{
		KdPrint(("Create file successfully!\n"));
	}
	else 
	{
		KdPrint(("Create file unsuccessfully!\n"));
	}
	//文件操作
	//。。。。。。。。。。

	//關閉文件句柄
	ZwClose(hfile);

3.獲取或修改文件的屬性

獲取和修改文件屬性,包括獲取文件大小,獲取或修改文件指針的位置,獲取或修改文件名,獲取或修改文件屬性(只讀信息,隱藏信息),獲取或修改文件創建,修改日期等。DDK提供了內核函數ZwSetInformationFile和ZwQueryInformationFile函數來獲取和修改文件屬性。

NTSTATUS
  ZwSetInformationFile(
                 IN HANDLE FileHandle,
		 OUT PIO_STATUS_BLOCK IoStatusBlock, 
		 IN PVOID FileInformation,
		 IN ULONG Length,
		 IN FILE_INFORMATION_CLASS FileInformationClass 
	);


NTSTATUS
 ZwQueryInformationFile(
           IN HANDLE FileHandle,
	   OUT PIO_STATUS_BLOCK IoStatusBlock,
	   OUT PVOID FileInformation, 
	   IN ULONG Length, 
	   IN FILE_INFORMATION_CLASS FileInformationClass
  );


FileHandle:文件句柄

IoStackBlock:返回設置狀態

FileInformation:依據FileInformationClass不同而不同。作爲輸入信息

Length:FileInformation的長度

FileInformationClass:描述修改屬性類型

下面代碼演示如何使用ZwQueryInformationFile函數查詢,修改文件屬性

OBJECT_ATTRIBUTES objectAttributes;
    IO_STATUS_BLOCK iostatus;
	HANDLE hfile;
    UNICODE_STRING logFileUnicodeString;

	//初始化UNICODE_STRING字符串
	RtlInitUnicodeString(&logFileUnicodeString,
		L"\\??\\C:\\1.log");
	//初始化objectAttributes
	InitializeObjectAttributes(&objectAttributes,
		   &logFileUnicodeString,
		   OBJ_CASE_INSENSITIVE,
		   NULL,
		   NULL);

	//創建文件
	NTSTATUS ntStatus = ZwCreateFile(&hfile,
		                      GENERIC_READ,
							  &objectAttributes,
							  &iostatus,
							  NULL,
							  FILE_ATTRIBUTE_NORMAL,
							  0,
							  FILE_OPEN,
							  FILE_SYNCHRONOUS_IO_NONALERT,
							  NULL,
							  0);
	if (NT_SUCCESS(ntStatus))
	{
		KdPrint(("open file successfully!\n"));
	}

	FILE_STANDARD_INFORMATION fsi;
	//讀取文件長度
	//讀取文件長度
	ntStatus = ZwQueryInformationFile(hfile,
									&iostatus,
									&fsi,
									sizeof(FILE_STANDARD_INFORMATION),
									FileStandardInformation);
	if (NT_SUCCESS(ntStatus))
	{
		KdPrint(("file length:%u\n",fsi.EndOfFile.QuadPart));
	}
	//修改當前文件指針
	FILE_POSITION_INFORMATION fpi;
	fpi.CurrentByteOffset.QuadPart = 100i64;
	ntStatus = ZwSetInformationFile(hfile,
		                             &iostatus,
									 &fpi,
									 sizeof(FILE_POSITION_INFORMATION),
									 FilePositionInformation);
	if (NT_SUCCESS(ntStatus))
	{
		KdPrint(("update the file pointer successfully!\n"));
	}

	//關閉文件句柄
	ZwClose(hfile);

4.文件的寫操作

DDK提供了文件寫操作的內核函數,其聲明如下:

NTSTATUS
 ZwWriteFile( 
           IN HANDLE FileHandle, 
	   IN HANDLE Event OPTIONAL,
	   IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
	   IN PVOID ApcContext OPTIONAL, 
	   OUT PIO_STATUS_BLOCK IoStatusBlock,
	   IN PVOID Buffer, 
	   IN ULONG Length, 
	   IN PLARGE_INTEGER ByteOffset OPTIONAL, 
	   IN PULONG Key OPTIONAL
  );

FileHandle:文件打開句柄

Event:很少用到,一般設置爲NULL

ApcRountine:很少用到,一般設置爲NULL

ApcContext:很少用到,一般設置爲NULL

IoStackBlock:記錄寫操作的狀態

Buffer:從這個緩衝區裏開始寫

Length:準備寫多少字節

ByteOffset:從文件得多少偏移地址開始寫

Key:很少用到,一般設置爲NULL

下面代碼演示如何進行文件寫操作:

OBJECT_ATTRIBUTES objectAttributes;
	IO_STATUS_BLOCK iostatus;
	HANDLE hfile;
	UNICODE_STRING logFileUnicodeString;

	//初始化UNICODE_STRING字符串
	RtlInitUnicodeString(&logFileUnicodeString,
		                   L"\\??\\C:\\1.log");
	//初始化objectAttributes
	InitializeObjectAttributes(&objectAttributes,
		&logFileUnicodeString,
		OBJ_CASE_INSENSITIVE,
		NULL,
		NULL);

	//創建文件
    NTSTATUS ntStatus = ZwCreateFile(&hfile,
		                       GENERIC_WRITE,
							   &objectAttributes,
							   &iostatus,
							   NULL,
							   FILE_ATTRIBUTE_NORMAL,
							   FILE_SHARE_WRITE,
							   FILE_OPEN_IF,
							   FILE_SYNCHRONOUS_IO_NONALERT,
							   NULL,
							   0);
#define BUFFER_SIZE 1024

	PUCHAR pBuffer = (PUCHAR)ExAllocatePool(PagedPool,BUFFER_SIZE);
	//構造要填充的數據
	RtlFillMemory(pBuffer,BUFFER_SIZE,0xAA);

	KdPrint(("The program will write %d bytes\n",BUFFER_SIZE));
	//寫文件
    ZwWriteFile(hfile,NULL,
		NULL,NULL,&iostatus,pBuffer,
		BUFFER_SIZE,NULL,NULL);

	KdPrint(("The program really wrote %d bytes\n",iostatus.Information));

	//構造要填充的數據
	RtlFillMemory(pBuffer,BUFFER_SIZE,0xBB);
    
	KdPrint(("The program will append %d bytes\n",BUFFER_SIZE));
	//追加數據
	LARGE_INTEGER number;
	number.QuadPart = 1024i64;//設置文件指針
	//對文件進行讀寫
	ZwWriteFile(hfile,NULL,NULL,
		NULL,&iostatus,pBuffer,
		BUFFER_SIZE,&number,NULL);

	KdPrint(("The program really append %d bytes\n",iostatus.Information));

	//關閉文件句柄
	ZwClose(hfile);
	ExFreePool(pBuffer);

5文件的讀操作:

DDK提供了文件讀操作的內核函數,其聲明如下:

NTSTATUS
 ZwReadFile( 
           IN HANDLE FileHandle, 
	   IN HANDLE Event OPTIONAL,
	   IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
	   IN PVOID ApcContext OPTIONAL,
	   OUT PIO_STATUS_BLOCK IoStatusBlock,
	   OUT PVOID Buffer,
	   IN ULONG Length,
	   IN PLARGE_INTEGER ByteOffset OPTIONAL, 
	   IN PULONG Key OPTIONAL
	);

下面代碼演示如何在驅動程序中讀文件:

#pragma INITCODE
VOID ReadFileTest()
{
	OBJECT_ATTRIBUTES objectAttributes;
	IO_STATUS_BLOCK iostatus;
	HANDLE hfile;
	UNICODE_STRING logFileUnicodeString;

	//初始化UNICODE_STRING字符串
	RtlInitUnicodeString(&logFileUnicodeString,
		       L"\\??\\C:\\1.log");

	//初始化objectAtrributes
	InitializeObjectAttributes(&objectAttributes,
		&logFileUnicodeString,
		OBJ_CASE_INSENSITIVE,
		NULL,
		NULL);

	//創建文件
	NTSTATUS ntStatus = ZwCreateFile(&hfile,
		                      GENERIC_READ,
		                      &objectAttributes,
							  &iostatus,
							  NULL,
							  FILE_ATTRIBUTE_NORMAL,
							  FILE_SHARE_READ,
							  FILE_OPEN,
							  FILE_SYNCHRONOUS_IO_NONALERT,
							  NULL,
							  0);
	if (!NT_SUCCESS(ntStatus))
	{
		KdPrint(("The file is not exist!\n"));
		return;
	}
	FILE_STANDARD_INFORMATION fsi;
	//讀取文件長度
	ntStatus = ZwQueryInformationFile(hfile,
		                               &iostatus,
									   &fsi,
									   sizeof(FILE_STANDARD_INFORMATION),
									   FileStandardInformation);
	KdPrint(("The program want to read %d bytes\n",fsi.EndOfFile.QuadPart));

	//爲讀取的文件分配緩衝區
	PUCHAR pBuffer = (PUCHAR)ExAllocatePool(PagedPool,
		                                     (LONG)fsi.EndOfFile.QuadPart);

	//讀取文件
	ZwReadFile(hfile,NULL,
		         NULL,NULL,
				 &iostatus,
				 pBuffer,
				 (LONG)fsi.EndOfFile.QuadPart,
				 NULL,NULL);
	
	KdPrint(("The program really read %d bytes\n",iostatus.Information));
	//關閉文件句柄
	ZwClose(hfile);

	//釋放緩衝區
	ExFreePool(pBuffer);


}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章