HIVE结构解析_第二章阅读

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

HIVE结构解析

在认识真正的HIVE文件之前,先列举HIVE文件的几个主要特征。先入为主的将它们呈现出来将有助于我们对其文件组织和数据结构的理解。

注册表由多个HIVE文件组成。

一个HIVE文件由多个巢箱(BIN)组成

HIVE文件的首部有一个文件头(基本块、base block),用于描述这个HIVE文件的一些全局信息

一个BIN由多个巢室(CELL)组成,

CELL可以分为具体的5种(后面介绍),用于存储不同的注册表数据。

本文中,我们并不统一使用HIVE、BIN和CELL的英文单词,而是和对应的中文词汇交替出现。在中文里,它们分别对应储巢、巢箱和巢室三个名词。

一个储巢被看成是一些称为块(block)的分配单元,类似于将磁盘分为簇的形式。根据定义,每一个注册表块的大小为4096字节(4KB),当新的数据要加入到一个储巢中来时,该储巢总是按照块的粒度来增加。一个储巢的第一个块是基本块(base block),包含了有关该储巢的全局信息,包括一个特征签名“regf”,更新序列号,储巢上一次写操作发生的时间戳,储巢格式版本号、检验和,以及该储巢文件的内部文件名等等。下面的_HBASE_BLOCK就是一个基本块的数据结构还原。

typedef struct _HBASE_BLOCK

{

ULONG Signature; /* 签名ASCII-"regf" = 0x66676572 (小端序)*/

ULONG Sequence1;

ULONG Sequence2;

LARGE_INTEGER TimeStamp; /* 最后一次写操作的时间戳 */

ULONG Major; /* 主版本号 */

ULONG Minor; /* 次版本号 */

ULONG Type;

ULONG Format;

ULONG RootCell; /* 第一个键记录的偏移 */

ULONG Length; /* 数据块长度*/

ULONG Cluster;

UCHAR name[64]; /* 储巢文件名*/

ULONG Reserved1[99];

ULONG CheckSum; /* 校验和*/

ULONG Reserved2[894];

ULONG BootType;

ULONG BootRecover;

} HBASE_BLOCK, *PHBASE_BLOCK;

Windows将一个储巢所存储的注册表条目组织在一种称为巢室的容器中,当一个巢室加入到一个储巢中,而且该巢室必须经过扩展才能容纳该巢室时,系统将创建一个巢箱的分配单元。巢箱是新巢室正好扩展到下一个块的边界的大小,系统将巢室的尾部和巢箱的尾部之间的任何空间都看作是空闲空间,因而可以分配其他的巢室。

巢箱也有头部的标识,包含了一个特殊的签名“hbin”,一个记录了该巢箱在储巢文件中偏移量的域,以及该巢箱的大小。下面是巢箱的数据结构。

typedef struct _HBIN

{

ULONG Signature; /* 签名 ASCII-"hbin" = 0x6E696268 (小端序)*/

ULONG FileOffset; /* 本巢箱相对第一个巢箱起始的偏移*/

ULONG Size; /* 本巢箱的大小*/

ULONG Reserved1[2];

LARGE_INTEGER TimeStamp;

ULONG Spare;

} HBIN, *PHBIN;

一个巢室可以容纳一个键、一个值、一个安全描述符、一列子键或者一列键值,分别有对应的巢室来存储数据。在巢室数据的开始之处,有一个数据域描述了该巢室数据的类型,具体的数据结构如下:

键巢室,包含了一个注册表键(也称为键节点)的巢室,一个键巢室包含一个特征签名(对于一个键是kn,一个符号链接是kl)、该键最近一次更新的时间戳、该键父键巢室的巢室索引、代表该键的子键的子键列表巢室的索引、该键的安全描述符巢室索引、一个代表该键类名的字符串键巢室索引,以及该键的名称。

typedef struct _CM_KEY_NODE

