天天看點

Win64 驅動核心程式設計-4.核心裡操作字元串

核心裡操作字元串

樣了,細數下來竟然有 4 種字元串!這四種字元串,分别是:CHAR*、WCHAR*、ANSI_STRING、UNICODE_STRING。當然,核心裡使用頻率最多的是 UNICODE_STRING,其次是 WCHAR*,再次是 CHAR*,而 ANSI_STRING,則幾乎沒見過有什麼核心函數使用。

  但其實這四種字元串也不是完全獨立的,ANSI_STRING可以看成是CHAR*的安全性擴充,UNICODE_STRING 可以看成是 WCHAR*的安全性擴充。CHAR* , 可以 了解成 成 CHAR  數組, 本質上來說 是一個位址, 它 的有效長度是多少?不知道 。 字元串 有多長?不清楚 , 遇到\0  就當成是 字元串 的 結尾。綜上所述,CHAR*的安全性是非常糟糕的,如果核心使用 CHAR*作為主要字元串,那麼會非常不穩定。WCHAR*和 和 CHAR* 類似 ,和 和 CHAR* 的唯一不同在于 一個WCHAR 占 占 2  個位元組,而一個 CHAR 隻占 1 個位元組。是以,WCHAR* 的安全性也是非常糟糕的。微軟為了安全性着想,推出了這兩種字元串的擴充集,ANSI_STRING和UNICODE_STRING。ANSI_STRING 和 UNICODE_STRING 都是結構體,定義如下:

