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 < 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)也有这种问题
相关参考:<<windows注册表文件格式的简单分析>>