程式扎記: [ Windows DDP ] Windows 內核函式 : 核心模式下的登錄表操作 (Part1)

標籤

2011年1月6日 星期四

[ Windows DDP ] Windows 內核函式 : 核心模式下的登錄表操作 (Part1)

前言 : 
在驅動程式開發中, 經常會用到對登錄表的操作. 與 Win32 API 不同, DDK 提供另外一套對登錄表操作的相關函式. 首先明確一下登錄表裡的幾個概念 : 
 

* 登錄表項目 : 登錄表中的一個專案, 類似目錄的概念. 每個項目儲存多個二元結構, 鍵名-鍵值. 每個項目可以有若干個子項.
* 登錄表子項 : 類似於目錄中的子目錄.
* 鍵名 : 透過鍵名可以尋找到對應的鍵值.
* 鍵值類型 : 每個鍵值儲存的時候有不同類型, 可以是整數或是字串等資料.
* 鍵值 : 鍵名下對應儲存的資料.

新建關閉登錄表 : 
和檔案操作類似, 對登錄表操作首先要先獲取一個登錄表控制碼. 對登錄表的操作都需要根據這個控制碼進行操作. 可以透過 ZwCreateKey 函式獲得打開的登錄表控制碼. 這個函式打開登錄表後, 返回一個操作控制碼. 其宣告如下 : 

- Syntax :
  1. NTSTATUS ZwCreateKey(  
  2.   __out       PHANDLE KeyHandle,  
  3.   __in        ACCESS_MASK DesiredAccess,  
  4.   __in        POBJECT_ATTRIBUTES ObjectAttributes,  
  5.   __reserved  ULONG TitleIndex,  
  6.   __in_opt    PUNICODE_STRING Class,  
  7.   __in        ULONG CreateOptions,  
  8.   __out_opt   PULONG Disposition  
  9. );  

參數說明 : 

* 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 函式) : 對登陸表進行開啟關閉操作
  1. #define  MY_REG_SOFTWARE_KEY_NAME       L"\\Registry\\Machine\\Software\\Zhangfan"  
  2. #pragma INITCODE  
  3. VOID CreateRegTest()   
  4. {  
  5.     //新建或打開某登錄表項目  
  6.     UNICODE_STRING RegUnicodeString;  
  7.     HANDLE hRegister;  
  8.   
  9.     //初始化UNICODE_STRING字串  
  10.     RtlInitUnicodeString( &RegUnicodeString,   
  11.         MY_REG_SOFTWARE_KEY_NAME);  
  12.       
  13.     OBJECT_ATTRIBUTES objectAttributes;  
  14.     //初始化objectAttributes  
  15.     InitializeObjectAttributes(&objectAttributes,  
  16.                             &RegUnicodeString,  
  17.                             OBJ_CASE_INSENSITIVE,//對大小寫敏感  
  18.                             NULL,   
  19.                             NULL );  
  20.     ULONG ulResult;  
  21.     //新建或帶開登錄表項目  
  22.     NTSTATUS ntStatus = ZwCreateKey( &hRegister,  
  23.                             KEY_ALL_ACCESS,  
  24.                             &objectAttributes,  
  25.                             0,  
  26.                             NULL,  
  27.                             REG_OPTION_NON_VOLATILE,  
  28.                             &ulResult);  
  29.   
  30.     if (NT_SUCCESS(ntStatus))  
  31.     {  
  32.         //判斷是被新新建,還是已經被新建  
  33.         if(ulResult==REG_CREATED_NEW_KEY)  
  34.         {  
  35.             KdPrint(("The register item is created\n"));  
  36.         }else if(ulResult==REG_OPENED_EXISTING_KEY)  
  37.         {  
  38.             KdPrint(("The register item has been created,and now is opened\n"));  
  39.         }  
  40.     }  
  41.   
  42.     //(2)新建或打開某登錄表項目的子項  
  43.     UNICODE_STRING subRegUnicodeString;  
  44.     HANDLE hSubRegister;  
  45.   
  46.     //初始化UNICODE_STRING字串  
  47.     RtlInitUnicodeString( &subRegUnicodeString,   
  48.         L"SubItem");  
  49.   
  50.     OBJECT_ATTRIBUTES subObjectAttributes;  
  51.     //初始化subObjectAttributes  
  52.     InitializeObjectAttributes(&subObjectAttributes,  
  53.                             &subRegUnicodeString,  
  54.                             OBJ_CASE_INSENSITIVE,//對大小寫敏感  
  55.                             hRegister,   
  56.                             NULL );  
  57.     //新建或帶開登錄表項目  
  58.     ntStatus = ZwCreateKey( &hSubRegister,  
  59.                             KEY_ALL_ACCESS,  
  60.                             &subObjectAttributes,  
  61.                             0,  
  62.                             NULL,  
  63.                             REG_OPTION_NON_VOLATILE,  
  64.                             &ulResult);  
  65.   
  66.     if (NT_SUCCESS(ntStatus))  
  67.     {  
  68.         //判斷是被新建,還是已經被新建  
  69.         if(ulResult==REG_CREATED_NEW_KEY)  
  70.         {  
  71.             KdPrint(("The sub register item is created\n"));  
  72.         }else if(ulResult==REG_OPENED_EXISTING_KEY)  
  73.         {  
  74.             KdPrint(("The sub register item has been created,and now is opened\n"));  
  75.         }  
  76.     }  
  77.   
  78.     //關閉登錄表控制碼  
  79.     ZwClose(hRegister);  
  80.     ZwClose(hSubRegister);  
  81. }  

