hive[Windows註冊表HIVE檔案]

hive[Windows註冊表HIVE檔案]

hive,指的是Windows註冊表HIVE檔案。

Windows註冊表HⅣE檔案

hive hive

相信大家對Windows系統的註冊表(registry)一定都不陌生了,我們可以用系統提供的註冊表編輯器(regedit)來訪問和修改註冊表中的數據。直觀的講,註冊表呈現出來的是圖1所示的形式,它由根鍵(rootkey)、子鍵(subkey)、鍵值(value)和數據(data)組成。數據之間有類型的分別,常見的有:REG_SZ、字元串型,REG_BINARY、二進制型, REG_DWORD、雙字型,REG_MULTI_SZ、多字元串值型和REG_EXPAND_SZ、長度可變的數據串型。

註冊表相當於Windows系統中所有32位硬體/驅動和32位應用程式的數據檔案,是一個系統信息的資料庫。既然是數據檔案,那在磁碟上就一定有註冊表的影子的存在。Windows 2000/XP的註冊表檔案在系統設定和預設用戶配置數據的情況下,是存放在\系統資料夾 \SYSTEM32\CONFIG目錄下的6個檔案,DEFAULT、SAM、SECURITY、SOFTWARE、USERDIFF和SYSTEM中,而用戶的配置信息存放在系統所在磁碟的\Documents and Setting\目錄,包括ntuser.dat,ntuser.ini和ntuser.dat.log。其中每個檔案的路徑都由註冊表項HKLM \SYSTEM\CurrentControlSet\Control\HⅣElist下的鍵值指出。

我們看到的註冊表結構是經過註冊表編輯器讀取之後呈現給我們的,其磁碟形式並不是一個簡單的大檔案,而是一組稱被為HⅣE的單獨檔案形式,HⅣE中文名曰“儲巢”。每個HⅣE檔案可以被理解為一棵單獨註冊表樹,就像Windows的PE格式一樣,它也有自己的組織形式。本文的任務就是要分析HⅣE檔案的組織形式並完成一個HⅣE格式的分析程式。

註冊表API工作原理簡述

Windows系統提供了大量的API給用戶訪問和修改註冊表中的數據,regedit就是基於這些API所實現的。註冊表API大致分為用戶空間的與核心空間的兩類,一般用戶調用前者,層層調用轉移,由核心的註冊表API再調用檔案系統的驅動等,去訪問磁碟上的HⅣE檔案,並最終返回請求的數據結果。這個過程有點冗長,但是為了註冊表里存放數據的安全考慮,損失一些性能表現還是值得的。

HⅣE結構解析

在認識真正的HⅣE檔案之前,我們先列舉HⅣE檔案的幾個主要特徵。先入為主的將它們呈現出來將有助於我們對其檔案組織和數據結構的理解。

註冊表由多個HⅣE檔案組成。

一個HⅣE檔案由多個巢箱(BIN)組成

HⅣE檔案的首部有一個檔案頭(基本塊、base block),用於描述這個HⅣE檔案的一些全局信息

一個BIN由多個巢室(CELL)組成,

CELL可以分為具體的5種(後面介紹),用於存儲不同的註冊表數據。

本文中,我們並不統一使用HⅣE、BIN和CELL的英文單詞,而是和對應的中文辭彙交替出現。在中文裡,它們分別對應儲巢、巢箱和巢室三個名詞。

一個儲巢被看成是一些稱為塊(block)的分配單元,類似於將磁碟分為簇的形式。根據定義,每一個註冊表塊的大小為4096位元組(4KB),當新的數據要加入到一個儲巢中來時,該儲巢總是按照塊的粒度來增加。一個儲巢的第一個塊是基本塊(base block),包含了有關該儲巢的全局信息,包括一個特徵簽名“regf”,更新序列號,儲巢上一次寫操作發生的時間戳,儲巢格式版本號、檢驗和,以及該儲巢檔案的內部檔案名稱等等。下面的_HBASE_BLOCK就是一個基本塊的數據結構還原。

typedef struct _HBASE_BLOCK

{

ULONG Signature; /* 簽名ASCⅡ-"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; /* 簽名 ASCⅡ-"hbin" = 0x6E696268 (小端序) */

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

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

ULONG Reserved1[2];

LARGE_INTEGERTimeStamp;

ULONG Spare;

} HBIN,*PHBIN;

