1. 程式人生 > >【Windows核心驅動開發】——讀取登錄檔

【Windows核心驅動開發】——讀取登錄檔

【我的】Windows驅動開發——讀取登錄檔

作者:zcr214 時間:2016/5/5

登錄檔對於驅動來說是很重要的小夥伴,登錄檔可以很好的扮演使用者到核心的橋樑角色,很多時候使用者可以通過修改登錄檔的內容來達到控制驅動的目的。那麼驅動要做的首先當然是讀取到登錄檔啦,WDK提供了標準的介面函式,所以直接使用就可以了。

1.    ZwOpenKey()開啟登錄檔

WDK提供了開啟登錄檔的介面函式ZwOpenKey,其原型如下:

NTSTATUS

NTAPI

ZwOpenKey(

_Out_PHANDLE KeyHandle,

_In_ACCESS_MASK DesiredAccess,

_In_

POBJECT_ATTRIBUTES ObjectAttributes

);

這個函式將得到一個開啟登錄檔的操作控制代碼指標,儲存在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指定的長度一定要吻合,否則將會出現錯誤,導致登錄檔開啟失敗。