底下是執行結果 : 
 

打開登錄表 : 
ZwCreateKey 函式既可以新建登錄表項, 也可以打開登錄表. 為了簡化打開登錄表項, DDK 提供了內核函式 ZwOpenKey 以簡化打開操作. 如果 ZwOpenKey 指定的項目不存在, 並不會新增這個項目, 而是返回一個錯誤狀態. 該函式宣告如下 : 

- Syntax :
  1. NTSTATUS ZwOpenKey(  
  2.   __out  PHANDLE KeyHandle,  
  3.   __in   ACCESS_MASK DesiredAccess,  
  4.   __in   POBJECT_ATTRIBUTES ObjectAttributes  
  5. );  

參數說明 : 

* KeyHandle : 返回被打開的控制碼
* DesiredAccess : 打開的權限, 一般設為 KEY_ALL_ACCESS
* ObjectAttributes : OBJECT_ATTRIBUTES 資料結構, 指示打開的狀態.

ZwOpenKey 的參數比 ZwCreateKey 的參數簡化, 程式設計師使用起來比較方便, 下面代碼演示如何使用 ZwOpenKey 打開登錄表 : 

- Driver.cpp (OpenRegTest 函式) : 打開登錄表
  1. #pragma INITCODE  
  2. VOID OpenRegTest()  
  3. {  
  4.     UNICODE_STRING RegUnicodeString;  
  5.     HANDLE hRegister;  
  6.   
  7.     //初始化UNICODE_STRING字串  
  8.     RtlInitUnicodeString( &RegUnicodeString,   
  9.         MY_REG_SOFTWARE_KEY_NAME);  
  10.       
  11.     OBJECT_ATTRIBUTES objectAttributes;  
  12.     //初始化objectAttributes  
  13.     InitializeObjectAttributes(&objectAttributes,  
  14.                             &RegUnicodeString,  
  15.                             OBJ_CASE_INSENSITIVE,//對大小寫敏感  
  16.                             NULL,   
  17.                             NULL );  
  18.     //打開登錄表  
  19.     NTSTATUS ntStatus = ZwOpenKey( &hRegister,  
  20.                             KEY_ALL_ACCESS,  
  21.                             &objectAttributes);  
  22.   
  23.     if (NT_SUCCESS(ntStatus))  
  24.     {  
  25.         KdPrint(("Open register successfully\n"));  
  26.     }  
  27.   
  28.     ZwClose(hRegister);  
  29. }  

底下是執行結果 : 
 

