在驅動程式開發中, 經常會用到對登錄表的操作. 與 Win32 API 不同, DDK 提供另外一套對登錄表操作的相關函式. 首先明確一下登錄表裡的幾個概念 :
* 登錄表項目 : 登錄表中的一個專案, 類似目錄的概念. 每個項目儲存多個二元結構, 鍵名-鍵值. 每個項目可以有若干個子項.
* 登錄表子項 : 類似於目錄中的子目錄.
* 鍵名 : 透過鍵名可以尋找到對應的鍵值.
* 鍵值類型 : 每個鍵值儲存的時候有不同類型, 可以是整數或是字串等資料.
* 鍵值 : 鍵名下對應儲存的資料.
新建關閉登錄表 :
和檔案操作類似, 對登錄表操作首先要先獲取一個登錄表控制碼. 對登錄表的操作都需要根據這個控制碼進行操作. 可以透過 ZwCreateKey 函式獲得打開的登錄表控制碼. 這個函式打開登錄表後, 返回一個操作控制碼. 其宣告如下 :
- Syntax :
- NTSTATUS ZwCreateKey(
- __out PHANDLE KeyHandle,
- __in ACCESS_MASK DesiredAccess,
- __in POBJECT_ATTRIBUTES ObjectAttributes,
- __reserved ULONG TitleIndex,
- __in_opt PUNICODE_STRING Class,
- __in ULONG CreateOptions,
- __out_opt PULONG Disposition
- );
參數說明 :
* KeyHandle : 獲得登錄表的控制碼
* DesiredAccess : 存取權限, 一般設置為 KEY_ALL_ACCESS.
* ObjectAttributes : OBJECT_ATTRIBUTES 資料結構.
* TitleIndex : 很少用到, 一般設置為 0.
* Class : 一般很少用到, 一般設置為 NULL.
* CreateOptions : 新建時的選項, 一般是設置為 REG_OPTION_NON_VOLATILE.
* Disposition : 返回是新建成功還是打開成功. 返回值是 REG_CREATED_NEW_KEY 或者是 REG_OPENED_EXISTING_KEY.
如果 ZwCreateKey 指定的項目不存在, 則直接新建這個項目, 並利用 Disposition 參數返回 REG_CREATED_NEW_KEY. 如果該專案已經存在, Disposition 參數返回 REG_OPENED_EXISTING_KEY. 以下程式演示如何使用 ZwCreateKey 函式打開登錄表, 在這裡透過 巨集 InitializeObjectAttributes 初始化 OBJECT_ATTRIBUTES 資料結構 :
- Driver.cpp (CreateRegTest 函式) : 對登陸表進行開啟關閉操作
- #define MY_REG_SOFTWARE_KEY_NAME L"\\Registry\\Machine\\Software\\Zhangfan"
- #pragma INITCODE
- VOID CreateRegTest()
- {
- //新建或打開某登錄表項目
- UNICODE_STRING RegUnicodeString;
- HANDLE hRegister;
- //初始化UNICODE_STRING字串
- RtlInitUnicodeString( &RegUnicodeString,
- MY_REG_SOFTWARE_KEY_NAME);
- OBJECT_ATTRIBUTES objectAttributes;
- //初始化objectAttributes
- InitializeObjectAttributes(&objectAttributes,
- &RegUnicodeString,
- OBJ_CASE_INSENSITIVE,//對大小寫敏感
- NULL,
- NULL );
- ULONG ulResult;
- //新建或帶開登錄表項目
- NTSTATUS ntStatus = ZwCreateKey( &hRegister,
- KEY_ALL_ACCESS,
- &objectAttributes,
- 0,
- NULL,
- REG_OPTION_NON_VOLATILE,
- &ulResult);
- if (NT_SUCCESS(ntStatus))
- {
- //判斷是被新新建,還是已經被新建
- if(ulResult==REG_CREATED_NEW_KEY)
- {
- KdPrint(("The register item is created\n"));
- }else if(ulResult==REG_OPENED_EXISTING_KEY)
- {
- KdPrint(("The register item has been created,and now is opened\n"));
- }
- }
- //(2)新建或打開某登錄表項目的子項
- UNICODE_STRING subRegUnicodeString;
- HANDLE hSubRegister;
- //初始化UNICODE_STRING字串
- RtlInitUnicodeString( &subRegUnicodeString,
- L"SubItem");
- OBJECT_ATTRIBUTES subObjectAttributes;
- //初始化subObjectAttributes
- InitializeObjectAttributes(&subObjectAttributes,
- &subRegUnicodeString,
- OBJ_CASE_INSENSITIVE,//對大小寫敏感
- hRegister,
- NULL );
- //新建或帶開登錄表項目
- ntStatus = ZwCreateKey( &hSubRegister,
- KEY_ALL_ACCESS,
- &subObjectAttributes,
- 0,
- NULL,
- REG_OPTION_NON_VOLATILE,
- &ulResult);
- if (NT_SUCCESS(ntStatus))
- {
- //判斷是被新建,還是已經被新建
- if(ulResult==REG_CREATED_NEW_KEY)
- {
- KdPrint(("The sub register item is created\n"));
- }else if(ulResult==REG_OPENED_EXISTING_KEY)
- {
- KdPrint(("The sub register item has been created,and now is opened\n"));
- }
- }
- //關閉登錄表控制碼
- ZwClose(hRegister);
- ZwClose(hSubRegister);
- }
底下是執行結果 :
打開登錄表 :
ZwCreateKey 函式既可以新建登錄表項, 也可以打開登錄表. 為了簡化打開登錄表項, DDK 提供了內核函式 ZwOpenKey 以簡化打開操作. 如果 ZwOpenKey 指定的項目不存在, 並不會新增這個項目, 而是返回一個錯誤狀態. 該函式宣告如下 :
- Syntax :
- NTSTATUS ZwOpenKey(
- __out PHANDLE KeyHandle,
- __in ACCESS_MASK DesiredAccess,
- __in POBJECT_ATTRIBUTES ObjectAttributes
- );
參數說明 :
* KeyHandle : 返回被打開的控制碼
* DesiredAccess : 打開的權限, 一般設為 KEY_ALL_ACCESS
* ObjectAttributes : OBJECT_ATTRIBUTES 資料結構, 指示打開的狀態.
ZwOpenKey 的參數比 ZwCreateKey 的參數簡化, 程式設計師使用起來比較方便, 下面代碼演示如何使用 ZwOpenKey 打開登錄表 :
- Driver.cpp (OpenRegTest 函式) : 打開登錄表
- #pragma INITCODE
- VOID OpenRegTest()
- {
- UNICODE_STRING RegUnicodeString;
- HANDLE hRegister;
- //初始化UNICODE_STRING字串
- RtlInitUnicodeString( &RegUnicodeString,
- MY_REG_SOFTWARE_KEY_NAME);
- OBJECT_ATTRIBUTES objectAttributes;
- //初始化objectAttributes
- InitializeObjectAttributes(&objectAttributes,
- &RegUnicodeString,
- OBJ_CASE_INSENSITIVE,//對大小寫敏感
- NULL,
- NULL );
- //打開登錄表
- NTSTATUS ntStatus = ZwOpenKey( &hRegister,
- KEY_ALL_ACCESS,
- &objectAttributes);
- if (NT_SUCCESS(ntStatus))
- {
- KdPrint(("Open register successfully\n"));
- }
- ZwClose(hRegister);
- }
底下是執行結果 :
添加, 修改登錄表鍵值 :
打開登錄表的控制碼後, 就可以對該項進行設置和修改了. 登錄表示以二元型式儲存的, 即 "鍵名" 與 "鍵值". 透過鍵名設定鍵值, 而鍵值可以劃分為以下幾類 :
在添加和修改登錄表鍵值的時候, 要分類進行添加與修改. DDK 提供了 ZwSetValueKey 函式 來完成這個任務, 其宣告如下 :
- Syntax :
- NTSTATUS ZwSetValueKey(
- __in HANDLE KeyHandle,
- __in PUNICODE_STRING ValueName,
- __in_opt ULONG TitleIndex,
- __in ULONG Type,
- __in_opt PVOID Data,
- __in ULONG DataSize
- );
參數說明 :
* KeyHandle : 登錄表控制碼
* ValueName : 要新建或修改的鍵名
* TitleIndex : 很少用, 一般設為 0.
* Type : 在上表選擇一種類型.
* DataSize : 記錄鍵值資料的大小
使用 ZwSetValueKey 函式的時候, 如果指定的鍵名不存在, 則直接新建. 如果指定鍵名已經存在, 則對已有鍵值進行修改. 當新建或者修改鍵值時後, 根據上表定義不同類型, Data 應該指向不同的資料結構, 並且用 DataSize 指明資料大小. 例如 REG_DWORD 類型, 對應的資料大小就是 4, REG_QWORD 資料類型, 資料大小就是 8, 如果是 REG_SZ, 資料長度是字串長度的兩倍加上兩個位元組, 下面程式碼演示如何些改與設置鍵值 :
- Driver.cpp (SetRegTest 函式) : 對鍵值進行操作
- #pragma INITCODE
- VOID SetRegTest()
- {
- UNICODE_STRING RegUnicodeString;
- HANDLE hRegister;
- //初始化UNICODE_STRING字串
- RtlInitUnicodeString( &RegUnicodeString,
- MY_REG_SOFTWARE_KEY_NAME);
- OBJECT_ATTRIBUTES objectAttributes;
- //初始化objectAttributes
- InitializeObjectAttributes(&objectAttributes,
- &RegUnicodeString,
- OBJ_CASE_INSENSITIVE,//對大小寫敏感
- NULL,
- NULL );
- //打開登錄表
- NTSTATUS ntStatus = ZwOpenKey( &hRegister,
- KEY_ALL_ACCESS,
- &objectAttributes);
- if (NT_SUCCESS(ntStatus))
- {
- KdPrint(("Open register successfully\n"));
- }
- UNICODE_STRING ValueName;
- //初始化ValueName
- RtlInitUnicodeString( &ValueName, L"REG_DWORD value");
- //設置REG_DWORD子鍵
- ULONG ulValue = 1000;
- ZwSetValueKey(hRegister,
- &ValueName,
- 0,
- REG_DWORD,
- &ulValue,
- sizeof(ulValue));
- //初始化ValueName
- RtlInitUnicodeString( &ValueName, L"REG_SZ value");
- WCHAR* strValue = L"hello world";
- //設置REG_SZ子鍵
- ZwSetValueKey(hRegister,
- &ValueName,
- 0,
- REG_SZ,
- strValue,
- wcslen(strValue)*2+2);
- //初始化ValueName
- RtlInitUnicodeString( &ValueName, L"REG_BINARY value");
- UCHAR buffer[10];
- RtlFillMemory(buffer,sizeof(buffer),0xFF);
- //設置REG_MULTI_SZ子鍵
- ZwSetValueKey(hRegister,
- &ValueName,
- 0,
- REG_BINARY,
- buffer,
- sizeof(buffer));
- //關閉登錄表控制碼
- ZwClose(hRegister);
- }
執行結果如下所示 :
查詢登錄表 :
驅動程式中有時需要對登錄表的項進行查詢, 從而獲取登錄表的鍵值. DDK 提供 ZwQueryValueKey 函式可以完成這個任務, 其宣告如下 :
- Syntax :
- NTSTATUS ZwQueryValueKey(
- __in HANDLE KeyHandle,
- __in PUNICODE_STRING ValueName,
- __in KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
- __out_opt PVOID KeyValueInformation,
- __in ULONG Length,
- __out PULONG ResultLength
- );
參數說明 :
* KeyHandle : 打開的登錄表控制碼
* ValueName : 要查詢的鍵名
* KeyValueInformationClass : 根據 KeyValueInformation 的不同選擇不同的查詢類別.
* KeyValueInformation : 選擇一種查詢類別. (如 KeyValueBasicInformation)
* Length : 要查資料的長度.
* ResultLength : 實際查詢資料的長度.
使用 ZwQueryValueKey 函式查詢登錄表單時, 需要用 KeyValueInformationClass 選擇一種查詢方式, 這可以是 KeyValueBasicInformation, KeyValueFullInformation 或者 KeyValuePartialInformation 中的一種. 這分別代表查詢基本資訊, 查詢全部資訊和查詢部分資訊, 每種查詢資訊會有對應的一種資料結構獲得查詢結果. 一般是選擇 KeyValuePartialInformation 就可以查詢鍵值的資料, 它對應查詢資料結構是 KEY_VALUE_PARTIAL_INFORMATION :
- Syntax :
- typedef struct _KEY_VALUE_PARTIAL_INFORMATION {
- ULONG TitleIndex;
- ULONG Type;
- ULONG DataLength;
- UCHAR Data[1];
- } KEY_VALUE_PARTIAL_INFORMATION, *PKEY_VALUE_PARTIAL_INFORMATION;
KEY_VALUE_PARTIAL_INFORMATION 的資料結構長度不固定, 所以首先要確定這個長度, 一般使用 ZwQueryValueKey 分為四個步驟 :
1. 用 ZwQueryValueKey 獲取這個資料結構的長度
2. 分配如此長度的記憶體, 用來查詢.
3. 再次呼叫 ZwQueryValueKey, 獲取鍵值.
4. 回收記憶體.
以下是針對查詢登錄表的示範程式碼 :
- Driver.cpp () : 查詢登錄表
- #pragma INITCODE
- VOID QueryRegTest()
- {
- UNICODE_STRING RegUnicodeString;
- HANDLE hRegister;
- //初始化UNICODE_STRING字串
- RtlInitUnicodeString( &RegUnicodeString,
- MY_REG_SOFTWARE_KEY_NAME);
- OBJECT_ATTRIBUTES objectAttributes;
- //初始化objectAttributes
- InitializeObjectAttributes(&objectAttributes,
- &RegUnicodeString,
- OBJ_CASE_INSENSITIVE,//對大小寫敏感
- NULL,
- NULL );
- //打開登錄表
- NTSTATUS ntStatus = ZwOpenKey( &hRegister,
- KEY_ALL_ACCESS,
- &objectAttributes);
- if (NT_SUCCESS(ntStatus))
- {
- KdPrint(("Open register successfully\n"));
- }
- UNICODE_STRING ValueName;
- //初始化ValueName
- RtlInitUnicodeString( &ValueName, L"REG_DWORD value");
- //讀取REG_DWORD子鍵
- ULONG ulSize;
- ntStatus = ZwQueryValueKey(hRegister,
- &ValueName,
- KeyValuePartialInformation ,
- NULL,
- 0,
- &ulSize);
- if (ntStatus==STATUS_OBJECT_NAME_NOT_FOUND || ulSize==0)
- {
- ZwClose(hRegister);
- KdPrint(("The item is not exist\n"));
- return;
- }
- PKEY_VALUE_PARTIAL_INFORMATION pvpi =
- (PKEY_VALUE_PARTIAL_INFORMATION)
- ExAllocatePool(PagedPool,ulSize);
- ntStatus = ZwQueryValueKey(hRegister,
- &ValueName,
- KeyValuePartialInformation ,
- pvpi,
- ulSize,
- &ulSize);
- if (!NT_SUCCESS(ntStatus))
- {
- ZwClose(hRegister);
- KdPrint(("Read regsiter error\n"));
- return;
- }
- //判斷是否為REG_DWORD類型
- if (pvpi->Type==REG_DWORD && pvpi->DataLength==sizeof(ULONG))
- {
- PULONG pulValue = (PULONG) pvpi->Data;
- KdPrint(("The value:%d\n",*pulValue));
- }
- ExFreePool(pvpi);
- //初始化ValueName
- RtlInitUnicodeString( &ValueName, L"REG_SZ value");
- //讀取REG_SZ子鍵
- ntStatus = ZwQueryValueKey(hRegister,
- &ValueName,
- KeyValuePartialInformation ,
- NULL,
- 0,
- &ulSize);
- if (ntStatus==STATUS_OBJECT_NAME_NOT_FOUND || ulSize==0)
- {
- ZwClose(hRegister);
- KdPrint(("The item is not exist\n"));
- return;
- }
- pvpi =
- (PKEY_VALUE_PARTIAL_INFORMATION)
- ExAllocatePool(PagedPool,ulSize);
- ntStatus = ZwQueryValueKey(hRegister,
- &ValueName,
- KeyValuePartialInformation ,
- pvpi,
- ulSize,
- &ulSize);
- if (!NT_SUCCESS(ntStatus))
- {
- ZwClose(hRegister);
- KdPrint(("Read regsiter error\n"));
- return;
- }
- //判斷是否為REG_SZ類型
- if (pvpi->Type==REG_SZ)
- {
- KdPrint(("The value:%S\n",pvpi->Data));
- }
- ZwClose(hRegister);
- }
底下是執行結果 :
補充說明 :
* Windows內核函數(3)-Windows驅動開發詳解筆記,註冊表操作
* [ Windows DDP ] Windows 內核函式 : 核心模式下的登錄表操作 (Part2)
沒有留言:
張貼留言