Win64 驅動核心程式設計-4.核心裡操作字元串

  可以看到,ANSI_STRING 和 UNICODE_STRING 的結構體大小是一樣的,唯一不同在于第三個成員,他們分别對應 CHAR*和 WCHAR*。它們的第一和第二個成員分别代表字元串的長度和記憶體的有效長度。比如 BUFFER 的長度是 260 位元組,而 BUFFER 隻是一個單詞”WERT”,則 ANSI_STRING 的 Length=4、MaximumLength=260;UNICODE_STRING 的 Length=8、MaximumLength=260。另外需要注意的是,CHAR*和 和 WCHAR* 都是以 0  結尾的 (CHAR*以 以 1個\0  結尾 ,WCHAR*以 以 2  個\0  結尾 ),但 但 ANSI_STRING 和 和 UNICODE_STRING  不一定以 以 0  結尾。 因為 有了長度的說明,就不需要用特殊辨別符來表示結尾了 . 有些自以為是 的人直接把ANSI_STRING 或 或 UNICODE_STRING 的 的 BUFFER  當作字元串用,是極端錯誤的。

  在核心裡,大部分的 C 語言字元串函數都是可以用的,無論是寬版的還是窄版的。比如核心裡可以照樣使用 strcpy 和 wcscpy,但某些字元串函數簽名可能要加上一個下劃線。比如 strlwr 要寫成_strlwr。ANSI_STRING 和 UNICODE_STRING 有一套專門的字元串函數(http://msdn.microsoft.com/en-us/library/windows/hardware/ff563638(v=vs.85).aspx),如果需要查詢怎麼使用,就用浏覽器打開,搜尋 AnsiString 或者 UnicodeString,并點選進去檢視說明即可。關于 CHAR*和 WCHAR*的字元串操作就不講了,不懂的請看任意的 C 語言教程。下面舉幾個關于 UNICODE_STRING 的例子(以下代碼借用了張帆寫的示例,特此聲明感謝)。1. 字元串初始化、2. 字元串拷貝 、3. 字元串比較 、4. 字元串變大寫 、5. 字元串與整型互相轉化 、6. ANSI_STRING  字元串與 UNICODE_STRING  字元串互相轉換。

//1.字元串初始化
VOID StringInitTest()
{
//(1)用 RtlInitAnsiString 初始化字元串
ANSI_STRING AnsiString1;
CHAR string1[] = "hello";//此處注意,原版的這裡面寫的是CHAR *,這樣的話會導緻改值失敗。
//初始化 ANSI_STRING 字元串
RtlInitAnsiString(&AnsiString1, string1);
DbgPrint("AnsiString1:%Z\n", &AnsiString1);//列印 hello
string1[0] = 'H';
string1[1] = 'E';
string1[2] = 'L';
string1[3] = 'L';
string1[4] = 'O';
//改變 string1,AnsiString1 同樣會導緻變化
DbgPrint("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);
DbgPrint("UnicodeString:%wZ\n", &UnicodeString1);
//清理記憶體
ExFreePool(UnicodeString1.Buffer);
UnicodeString1.Buffer = NULL;
UnicodeString1.Length = UnicodeString1.MaximumLength = 0;
}
//2.字元串拷貝
VOID StringCopyTest()
{
//初始化 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
DbgPrint("UnicodeString1:%wZ\n", &UnicodeString1);
DbgPrint("UnicodeString2:%wZ\n", &UnicodeString2);
//銷毀 UnicodeString2
//注意!!UnicodeString1 不用銷毀
RtlFreeUnicodeString(&UnicodeString2);
}
 
//3.字元串比較
VOID StringCompareTest()
{
//初始化 UnicodeString1
UNICODE_STRING UnicodeString1;
RtlInitUnicodeString(&UnicodeString1, L"Hello World");
//初始化 UnicodeString2
UNICODE_STRING UnicodeString2;
RtlInitUnicodeString(&UnicodeString1, L"Hello");
if (RtlEqualUnicodeString(&UnicodeString1, &UnicodeString2, TRUE))
DbgPrint("UnicodeString1 and UnicodeString2 are equal\n");
else
DbgPrint("UnicodeString1 and UnicodeString2 are NOT equal\n");
}
 
//4.字元串變大寫
VOID StringToUpperTest()
{
//初始化 UnicodeString1
UNICODE_STRING UnicodeString1;
UNICODE_STRING UnicodeString2;
RtlInitUnicodeString(&UnicodeString1, L"Hello World");
//變化前
DbgPrint("UnicodeString1:%wZ\n", &UnicodeString1);
//變大寫
RtlUpcaseUnicodeString(&UnicodeString2, &UnicodeString1, TRUE);
//變化後
DbgPrint("UnicodeString2:%wZ\n", &UnicodeString2);
//銷毀 UnicodeString2(UnicodeString1 不用銷毀)
RtlFreeUnicodeString(&UnicodeString2);
}
//5.字元串與整型互相轉化
VOID StringToIntegerTest()
{
//(1)字元串轉換成數字
//初始化 UnicodeString1
UNICODE_STRING UnicodeString1;
RtlInitUnicodeString(&UnicodeString1, L"-100");
ULONG lNumber;
NTSTATUS nStatus = RtlUnicodeStringToInteger(&UnicodeString1, 10, &lNumber);
if (NT_SUCCESS(nStatus))
{
DbgPrint("Conver to integer succussfully!\n");
DbgPrint("Result:%d\n", lNumber);
}
else
{
DbgPrint("Conver to integer unsuccessfully!\n");
}
//(2)數字轉換成字元串
//初始化 UnicodeString2
UNICODE_STRING UnicodeString2 = { 0 };
UnicodeString2.Buffer = (PWSTR)ExAllocatePool(PagedPool, BUFFER_SIZE);
UnicodeString2.MaximumLength = BUFFER_SIZE;
nStatus = RtlIntegerToUnicodeString(200, 10, &UnicodeString2);
if (NT_SUCCESS(nStatus))
{
DbgPrint("Conver to string succussfully!\n");
DbgPrint("Result:%wZ\n", &UnicodeString2);
}
else
{
DbgPrint("Conver to string unsuccessfully!\n");
}
//銷毀 UnicodeString2
//注意!!UnicodeString1 不用銷毀
RtlFreeUnicodeString(&UnicodeString2);
}
//6. ANSI_STRING 字元串與 UNICODE_STRING 字元串互相轉換
VOID StringConverTest()
{
//(1)将 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))
{
DbgPrint("Conver succussfully!\n");
DbgPrint("Result:%Z\n", &AnsiString1);
}
else
{
DbgPrint("Conver unsuccessfully!\n");
}
//銷毀 AnsiString1
RtlFreeAnsiString(&AnsiString1);
//(2)将 ANSI_STRING 字元串轉換成 UNICODE_STRING 字元串
//初始化 AnsiString2
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");
}
//銷毀 UnicodeString2
RtlFreeUnicodeString(&UnicodeString2);
}      

測試結果:

Win64 驅動核心程式設計-4.核心裡操作字元串

1.緩沖區長度溢出;2.操作的指針無效。是以大家以後在做項目時,遇到需要操作字元串的場景還是要格外當心。

最後再加上看的資料的那個作者自己寫的三個函數,留着筆記:

//UNICODE_STRINGz 轉換為 CHAR*

//輸入 UNICODE_STRING 的指針,輸出窄字元串,BUFFER 需要已經配置設定好空間

VOID UnicodeToChar(PUNICODE_STRING dst, char *src)
{
ANSI_STRING string;
RtlUnicodeStringToAnsiString(&string,dst, TRUE);
strcpy(src,string.Buffer);
RtlFreeAnsiString(&string);
}
 
//WCHAR*轉換為 CHAR*
//輸入寬字元串首位址,輸出窄字元串,BUFFER 需要已經配置設定好空間
VOID WcharToChar(PWCHAR src, PCHAR dst)
{
UNICODE_STRING uString;
ANSI_STRING aString;
RtlInitUnicodeString(&uString,src);
RtlUnicodeStringToAnsiString(&aString,&uString,TRUE);
strcpy(dst,aString.Buffer);
RtlFreeAnsiString(&aString);
}
//CHAR*轉 WCHAR*
//輸入窄字元串首位址,輸出寬字元串,BUFFER 需要已經配置設定好空間
VOID CharToWchar(PCHAR src, PWCHAR dst)
{
UNICODE_STRING uString;
ANSI_STRING aString;
RtlInitAnsiString(&aString,src);
RtlAnsiStringToUnicodeString(&uString,&aString,TRUE);
wcscpy(dst,uString.Buffer);
RtlFreeUnicodeString(&uString);
}