添加, 修改登錄表鍵值 : 
打開登錄表的控制碼後, 就可以對該項進行設置和修改了. 登錄表示以二元型式儲存的, 即 "鍵名" 與 "鍵值". 透過鍵名設定鍵值, 而鍵值可以劃分為以下幾類 : 
 
在添加和修改登錄表鍵值的時候, 要分類進行添加與修改. DDK 提供了 ZwSetValueKey 函式 來完成這個任務, 其宣告如下 : 

- Syntax :
  1. NTSTATUS ZwSetValueKey(  
  2.   __in      HANDLE KeyHandle,  
  3.   __in      PUNICODE_STRING ValueName,  
  4.   __in_opt  ULONG TitleIndex,  
  5.   __in      ULONG Type,  
  6.   __in_opt  PVOID Data,  
  7.   __in      ULONG DataSize  
  8. );  

參數說明 : 

* KeyHandle : 登錄表控制碼
* ValueName : 要新建或修改的鍵名
* TitleIndex : 很少用, 一般設為 0.
* Type : 在上表選擇一種類型.
* DataSize : 記錄鍵值資料的大小

使用 ZwSetValueKey 函式的時候, 如果指定的鍵名不存在, 則直接新建. 如果指定鍵名已經存在, 則對已有鍵值進行修改. 當新建或者修改鍵值時後, 根據上表定義不同類型, Data 應該指向不同的資料結構, 並且用 DataSize 指明資料大小. 例如 REG_DWORD 類型, 對應的資料大小就是 4, REG_QWORD 資料類型, 資料大小就是 8, 如果是 REG_SZ, 資料長度是字串長度的兩倍加上兩個位元組, 下面程式碼演示如何些改與設置鍵值 : 

- Driver.cpp (SetRegTest 函式) : 對鍵值進行操作
  1. #pragma INITCODE  
  2. VOID SetRegTest()  
  3. {  
  4.     UNICODE_STRING RegUnicodeString;  
  5.     HANDLE hRegister;  
  6.   
  7.     //初始化UNICODE_STRING字串  
  8.     RtlInitUnicodeString( &RegUnicodeString,   
  9.         MY_REG_SOFTWARE_KEY_NAME);  
  10.       
  11.     OBJECT_ATTRIBUTES objectAttributes;  
  12.     //初始化objectAttributes  
  13.     InitializeObjectAttributes(&objectAttributes,  
  14.                             &RegUnicodeString,  
  15.                             OBJ_CASE_INSENSITIVE,//對大小寫敏感  
  16.                             NULL,   
  17.                             NULL );  
  18.     //打開登錄表  
  19.     NTSTATUS ntStatus = ZwOpenKey( &hRegister,  
  20.                             KEY_ALL_ACCESS,  
  21.                             &objectAttributes);  
  22.   
  23.     if (NT_SUCCESS(ntStatus))  
  24.     {  
  25.         KdPrint(("Open register successfully\n"));  
  26.     }  
  27.   
  28.     UNICODE_STRING ValueName;  
  29.     //初始化ValueName  
  30.     RtlInitUnicodeString( &ValueName, L"REG_DWORD value");  
  31.   
  32.     //設置REG_DWORD子鍵  
  33.     ULONG ulValue = 1000;  
  34.     ZwSetValueKey(hRegister,  
  35.                 &ValueName,  
  36.                 0,  
  37.                 REG_DWORD,  
  38.                 &ulValue,  
  39.                 sizeof(ulValue));  
  40.   
  41.     //初始化ValueName  
  42.     RtlInitUnicodeString( &ValueName, L"REG_SZ value");  
  43.     WCHAR* strValue = L"hello world";  
  44.   
  45.     //設置REG_SZ子鍵  
  46.     ZwSetValueKey(hRegister,  
  47.                 &ValueName,  
  48.                 0,  
  49.                 REG_SZ,  
  50.                 strValue,  
  51.                 wcslen(strValue)*2+2);  
  52.   
  53.   
  54.     //初始化ValueName  
  55.     RtlInitUnicodeString( &ValueName, L"REG_BINARY value");  
  56.       
  57.     UCHAR buffer[10];  
  58.     RtlFillMemory(buffer,sizeof(buffer),0xFF);  
  59.   
  60.     //設置REG_MULTI_SZ子鍵  
  61.     ZwSetValueKey(hRegister,  
  62.                 &ValueName,  
  63.                 0,  
  64.                 REG_BINARY,  
  65.                 buffer,  
  66.                 sizeof(buffer));  
  67.   
  68.     //關閉登錄表控制碼  
  69.     ZwClose(hRegister);  
  70. }  

