binhive.c

来自「一个类似windows」· C语言 代码 · 共 1,660 行 · 第 1/3 页

C
1,660
字号
/*
 *  FreeLoader
 *
 *  Copyright (C) 2001  Rex Jolliff
 *  Copyright (C) 2001  Eric Kohl
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <freeldr.h>

#define NDEBUG
#include <debug.h>

#define  REG_HIVE_ID                   0x66676572 /* "regf" */
#define  REG_BIN_ID                    0x6e696268 /* "hbin" */
#define  REG_KEY_CELL_ID               0x6b6e
#define  REG_HASH_TABLE_BLOCK_ID       0x666c
#define  REG_VALUE_CELL_ID             0x6b76

#define  REG_BLOCK_SIZE                4096
#define  REG_HBIN_DATA_OFFSET          32
#define  REG_INIT_BLOCK_LIST_SIZE      32
#define  REG_INIT_HASH_TABLE_SIZE      3
#define  REG_EXTEND_HASH_TABLE_SIZE    4
#define  REG_VALUE_LIST_CELL_MULTIPLE  4


#define ABS_VALUE(V) (((V) < 0) ? -(V) : (V))


/* BLOCK_OFFSET = offset in file after header block */
typedef ULONG BLOCK_OFFSET, *PBLOCK_OFFSET;

/* header for registry hive file : */
typedef struct _HIVE_HEADER
{
  /* Hive identifier "regf" (0x66676572) */
  ULONG  BlockId;

  /* Update counter */
  ULONG  UpdateCounter1;

  /* Update counter */
  ULONG  UpdateCounter2;

  /* When this hive file was last modified */
  ULONGLONG  DateModified;

  /* Registry format major version (1) */
  ULONG  MajorVersion;

  /* Registry format minor version (3)
     Version 3 added fast indexes, version 5 has large value optimizations */
  ULONG  MinorVersion;

  /* Registry file type (0 - Primary, 1 - Log) */
  ULONG  Type;

  /* Registry format (1 is the only defined value so far) */
  ULONG  Format;

  /* Offset into file from the byte after the end of the base block.
     If the hive is volatile, this is the actual pointer to the KEY_CELL */
  BLOCK_OFFSET  RootKeyOffset;

  /* Size of each hive block ? */
  ULONG  BlockSize;

  /* (1?) */
  ULONG  Unused7;

  /* Name of hive file */
  WCHAR  FileName[48];

  ULONG  Reserved[99];

  /* Checksum of first 0x200 bytes */
  ULONG  Checksum;
} __attribute__((packed)) HIVE_HEADER, *PHIVE_HEADER;


typedef struct _BIN_HEADER
{
  /* Bin identifier "hbin" (0x6E696268) */
  ULONG  HeaderId;

  /* Block offset of this bin */
  BLOCK_OFFSET  BinOffset;

  /* Size in bytes, multiple of the block size (4KB) */
  ULONG  BinSize;

  ULONG  Reserved[2];

  /* When this bin was last modified */
  ULONGLONG  DateModified;

  /* ? (In-memory only) */
  ULONG  MemAlloc;
} __attribute__((packed)) HBIN, *PHBIN;


typedef struct _CELL_HEADER
{
  /* <0 if used, >0 if free */
  LONG  CellSize;
} __attribute__((packed)) CELL_HEADER, *PCELL_HEADER;


typedef struct _KEY_CELL
{
  /* Size of this cell */
  LONG  CellSize;

  /* Key cell identifier "kn" (0x6b6e) */
  USHORT  Id;

  /* Flags */
  USHORT  Flags;

  /* Time of last flush */
  ULONGLONG  LastWriteTime;		/* FILETIME */

  /* ? */
  ULONG  UnUsed1;

  /* Block offset of parent key cell */
  BLOCK_OFFSET  ParentKeyOffset;

  /* Count of sub keys for the key in this key cell */
  ULONG  NumberOfSubKeys;

  /* ? */
  ULONG  UnUsed2;

  /* Block offset of has table for FIXME: subkeys/values? */
  BLOCK_OFFSET  HashTableOffset;

  /* ? */
  ULONG  UnUsed3;

  /* Count of values contained in this key cell */
  ULONG  NumberOfValues;

  /* Block offset of VALUE_LIST_CELL */
  BLOCK_OFFSET  ValueListOffset;

  /* Block offset of security cell */
  BLOCK_OFFSET  SecurityKeyOffset;

  /* Block offset of registry key class */
  BLOCK_OFFSET  ClassNameOffset;

  /* ? */
  ULONG  Unused4[5];

  /* Size in bytes of key name */
  USHORT NameSize;

  /* Size of class name in bytes */
  USHORT ClassSize;

  /* Name of key (not zero terminated) */
  CHAR  Name[0];
} __attribute__((packed)) KEY_CELL, *PKEY_CELL;


