一、前言
大量的系統安全問題是由於薄弱的緩沖處理以及由此產生的緩沖區溢出造成的,而薄弱的緩沖區處理常常與字符串操作相關。c/c++語言運行庫提供的標准字符串操作函數(strcpy, strcat, sprintf等)不能阻止在超出字符串尾端的寫入。
基於Windows XP SP1以及隨后的操作系統的Windows DDK版本提供了安全字符串函數(safe string functions)。這類函數被設計的目的是用來取代相同功能的c/c++標准函數和其它微軟提供的庫函數。這類函數具有以下特征:
- 每個函數以目標緩沖區所占的字節大小作為其一個輸入參數,因此可以保證在寫入時不會超出緩沖區末端。
- 每個函數的輸出字符串均以NULL結尾(null-terminate),即使該函數可能會對正確的結果進行截斷。
- 所有函數均有返回值,類型為NTSTATUS,只有返回STATUS_SUCCESS時,操作結果才正確。
- 每個函數均有兩種類型的版本,按字節或者按字符數。例如,RtlStringCbCatW和RtlStringCchCatW。
- 每個函數均有支持雙字節的unicode字符(以W作為后綴)和單字節的ANSI字符(以A作為后綴)的版本。例如:RtlStringCbCatW和RtlStringCbCatA。
- 大部分函數有提供擴展版本的函數(以Ex作為后綴),例如,RtlStringCbCatW和RtlStringCbCatExW。
二、如何在內核驅動代碼中引入安全字符串函數
有兩種方式可以引入安全字符串函數:
l 以內聯的方式引入,包含在ntstrsafe.h中
l 在鏈接時以庫的方式引入
其中,如果代碼需要在系統為Windows XP及以后版本運行時,可以使用內聯的方式;如果代碼需要運行在早於Windows XP時,則必須使用鏈接庫的方式。
以內聯方式引入
只需包含頭文件即可
#include <ntstrsafe.h>
以鏈接庫的方式
- 在包含頭文件之前先定義宏
#define NTSTRSAFE_LIB
#include <ntstrsafe.h>
- 在項目的sources文件中,添加一TARGETLIBS條目如下: $(DDK_LIB_PATH)\ntstrsafe.lib.
在默認情況下,當引入了安全字符串函數后,那些被取代的c/c++運行庫函數將變得無效,編譯是會報錯,提示需要使用安全字符串函數。
如果還希望繼續使用c/c++運行庫函數,即在使用安全字符串函數的時候,c/c++運行庫函數還可以繼續使用,則需要在包含ntstrsafe.h之前先定義宏NTSTRSAFE_NO_DEPRECATE
#define NTSTRSAFE_NO_DEPRECATE
The maximum number of characters that any ANSI or Unicode string can contain is STRSAFE_MAX_CCH. This constant is defined in ntstrsafe.h.
字符串最長長度為STRSAFE_MAX_CCH,該宏在ntstrsafe.h中定義。另外,如果一個字符串需要被轉換成UNICODE_STRING結構,則該字符串長度不能超過65535.
三、內核模式安全字符串函數概述
下表概述了可以在內核驅動中使用的安全字符串函數,並指明了它們用來何種類型的c/c++運行庫函數。
說明:
函數名含有Cb的是以字節數為單位,含有Cch的是以字符數為單位。
函數名 |
作用 |
取代 |
RtlStringCbCat |
將源字符串連接到目的字符串的末尾 |
strcat |
RtlStringCbCatN |
將源字符串指定數目的字符連接到目的字符串的末尾 |
strncat |
RtlStringCbCopy |
將源字符串拷貝到目的字符串 |
strcpy |
RtlStringCbCopyN |
將源字符串指定數目的字符拷貝到目的字符串 |
strncpy |
RtlStringCbLength |
確定字符串的長度 |
strlen |
RtlStringCbPrintf |
格式化輸出 |
sprintf |
RtlStringCbVPrintf |
可變格式化輸出 |
vsprintf |
各個函數的作用可以通過它所取代的c/c++函數可以大概看出,具體用法請查閱DDK幫助文檔。
驅動中使用的字符串操作函數 ,這里給出ANSI和UNICODE的對比
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ansi轉換Unicode
RtlAnsiStringToUnicodeString
Unicode轉換Ansi
RtlUnicodeStringToAnsiString
以上兩個函數第三個參數都為True時需要使用RtlFreeUnicodeString/RtlFreeAnsiString來釋放空間。我設置過False 但是測試時候 第一次可以 第二次就藍屏了。如果讀者知道為什么 可以在下方評論留言給我。謝謝
KdPrint輸出unicode_string類型字符串使用 %wZ
// convert the name to UNICODE_STRING
wchar_t name[100];
auto status =
RtlStringCchPrintfW(name, RTL_NUMBER_OF(name), L"%S", export_name);
if (!NT_SUCCESS(status)) {
return true;
}
UNICODE_STRING name_u = {};
RtlInitUnicodeString(&name_u, name);