執行結果如下所示 : 
 

查詢登錄表 : 
驅動程式中有時需要對登錄表的項進行查詢, 從而獲取登錄表的鍵值. DDK 提供 ZwQueryValueKey 函式可以完成這個任務, 其宣告如下 : 

- Syntax :
  1. NTSTATUS ZwQueryValueKey(  
  2.   __in       HANDLE KeyHandle,  
  3.   __in       PUNICODE_STRING ValueName,  
  4.   __in       KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,  
  5.   __out_opt  PVOID KeyValueInformation,  
  6.   __in       ULONG Length,  
  7.   __out      PULONG ResultLength  
  8. );  

參數說明 : 

* KeyHandle : 打開的登錄表控制碼
* ValueName : 要查詢的鍵名
* KeyValueInformationClass : 根據 KeyValueInformation 的不同選擇不同的查詢類別.
* KeyValueInformation : 選擇一種查詢類別. (如 KeyValueBasicInformation)
* Length : 要查資料的長度.
* ResultLength : 實際查詢資料的長度.

使用 ZwQueryValueKey 函式查詢登錄表單時, 需要用 KeyValueInformationClass 選擇一種查詢方式, 這可以是 KeyValueBasicInformation, KeyValueFullInformation 或者 KeyValuePartialInformation 中的一種. 這分別代表查詢基本資訊, 查詢全部資訊和查詢部分資訊, 每種查詢資訊會有對應的一種資料結構獲得查詢結果. 一般是選擇 KeyValuePartialInformation 就可以查詢鍵值的資料, 它對應查詢資料結構是 KEY_VALUE_PARTIAL_INFORMATION : 

- Syntax :
  1. typedef struct _KEY_VALUE_PARTIAL_INFORMATION {  
  2.   ULONG TitleIndex;  
  3.   ULONG Type;  
  4.   ULONG DataLength;  
  5.   UCHAR Data[1];  
  6. } KEY_VALUE_PARTIAL_INFORMATION, *PKEY_VALUE_PARTIAL_INFORMATION;  

KEY_VALUE_PARTIAL_INFORMATION 的資料結構長度不固定, 所以首先要確定這個長度, 一般使用 ZwQueryValueKey 分為四個步驟 : 

1. 用 ZwQueryValueKey 獲取這個資料結構的長度
2. 分配如此長度的記憶體, 用來查詢.
3. 再次呼叫 ZwQueryValueKey, 獲取鍵值.
4. 回收記憶體.

以下是針對查詢登錄表的示範程式碼 : 