/* KEY_CELL.Type constants */
#define  REG_LINK_KEY_CELL_TYPE        0x10
#define  REG_KEY_NAME_PACKED           0x20
#define  REG_ROOT_KEY_CELL_TYPE        0x0c


// hash record :
// HashValue=four letters of value's name
typedef struct _HASH_RECORD
{
  BLOCK_OFFSET  KeyOffset;
  ULONG  HashValue;
} __attribute__((packed)) HASH_RECORD, *PHASH_RECORD;


typedef struct _HASH_TABLE_CELL
{
  LONG  CellSize;
  USHORT  Id;
  USHORT  HashTableSize;
  HASH_RECORD  Table[0];
} __attribute__((packed)) HASH_TABLE_CELL, *PHASH_TABLE_CELL;


typedef struct _VALUE_LIST_CELL
{
  LONG  CellSize;
  BLOCK_OFFSET  ValueOffset[0];
} __attribute__((packed)) VALUE_LIST_CELL, *PVALUE_LIST_CELL;


typedef struct _VALUE_CELL
{
  LONG  CellSize;
  USHORT  Id;	// "kv"
  USHORT  NameSize;	// length of Name
  ULONG  DataSize;	// length of datas in the cell pointed by DataOffset
  BLOCK_OFFSET  DataOffset;// datas are here if high bit of DataSize is set
  ULONG  DataType;
  USHORT  Flags;
  USHORT Unused1;
  CHAR  Name[0]; /* warning : not zero terminated */
} __attribute__((packed)) VALUE_CELL, *PVALUE_CELL;

/* VALUE_CELL.Flags constants */
#define REG_VALUE_NAME_PACKED             0x0001

/* VALUE_CELL.DataSize mask constants */
#define REG_DATA_SIZE_MASK                 0x7FFFFFFF
#define REG_DATA_IN_OFFSET                 0x80000000


typedef struct _DATA_CELL
{
  LONG  CellSize;
  CHAR  Data[0];
} __attribute__((packed)) DATA_CELL, *PDATA_CELL;


typedef struct _REGISTRY_HIVE
{
  ULONG  FileSize;
  PHIVE_HEADER  HiveHeader;
  ULONG  BlockListSize;
  PHBIN  *BlockList;
  ULONG  FreeListSize;
  ULONG  FreeListMax;
  PCELL_HEADER *FreeList;
  BLOCK_OFFSET *FreeListOffset;
} REGISTRY_HIVE, *PREGISTRY_HIVE;


static PVOID MbBase = NULL;
static ULONG MbSize = 0;

/* FUNCTIONS ****************************************************************/

static VOID
InitMbMemory (PVOID ChunkBase)
{
  MbBase = ChunkBase;
  MbSize = 0;
}


static PVOID
AllocateMbMemory (ULONG MemSize)
{
  PVOID CurBase;

  CurBase = MbBase;

  MbBase = (PVOID)((ULONG)MbBase + MemSize);
  MbSize += MemSize;

  return CurBase;
}

static VOID
FreeMbMemory (VOID)
{
  MbSize = 0;
}

static ULONG
GetMbAllocatedSize (VOID)
{
  return MbSize;
}


static VOID
CmiCreateDefaultHiveHeader (PHIVE_HEADER Header)
{
  assert(Header);
  memset (Header, 0, REG_BLOCK_SIZE);
  Header->BlockId = REG_HIVE_ID;
  Header->UpdateCounter1 = 0;
  Header->UpdateCounter2 = 0;
  Header->DateModified = 0;
  Header->MajorVersion = 1;
  Header->MinorVersion = 3;
  Header->Type = 0;
  Header->Format = 1;
  Header->Unused7 = 1;
  Header->RootKeyOffset = -1;
  Header->BlockSize = REG_BLOCK_SIZE;
  Header->Checksum = 0;
}


static VOID
CmiCreateDefaultBinCell (PHBIN BinCell)
{
  assert(BinCell);
  memset (BinCell, 0, REG_BLOCK_SIZE);
  BinCell->HeaderId = REG_BIN_ID;
  BinCell->DateModified = 0ULL;
  BinCell->BinSize = REG_BLOCK_SIZE;
}


