读书频道 > 网站 > 网页设计 > 网络分析技术揭秘:原理、实践与WinPcap深入解析
3.1.8 内核的注册表操作
12-08-11    叶孤城
收藏    我要投稿   
本书结合著名的开源软件库WinPcap来说明网络分析技术的实现原理及使用方法。其中包括WinPcap内核驱动,编译与使用,数据包的捕获、发送、内核过滤与接收,以及网络流量的统计与网络状态的分析等重要内容,而且作...立即去当当网订购
在驱动程序的开发中,经常会对注册表进行操作。注册表的主要组成如图3-1所示,注册表的五个主要组成部分如下。
 
图3-1注册表的主要组成
注册表项:注册表中的一个项目,类似于目录。每个项中存储着多个二元结构,即键名-键值。每个项中,可以有若干个子项。
注册表子项:类似于目录中的子目录。
键名:类似于目录下的文件名,可用于索引,通过键名可以寻找到相应的键值。
键值类别:每个键值存储的时候会有不同类别,即键值类别,比如整型、字符串等类型。
键值:键名下对应存储的数据值。
WDK提供了一套对注册表进行操作的函数集,下面对这些函数进行简要介绍。
1. 初始化一个OBJECT_ATTRIBUTES结构体
为了能对注册表进行操作,必须先调用InitializeObjectAttributes函数初始化一个OBJECT_ATTRIBUTES结构体,其原型如下:
VOID InitializeObjectAttributes(
    OUT POBJECT_ATTRIBUTES InitializedAttributes,
    IN PUNICODE_STRING ObjectName,
    IN ULONG Attributes,
    IN HANDLE RootDirectory,
    IN PSECURITY_DESCRIPTOR SecurityDescriptor
);
上述函数中,参数InitializedAttributes指向需要初始化的OBJECT_ATTRIBUTES结构体;参数ObjectName 描述需要打开的注册表对象的名称,用Unicode字符串表示;参数Attributes为标识,若将其设置为OBJ_CASE_INSENSITIVE,则在用ObjectName与已存在的对象进行比较时就不区分大小写了;参数RootDirectory用于描述ObjectName参数的根目录,如果ObjectName是一个完全的名称,则设为NULL;参数SecurityDescriptor为一个安全描述,如果把它设为NULL,驱动程序将采用默认的安全设置。
2. 打开注册表
WDK提供了内核函数ZwOpenKey,用来打开一个已存在的注册表项。如果ZwOpenKey指定的项不存在,则其不会创建这个项,而是返回一个错误状态。该函数的原型如下:
NTSTATUS ZwOpenKey(OUT PHANDLE KeyHandle,
    IN ACCESS_MASK DesiredAccess,
    IN POBJECT_ATTRIBUTES ObjectAttributes
);
上述函数中,参数KeyHandle返回被打开的句柄;参数DesiredAccess为打开的权限,一般设为KEY_ALL_ACCESS;参数ObjectAttributes是OBJECT_ATTRIBUTES数据结构,用于指示打开的状态。
如果打开成功,函数ZwOpenKey返回STATUS_SUCCESS,否则返回一个错误代码,错误代码可能为STATUS_INVALID_HANDLE或STATUS_ACCESS_DENIED。
3. 关闭注册表
已打开的注册表,如果不再使用,需要采用ZwClose函数关闭它,其原型如下:
NTSTATUS ZwClose( IN HANDLE Handle );
上述函数中,参数Handle为所要关闭的注册表句柄。
4. 查询注册表
驱动程序中有时需要对注册表的项进行查询,从而获取注册表的键值。WDK提供的ZwQueryValueKey函数可以完成这个任务,其原型如下:
NTSTATUS ZwQueryValueKey(
    IN HANDLE KeyHandle,
    IN PUNICODE_STRING ValueName,
    IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
    OUT PVOID KeyValueInformation,
    IN ULONG Length,
    OUT PULONG ResultLength
);
上述函数中,参数KeyHandle为打开的注册表句柄;参数ValueName为要查询的键名;参数KeyValueInformationClass决定不同的查询类别;参数KeyValueInformation用于选择一种查询类别,可选择KeyValueBasicInformation、KeyValueFullInformation或KeyValuePartialInformation类型;参数Length为要查询数据的长度;参数ResultLength为实际查询返回的数据长度。
如果打开成功,函数返回STATUS_SUCCESS,否则返回一个错误代码。
使用ZwQueryValueKey函数查询注册表单时,需要用KeyValueInformationClass参数来选择一种查询方式,可以是KeyValueBasicInformation、KeyValueFullInformation或者KeyValuePartialInformation中的一种。它们分别代表查询基本信息、查询全部信息和查询部分信息,并且每种查询类型会用一种对应的数据结构来获得查询结果。
一般情况下,选择KeyValuePartialInformation参数就可以查询键值的数据了,它对应的查询数据结构是KEY_VALUE_PARTIAL_INFORMATION,具体定义如下:
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函数时,一般会按下列四个步骤进行操作:
用ZwQueryValueKey函数获取这个数据结构的长度。
分配如此长度的内存,用来查询。
再次调用ZwQueryValueKey函数,获取键值。
回收内存。
如果选择KeyValueFullInformation参数,它对应的是KEY_VALUE_FULL_INFORMATION查询数据结构体,具体定义如下:
Typedef struct _KEY_VALUE_FULL_INFORMATION{
    ULONG TitleIndex;
    ULONG Type;
    ULONG DataOffset;
    ULONG DataLength;
    ULONG NameLength;
    WCHAR Name[1]; //
}KEY_VALUE_FULL_INFORMATION,*PKEY_VALUE_FULL_INFORMATION;
5. 枚举子项
在注册表中,经常使用另外两种操作,分别是枚举子项和枚举子键。枚举子项就是事先不知道该项中有多少个子项目,用某个函数将子项一一列举出来。而枚举子键是指事先不知道该项中有多少个子键,用某个函数将子键一一列举出来。
WDK提供了列举子项的ZwQueryKey函数和ZwEnumerateKey函数。ZwQueryKey函数的作用主要是获得某注册表项究竟有多少个子项,而ZwEnumerateKey函数的作用主要是针对某个子项获取其具体信息。接下来看一看这两个函数的原型。
ZwQueryKey函数的原型如下:
NTSTATUS ZwQueryKey(
    IN HANDLE KeyHandle,
    IN KEY_INFORMATION_CLASS  KeyInformationClass,
    OUT PVOID  KeyInformation,
    IN ULONG  Length,
    OUT PULONG  ResultLength
);
上述函数中,参数KeyHandle为注册表项的句柄;参数KeyInformationClass为查询的类别,一般选择KeyFullInformation;参数KeyInformation为查询的数据指针,如果KeyInformationClass是KeyFullInformation,则该指针指向一个KEY_FULL_INFORMATION的数据结构;参数Length为数据长度;参数ResultLength为实际返回数据的长度。
如果函数执行成功,返回STATUS_SUCCESS,否则返回一个错误代码。
ZwEnumerateKey函数的原型如下:
NTSTATUS ZwEnumerateKey(
    IN HANDLE  KeyHandle,
    IN ULONG  Index,
    IN KEY_INFORMATION_CLASS  KeyInformationClass,
    OUT PVOID  KeyInformation,
    IN ULONG  Length,
    OUT PULONG  ResultLength
);
上述函数中,参数KeyHandle为注册表项句柄;参数Index为从0开始的索引;参数KeyInformationClass为子项的信息类型;参数Length为子项信息的长度;参数ResultLength为返回子键信息的长度。
如果函数执行成功,返回STATUS_SUCCESS,否则返回一个错误代码。
在使用ZwQueryKey函数时,可以将参数KeyInformationClass指定为KeyFullInformation。这样,参数KeyInformation就可对应一个KEY_FULL_INFORMATION的数据结构了,该数据结构中的SubKeys指明了该项中有多少个子项。KEY_FULL_INFORMATION数据结构的长度可变,所以要调用两次ZwQueryKey,第一次获取KEY_FULL_INFORMATION结构的长度,第二次才是真正获取KEY_FULL_INFORMATION结构的数据。
在使用ZwEnumerateKey函数时,需要将参数KeyInformationClass设置为KeyBasicInformation,这样,参数KeyInformation就能对应KEY_BASIC_INFORMATION的数据结构了。KEY_BASIC_INFORMATION数据结构长度也是可变的,故同样需要调用ZwEnumerateKey两次,第一次获取KEY_BASIC_INFORMATION结构的长度,第二次获取KEY_BASIC_INFORMATION结构的数据。
6. 枚举子键
和枚举子项类似,通过ZwQueryKey和ZwEnumerateValueKey这两个函数的配合可以完成对子键的枚举。ZwEnumerateValueKey函数的使用方法和ZwEnumerateKey函数类似,其原型如下:
NTSTATUS ZwEnumerateValueKey(
    IN HANDLE KeyHandle,
    IN ULONG Index,
    IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
    OUT PVOID KeyValueInformation,
    IN ULONG Length,
    OUT PULONG ResultLength
);
7. WinPcap中的注册表操作实例
下面是在WinPcap中对注册表进行操作的代码片断:
PWCHAR getAdaptersList(void)
{
    ?
    /*
    *设置一个OBJECT_ATTRIBUTES类型的参数objAttrs,为了便于后续调用,
    *其中NDIS_STRING AdapterListKey =
    *  NDIS_STRING_CONST("\\Registry\\Machine\\System\\
    *  CurrentControlSet\\Control\\Class\\
    *  {4D36E972-E325-11CE-BFC1-08002BE10318}");
    */
    InitializeObjectAttributes(&objAttrs, &AdapterListKey,
        OBJ_CASE_INSENSITIVE, NULL, NULL);
 
    /*打开注册表表项,返回objAttrs中所描述的注册表表项的句柄*/
    status = ZwOpenKey(&keyHandle, KEY_READ, &objAttrs);
    if (!NT_SUCCESS(status)) {
        //打开失败
    }
    else { //打开成功         
        ULONG resultLength;
          KEY_VALUE_PARTIAL_INFORMATION valueInfo;
          CHAR AdapInfo[1024];
          UINT i=0;
 
          /*遍历设备链表,获取一个已打开注册表项的子项信息*/
          while((status=ZwEnumerateKey(keyHandle,i,
          KeyBasicInformation,AdapInfo,sizeof(AdapInfo),
          &resultLength))==STATUS_SUCCESS)
          {
              ?     
              /*OBJECT_ATTRIBUTES类型的参数objAttrs,为了便于后续调用*/
              InitializeObjectAttributes(&objAttrs,
                  &AdapterKeyName,OBJ_CASE_INSENSITIVE,NULL, NULL);
              /*objAttrs*/
              status=ZwOpenKey(&ExportKeyHandle,KEY_READ,&objAttrs);
              /*揈xport?/
              status = ZwQueryValueKey(ExportKeyHandle,
                  &FinalExportKey,KeyValuePartialInformation,
                  &valueInfo,sizeof(valueInfo),&resultLength);
 
              if (!NT_SUCCESS(status) && (status != STATUS_BUFFER_OVERFLOW))
              {
                  //查询失败
              }
              else { //查询成功
                  /*计算所需的内存大小*/
                  …
                  /*分配内存,用于查询*/
                  …
                  if (valueInfoP != NULL)
                  {//分配内存成功
                      status = ZwQueryValueKey(ExportKeyHandle,
                          &FinalExportKey,
                          KeyValuePartialInformation,
                          valueInfoP,
                          valueInfoLength, &resultLength);
                      if (!NT_SUCCESS(status)) {
                          //查询失败
                      }
                      else{//查询成功
                          …
                      }
                      ExFreePool(valueInfoP);
                  }
              else {
              //用于查询的内存分配失败
                }
            }//一次查找“Export”键名的键值信息结束
            …
            //关闭注册表子项
            ZwClose (ExportKeyHandle);
            i++;
        }//结束while语句
        /*关闭注册表项*/
        ZwClose (keyHandle);
    }
    ?
}
点击复制链接 与好友分享!回本站首页
分享到: 更多
您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力  
上一篇:1.3 功能
下一篇:1.5 小结
相关文章
图文推荐
JavaScript网页动画设
1.9 响应式
1.8 登陆页式
1.7 主题式
排行
热门
文章
下载
读书

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训
版权所有: 红黑联盟--致力于做最好的IT技术学习网站