- Driver.cpp () : 查詢登錄表
  1. #pragma INITCODE  
  2. VOID QueryRegTest()  
  3. {  
  4.     UNICODE_STRING RegUnicodeString;  
  5.     HANDLE hRegister;  
  6.   
  7.     //初始化UNICODE_STRING字串  
  8.     RtlInitUnicodeString( &RegUnicodeString,   
  9.         MY_REG_SOFTWARE_KEY_NAME);  
  10.       
  11.     OBJECT_ATTRIBUTES objectAttributes;  
  12.     //初始化objectAttributes  
  13.     InitializeObjectAttributes(&objectAttributes,  
  14.                             &RegUnicodeString,  
  15.                             OBJ_CASE_INSENSITIVE,//對大小寫敏感  
  16.                             NULL,   
  17.                             NULL );  
  18.     //打開登錄表  
  19.     NTSTATUS ntStatus = ZwOpenKey( &hRegister,  
  20.                             KEY_ALL_ACCESS,  
  21.                             &objectAttributes);  
  22.   
  23.     if (NT_SUCCESS(ntStatus))  
  24.     {  
  25.         KdPrint(("Open register successfully\n"));  
  26.     }  
  27.   
  28.     UNICODE_STRING ValueName;  
  29.     //初始化ValueName  
  30.     RtlInitUnicodeString( &ValueName, L"REG_DWORD value");  
  31.   
  32.     //讀取REG_DWORD子鍵  
  33.     ULONG ulSize;  
  34.     ntStatus = ZwQueryValueKey(hRegister,  
  35.                 &ValueName,  
  36.                 KeyValuePartialInformation ,  
  37.                 NULL,  
  38.                 0,  
  39.                 &ulSize);  
  40.   
  41.     if (ntStatus==STATUS_OBJECT_NAME_NOT_FOUND || ulSize==0)  
  42.     {  
  43.         ZwClose(hRegister);  
  44.         KdPrint(("The item is not exist\n"));  
  45.         return;  
  46.     }  
  47.     PKEY_VALUE_PARTIAL_INFORMATION pvpi =   
  48.         (PKEY_VALUE_PARTIAL_INFORMATION)  
  49.         ExAllocatePool(PagedPool,ulSize);  
  50.   
  51.     ntStatus = ZwQueryValueKey(hRegister,  
  52.                 &ValueName,  
  53.                 KeyValuePartialInformation ,  
  54.                 pvpi,  
  55.                 ulSize,  
  56.                 &ulSize);  
  57.     if (!NT_SUCCESS(ntStatus))  
  58.     {  
  59.         ZwClose(hRegister);  
  60.         KdPrint(("Read regsiter error\n"));  
  61.         return;  
  62.     }  
  63.     //判斷是否為REG_DWORD類型  
  64.     if (pvpi->Type==REG_DWORD && pvpi->DataLength==sizeof(ULONG))  
  65.     {  
  66.         PULONG pulValue = (PULONG) pvpi->Data;  
  67.         KdPrint(("The value:%d\n",*pulValue));  
  68.     }  
  69.   
  70.     ExFreePool(pvpi);  
  71.   
  72.     //初始化ValueName  
  73.     RtlInitUnicodeString( &ValueName, L"REG_SZ value");  
  74.     //讀取REG_SZ子鍵  
  75.     ntStatus = ZwQueryValueKey(hRegister,  
  76.                 &ValueName,  
  77.                 KeyValuePartialInformation ,  
  78.                 NULL,  
  79.                 0,  
  80.                 &ulSize);  
  81.   
  82.     if (ntStatus==STATUS_OBJECT_NAME_NOT_FOUND || ulSize==0)  
  83.     {  
  84.         ZwClose(hRegister);  
  85.         KdPrint(("The item is not exist\n"));  
  86.         return;  
  87.     }  
  88.     pvpi =   
  89.         (PKEY_VALUE_PARTIAL_INFORMATION)  
  90.         ExAllocatePool(PagedPool,ulSize);  
  91.   
  92.     ntStatus = ZwQueryValueKey(hRegister,  
  93.                 &ValueName,  
  94.                 KeyValuePartialInformation ,  
  95.                 pvpi,  
  96.                 ulSize,  
  97.                 &ulSize);  
  98.     if (!NT_SUCCESS(ntStatus))  
  99.     {  
  100.         ZwClose(hRegister);  
  101.         KdPrint(("Read regsiter error\n"));  
  102.         return;  
  103.     }  
  104.     //判斷是否為REG_SZ類型  
  105.     if (pvpi->Type==REG_SZ)  
  106.     {  
  107.         KdPrint(("The value:%S\n",pvpi->Data));  
  108.     }  
  109.   
  110.     ZwClose(hRegister);  
  111. }  

底下是執行結果 : 
 

補充說明 : 
* Windows內核函數(3)-Windows驅動開發詳解筆記,註冊表操作
[ Windows DDP ] Windows 內核函式 : 核心模式下的登錄表操作 (Part2)

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!