【Windows核心驅動開發】——讀取登錄檔
【我的】Windows驅動開發——讀取登錄檔
作者:zcr214 時間:2016/5/5
登錄檔對於驅動來說是很重要的小夥伴,登錄檔可以很好的扮演使用者到核心的橋樑角色,很多時候使用者可以通過修改登錄檔的內容來達到控制驅動的目的。那麼驅動要做的首先當然是讀取到登錄檔啦,WDK提供了標準的介面函式,所以直接使用就可以了。
1. ZwOpenKey()開啟登錄檔
WDK提供了開啟登錄檔的介面函式ZwOpenKey,其原型如下:
NTSTATUS
NTAPI
ZwOpenKey(
_Out_PHANDLE KeyHandle,
_In_ACCESS_MASK DesiredAccess,
_In_
);
這個函式將得到一個開啟登錄檔的操作控制代碼指標,儲存在KeyHandle,並返回狀態值。
需要ACCESS_MASK來指定開啟該登錄檔的許可權,讀許可權對應KEY_READ,寫許可權對應KEY_WRITE,如果需要全部許可權則是KEY_ALL_ACCESS。
從第三個引數可以看出,它不是接受一個字串來表示一個登錄檔項,而是要求輸入一個OBJECT_ATTRIBUTES的指標,這需要我們提前初始化一個OBJECT_ATTRIBUTES。下面舉個初始化的例子:
OBJECT_ATTRIBUTESobj_attr={0};
InitializeObjectAttributes
&obj_attr,
Reg_Key_Path,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
其中obj_attr是初始化的OBJECT_ATTRIBUTES,Reg_Key_Path是登錄檔項的路徑,它是一個UNICODE_STRING字串,如UNICODE_STRINGReg_key_path=RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\ControlSet001\\services\\SwapBuffers\\");其餘三個引數表示大小寫敏感和安全描述符,一般情況下就按照這個例子這樣填寫即可。
初始化完畢後就可以開啟登錄檔了。
status=ZwOpenKey(&Reg_Handle,KEY_READ,&obj_attr);
2. ZwQueryValueKey()讀取鍵值
讀取登錄檔子鍵的值,使用ZwQueryValueKey(),其原型如下:
NTSTATUS
NTAPI
ZwQueryValueKey(
_In_HANDLE KeyHandle,
_In_PUNICODE_STRING ValueName,
_In_KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
_Out_writes_bytes_opt_(Length)PVOIDKeyValueInformation,
_In_ULONG Length,
_Out_PULONG ResultLength
);
KeyHandle是之前使用ZwOpenKey開啟的登錄檔操作控制代碼。
ValueName是要讀取的子鍵的名字。
KeyValueInformationClass是獲取的資訊型別,包括Basic,Full,Partial三種,Basic資訊包含子鍵名和型別,Full包含子鍵名,型別和值,Partial包含型別和值,一般的我們讀取登錄檔顯然已經知道了名字,是為了得到資料型別和值,因此獲取Partial資訊最常用。即KeyValuePartialInformation。
KeyValueInformation指標所指的記憶體,用於儲存函式返回來的登錄檔鍵值資訊,是一個_KEY_VALUE_PARTIAL_INFORMATION結構,其原型如下:
typedefstruct_KEY_VALUE_PARTIAL_INFORMATION{
ULONGTitleIndex;
ULONGType;
ULONGDataLength;
_Field_size_bytes_(DataLength)UCHARData[1];//Variable size
}KEY_VALUE_PARTIAL_INFORMATION,*PKEY_VALUE_PARTIAL_INFORMATION;
Length是使用者指定的輸出空間KeyValueInformation的長度。
ResultLength是返回回來的實際需要的長度。
通過分析這個函式可以知道,主要問題存在於讀取長度這裡,如果為了方便總是定義一個足夠大的空間,這樣勢必造成記憶體的浪費,所以實際應用中,應該耐心的獲取合適的長度,不足時再動態分配記憶體,所以應如下讀取鍵值。
//用來試探大小的Reg_try_info
KEY_VALUE_PARTIAL_INFORMATIONReg_try_info;
//實際獲取的Reg_info
PKEY_VALUE_PARTIAL_INFORMATIONReg_info;
ULONGReal_length;
UNICODE_STRINGReg_key_Name=RTL_CONSTANT_STRING(L"ChosenVolume");
//下面開始試圖讀取值
status=ZwQueryValueKey(
Reg_Handle,
Reg_Key_Name,
KeyValuePartialInformation,
&Reg_try_info,
sizeof(KEY_VALUE_PARTIAL_INFORMATION),
&Real_length);
if(!NT_SUCCESS(status)
&&status!=STATUS_BUFFER_OVERFLOW
&&status!=STATUS_BUFFER_TOO_SMALL)
{
//錯誤處理
DbgPrint("getRegistryValue:開啟登錄檔鍵值失敗");
ZwClose(Reg_Handle);
returnNULL;
}
//如果讀取成功,則分配足夠的空間再次讀取
Reg_info=(PKEY_VALUE_PARTIAL_INFORMATION)
ExAllocatePoolWithTag(NonPagedPool,Real_length,NAME_TAG);
if(Reg_info==NULL)
{
//錯誤處理
DbgPrint("getRegistryValue:開啟登錄檔鍵值對指標為空");
ZwClose(Reg_Handle);
returnNULL;
}
status=ZwQueryValueKey(
Reg_Handle,
Reg_Key_Name,
KeyValuePartialInformation,
Reg_info,
Real_length,
&Real_length);
ZwClose(Reg_Handle);
所有的操作完畢,如果此時status為成功,那麼登錄檔資訊已經儲存在Reg_info->Data中了,它是UCHAR型別的可變長度陣列,下面只需要再轉化成需要的字串型別PCHAR,UNICODE_STRING等即可。
3. 注意的問題
傳入函式的所有型別的引數都要嚴格的初始化定義,最有可能出問題的地方就是UNICODE_STRING字串。
例如在初始化OBJECT_ATTRIBUTE或ZwQueryValueKey時,如果Reg_Key_Path或Reg_Key_Name並不是一個靜態定義好的UNICODE_STRING,而是作為函式引數傳遞過來的,它的Buffer實際長度和Length指定的長度一定要吻合,否則將會出現錯誤,導致登錄檔開啟失敗。