一個巢室可以容納一個鍵、一個值、一個安全描述符、一列子鍵或者一列鍵值,分別有對應的巢室來存儲數據。在巢室數據的開始之處,有一個數據域描述了該巢室數據的類型,具體的數據結構如下:

鍵巢室,包含了一個註冊表鍵(也稱為鍵節點)的巢室,一個鍵巢室包含一個特徵簽名(對於一個鍵是kn,一個符號連結是kl)、該鍵最近一次更新的時間戳、該鍵父鍵巢室的巢室索引、代表該鍵的子鍵的子鍵列表巢室的索引、該鍵的安全描述符巢室索引、一個代表該鍵類名的字元串鍵巢室索引,以及該鍵的名稱。

typedef struct _CM_KEY_NODE

{

USHORT Signature; /*簽名ASCⅡ-"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; /* 簽名ASCⅡ-"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; /* 簽名ASCⅡ-"sk" = 0x6B73 (小端序)*/

WORD Reserved;

ULONG Flink; /*上一個"sk"記錄的偏移 */

ULONG Blink; /*下一個"sk"記錄的偏移 */

ULONG ReferenceCount; /* 引用計數 */

ULONG DescriptorLength; /* 數據大小*/

SECURITY_DESCRIPTOR_RELATⅣE Descriptor; /* 數據 */

} CM_KEY_SECURITY,*PCM_KEY_SECURITY;

儲巢的結構是通過一些連結建立起來的,這些連結稱為巢室索引(cell index)。每個巢室索引是一個巢室在儲巢檔案中的偏移。因此,巢室索引就像是一個指針,從一個巢室指向另一個巢室,配置管理器將巢室索引解釋為相對於儲巢起始處的偏移。因此,假如你想找到子鍵A的鍵巢室,並且A的父鍵是B,那么就必須先利用B的巢室中的子鍵列表巢室索引,找到包含B的所有子鍵列表的那個巢室,然後再利用該子鍵列表巢室中的巢室索引列表,找到B的每個子鍵的巢室,隨即找到A。

巢室,巢箱和塊之間的區別很容易讓人混淆,所以我們來看一個簡單的註冊表儲巢的布局示例,如圖5。該示例中包含了一個基本塊和兩個巢箱,第一個巢箱是空的,第二個巢箱包含了幾個巢室。該巢室有兩個鍵,一個是根鍵Root,另一個是Root的子鍵——SubKey。Root有兩個值,Val1和 Val2,通過一個子鍵列表巢室,可以定位到根鍵的子鍵,通過一個值列表巢室,可以定位到根鍵的值。第二個巢箱中,空閒的空間屬於空的巢室。

獲得HⅣE檔案

知道了HⅣE們的存放位置,自然就想到要把它們抓過來,逐個解剖。抓捕工作看似棘手,但解決起來也很簡單。HⅣE是Windows的重要資源,自啟動以來就只能被系統獨占訪問。我們換一種思路,在另外一個系統中啟動,如同一台機器上的Linux,那目前系統的HⅣE檔案不是就可以訪問了嗎?但是這裡如果你非要在當前系統訪問這個HⅣE檔案,就只有求助於檔案系統驅動了。後者已經超出了本文的討論範圍,故我們不作考慮。

不過,為了示例學習的需要,我們總希望HⅣE檔案能相對簡單一些,讓我們把它的結構看個清楚明白。系統內部的HⅣE檔案一般都不太適合,從其檔案尺寸已經達到MB級別,就可看出其數據量的巨大。因此,初期我們需要自己建立一個小型的HⅣE以供學習之用。

為了以後敘述的方便,我們在HKLM\SAM下建立了子鍵test_root,然後在test_root下再建立兩個子鍵1test和2test,並且在 1test下新建了五種不同的值,並填寫了相應的數據.然後,用RegSaveKey函式編個小程式,把test_root保存為HⅣE檔案。這樣 test_root就變成這個HⅣE檔案的根鍵。接下來,我們就可以細細剖析HⅣE檔案中每一個部分的結構和功能了。

HⅣE格式實例分析

我們用一個16進制編輯器打開test_root檔案,首先就可以看到基本塊的簽名——“regf”字元串,這是registry file的縮寫,標誌它是個註冊表檔案...

HⅣE檔案讀取程式

基於上面的結構解釋和分析,我們可以寫出一個HⅣE檔案的讀取程式。針對上述分析示例的讀取效果,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的編碼。

相關詞條

相關搜尋

熱門詞條

聯絡我們