{

USHORT Signature; /* 签名ASCII-"kn" = 0x6B6E (小端序)*/ USHORT Flags; /* 根键标识: 0x2C, 其他为0x20 */

LARGE_INTEGER LastWriteTime;

ULONG Spare;

ULONG Parent; /* 父键的偏移*/

ULONG SubKeyCounts[2]; /* SubKeyCounts[0]为子键的个数 */

union /* 偏移为0x001C 联合体 */

{

struct

{

ULONG SubKeyLists[2]; /* SubKeyLists[0]为子键列表相差本BIN的偏移*/

CHILD_LIST ValueList; /* ValueList结构体*/

};

ULONG ChildHiveReference[4];

};

ULONG Security; /* 安全描述符记录的偏移 */

ULONG Class; /* 类名的偏移 */

ULONG MaxNameLen: 16;

ULONG UserFlags: 4;

ULONG VirtControlFlags: 4;

ULONG Debug: 8;

ULONG MaxClassLen;

ULONG MaxValueNameLen;

ULONG MaxValueDataLen;

ULONG WorkVar;

USHORT NameLength; /* 键名长度 */

USHORT ClassLength; /* 类名长度*/

PBYTE Name; /* 键名称*/

}CM_KEY_NODE, *PCM_KEY_NODE;

值巢室,一个巢室,包含了关于一个键的值的信息,该巢室包含一个签名kv,该值的类型,如REG_DWORD或REG_BINARY,以及该值的名称。一个值巢室也包含了另一个值巢室的索引,后者包含了对前者的数据。

typedef struct _CM_KEY_VALUE

{

WORD Signature; /* 签名ASCII-"kv" = 0x6B76(小端序)*/

WORD NameLength; /* 名称长度 */

ULONG DataLength; /* 数据长度*/

ULONG Data; /*数据偏移或数据, 如果DataLength最高位为1,那么它就是数据,

且DataLenth&0x7FFFFFFF为数据长度;否则*/

ULONG Type; /* 值类型*/

WORD Flags;

WORD Spare;

PWCHAR Name; /* 值名称 */

} CM_KEY_VALUE, *PCM_KEY_VALUE;

子键列表巢室,有一系列的键巢室的巢室索引构成的巢室,这些键巢室是同一个父键下面的所有子键。

typedef struct _CM_KEY_INDEX

{

WORD Signature;

WORD Count;

ULONG List[1];

} CM_KEY_INDEX, *PCM_KEY_INDEX;

如果Signature==CM_KEY_FAST_LEAF,签名为“fl”,或者

Signature==CM_KEY_HASH_LEAF,签名为“hl”,那么List后是一个结构体:

struct

{

ULONG offset;

ULONG HashKey;

}

否则为:ULONG offset;

值列表巢室,有一系列的值巢室的巢室索引构成的巢室,这些值巢室是同一个父键下面的所有值。其数据结构即上文说到的结构。即上面

_CM_KEY_NODE的联合体中ValueList数据域。

typedef struct _CHILD_LIST

{

ULONG Count; /* ValueList.Count值的个数*/

ULONG List; /* ValueList.List值列表相差本BIN的偏移 */

} CHILD_LIST, *PCHILD_LIST;

安全描述符巢室,包含了一个安全描述符巢室,其首部的特征签名为ks,以及一个引用计数,该引用计数值记录了所有共享安全描述符的键节点数目,多个键巢室可以共享同样的安全描述符巢室。

typedef struct _CM_KEY_SECURITY

{

WORD Signature; /* 签名ASCII-"sk" = 0x6B73 (小端序)*/

WORD Reserved;

ULONG Flink; /*上一个"sk"记录的偏移*/

ULONG Blink; /*下一个"sk"记录的偏移*/

ULONG ReferenceCount; /* 引用计数*/

ULONG DescriptorLength; /* 数据大小 */

SECURITY_DESCRIPTOR_RELATIVE Descriptor; /* 数据 */

} CM_KEY_SECURITY, *PCM_KEY_SECURITY;