static VOID
CmiCreateDefaultRootKeyCell (PKEY_CELL RootKeyCell, PCWSTR KeyName)
{
  PWCHAR BaseKeyName;
  ULONG NameSize;
  ULONG CellSize;
  ULONG i;
  BOOL Packable = TRUE;

  assert (RootKeyCell);

  BaseKeyName = wcsrchr(KeyName, L'\\') + 1;
  NameSize = wcslen(BaseKeyName);
  for (i = 0; i < NameSize; i++)
    {
      if (KeyName[i] & 0xFF00)
        {
          Packable = FALSE;
          NameSize *= sizeof(WCHAR);
          break;
        }
    }

  CellSize = ROUND_UP(sizeof(KEY_CELL) + NameSize, 16);

  memset (RootKeyCell, 0, CellSize);
  RootKeyCell->CellSize = -CellSize;
  RootKeyCell->Id = REG_KEY_CELL_ID;
  RootKeyCell->Flags = REG_ROOT_KEY_CELL_TYPE;
  RootKeyCell->LastWriteTime = 0ULL;
  RootKeyCell->ParentKeyOffset = 0;
  RootKeyCell->NumberOfSubKeys = 0;
  RootKeyCell->HashTableOffset = -1;
  RootKeyCell->NumberOfValues = 0;
  RootKeyCell->ValueListOffset = -1;
  RootKeyCell->SecurityKeyOffset = 0;
  RootKeyCell->ClassNameOffset = -1;
  RootKeyCell->NameSize = NameSize;
  RootKeyCell->ClassSize = 0;
  if (Packable)
    {
      for(i = 0; i < NameSize; i++)
        {
          ((PCHAR)RootKeyCell->Name)[i] = BaseKeyName[i];
        }
      RootKeyCell->Flags |= REG_KEY_NAME_PACKED;
    }
  else
    {
      memcpy (RootKeyCell->Name, BaseKeyName, NameSize);
    }
}


static PREGISTRY_HIVE
CmiCreateHive (PCWSTR KeyName)
{
  PREGISTRY_HIVE Hive;
  PCELL_HEADER FreeCell;
  PKEY_CELL RootKeyCell;
  PHBIN BinCell;

  Hive = (PREGISTRY_HIVE) MmAllocateMemory (sizeof(REGISTRY_HIVE));
  if (Hive == NULL)
    {
      return NULL;
    }
  memset (Hive, 0, sizeof(REGISTRY_HIVE));

  DbgPrint((DPRINT_REGISTRY, "Hive %x\n", Hive));

  /* Create hive beader (aka 'base block') */
  Hive->HiveHeader = (PHIVE_HEADER) AllocateMbMemory (REG_BLOCK_SIZE);
  if (Hive->HiveHeader == NULL)
    {
      MmFreeMemory (Hive);
      return NULL;
    }
  CmiCreateDefaultHiveHeader(Hive->HiveHeader);
  Hive->FileSize = REG_BLOCK_SIZE;

  /* Allocate block list */
  Hive->BlockListSize = 1;
  Hive->BlockList = MmAllocateMemory (sizeof(PHBIN) * Hive->BlockListSize);
  if (Hive->BlockList == NULL)
    {
      MmFreeMemory (Hive);
      return NULL;
    }

  /* Allocate free cell list */
  Hive->FreeListMax = 32;
  Hive->FreeList = MmAllocateMemory(sizeof(PCELL_HEADER) * Hive->FreeListMax);
  if (Hive->FreeList == NULL)
    {
      MmFreeMemory (Hive->BlockList);
      MmFreeMemory (Hive);
      return NULL;
    }
  Hive->FreeListOffset = MmAllocateMemory(sizeof(BLOCK_OFFSET) * Hive->FreeListMax);
  if (Hive->FreeListOffset == NULL)
    {
      MmFreeMemory (Hive->FreeList);
      MmFreeMemory (Hive->BlockList);
      MmFreeMemory (Hive);
      return NULL;
    }

  /* Allocate first bin */
  Hive->BlockList[0] = (PHBIN) AllocateMbMemory (REG_BLOCK_SIZE);
  if (Hive->BlockList[0] == NULL)
    {
      MmFreeMemory (Hive->FreeListOffset);
      MmFreeMemory (Hive->FreeList);
      MmFreeMemory (Hive->BlockList);
      MmFreeMemory (Hive);
      return NULL;
    }
  Hive->FileSize += REG_BLOCK_SIZE;

  /* Init first bin */
  BinCell = (PHBIN)Hive->BlockList[0];
  CmiCreateDefaultBinCell(BinCell);
  BinCell->BinOffset = 0;

  /* Init root key cell */
  RootKeyCell = (PKEY_CELL)((ULONG)BinCell + REG_HBIN_DATA_OFFSET);
  CmiCreateDefaultRootKeyCell(RootKeyCell, KeyName);
  Hive->HiveHeader->RootKeyOffset = REG_HBIN_DATA_OFFSET;

  /* Init free cell */
  FreeCell = (PCELL_HEADER)((ULONG)RootKeyCell - RootKeyCell->CellSize);
  FreeCell->CellSize = REG_BLOCK_SIZE - (REG_HBIN_DATA_OFFSET - RootKeyCell->CellSize);

  Hive->FreeList[0] = FreeCell;
  Hive->FreeListOffset[0] = REG_HBIN_DATA_OFFSET - RootKeyCell->CellSize;
  Hive->FreeListSize++;

  return Hive;
}


