前言 :
和應用程式一樣, 驅動程式需要經常和字串打交道. 其中包含 ASCII 字串, 寬字串還有 DDK 定義的 ANSI_STRING 資料結構和 UNICODE_STRING 資料結構.
ASCII 字串和寬字串 :
應用程式中, 往往使用兩種字元 : 一種是 char 型的字串, 負責記錄 ANSI 字元集, 它是指向一個 char 陣列的指標, 每個 char 型變數的大小為一個位元組, 字串是以 0 旗標字串結束. 還有一種是 wchar_t 型的寬字串, 負責描述 unicode 字元集的字串, 它是只像一個 wchar_t 陣列的指標, wchar_t 字元大小為兩個位元組, 字串以 0 旗標字串結束. ANSI 字元的使用如下 :
char* str1 = "abc";
str1 指標指向內容是 61 626 300. UNICODE 字元的構造如下 :
wchar_t *str2 = L"abc";
str2 指標指向的內容是 6100 620 063 000 000. 在建構字串的時候使用一個關鍵字 "L" 編譯器會自動生成所需要的寬字元.
在驅動程式開發中, DDK 將 char 和 wchar_t 類別替換成 CHAR 和 WCHAR 類別. 對於這兩類的字串, DDK 提供對應的字串操作函式, 例如 strcpy, sprintf, strcat, strlen 等. 但 DDK 的幫助文檔中, 不會查到這些函式的用法. 微軟公司不鼓勵直接使用這些函式, 取而代之的是使用同樣功能的巨集. 驅動程式可以用 KdPrint 列印 ASCII 字串與寬字串. KdPrint 類似於 C 語言的 printf 函式. 例如列印一個 ASCII 字串/寬字元 :
- CHAR *string="Hello";
- KdPrint(("%s\n", string));
- WCHAR *strings=L"Hello";
- KdPrint(("%S\n",string2));
ANSI_STRING 字串與 UNICODE_STRING 字串 :
DDK 不鼓勵程式設計師使用 C 語言的字串, 主要是因為 : 標準 C 的字串處理函式容易導致緩衝區溢位等錯誤. 如果程式設計師不對字串長度進行檢驗, 很容易導致這個錯誤, 從而導致整個系統的崩潰. DDK 鼓勵程式設計師使用 DDK 自訂的字串, 這種資料格式定義如下 :
- ANSI_STRING Structure :
- typedef struct _STRING {
- USHORT Length;
- USHORT MaximumLength;
- PCHAR Buffer;
- } ANSI_STRING, *PANSI_STRING;
這個資料結構對 ANSII 字串進行了封裝.
參數說明 :
* Length : 字元長度.
* MaximumLength : 整個字串緩衝區的最大長度
* Buffer : 緩衝區指標.
和 ANSI_STRING 相對應, DDK 將寬字串封裝成 UNICODE_STRING 資料結構 :
- UNICODE_STRING Structure :
- typedef struct _UNICODE_STRING {
- USHORT Length;
- USHORT MaximumLength;
- PWSTR Buffer;
- } UNICODE_STRING, *PUNICODE_STRING;
參數說明 :
* Length : 字元長度. 單位是位元組. 如果是 N 個字元, 那麼 Length 等於 N 的 2 倍.
* MaximumLength : 整個字串緩衝區的最大長度
* Buffer : 緩衝區指標.
和 ANSI_STRING 不同的是 UNICODE_STRING 的緩衝區是記錄寬字元的緩衝區. 和 ANSI_STRING 一樣, 字串的結束不是以 NULL 為旗標, 而是依靠欄位 Length. 關於 ANSI_STRING 字串和 UNICODE_STRING 字串, KdPrint 同樣提供了列印 log 的方法 :
- ANSI_STRING ansiString;
-
- KdPrint("%Z\n", &ansiString);
- UNICODE_STRING uniString;
-
- KdPrint("%wZ\n", &uniString);
字元初始化與銷毀 :
ANSI_STRING 字串和 UNICODE_STRING 字串使用前需要進行初始化. 有兩種辦法建構這個資料結構.
(1) 方法一是使用 DDK 提供了對應函式.
初始化 ANSI_STRING 字串函式 RtlInitAnsiString :
- Syntax :
- VOID RtlInitAnsiString(
- __out PANSI_STRING DestinationString,
- __in_opt PCSZ SourceString
- );
參數說明 :
* DestinationString : 要初始化的 ANSI_STRING 字串.
* SourceString : 字串的內容.
初始化 UNICODE_STRING 字串函式 RtlInitUnicodeString :
- Syntax :
- VOID RtlInitUnicodeString(
- __out PUNICODE_STRING DestinationString,
- __in_opt PCWSTR SourceString
- );
參數說明 :
* DestinationString : 要初始化的 UNICODE_STRING字串.
* SourceString : 字串的內容.
以使用 RtlInitAnsiString 為例, 其使用方法是 :
- ANSI_STRING AnsiString1;
- CHAR* string1="hello";
- RtlInitAnsiString(&AnsiString1, string1);
這種辦法是將 AnsString1 中的 Buffer 指標指向 string1 指標指向的地方. 這種初始化的優點是操作簡單, 用完後不用清理記憶體. 但帶來另外一個問題是, 如果修改 string1, 同時會導致 AnsiString1 字元發生變化!! 看下面程式碼 :
- 測試 ANSI_STRING 範例代碼 : 修改 string1 會影響 AnsiString1
-
- ANSI_STRING AnsiString1;
- CHAR * string1= "hello";
-
- RtlInitAnsiString(&AnsiString1,string1);
- KdPrint(("AnsiString1:%Z\n",&AnsiString1));
-
- string1[0]='H';
- string1[1]='E';
- string1[2]='L';
- string1[3]='L';
- string1[4]='O';
-
- KdPrint(("AnsiString1:%Z\n",&AnsiString1));
(2) 另一種方法是程式設計師自己申請記憶體, 並初始化記憶體, 當不用字串時, 需要回收字串佔用的記憶體 :
- 範例代碼 : 程式設計師自己初始化 UNICODE_STRING
- #define BUFFER_SIZE 1024
- UNICODE_STRING UnicodeString1 = {0};
-
- UnicodeString1.MaximumLength = BUFFER_SIZE;
-
- UnicodeString1.Buffer = (PWSTR)ExAllocatePool(PagedPool,BUFFER_SIZE);
- WCHAR* wideString = L"hello";
-
-
- 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;
對於最後一步清理記憶體, DDK 同樣給出了簡化函式, 分別是 RtlFreeAnsiString 和 RtlFreeUnicodeString, 這兩個函式內部呼叫了 ExFreePool 去回收記憶體.
字串複製 :
DDK 提供針對 ANSI_STRING 字串和 UNICODE_STRING 字串的複製字串命令, 分別是 RtlCopyString 與 RtlCopyUnicodeString. 下面程式演示了如何使用 RtlCopyUnicodeString 與 RtlCopyString 函式的使用方法 :
- 範例代碼 : For RtlCopyUnicodeString 函式 & RtlCopyString 函式
- #pragma INITCODE
- VOID StringCopyTest()
- {
-
- UNICODE_STRING UnicodeString1;
- RtlInitUnicodeString(&UnicodeString1,L"Hello World");
-
-
- UNICODE_STRING UnicodeString2={0};
- UnicodeString2.Buffer = (PWSTR)ExAllocatePool(PagedPool,BUFFER_SIZE);
- UnicodeString2.MaximumLength = BUFFER_SIZE;
-
-
- RtlCopyUnicodeString(&UnicodeString2,&UnicodeString1);
-
-
- KdPrint(("UnicodeString1:%wZ\n",&UnicodeString1));
- KdPrint(("UnicodeString2:%wZ\n",&UnicodeString2));
-
-
-
- RtlFreeUnicodeString(&UnicodeString2);
- }
字串比較 :
DDK 提供了對 ANSI_STRING 字串和 UNICODE_STRING 字串的相關字串比較的命令, 分別是 RtlCompareString 與 RtlCompareUnicodeString. 這兩個函式的參數型式相同, 以 RtlCompareUnicodeString 為例, 前面兩個參數分別是需要比較的字串, 第三個參數指定是否對大小寫敏感. 如果函式返回值為 0, 表示兩個字串相等. 如果小於零, 則表示第一個字串小於第二個字串. 反之亦然.
同時DDK 又有提供了 RtlEqualString 和 RtlEqualUnicodeString 函式, 其使用方法和上面兩個函式類似, 只是返回非零代表相等, 零代表不相等. 下面程式演示了如何使用 RtlCompareUnicodeString 函式 :
- RtlCompareUnicodeString 使用範例代碼 :
- #pragma INITCODE
- VOID StringCompareTest()
- {
-
- UNICODE_STRING UnicodeString1;
- RtlInitUnicodeString(&UnicodeString1,L"Hello World");
-
-
- UNICODE_STRING UnicodeString2;
- RtlInitUnicodeString(&UnicodeString1,L"Hello");
-
- if (RtlEqualUnicodeString(&UnicodeString1,&UnicodeString2,TRUE))
- {
- KdPrint(("UnicodeString1 and UnicodeString2 are equal\n"));
- }else
- {
- KdPrint(("UnicodeString1 and UnicodeString2 are NOT equal\n"));
- }
-
- }
字串轉化成大寫 :
DDK 提供了對 ANSI_STRING 字串和 UNICODE_STRING 字串的大小寫轉換函式 RtlUpcaseUnicodeString 與 RtlUpperString. 下面代碼演示如何使用 RtlUpcaseUnicodeString 函式 :
- RtlUpcaseUnicodeString函式範例代碼 :
- #pragma INITCODE
- VOID StringToUpperTest()
- {
-
- UNICODE_STRING UnicodeString1;
- RtlInitUnicodeString(&UnicodeString1,L"Hello World");
-
-
- KdPrint(("UnicodeString1:%wZ\n",&UnicodeString1));
-
-
- RtlUpcaseUnicodeString(&UnicodeString1,&UnicodeString1,FALSE);
-
-
- KdPrint(("UnicodeString1:%wZ\n",&UnicodeString1));
- }
字串與整數型數字相互轉換 :
DDK 提供了 UNICODE_STRING 字串與整數相互轉換的內核函式.
(1) 將 UNICODE_STRING 字串轉換成整數.
這個函式是 RtlUnicodeStringToInteger, 其宣告是 :
- Syntax :
- NTSTATUS RtlUnicodeStringToInteger(
- __in PCUNICODE_STRING String,
- __in_opt ULONG Base,
- __out PULONG Value
- );
(2) 將整數轉換成 UNICODE_STRING 字串.
這個函式是 RtlIntegerToUnicodeString, 其宣告是 :
- Syntax :
- NTSTATUS RtlIntegerToUnicodeString(
- __in ULONG Value,
- __in_opt ULONG Base,
- __inout PUNICODE_STRING String
- );
以下是字串與整數互相轉換的範例 :
- 字串與整數互相轉換範例代碼 :
- #pragma INITCODE
- VOID StringToIntegerTest()
- {
-
-
- UNICODE_STRING UnicodeString1;
- RtlInitUnicodeString(&UnicodeString1,L"-100");
-
- ULONG lNumber;
- NTSTATUS nStatus = RtlUnicodeStringToInteger(&UnicodeString1,10,&lNumber);
- if ( NT_SUCCESS(nStatus))
- {
- KdPrint(("Conver to integer succussfully!\n"));
- KdPrint(("Result:%d\n",lNumber));
- }else
- {
- KdPrint(("Conver 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 succussfully!\n"));
- KdPrint(("Result:%wZ\n",&UnicodeString2));
- }else
- {
- KdPrint(("Conver to string unsuccessfully!\n"));
- }
-
-
-
- RtlFreeUnicodeString(&UnicodeString2);
-
- }
ANSI_STRING 字串與 UNICODE_STRING 字串相互轉換 :
DDK 提供了 ANSI_STRING 字串與 UNICODE_STRING 字串相互轉換的相關函式.
(1) 將 UNICODE_STRING 字串轉成 ANSI_STRING 字串.
對應轉換函式是 RtlUnicodeStringToAnsiString, 其宣告是 :
- Syntax :
- NTSTATUS RtlUnicodeStringToAnsiString(
- __inout PANSI_STRING DestinationString,
- __in PCUNICODE_STRING SourceString,
- __in BOOLEAN AllocateDestinationString
- );
(2) 將 ANSI_STRING 字串轉換成 UNICODE_STRING 字串
對應轉換函式是 RtlAnsiStringToUnicodeString, 其宣告是 :
- Syntax :
- NTSTATUS RtlAnsiStringToUnicodeString(
- __inout PUNICODE_STRING DestinationString,
- __in PCANSI_STRING SourceString,
- __in BOOLEAN AllocateDestinationString
- );
底下程式碼演是兩種字串間的轉換 :
- 範例代碼 :
- #pragma INITCODE
- VOID StringConverTest()
- {
-
-
- UNICODE_STRING UnicodeString1;
- RtlInitUnicodeString(&UnicodeString1,L"Hello World");
-
- ANSI_STRING AnsiString1;
- NTSTATUS nStatus = RtlUnicodeStringToAnsiString(&AnsiString1,&UnicodeString1,TRUE);
-
- if ( NT_SUCCESS(nStatus))
- {
- KdPrint(("Conver succussfully!\n"));
- KdPrint(("Result:%Z\n",&AnsiString1));
- }else
- {
- KdPrint(("Conver unsuccessfully!\n"));
- }
-
-
- RtlFreeAnsiString(&AnsiString1);
-
-
-
- ANSI_STRING AnsiString2;
- RtlInitString(&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"));
- }
-
-
- RtlFreeUnicodeString(&UnicodeString2);
- }
沒有留言:
張貼留言