储巢的结构是通过一些链接建立起来的,这些链接称为巢室索引(cell index)。每个巢室索引是一个巢室在储巢文件中的偏移。因此,巢室索引就像是一个指针,从一个巢室指向另一个巢室,配置管理器将巢室索引解释为相对于储巢起始处的偏移。因此,假如你想找到子键A的键巢室,并且A的父键是B,那么就必须先利用B的巢室中的子键列表巢室索引,找到包含B的所有子键列表的那个巢室,然后再利用该子键列表巢室中的巢室索引列表,找到B的每个子键的巢室,随即找到A。

巢室,巢箱和块之间的区别很容易让人混淆,所以我们来看一个简单的注册表储巢的布局示例,如图5。该示例中包含了一个基本块和两个巢箱,第一个巢箱是空的,第二个巢箱包含了几个巢室。该巢室有两个键,一个是根键Root,另一个是Root的子键——SubKey。Root有两个值,Val1和Val2,通过一个子键列表巢室,可以定位到根键的子键,通过一个值列表巢室,可以定位到根键的值。第二个巢箱中,空闲的空间属于空的巢室。

编辑本段获得HIVE文件

知道了HIVE们的存放位置,自然就想到要把它们抓过来,逐个解剖。抓捕工作看似棘手,但解决起来也很简单。HIVE是Windows的重要资源,自启动以来就只能被系统独占访问。我们换一种思路,在另外一个系统中启动,如同一台机器上的Linux,那目前系统的HIVE文件不是就可以访问了吗?但是这里如果你非要在当前系统访问这个HIVE文件,就只有求助于文件系统驱动了。后者已经超出了本文的讨论范围,故我们不作考虑。

不过,为了示例学习的需要,我们总希望HIVE文件能相对简单一些,让我们把它的结构看个清楚明白。系统内部的HIVE文件一般都不太适合,从其文件尺寸已经达到MB级别,就可看出其数据量的巨大。因此,初期我们需要自己建立一个小型的HIVE以供学习之用。

为了以后叙述的方便,我们在HKLM\SAM下建立了子键test_root,然后在test_root下再建立两个子键1test和2test,并且在 1test下新建了五种不同的值,并填写了相应的数据.然后,用RegSaveKey函数编个小程序,把test_root保存为HIVE文件。这样 test_root就变成这个HIVE文件的根键。接下来,我们就可以细细剖析HIVE文件中每一个部分的结构和功能了。

编辑本段HIVE格式实例分析

我们用一个16进制编辑器打开test_root文件,首先就可以看到基本块的签名——“regf”字符串,这是registry file的缩写,标志它是个注册表文件...

编辑本段HIVE文件读取程序

基于上面的结构解释和分析,我们可以写出一个HIVE文件的读取程序。针对上述分析示例的读取效果,test_root下有子键1test_subkey和

2test,前者有1_REG_SZ、2_REG_BINARY、3_REG_DWORD、 4_REG_MULIT_SZ 和5_REG_EXPAND_SZ 5个键值。[]内的是键值的类型,()内的是值的长度,以字节为单位,对于REG_SZ型数据是打印出其unicode的编码。

编辑本段后记

本文中HIVE的数据格式大部分来源于网络和自己的整理,因为缺乏Microsoft官方的文档支持,所以我们并不能保证分析程序在所有情况下都是正确的。Petter Nordahl-Hagen曾经写过一个NT Registry Hive access library,但年代稍显久远,Windows可能会随版本变化修改这些结构的组织形式,其对XP系统已有局部不再适用。注册表在安全方面有很多应用场景,如果需要更加深入的学习该领域的知识,可以借助windbg,利用其导出的Windows内核数据结构进一步了解注册表文件的组织形式。

扩展阅读:

?1

/thread-71914-1-1.html

?2

/hadoop/Hive

相关文档
最新文档