static VOID
CmiCleanupHive(PREGISTRY_HIVE Hive, BOOL Release)
{
  MmFreeMemory (Hive->FreeListOffset);
  MmFreeMemory (Hive->FreeList);
  MmFreeMemory (Hive->BlockList);
  MmFreeMemory (Hive);

  if (Release)
    {
      FreeMbMemory ();
    }
}


static PHBIN
CmiGetBin (PREGISTRY_HIVE Hive,
	   BLOCK_OFFSET BlockOffset)
{
  ULONG BlockIndex;

  if (BlockOffset == (ULONG) -1)
    return NULL;

  BlockIndex = BlockOffset / REG_BLOCK_SIZE;
  if (BlockIndex >= Hive->BlockListSize)
    return NULL;

  return Hive->BlockList[BlockIndex];
}


static BOOL
CmiMergeFree(PREGISTRY_HIVE RegistryHive,
	     PCELL_HEADER FreeBlock,
	     BLOCK_OFFSET FreeOffset)
{
  BLOCK_OFFSET BlockOffset;
  BLOCK_OFFSET BinOffset;
  ULONG BlockSize;
  ULONG BinSize;
  PHBIN Bin;
  ULONG i;

  DbgPrint((DPRINT_REGISTRY, "CmiMergeFree(Block %lx  Offset %lx  Size %lx) called\n",
	   FreeBlock, FreeOffset, FreeBlock->CellSize));

  Bin = CmiGetBin (RegistryHive, FreeOffset);
  if (Bin == NULL)
    return FALSE;

  DbgPrint((DPRINT_REGISTRY, "Bin %p\n", Bin));

  BinOffset = Bin->BinOffset;
  BinSize = Bin->BinSize;
  DbgPrint((DPRINT_REGISTRY, "Bin %p  Offset %lx  Size %lx\n", Bin, BinOffset, BinSize));

  for (i = 0; i < RegistryHive->FreeListSize; i++)
    {
      BlockOffset = RegistryHive->FreeListOffset[i];
      BlockSize = RegistryHive->FreeList[i]->CellSize;
      if (BlockOffset > BinOffset &&
	  BlockOffset < BinOffset + BinSize)
	{
	  DbgPrint((DPRINT_REGISTRY, "Free block: Offset %lx  Size %lx\n",
		   BlockOffset, BlockSize));

	  if ((i < (RegistryHive->FreeListSize - 1)) &&
	      (BlockOffset + BlockSize == FreeOffset) &&
	      (FreeOffset + FreeBlock->CellSize == RegistryHive->FreeListOffset[i + 1]))
	    {
	      DbgPrint((DPRINT_REGISTRY, "Merge current block with previous and next block\n"));

	      RegistryHive->FreeList[i]->CellSize +=
		(FreeBlock->CellSize + RegistryHive->FreeList[i + 1]->CellSize);

	      FreeBlock->CellSize = 0;
	      RegistryHive->FreeList[i + 1]->CellSize = 0;


	      if ((i + 2) < RegistryHive->FreeListSize)
		{
		  memmove (&RegistryHive->FreeList[i + 1],
			   &RegistryHive->FreeList[i + 2],
			   sizeof(RegistryHive->FreeList[0])
			     * (RegistryHive->FreeListSize - i - 2));
		  memmove (&RegistryHive->FreeListOffset[i + 1],
			   &RegistryHive->FreeListOffset[i + 2],
			   sizeof(RegistryHive->FreeListOffset[0])
			     * (RegistryHive->FreeListSize - i - 2));

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?