hive是基于Hadoop的一個數(shù)據(jù)倉庫工具,可以將結構化的數(shù)據(jù)文件映射為一張數(shù)據(jù)庫表,并提供完整的sql查詢功能,可以將sql語句轉換為MapReduce任務進行運行。 其優(yōu)點是學習成本低,可以通過類SQL語句快速實現(xiàn)簡單的MapReduce統(tǒng)計,不必開發(fā)專門的MapReduce應用,十分適合數(shù)據(jù)倉庫的統(tǒng)計分析。另外一個是Windows注冊表文件。 Windows注冊表HIVE文件編輯本段回目錄 相信大家對Windows系統(tǒng)的注冊表(registry)一定都不陌生了,我們可以用系統(tǒng)提供的注冊表編輯器(regedit)來訪問和修改注冊表中的數(shù)據(jù)。直觀的講,注冊表呈現(xiàn)出來的是圖1所示的形式,它由根鍵(rootkey)、子鍵(subkey)、鍵值(value)和數(shù)據(jù)(data)組成。數(shù)據(jù)之間有 hive類型的分別,常見的有:REG_SZ、字符串型,REG_BINARY、二進制型, REG_DWORD、雙字型,REG_MULTI_SZ、多字符串值型和REG_EXPAND_SZ、長度可變的數(shù)據(jù)串型。
我們看到的注冊表結構是經過注冊表編輯器讀取之后呈現(xiàn)給我們的,其磁盤形式并不是一個簡單的大文件,而是一組稱被為HIVE的單獨文件形式,HIVE中文名曰“儲巢”。每個HIVE文件可以被理解為一棵單獨注冊表樹,就像Windows的PE格式一樣,它也有自己的組織形式。本文的任務就是要分析HIVE文件的組織形式并完成一個HIVE格式的分析程序。 注冊表API工作原理簡述編輯本段回目錄 Windows系統(tǒng)提供了大量的API給用戶訪問和修改注冊表中的數(shù)據(jù),regedit就是基于這些API所實現(xiàn)的。注冊表API大致分為用戶空間的與內核空間的兩類,一般用戶調用前者,層層調用轉移,由內核的注冊表API再調用文件系統(tǒng)的驅動等,去訪問磁盤上的HIVE文件,并最終返回請求的數(shù)據(jù)結果。這個過程有點冗長,但是為了注冊表里存放數(shù)據(jù)的安全考慮,損失一些性能表現(xiàn)還是值得的。
HIVE結構解析編輯本段回目錄 在認識真正的HIVE文件之前,我們先列舉HIVE文件的幾個主要特征。先入為主的將它們呈現(xiàn)出來將有助于我們對其文件組織和數(shù)據(jù)結構的理解。
注冊表由多個HIVE文件組成。 一個HIVE文件由多個巢箱(BIN)組成 HIVE文件的首部有一個文件頭(基本塊、base block),用于描述這個HIVE文件的一些全局信息 一個BIN由多個巢室(CELL)組成, CELL可以分為具體的5種(后面介紹),用于存儲不同的注冊表數(shù)據(jù)。 本文中,我們并不統(tǒng)一使用HIVE、BIN和CELL的英文單詞,而是和對應的中文詞匯交替出現(xiàn)。在中文里,它們分別對應儲巢、巢箱和巢室三個名詞。 一個儲巢被看成是一些稱為塊(block)的分配單元,類似于將磁盤分為簇的形式。根據(jù)定義,每一個注冊表塊的大小為4096字節(jié)(4KB),當新的數(shù)據(jù)要加入到一個儲巢中來時,該儲巢總是按照塊的粒度來增加。一個儲巢的第一個塊是基本塊(base block),包含了有關該儲巢的全局信息,包括一個特征簽名“regf”,更新序列號,儲巢上一次寫操作發(fā)生的時間戳,儲巢格式版本號、檢驗和,以及該儲巢文件的內部文件名等等。下面的_HBASE_BLOCK就是一個基本塊的數(shù)據(jù)結構還原。 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; /* 數(shù)據(jù)塊長度 */ ULONG Cluster; UCHAR name[64]; /* 儲巢文件名 */ ULONG Reserved1[99]; ULONG CheckSum; /* 校驗和 */ ULONG Reserved2[894]; ULONG BootType; ULONG BootRecover; } HBASE_BLOCK, *PHBASE_BLOCK; Windows將一個儲巢所存儲的注冊表條目組織在一種稱為巢室的容器中,當一個巢室加入到一個儲巢中,而且該巢室必須經過擴展才能容納該巢室時,系統(tǒng)將創(chuàng)建一個巢箱的分配單元。巢箱是新巢室正好擴展到下一個塊的邊界的大小,系統(tǒng)將巢室的尾部和巢箱的尾部之間的任何空間都看作是空閑空間,因而可以分配其他的巢室。 巢箱也有頭部的標識,包含了一個特殊的簽名“hbin”,一個記錄了該巢箱在儲巢文件中偏移量的域,以及該巢箱的大小。下面是巢箱的數(shù)據(jù)結構。 typedef struct _HBIN { ULONG Signature; /* 簽名 ASCII-"hbin" = 0x6E696268 (小端序) */ ULONG FileOffset; /* 本巢箱相對第一個巢箱起始的偏移 */ ULONG Size; /* 本巢箱的大小 */ ULONG Reserved1[2]; LARGE_INTEGER TimeStamp; ULONG Spare; } HBIN, *PHBIN; 一個巢室可以容納一個鍵、一個值、一個安全描述符、一列子鍵或者一列鍵值,分別有對應的巢室來存儲數(shù)據(jù)。在巢室數(shù)據(jù)的開始之處,有一個數(shù)據(jù)域描述了該巢室數(shù)據(jù)的類型,具體的數(shù)據(jù)結構如下: 鍵巢室,包含了一個注冊表鍵(也稱為鍵節(jié)點)的巢室,一個鍵巢室包含一個特征簽名(對于一個鍵是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]為子鍵的個數(shù) */ union /* 偏移為0x001C 聯(lián)合體 */ { 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,以及該值的名稱。一個值巢室也包含了另一個值巢室的索引,后者包含了對前者的數(shù)據(jù)。 typedef struct _CM_KEY_VALUE { WORD Signature; /* 簽名ASCII-"kv" = 0x6B76(小端序) */ WORD NameLength; /* 名稱長度 */ ULONG DataLength; /* 數(shù)據(jù)長度 */ ULONG Data; /*數(shù)據(jù)偏移或數(shù)據(jù), 如果DataLength最高位為1,那么它就是數(shù)據(jù), 且DataLenth&0x7FFFFFFF為數(shù)據(jù)長度;否則 */ 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; 值列表巢室,有一系列的值巢室的巢室索引構成的巢室,這些值巢室是同一個父鍵下面的所有值。其數(shù)據(jù)結構即上文說到的結構。即上面 _CM_KEY_NODE的聯(lián)合體中ValueList數(shù)據(jù)域。 typedef struct _CHILD_LIST { ULONG Count; /* ValueList.Count值的個數(shù) */ ULONG List; /* ValueList.List值列表相差本BIN的偏移 */ } CHILD_LIST, *PCHILD_LIST; 安全描述符巢室,包含了一個安全描述符巢室,其首部的特征簽名為ks,以及一個引用計數(shù),該引用計數(shù)值記錄了所有共享安全描述符的鍵節(jié)點數(shù)目,多個鍵巢室可以共享同樣的安全描述符巢室。 typedef struct _CM_KEY_SECURITY { WORD Signature; /* 簽名ASCII-"sk" = 0x6B73 (小端序)*/ WORD Reserved; ULONG Flink; /*上一個"sk"記錄的偏移 */ ULONG Blink; /*下一個"sk"記錄的偏移 */ ULONG ReferenceCount; /* 引用計數(shù) */ ULONG DescriptorLength; /* 數(shù)據(jù)大小 */ SECURITY_DESCRIPTOR_RELATIVE Descriptor; /* 數(shù)據(jù) */ } CM_KEY_SECURITY, *PCM_KEY_SECURITY; 儲巢的結構是通過一些鏈接建立起來的,這些鏈接稱為巢室索引(cell index)。每個巢室索引是一個巢室在儲巢文件中的偏移。因此,巢室索引就像是一個指針,從一個巢室指向另一個巢室,配置管理器將巢室索引解釋為相對于儲巢起始處的偏移。因此,假如你想找到子鍵A的鍵巢室,并且A的父鍵是B,那么就必須先利用B的巢室中的子鍵列表巢室索引,找到包含B的所有子鍵列表的那個巢室,然后再利用該子鍵列表巢室中的巢室索引列表,找到B的每個子鍵的巢室,隨即找到A。 巢室,巢箱和塊之間的區(qū)別很容易讓人混淆,所以我們來看一個簡單的注冊表儲巢的布局示例,如圖5。該示例中包含了一個基本塊和兩個巢箱,第一個巢箱是空的,第二個巢箱包含了幾個巢室。該巢室有兩個鍵,一個是根鍵Root,另一個是Root的子鍵——SubKey。Root有兩個值,Val1和 Val2,通過一個子鍵列表巢室,可以定位到根鍵的子鍵,通過一個值列表巢室,可以定位到根鍵的值。第二個巢箱中,空閑的空間屬于空的巢室。 獲得HIVE文件編輯本段回目錄 知道了HIVE們的存放位置,自然就想到要把它們抓過來,逐個解剖。抓捕工作看似棘手,但解決起來也很簡單。HIVE是Windows的重要資源,自啟動以來就只能被系統(tǒng)獨占訪問。我們換一種思路,在另外一個系統(tǒng)中啟動,如同一臺機器上的Linux,那目前系統(tǒng)的HIVE文件不是就可以訪問了嗎?但是這里如果你非要在當前系統(tǒng)訪問這個HIVE文件,就只有求助于文件系統(tǒng)驅動了。后者已經超出了本文的討論范圍,故我們不作考慮。
不過,為了示例學習的需要,我們總希望HIVE文件能相對簡單一些,讓我們把它的結構看個清楚明白。系統(tǒng)內部的HIVE文件一般都不太適合,從其文件尺寸已經達到MB級別,就可看出其數(shù)據(jù)量的巨大。因此,初期我們需要自己建立一個小型的HIVE以供學習之用。 為了以后敘述的方便,我們在HKLM\SAM下建立了子鍵test_root,然后在test_root下再建立兩個子鍵1test和2test,并且在 1test下新建了五種不同的值,并填寫了相應的數(shù)據(jù).然后,用RegSaveKey函數(shù)編個小程序,把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個鍵值。[]內的是鍵值的類型,()內的是值的長度,以字節(jié)為單位,對于REG_SZ型數(shù)據(jù)是打印出其unicode的編碼。
后記編輯本段回目錄 本文中HIVE的數(shù)據(jù)格式大部分來源于網(wǎng)絡和自己的整理,因為缺乏Microsoft官方的文檔支持,所以我們并不能保證分析程序在所有情況下都是正確的。Petter Nordahl-Hagen曾經寫過一個NT Registry Hive access library,但年代稍顯久遠,Windows可能會隨版本變化修改這些結構的組織形式,其對XP系統(tǒng)已有局部不再適用。注冊表在安全方面有很多應用場景,如果需要更加深入的學習該領域的知識,可以借助windbg,利用其導出的 Windows內核數(shù)據(jù)結構進一步了解注冊表文件的組織形式。
|
|