1.HIVE结构
首先,要明白的是注册表是由多个hive文件组成.
而一个hive是由许多bin组成,一个bin是由很多cell组成.
而cell可以有好几种类型.比如 key cell(cm_key_node) value cell(CM_KEY_VALUE) subbkey-list cell,value-list cell等
当新的数据要扩张一个hive时,总是按照block的粒度(4kb)来增加,一个hive的第一个块是base block.包含了有关该hive的全局信息.参考结构 _hbase_block
bin也有头部,其中包含了一个特征签名hbin,一个记录该bin在hive中的offset(偏移),以及该bin的大小。bin头的大小一般为0x20。
所以一个hive的磁盘映像看起来如下图
+===============+
+ _hbase_block (4kb) +
+===============+
+ _bin0 +
+===============+
+ _bin1 +
+===============+
+ _bin2 +
+===============+
+ .........+
+===============+
+ _bin N +
+===============+
bin头的结构_hbin如下:
typedef struct _HBIN
{
+0x000 ULONG Signature; //”hbin”字符串
+0x004 ULONG FileOffset; //本BIN相差0x1000的偏移
+0x008 ULONG Size; //本BIN的大小
+0x00c ULONG Reserved1[2];
+0x014 LARGE_INTEGER TimeStamp;
+0x01c ULONG Spare;
+0x020 ULONG Reserved2;
+0x024
//
//这里开始是cell数据
//
}
所以定位到第一个cell的步骤如下:
1,将磁盘上的hive文件*.dat通过CreateFile()和CreateFileMapping()映射到内存,得到hFileMap.
2,hFileMap加上一个_base_block大小(0x1000),定位到bin头,即RootBin=hFileMap+0x1000.
3,RootBin加上一个_hbin的大小,即可定位到第一个cell,即pKeyNode=RootBin+0x24;(假设该hive的第一个cell为_CM_KEY_NODE结构)
2.关于cell的几个结构
一个cell可以容纳一个键,一个值,一个安全描述,一列子键,或者一列键值.
typedef struct _CELL_DATA
{
union _u //注意它是一个联合体
{
CM_KEY_NODE KeyNode; //键结构
CM_KEY_VALUE KeyValue;//值的结构, 包含一个键的值的信息.
CM_KEY_SECURITY KeySecurity;//安全描述
CM_KEY_INDEX KeyIndex;//一列子键,又一系列的键cell的cellindex所构成的cell,这些cell是一个公共parent key的所有subkey.
CM_BIG_DATA ValueData;
HCELL_INDEX KeyList[1];//一列键值,
WCHAR KeyString[1];
} u;
} CELL_DATA, *PCELL_DATA;
lkd> DT _CM_KEY_NODE
nt!_CM_KEY_NODE
+0x000 Signature : Uint2B //”nk”字符串
+0x002 Flags : Uint2B
+0x004 LastWriteTime : _LARGE_INTEGER
+0x00c Spare : Uint4B
+0x010 Parent : Uint4B
+0x014 SubKeyCounts : [2] Uint4B //SubKeyCounts[0]子键的个数
+0x01c SubKeyLists : [2] Uint4B //SubKeyLists[0]子键列表相差本BIN的偏移
+0x024 ValueList : _CHILD_LIST //ValueList.Count值的个数
//ValueList.List值列表相差本BIN的偏移
+0x01c ChildHiveReference : _CM_KEY_REFERENCE
+0x02c Security : Uint4B
+0x030 Class : Uint4B
+0x034 MaxNameLen : Pos 0, 16 Bits
+0x034 UserFlags : Pos 16, 4 Bits
+0x034 VirtControlFlags : Pos 20, 4 Bits
+0x034 Debug : Pos 24, 8 Bits
+0x038 MaxClassLen : Uint4B
+0x03c MaxValueNameLen : Uint4B
+0x040 MaxValueDataLen : Uint4B
+0x044 WorkVar : Uint4B
+0x048 NameLength : Uint2B //键名的长度
+0x04a ClassLength : Uint2B
+0x04c Name : [1] Uint2B //键名
补充下_CHILD_LIST结构:
nt!_CHILD_LIST
+0x000 Count : Uint4B //ValueList.Count值的个数
+0x004 List : Uint4B //ValueList.List值列表相差本BIN的偏移
通过观察_CM_KEY_NODE结构,
我们发现pKeyNode+0x4c获得键名,即RootKeyName=pKeyNode->Name;
通过ValueList域,可得到值列表相差本bin的偏移,可以定位该键的值结构.即_CM_KEY_VALUE.
ValueIndex=(ULONG *)(Bin+ValueLists+0x4);
value=(PCM_KEY_VALUE)(Bin+ValueIndex[i]+0x4);
而通过SubKeyLists域,可得到子键列表相差本bin的偏移.
DWORD SubKeyLists=KeyNode->SubKeyLists[0];
KeyIndex=(PCM_KEY_INDEX)(RootBin+SubKeyLists+0x4);
lkd> DT _CM_KEY_VALUE
nt!_CM_KEY_VALUE
+0x000 Signature : Uint2B //”vk”字符串
+0x002 NameLength : Uint2B
+0x004 DataLength : Uint4B //数据长度,以字节计,包括结束符
+0x008 Data : Uint4B //注意:数据偏移或数据判断:如果DataLenth最高位为1,那么它就是数据,且DataLenth&0x7FFFFFFF为数据长度
+0x00c Type : Uint4B
+0x010 Flags : Uint2B
+0x012 Spare : Uint2B
+0x014 Name : [1] Uint2B
通过_CM_KEY_VALUE结构,可以获得该键值的类型(type),名字(Name),以及数据(Data)
lkd> dt _CM_KEY_INDEX
nt!_CM_KEY_INDEX
+0x000 Signature : Uint2B
+0x002 Count : Uint2B //这里也是表示子键的数量,与前面的相同
+0x004 List : [1] Uint4B //这里要注意了
如果Signature==CM_KEY_FAST_LEAF或CM_KEY_HASH_LEAF,那么从0x004偏移开始的数组元素结构如下:
struct
{
ULONG offset;
ULONG HashKey;
}
否则:
ULONG offset;
对于_CM_KEY_INDEX结构,通过List域可以定位所有子键的_CM_KEY_NODE结构,具体实现如下:
DWORD KeyNodeOffset=KeyIndex->List[i*2]; //(其中的i是,USHORT i=0,i<count,i++)
KeyNode=(PCM_KEY_NODE)(Bin+KeyNodeOffset+0x4);
这样定位到每个子键的_CM_KEY_NODE结构,就能获得该子键的键值,以及该子键下面的子子键,
这样重复上述的步骤就能实现对整个hive的解析.
ps下:通过是ring3的RegSaveKey(),可以生成一个Hive文件.
具体实现的代码如下:
#include <windows.h> #include <stdio.h> #include <stdlib.h>
#define REGF 0x66676572 //fger #define HBIN 0x6e696268 //nibh #define CM_KEY_FAST_LEAF 0x666c // fl #define CM_KEY_HASH_LEAF 0x686c // hl
//数据结构定义 typedef struct _CHILD_LIST { ULONG Count; ULONG List; }CHILD_LIST;
typedef struct _CM_KEY_NODE { USHORT Signature; CHAR Reserve_1[18]; ULONG SubKeyCounts[2]; ULONG SubKeyLists[2]; CHILD_LIST ValueList; CHAR Reserve_2[28]; USHORT NameLength; SHORT ClassName; CHAR Name; } CM_KEY_NODE,*PCM_KEY_NODE;
typedef struct _CM_KEY_INDEX { USHORT Signature; USHORT Count; ULONG List[1]; } CM_KEY_INDEX, *PCM_KEY_INDEX;
typedef struct _CM_KEY_VALUE { USHORT Signature; SHORT NameLength; ULONG DataLength; ULONG Data; ULONG Type; CHAR Reserve_1[4]; CHAR Name; }CM_KEY_VALUE,*PCM_KEY_VALUE;
VOID TpyeKeyAndValue(PVOID FileMemAddr); VOID TypeSubKey(PCHAR Bin,PCM_KEY_INDEX KeyIndex); VOID TypeSubKeyName(PCHAR Bin,PCM_KEY_NODE KeyNode); VOID TypeValue(PCHAR Bin,PCM_KEY_VALUE value);
int main(int argc,char *argv[]) { HANDLE hFile; HANDLE hFileMap; DWORD FileSize; PVOID FileMem;
#ifndef _DEBUG if(argc!=2) { printf("*************************\n"); printf("Useage:\nHivePase FileName\n"); printf("*************************\n"); system("pause"); return 1; } hFile=CreateFile(argv[1],GENERIC_READ,0,NULL, OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NULL); #else hFile=CreateFile("c:\\test_root.dat",GENERIC_READ,0,NULL, OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NULL); #endif
if(hFile==INVALID_HANDLE_VALUE) { printf("Error:File doesn's Exist!\n"); system("pause"); return 1; } FileSize=GetFileSize(hFile,NULL); hFileMap=CreateFileMapping(hFile,NULL,PAGE_READONLY | SEC_COMMIT,0,FileSize,NULL); if(hFileMap==NULL) { printf("CreateFileMapping Error!\n"); CloseHandle(hFile); system("pause"); return 1; } CloseHandle(hFile); FileMem=MapViewOfFile(hFileMap,FILE_MAP_READ,0,0,0); if(FileMem==NULL) { printf("MapViewOfFile Error!\n"); CloseHandle(hFileMap); system("pause"); return 1; } CloseHandle(hFileMap); TpyeKeyAndValue(FileMem); printf("\nSuccess!\n"); system("pause"); return 0; }
VOID TpyeKeyAndValue(PVOID FileMemAddr) { char RootKeyName[256]; PCHAR RootBin; PCM_KEY_NODE KeyNode; PCM_KEY_INDEX KeyIndex; if(*(ULONG*)FileMemAddr!=REGF) { printf("Not a Hive File!\n"); system("pause"); } RootBin=(char*)FileMemAddr+0x1000; KeyNode=(PCM_KEY_NODE)(RootBin+0x24); if(*(ULONG*)RootBin!=HBIN) { printf("Hive File Error!\n"); system("pause"); } ZeroMemory(RootKeyName,256); memcpy(RootKeyName,&KeyNode->Name,KeyNode->NameLength); printf("Root Key: %s\n",RootKeyName); DWORD SubKeyLists=KeyNode->SubKeyLists[0]; KeyIndex=(PCM_KEY_INDEX)(RootBin+SubKeyLists+0x4); TypeSubKey(RootBin,KeyIndex);
}
VOID TypeSubKey(PCHAR Bin,PCM_KEY_INDEX KeyIndex) { USHORT KeyCount; PCM_KEY_NODE KeyNode; KeyCount=KeyIndex->Count; for(USHORT i=0;i<KeyCount;i++) { if(KeyIndex->Signature==CM_KEY_FAST_LEAF || KeyIndex->Signature==CM_KEY_HASH_LEAF) { DWORD KeyNodeOffset=KeyIndex->List[i*2]; KeyNode=(PCM_KEY_NODE)(Bin+KeyNodeOffset+0x4); TypeSubKeyName(Bin,KeyNode); } else { DWORD KeyNodeOffset=KeyIndex->List[i*2]; KeyNode=(PCM_KEY_NODE)(Bin+KeyNodeOffset+0x4); TypeSubKeyName(Bin,KeyNode); } }
}
VOID TypeSubKeyName(PCHAR Bin,PCM_KEY_NODE KeyNode) { char SubKeyName[256]; ULONG ValueLists; ULONG ValueCount; ULONG *ValueIndex; PCM_KEY_VALUE value; PCM_KEY_INDEX KeyIndex; ZeroMemory(SubKeyName,256); strncpy(SubKeyName,&KeyNode->Name,KeyNode->NameLength); printf("Sub Key: %s\n",SubKeyName); ValueLists=KeyNode->ValueList.List; ValueCount=KeyNode->ValueList.Count; if(ValueLists!=-1) { ValueIndex=(ULONG *)(Bin+ValueLists+0x4); for(ULONG i=0;i<ValueCount;i++) { value=(PCM_KEY_VALUE)(Bin+ValueIndex[i]+0x4); TypeValue(Bin,value); } } if(KeyNode->SubKeyLists[0]!=-1) { KeyIndex=(PCM_KEY_INDEX)(Bin+KeyNode->SubKeyLists[0]+0x4); TypeSubKey(Bin,KeyIndex); } }
VOID TypeValue(PCHAR Bin,PCM_KEY_VALUE value) { char ValueName[256]; ULONG DataLenth; PCHAR Data; ZeroMemory(ValueName,256); strncpy(ValueName,&value->Name,value->NameLength); printf("Value Name: %s\n",ValueName); switch(value->Type) { case REG_SZ: printf("REG_SZ "); break; case REG_BINARY: printf("REG_BINARY "); break; case REG_DWORD: printf("REG_DWORD "); break; case REG_MULTI_SZ: printf("REG_MULIT_SZ "); break; case REG_EXPAND_SZ: printf("REG_EXPAND_SZ "); break; default: break; } if(value->Type==REG_DWORD) { Data=(PCHAR)&value->Data; printf("%08x",*(ULONG*)Data); } else { if(value->DataLength & 0x80000000) { DataLenth=value->DataLength & 0x7FFFFFFF; Data=(PCHAR)&value->Data; for(ULONG i=0;i<DataLenth;i++) { if(value->Type==REG_BINARY) { printf("%1x",Data[i]); } else { printf("%c",Data[i]); } } } else { DataLenth=value->DataLength; DWORD DataOffset=value->Data; Data=Bin+DataOffset+0x4; for(ULONG i=0;i<DataLenth;i++) { if(value->Type==REG_BINARY) { printf("%1x",Data[i]); } else { printf("%c",Data[i]); } } } } printf("\n"); } |
关于上面code的一些补充说明:(by linxer)
1.对nk节点下二级索引子键的情况没有处理(有ri节点情况,当子键过多的时候系统会使用ri节点的)
2.TypeValue里对值名称的处理有问题,问题出在这下面两行代码
strncpy(ValueName,&value->Name,value->NameLength);
printf("Value Name: %s\n",ValueName);
vk节点的值名称未必就是ascii字符串,因此用str系列函数是不可以的,正确用法应该是
memcpy(ValueName,&value->Name,value->NameLength);因此显示语句也不能用printf来显示,目前我看到的很多代码都有这样的问题(chntpw),还有一些使用的hive解析的工具(SnipeSword0225)也有这种问题
(责任编辑:admin)