windows config manager

Introduction

the configuration manager, the executive subsystem that implements the registry,organizes the registry`s on-disk files.
windows organizes the registry data that a hive stores in containers called cells. a cell can hold a key, a value,a security descriptor,a list of subkeys, or a list of key values.

config manager ntapi

NtCreateKey

函数中开始处先验证用户参数是否可访问,然后调用对象管理器来得到对应的句柄

    /* Do the create */
    Status = ObOpenObjectByName(ObjectAttributes,
                                CmpKeyObjectType,
                                PreviousMode,
                                NULL,
                                DesiredAccess,
                                &ParseContext,
                                &Handle)

这里面我们创建的就是一个key的内核对象。我们得到的是对象句柄,将句柄传递出去,给用户使用,当然NtOpenKey也是做了如此的操作。

NtDeleteKey

然后我们看看NtDeleteKey,这个函数中通过参数句柄值使用对象管理器来实际获得到对象体,然后包装对象体的信息,调用配置管理器的回调函数。

    /* Setup the callback */
    PostOperationInfo.Object = (PVOID)KeyObject;
    DeleteKeyInfo.Object = (PVOID)KeyObject;
    Status = CmiCallRegisteredCallbacks(RegNtPreDeleteKey, &DeleteKeyInfo);

然后我们调用内部实际的函数CmDeleteKey()来删除键,在这个之前我们要确认这个键值我们有权限删除。
我们如下定义着key的对象体

//
// Key Body
//
typedef struct _CM_KEY_BODY
{
    ULONG Type;
    struct _CM_KEY_CONTROL_BLOCK *KeyControlBlock;
    struct _CM_NOTIFY_BLOCK *NotifyBlock;
    HANDLE ProcessID;
    LIST_ENTRY KeyBodyList;
} CM_KEY_BODY, *PCM_KEY_BODY;

CmDeleteKey
我们来分析一下CmDeleteKey,相对来说有点麻烦。

NTSTATUS
NTAPI
CmDeleteKey(IN PCM_KEY_BODY KeyBody)
{
    NTSTATUS Status;
    PHHIVE Hive;
    PCM_KEY_NODE Node, Parent;
    HCELL_INDEX Cell, ParentCell;
    PCM_KEY_CONTROL_BLOCK Kcb;
    /* Acquire hive lock */
    CmpLockRegistry();
    /* Get the kcb */
    Kcb = KeyBody->KeyControlBlock;
    /* Don't allow deleting the root */
    if (!Kcb->ParentKcb)
    {
        /* Fail */
        CmpUnlockRegistry();
        return STATUS_CANNOT_DELETE;
    }
    /* Lock parent and child */
    CmpAcquireTwoKcbLocksExclusiveByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey);
    /* Check if we're already being deleted */
    if (Kcb->Delete)
    {
        /* Don't do it twice */
        Status = STATUS_SUCCESS;
        goto Quickie2;
    }
    /* Get the hive and node */
    Hive = Kcb->KeyHive;
    Cell = Kcb->KeyCell;
    /* Lock flushes */
    CmpLockHiveFlusherShared((PCMHIVE)Hive);
    /* Get the key node */
    Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
    ASSERT(Node);
    /* Sanity check */
    ASSERT(Node->Flags == Kcb->Flags);
    /* Check if we don't have any children */
    if (!(Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile]) &&
        !(Node->Flags & KEY_NO_DELETE))
    {
        /* Send notification to registered callbacks */
        CmpReportNotify(Kcb, Hive, Cell, REG_NOTIFY_CHANGE_NAME);
        /* Get the parent and free the cell */
        ParentCell = Node->Parent;
        Status = CmpFreeKeyByCell(Hive, Cell, TRUE);
        if (NT_SUCCESS(Status))
        {
            /* Flush any notifications */
            CmpFlushNotifiesOnKeyBodyList(Kcb, FALSE);
            /* Clean up information we have on the subkey */
            CmpCleanUpSubKeyInfo(Kcb->ParentKcb);
            /* Get the parent node */
            Parent = (PCM_KEY_NODE)HvGetCell(Hive, ParentCell);
            if (Parent)
            {
                /* Update the maximum name length */
                Kcb->ParentKcb->KcbMaxNameLen = (USHORT)Parent->MaxNameLen;
                /* Make sure we're dirty */
                ASSERT(HvIsCellDirty(Hive, ParentCell));
                /* Update the write time */
                KeQuerySystemTime(&Parent->LastWriteTime);
                Kcb->ParentKcb->KcbLastWriteTime = Parent->LastWriteTime;
                /* Release the cell */
                HvReleaseCell(Hive, ParentCell);
            }
            /* Set the KCB in delete mode and remove it */
            Kcb->Delete = TRUE;
            CmpRemoveKeyControlBlock(Kcb);
            /* Clear the cell */
            Kcb->KeyCell = HCELL_NIL;
        }
    }
    else
    {
        /* Fail */
        Status = STATUS_CANNOT_DELETE;
    }
    /* Release the cell */
    HvReleaseCell(Hive, Cell);
    /* Release flush lock */
    CmpUnlockHiveFlusher((PCMHIVE)Hive);
    /* Release the KCB locks */
Quickie2:
    CmpReleaseTwoKcbLockByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey);
    /* Release hive lock */
    CmpUnlockRegistry();
    return Status;
}

参数是key的对象,在key的对象体中我们能获得相关的核心信息,如cell 和hive。
函数开始申请互斥锁,获得key的kcb,即核心信息,针对于root key,我们不处理,因为不允许删除,然后我们判断这个键是否已经删除,如果没有,我们继续操作,这些都是前期的操作,通过kcb我们获得要操作的hive和cell的地址。

    /* Get the hive and node */
    Hive = Kcb->KeyHive;
    Cell = Kcb->KeyCell;

然后调用HvGetCell 函数来获得cell所在地址的数据信息PCM_KEY_NODE,在hvGetCell函数中,cell是一个地址偏移值。

      CellType = (CellIndex & HCELL_TYPE_MASK) >> HCELL_TYPE_SHIFT;
      CellBlock = (CellIndex & HCELL_BLOCK_MASK) >> HCELL_BLOCK_SHIFT;
      CellOffset = (CellIndex & HCELL_OFFSET_MASK) >> HCELL_OFFSET_SHIFT;
      ASSERT(CellBlock < RegistryHive->Storage[CellType].Length);
      Block = (PVOID)RegistryHive->Storage[CellType].BlockList[CellBlock].BlockAddress;
      ASSERT(Block != NULL);
      return (PVOID)((ULONG_PTR)Block + CellOffset);

无非是在hive中找到cell所在的地址,在这个地址上操作。然后我们调用CmpFreeKeyByCell来进行具体的释放cell。然后我们跟着操作一下父key。

RegCreateKeyExW

我们从advapi32开始研究,这个dll中提供用户层面的注册表调用,内部和对象管理器,配置管理器进行交互,我们在分析配置管理器的时候首先看一下接口。
这个函数用来创建键,函数中我们根据第一个参数来实际的打开根键。

LONG
WINAPI
RegCreateKeyExW(
    _In_ HKEY hKey,
    _In_ LPCWSTR lpSubKey,
    _In_ DWORD Reserved,
    _In_opt_ LPWSTR lpClass,
    _In_ DWORD dwOptions,
    _In_ REGSAM samDesired,
    _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    _Out_ PHKEY phkResult,
    _Out_opt_ LPDWORD lpdwDisposition)

然后我们判断是否在hkcr下创建新键,如果是我们调用CreateHKCRKey来进行特殊的创建。
如果是其他情况,我们使用CreateNestedKey来进行创建。

CreateNestedKey

函数中通过两种方式创建,首先全路径创建,如果无法创建成功,我们去掉最后一个键名称然后创建,之后再添加上最后一个键名,全路径键创建。

static NTSTATUS
CreateNestedKey(PHKEY KeyHandle,
                POBJECT_ATTRIBUTES ObjectAttributes,
                PUNICODE_STRING ClassString,
                DWORD dwOptions,
                REGSAM samDesired,
                DWORD *lpdwDisposition)
{
    OBJECT_ATTRIBUTES LocalObjectAttributes;
    UNICODE_STRING LocalKeyName;
    ULONG Disposition;
    NTSTATUS Status;
    ULONG FullNameLength;
    ULONG Length;
    PWCHAR Ptr;
    HANDLE LocalKeyHandle;
    Status = NtCreateKey((PHANDLE) KeyHandle,
                         samDesired,
                         ObjectAttributes,
                         0,
                         ClassString,
                         dwOptions,
                         (PULONG)lpdwDisposition);
    TRACE("NtCreateKey(%wZ) called (Status %lx)\n", ObjectAttributes->ObjectName, Status);
    if (Status != STATUS_OBJECT_NAME_NOT_FOUND)
        return Status;
    /* Copy object attributes */
    RtlCopyMemory(&LocalObjectAttributes,
                  ObjectAttributes,
                  sizeof(OBJECT_ATTRIBUTES));
    RtlCreateUnicodeString(&LocalKeyName,
                           ObjectAttributes->ObjectName->Buffer);
    LocalObjectAttributes.ObjectName = &LocalKeyName;
    FullNameLength = LocalKeyName.Length / sizeof(WCHAR);
    LocalKeyHandle = NULL;
    /* Remove the last part of the key name and try to create the key again. */
    while (Status == STATUS_OBJECT_NAME_NOT_FOUND)
    {
        Ptr = wcsrchr(LocalKeyName.Buffer, '\\');
        if (Ptr == NULL || Ptr == LocalKeyName.Buffer)
        {
            Status = STATUS_UNSUCCESSFUL;
            break;
        }
        *Ptr = (WCHAR)0;
        LocalKeyName.Length = wcslen(LocalKeyName.Buffer) * sizeof(WCHAR);
        Status = NtCreateKey(&LocalKeyHandle,
                             KEY_CREATE_SUB_KEY,
                             &LocalObjectAttributes,
                             0,
                             NULL,
                             0,
                             &Disposition);
        TRACE("NtCreateKey(%wZ) called (Status %lx)\n", &LocalKeyName, Status);
    }
    if (!NT_SUCCESS(Status))
    {
        RtlFreeUnicodeString(&LocalKeyName);
        return Status;
    }
    /* Add removed parts of the key name and create them too. */
    Length = wcslen(LocalKeyName.Buffer);
    while (TRUE)
    {
        if (LocalKeyHandle)
            NtClose (LocalKeyHandle);
        LocalKeyName.Buffer[Length] = L'\\';
        Length = wcslen (LocalKeyName.Buffer);
        LocalKeyName.Length = Length * sizeof(WCHAR);
        if (Length == FullNameLength)
        {
            Status = NtCreateKey((PHANDLE) KeyHandle,
                                 samDesired,
                                 ObjectAttributes,
                                 0,
                                 ClassString,
                                 dwOptions,
                                 (PULONG)lpdwDisposition);
            break;
        }
        Status = NtCreateKey(&LocalKeyHandle,
                             KEY_CREATE_SUB_KEY,
                             &LocalObjectAttributes,
                             0,
                             NULL,
                             0,
                             &Disposition);
        TRACE("NtCreateKey(%wZ) called (Status %lx)\n", &LocalKeyName, Status);
        if (!NT_SUCCESS(Status))
            break;
    }
    RtlFreeUnicodeString(&LocalKeyName);
    return Status;
}

当然函数内部调用的是NtCreateKey.

NtQueryValueKey

我们来看看查询,我们如何查询键值呢。当然上层是调用RegQueryValueExW来查询键值,在其函数内部我们调用系统提供的API来处理,这个API是NtQueryValueKey在这个函数内部,我们接着调用对象管理器和配置管理器来进行实际的查询值处理。
函数中我们通过key句柄去对象管理器中查询到key对象的对象体,然后再去配置管理器中进行具体的查找。
所以其真实的内部查询函数式CmQueryValueKey函数,用来查询具体的信息,这个是配置管理器的API函数。

NTSTATUS
NTAPI
CmQueryValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
                IN UNICODE_STRING ValueName,
                IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
                IN PVOID KeyValueInformation,
                IN ULONG Length,
                IN PULONG ResultLength)
{
    NTSTATUS Status;
    PCM_KEY_VALUE ValueData;
    ULONG Index;
    BOOLEAN ValueCached = FALSE;
    PCM_CACHED_VALUE *CachedValue;
    HCELL_INDEX CellToRelease;
    VALUE_SEARCH_RETURN_TYPE Result;
    PHHIVE Hive;
    PAGED_CODE();
    /* Acquire hive lock */
    CmpLockRegistry();
    /* Lock the KCB shared */
    CmpAcquireKcbLockShared(Kcb);
    /* Don't touch deleted keys */
DoAgain:
    if (Kcb->Delete)
    {
        /* Undo everything */
        CmpReleaseKcbLock(Kcb);
        CmpUnlockRegistry();
        return STATUS_KEY_DELETED;
    }
    /* We don't deal with this yet */
    if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)
    {
        /* Shouldn't happen */
        ASSERT(FALSE);
    }
    /* Get the hive */
    Hive = Kcb->KeyHive;
    /* Find the key value */
    Result = CmpFindValueByNameFromCache(Kcb,
                                         &ValueName,
                                         &CachedValue,
                                         &Index,
                                         &ValueData,
                                         &ValueCached,
                                         &CellToRelease);
    if (Result == SearchNeedExclusiveLock)
    {
        /* Check if we need an exclusive lock */
        ASSERT(CellToRelease == HCELL_NIL);
        ASSERT(ValueData == NULL);
        /* Try with exclusive KCB lock */
        CmpConvertKcbSharedToExclusive(Kcb);
        goto DoAgain;
    }
    if (Result == SearchSuccess)
    {
        /* Sanity check */
        ASSERT(ValueData != NULL);
        /* User data, protect against exceptions */
        _SEH2_TRY
        {
            /* Query the information requested */
            Result = CmpQueryKeyValueData(Kcb,
                                          CachedValue,
                                          ValueData,
                                          ValueCached,
                                          KeyValueInformationClass,
                                          KeyValueInformation,
                                          Length,
                                          ResultLength,
                                          &Status);
            if (Result == SearchNeedExclusiveLock)
            {
                /* Release the value cell */
                if (CellToRelease != HCELL_NIL)
                {
                    HvReleaseCell(Hive, CellToRelease);
                    CellToRelease = HCELL_NIL;
                }
                /* Try with exclusive KCB lock */
                CmpConvertKcbSharedToExclusive(Kcb);
                _SEH2_YIELD(goto DoAgain);
            }
        }
        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
        {
            Status = _SEH2_GetExceptionCode();
        }
        _SEH2_END;
    }
    else
    {
        /* Failed to find the value */
        Status = STATUS_OBJECT_NAME_NOT_FOUND;
    }
    /* If we have a cell to release, do so */
    if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);
    /* Release locks */
    CmpReleaseKcbLock(Kcb);
    CmpUnlockRegistry();
    return Status;
}

依旧是要求互斥化访问,这个是老生常谈,锁定hive,锁定KCB。
然后我们根据键名字在缓存中查询位置。

hive file list management

hive 文件的列表位于注册表中。如下主键,我们要修改hive文件列表,其实就是修改这个键下的键和键值\REGISTRY\MACHINE\SYSTEM\CurrentControlSet\Control\hivelist,首先我们先创建或者是打开这个主键,然后通过参数hive获得要加入列表的文件路径,然后我们调用ZwSetValueKey 来设置信息。当我们要删除hive文件列表时,也是一样,先找到主键,然后使用键删除函数,来删除hive文件键值.

cell values

CmpGetValueData

我们先看看如何获取值,内部调用CmpGetValueData 来进行值的获取。

BOOLEAN
NTAPI
CmpGetValueData(IN PHHIVE Hive,
                IN PCM_KEY_VALUE Value,                    这个是获取标志信息,根据它来获取信息。
                IN PULONG Length,
                OUT PVOID *Buffer,
                OUT PBOOLEAN BufferAllocated,
                OUT PHCELL_INDEX CellToRelease)
{
    PAGED_CODE();
    /* Sanity check */
    ASSERT(Value->Signature == CM_KEY_VALUE_SIGNATURE);
    /* Set failure defaults */
    *BufferAllocated = FALSE;
    *Buffer = NULL;
    *CellToRelease = HCELL_NIL;
    /* Check if this is a small key */
    if (CmpIsKeyValueSmall(Length, Value->DataLength))
    {
        /* Return the data immediately */
        *Buffer = &Value->Data;
        return TRUE;
    }
    /* Unsupported */
    ASSERT_VALUE_BIG(Hive, *Length);
    /* Get the data from the cell */
    *Buffer = HvGetCell(Hive, Value->Data);
    if (!(*Buffer)) return FALSE;
    /* Return success and the cell to be released */
    *CellToRelease = Value->Data;
    return TRUE;
}

核心的地方是我们使用HvGetCell来获取值。当然其函数内部开始已经介绍过,通过cell的索引值来获取。
我们来详细说说PCM_KEY_VALUE 这是一个value cell,是cell的一种类型,在这个cell中存储的是一个键值。我们通过这个结构里面的cell 的偏移值来得到值的缓存地址。

typedef struct _CM_KEY_VALUE
{
    USHORT Signature;
    USHORT NameLength;
    ULONG DataLength;
    HCELL_INDEX Data;
    ULONG Type;
    USHORT Flags;
    USHORT Unused1;
    WCHAR Name[ANYSIZE_ARRAY];
} CM_KEY_VALUE, *PCM_KEY_VALUE;

在windows internals 中也介绍到关于cell 的类型,我们暂时不一一介绍,在碰到的时候,我们在介绍。
目前这个结构中的HCELL_INDEX Data;就是一个键值的偏移值。
之前这些是值的获取,那么我们怎么添加一个值呢。
其实我们只需要在hive中添加一个cell就可以,重要的是我们需要进行数据的操作。

CmpAddValueToList

下面我们看看如何往列表中添加值,这里的意思就是如何添加一个cell。

NTSTATUS 
NTAPI
CmpAddValueToList(IN PHHIVE Hive,
                  IN HCELL_INDEX ValueCell,
                  IN ULONG Index,
                  IN ULONG Type,
                  IN OUT PCHILD_LIST ChildList)
{
    HCELL_INDEX ListCell;
    ULONG ChildCount, Length, i;
    PCELL_DATA CellData;
    PAGED_CODE();
    /* Sanity check */
    ASSERT((((LONG)Index) >= 0) && (Index <= ChildList->Count));
    /* Get the number of entries in the child list */
    ChildCount = ChildList->Count;
    ChildCount++;
    if (ChildCount > 1)
    {
        /* The cell should be dirty at this point */
        ASSERT(HvIsCellDirty(Hive, ChildList->List));
        /* Check if we have less then 100 children */
        if (ChildCount < 100)
        {
            /* Allocate just enough as requested */
            Length = ChildCount * sizeof(HCELL_INDEX);
        }
        else
        {
            /* Otherwise, we have quite a few, so allocate a batch */
            Length = ROUND_UP(ChildCount, 100) * sizeof(HCELL_INDEX);
            if (Length > HBLOCK_SIZE)
            {
                /* But make sure we don't allocate beyond our block size */
                Length = ROUND_UP(Length, HBLOCK_SIZE);
            }
        }
        /* Perform the allocation */
        ListCell = HvReallocateCell(Hive, ChildList->List, Length);
    }
    else
    {
        /* This is our first child, so allocate a single cell */
        ListCell = HvAllocateCell(Hive, sizeof(HCELL_INDEX), Type, HCELL_NIL);
    }
    /* Fail if we couldn't get a cell */
    if (ListCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;
    /* Set this cell as the child list's list cell */
    ChildList->List = ListCell;
    /* Get the actual key list memory */
    CellData = HvGetCell(Hive, ListCell);
    ASSERT(CellData != NULL);
    /* Loop all the children */
    for (i = ChildCount - 1; i > Index; i--)
    {
        /* Move them all down */
        CellData->u.KeyList[i] = CellData->u.KeyList[i - 1];
    }
    /* Insert us on top now */
    CellData->u.KeyList[Index] = ValueCell;
    ChildList->Count = ChildCount;
    /* Release the list cell and make sure the value cell is dirty */
    HvReleaseCell(Hive, ListCell);
    ASSERT(HvIsCellDirty(Hive, ValueCell));
    /* We're done here */
    return STATUS_SUCCESS;
}

我们先计算出列表中子节点的计数,如果里面已经有一个以上的cell,我们计算总体cell大小重新申请。如果没有子节点,说明是第一个节点,那么我们使用HvAllocateCell 来申请节点。修改链表中子节点的链接指针,修改cell结构体中链表指针(此处就是数组内数据后移,将cell插入到数组内指定位置)
在这里面我们使用hvAllocateCell来申请一个cell index,然后我们在通过HvGetCell函数来实际获取一个cell,当然这个函数之前说过,用来实际获得cell的内存地址,我们将这个地址转换为数据结构,操作数据结构就是操作这个地址(事实上就是用cell结构来表示这块内存)。
下面是一个cell的结构,在windows internals 中也实际的介绍了关于cell的结构,其有几种类型,如下可以清楚的看出各种类型。之前的分析中我们包含了CM_KEY_VALUE。

typedef struct _CELL_DATA
{
    union
    {
        CM_KEY_NODE KeyNode;
        CM_KEY_VALUE KeyValue;
        CM_KEY_SECURITY KeySecurity;
        CM_KEY_INDEX KeyIndex;
        HCELL_INDEX KeyList[ANYSIZE_ARRAY];
        WCHAR KeyString[ANYSIZE_ARRAY];
    } u;
} CELL_DATA, *PCELL_DATA;

CmpRemoveValueFromList

前面我们说到了值的获取和添加,这里该到值的删除了。这里面论及的是内核配置管理器的内部函数,也就是说它不是api,是内部数据结构核心操作函数,api会调用这些函数来完成具体的功能。
这个函数用通过NtDeleteValueKey来具体调用。我们追本溯源,探索数据结构和算法的具体实现。

NTSTATUS
NTAPI
CmpRemoveValueFromList(IN PHHIVE Hive,
                       IN ULONG Index,
                       IN OUT PCHILD_LIST ChildList)
{
    ULONG Count;
    PCELL_DATA CellData;
    HCELL_INDEX NewCell;
    PAGED_CODE();
    /* Sanity check */
    ASSERT((((LONG)Index) >= 0) && (Index <= ChildList->Count));
    /* Get the new count after removal */
    Count = ChildList->Count - 1;
    if (Count > 0)
    {
        /* Get the actual list array */
        CellData = HvGetCell(Hive, ChildList->List);
        if (!CellData) return STATUS_INSUFFICIENT_RESOURCES;
        /* Make sure cells data have been made dirty */
        ASSERT(HvIsCellDirty(Hive, ChildList->List));
        ASSERT(HvIsCellDirty(Hive, CellData->u.KeyList[Index]));
        /* Loop the list */
        while (Index < Count)
        {
            /* Move everything up */
            CellData->u.KeyList[Index] = CellData->u.KeyList[Index + 1];
            Index++;
        }
        /* Re-allocate the cell for the list by decreasing the count */
        NewCell = HvReallocateCell(Hive,
                                   ChildList->List,
                                   Count * sizeof(HCELL_INDEX));
        ASSERT(NewCell != HCELL_NIL);
        HvReleaseCell(Hive,ChildList->List);
        /* Update the list cell */
        ChildList->List = NewCell;
    }
    else
    {
        /* Otherwise, we were the last entry, so free the list entirely */
        HvFreeCell(Hive, ChildList->List);
        ChildList->List = HCELL_NIL;
    }
    /* Update the child list with the new count */
    ChildList->Count = Count;
    return STATUS_SUCCESS;
}

函数中我们首先判断列表中节点数是否大于0,对于空节点,很简单,我们使用HvFreeCell(Hive, ChildList->List); free掉这个父节点。如果我们列表中子节点数大于零,我们根据ChildList->List来获取到这个节点的cell结构,修改内部keylist,指定位置,其后的数据前移。然后重新根据计算数据大小,申请内存拷贝数据。

config manager init

我们来看看配置管理器config manager的初始化,这个函数位于cmsysini.c文件中。
BOOLEAN
NTAPI
INIT_FUNCTION
CmInitSystem1(VOID)
这个函数在执行体ex Phase1InitializationDiscard 被调用,也就是说在系统阶段1被初始化。
对于CmInitSystem1函数,在函数开始,我们初始化hive list
/* Initialize the hive list and lock */
InitializeListHead(&CmpHiveListHead);
然后初始化缓存cache,初始化回调。
比较重要的一步是调用对象管理器初始化key的对象类型,使得对象管理器中可以按照我们key的模板初始化我们自己的key对象。这一步在cm中使用的是CmpCreateObjectTypes()函数。
然后初始化hive,这里使用的函数是CmpInitializeHive(),这个函数是我们主要要分析的函数之一。下面我们来看看其相关的实现。

NTSTATUS
NTAPI
CmpInitializeHive(OUT PCMHIVE *RegistryHive,
                  IN ULONG OperationType,
                  IN ULONG HiveFlags,
                  IN ULONG FileType,
                  IN PVOID HiveData OPTIONAL,
                  IN HANDLE Primary,
                  IN HANDLE Log,
                  IN HANDLE External,
                  IN PCUNICODE_STRING FileName OPTIONAL,
                  IN ULONG CheckFlags)

在这个函数中其实就是在初始化PCMHIVE 结构。函数里面做了前期的参数检查,然后就是设置hive内部的数据。在cm初始化函数中如下调用hive初始化函数。

    /* Build the master hive */
    Status = CmpInitializeHive((PCMHIVE*)&CmiVolatileHive,
                               HINIT_CREATE,
                               HIVE_VOLATILE,
                               HFILE_TYPE_PRIMARY,
                               NULL,
                               NULL,
                               NULL,
                               NULL,
                               NULL,
                               0);

由此我们可以获知CmiVolatileHive这个全局的hive的重要性。下面我们研究一下这个函数。其实我们可以直接看PCMHIVE结构即可。上面这个函数就是填写这个数据。

//
// Configuration Manager Hive Structure
//
typedef struct _CMHIVE
{
    HHIVE Hive;
    HANDLE FileHandles[HFILE_TYPE_MAX];
    LIST_ENTRY NotifyList;
    LIST_ENTRY HiveList;
    EX_PUSH_LOCK HiveLock;
    PKTHREAD HiveLockOwner;
    PKGUARDED_MUTEX ViewLock;
    PKTHREAD ViewLockOwner;
    EX_PUSH_LOCK WriterLock;
    PKTHREAD WriterLockOwner;
    PERESOURCE FlusherLock;
    EX_PUSH_LOCK SecurityLock;
    PKTHREAD HiveSecurityLockOwner;
    LIST_ENTRY LRUViewListHead;
    LIST_ENTRY PinViewListHead;
    PFILE_OBJECT FileObject;
    UNICODE_STRING FileFullPath;
    UNICODE_STRING FileUserName;
    USHORT MappedViews;
    USHORT PinnedViews;
    ULONG UseCount;
    ULONG SecurityCount;
    ULONG SecurityCacheSize;
    LONG SecurityHitHint;
    PCM_KEY_SECURITY_CACHE_ENTRY SecurityCache;
    LIST_ENTRY SecurityHash[CMP_SECURITY_HASH_LISTS];
    PKEVENT UnloadEvent;
    PCM_KEY_CONTROL_BLOCK RootKcb;
    BOOLEAN Frozen;
    PWORK_QUEUE_ITEM UnloadWorkItem;
    BOOLEAN GrowOnlyMode;
    ULONG GrowOffset;
    LIST_ENTRY KcbConvertListHead;
    LIST_ENTRY KnodeConvertListHead;
    PCM_CELL_REMAP_BLOCK CellRemapArray;
    CM_USE_COUNT_LOG UseCountLog;
    CM_USE_COUNT_LOG LockHiveLog;
    ULONG Flags;
    LIST_ENTRY TrustClassEntry;
    ULONG FlushCount;
    BOOLEAN HiveIsLoading;
    PKTHREAD CreatorOwner;
} CMHIVE, *PCMHIVE;

我们首先为这个数据申请空间,使用的是ExAllocatePoolWithTag函数。
然后初始化起步的链表,置空某些成员,为内部成员申请空间比如锁。初始化锁,初始化访问资源锁,这些都是在初始化这个CMHIVE数据。

    /* Initialize it */
    Status = HvInitialize(&Hive->Hive,
                          OperationType,
                          FileType,
                          HiveFlags,
                          HiveData,
                          CmpAllocate,
                          CmpFree,
                          CmpFileSetSize,
                          CmpFileWrite,
                          CmpFileRead,
                          CmpFileFlush,
                          Cluster,
                          FileName);

当我们初始化完hive后,我们特别的去初始化hive的第一个成员HHIVE,我们第一次调用这个函数是去创建hive中的baseblock.

CmpCreateRegistryRoot

我们回到开始的config manager 初始化的地方,当hive初始化结束后我们开始创建注册表的根节点。

BOOLEAN
NTAPI
INIT_FUNCTION
CmpCreateRegistryRoot(VOID)
{
    UNICODE_STRING KeyName;
    OBJECT_ATTRIBUTES ObjectAttributes;
    PCM_KEY_BODY RootKey;
    HCELL_INDEX RootIndex;
    NTSTATUS Status;
    PCM_KEY_NODE KeyCell;
    PSECURITY_DESCRIPTOR SecurityDescriptor;
    PCM_KEY_CONTROL_BLOCK Kcb;
    PAGED_CODE();
    /* Setup the root node */
    if (!CmpCreateRootNode(&CmiVolatileHive->Hive, L"REGISTRY", &RootIndex))
    {
        /* We failed */
        return FALSE;
    }
    /* Create '\Registry' key. */
    RtlInitUnicodeString(&KeyName, L"\\REGISTRY");
    SecurityDescriptor = CmpHiveRootSecurityDescriptor();
    InitializeObjectAttributes(&ObjectAttributes,
                               &KeyName,
                               OBJ_CASE_INSENSITIVE,
                               NULL,
                               NULL);
    Status = ObCreateObject(KernelMode,
                            CmpKeyObjectType,
                            &ObjectAttributes,
                            KernelMode,
                            NULL,
                            sizeof(CM_KEY_BODY),
                            0,
                            0,
                            (PVOID*)&RootKey);
    ExFreePoolWithTag(SecurityDescriptor, TAG_CM);
    if (!NT_SUCCESS(Status)) return FALSE;
    /* Sanity check, and get the key cell */
    ASSERT((&CmiVolatileHive->Hive)->ReleaseCellRoutine == NULL);
    KeyCell = (PCM_KEY_NODE)HvGetCell(&CmiVolatileHive->Hive, RootIndex);
    if (!KeyCell) return FALSE;
    /* Create the KCB */
    RtlInitUnicodeString(&KeyName, L"\\REGISTRY");
    Kcb = CmpCreateKeyControlBlock(&CmiVolatileHive->Hive,
                                   RootIndex,
                                   KeyCell,
                                   NULL,
                                   0,
                                   &KeyName);
    if (!Kcb)
    {
        ObDereferenceObject(RootKey);
        return FALSE;
    }
    /* Initialize the object */
    RootKey->KeyControlBlock = Kcb;
    RootKey->Type = CM_KEY_BODY_TYPE;
    RootKey->NotifyBlock = NULL;
    RootKey->ProcessID = PsGetCurrentProcessId();
    /* Link with KCB */
    EnlistKeyBodyWithKCB(RootKey, 0);
    /* Insert the key into the namespace */
    Status = ObInsertObject(RootKey,
                            NULL,
                            KEY_ALL_ACCESS,
                            0,
                            NULL,
                            &CmpRegistryRootHandle);
    if (!NT_SUCCESS(Status))
    {
        ObDereferenceObject(RootKey);
        return FALSE;
    }
    /* Reference the key again so that we never lose it */
    Status = ObReferenceObjectByHandle(CmpRegistryRootHandle,
                                       KEY_READ,
                                       NULL,
                                       KernelMode,
                                       (PVOID*)&RootKey,
                                       NULL);
    if (!NT_SUCCESS(Status))
    {
        ObDereferenceObject(RootKey);
        return FALSE;
    }
    /* Completely sucessful */
    return TRUE;
}

创建根节点,然后在对象管理器中创建根结点key对象,创建根节点的KCB,接着我们在对象管理器中获取到的key对象体,我们设置key对象体内部数据

//
// Key Body
//
typedef struct _CM_KEY_BODY
{
    ULONG Type;
    struct _CM_KEY_CONTROL_BLOCK *KeyControlBlock;
    struct _CM_NOTIFY_BLOCK *NotifyBlock;
    HANDLE ProcessID;
    LIST_ENTRY KeyBodyList;
} CM_KEY_BODY, *PCM_KEY_BODY;、

接着还是对象管理器操作,我们使用ObInsertObject来将根节点key对象插入到对象管理器中,如此我们就获得了一个可访问的句柄CmpRegistryRootHandle,引用这个句柄,使得对象管理器中对其增加引用计数,这样我们就不能失去这个对象,因为引用计数无法自动降低,只有我们手动去销毁这个对象时,才释放这个对象。
然后在config manager中我们继续创建其他键,这些键的创建就没有根键那么麻烦,使用的是NtCreateKey来创建的。
如下键值先被创建出来,初始化对象属性,然后使用NtCreateKey来创建。
L”\REGISTRY\MACHINE”
L”\REGISTRY\USER”
如此看来可以知道根结点的创建还是不同寻常的。 下面函数在CmpCreateRegistryRoot开始处被调用用来创建根节点。

BOOLEAN
NTAPI
INIT_FUNCTION
CmpCreateRootNode(IN PHHIVE Hive,
                  IN PCWSTR Name,
                  OUT PHCELL_INDEX Index)
{
    UNICODE_STRING KeyName;
    PCM_KEY_NODE KeyCell;
    LARGE_INTEGER SystemTime;
    PAGED_CODE();
    /* Initialize the node name and allocate it */
    RtlInitUnicodeString(&KeyName, Name);
    *Index = HvAllocateCell(Hive,
                            FIELD_OFFSET(CM_KEY_NODE, Name) +
                            CmpNameSize(Hive, &KeyName),
                            Stable,
                            HCELL_NIL);
    if (*Index == HCELL_NIL) return FALSE;
    /* Set the cell index and get the data */
    Hive->BaseBlock->RootCell = *Index;
    KeyCell = (PCM_KEY_NODE)HvGetCell(Hive, *Index);
    if (!KeyCell) return FALSE;
    /* Setup the cell */
    KeyCell->Signature = (USHORT)CM_KEY_NODE_SIGNATURE;
    KeyCell->Flags = KEY_HIVE_ENTRY | KEY_NO_DELETE;
    KeQuerySystemTime(&SystemTime);
    KeyCell->LastWriteTime = SystemTime;
    KeyCell->Parent = HCELL_NIL;
    KeyCell->SubKeyCounts[Stable] = 0;
    KeyCell->SubKeyCounts[Volatile] = 0;
    KeyCell->SubKeyLists[Stable] = HCELL_NIL;
    KeyCell->SubKeyLists[Volatile] = HCELL_NIL;
    KeyCell->ValueList.Count = 0;
    KeyCell->ValueList.List = HCELL_NIL;
    KeyCell->Security = HCELL_NIL;
    KeyCell->Class = HCELL_NIL;
    KeyCell->ClassLength = 0;
    KeyCell->MaxNameLen = 0;
    KeyCell->MaxClassLen = 0;
    KeyCell->MaxValueNameLen = 0;
    KeyCell->MaxValueDataLen = 0;
    /* Copy the name (this will also set the length) */
    KeyCell->NameLength = CmpCopyName(Hive, (PWCHAR)KeyCell->Name, &KeyName);
    /* Check if the name was compressed */
    if (KeyCell->NameLength < KeyName.Length)
    {
        /* Set the flag */
        KeyCell->Flags |= KEY_COMP_NAME;
    }
    /* Return success */
    HvReleaseCell(Hive, *Index);
    return TRUE;
}

函数中使用HvAllocateCell 申请一个cell,这个返回的是cell的索引地址,我们根据这个偏移地址加上首地址计算出cell的地址,转换成要操作的数据结构,当然这个cell是一个节点数据。其结构如下:
这个是cell的类型之一,之前介绍过一种是值的类型。

//
// Node Key
//
typedef struct _CM_KEY_NODE
{
    USHORT Signature;
    USHORT Flags;
    LARGE_INTEGER LastWriteTime;
    ULONG Spare;
    HCELL_INDEX Parent;
    ULONG SubKeyCounts[HTYPE_COUNT];
    union
    {
        struct
        {
            HCELL_INDEX SubKeyLists[HTYPE_COUNT];
            CHILD_LIST ValueList;
        };
        CM_KEY_REFERENCE ChildHiveReference;
    };
    HCELL_INDEX Security;
    HCELL_INDEX Class;
    ULONG MaxNameLen;
    ULONG MaxClassLen;
    ULONG MaxValueNameLen;
    ULONG MaxValueDataLen;
    ULONG WorkVar;
    USHORT NameLength;
    USHORT ClassLength;
    WCHAR Name[ANYSIZE_ARRAY];
} CM_KEY_NODE, *PCM_KEY_NODE;

我们在结构中设置根节点的名字,和数据。

我们回归到config manager 初始化。继续CmInitSystem1函数会初始化系统hive,并设置这个hive的文件名为L”\SystemRoot\System32\Config\SYSTEM”
然后创建硬件hive。设置这个硬件hive的key名字,然后连接到主hive上。

    /* Attach it to the machine key */
    RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\HARDWARE");
    Status = CmpLinkHiveToMaster(&KeyName,
                                 NULL,
                                 (PCMHIVE)HardwareHive,
                                 TRUE,
                                 SecurityDescriptor);

最后针对于硬件key做对应自己的处理。

cell的申请、释放、使用

这里更加倾向于cell的管理。
对于cell的管理,其代码位于lib\cmlib,这个令我有点意外,位于这个目录,难道不是一个系统文件吗?或许这是操作系统内核库,更加底层。因为我在lib文件夹中还发现了rtl的实现。

或许这个lib文件夹也放着比较核心的东西。应该放置的是系统库。
我们需要进一步分析cell的各种管理方式来准确了解config manager的核心。

cell的申请

HCELL_INDEX CMAPI
HvAllocateCell(
   PHHIVE RegistryHive,
   ULONG Size,
   HSTORAGE_TYPE Storage,
   HCELL_INDEX Vicinity)
{
   PHCELL FreeCell;
   HCELL_INDEX FreeCellOffset;
   PHCELL NewCell;
   PHBIN Bin;
   ASSERT(RegistryHive->ReadOnly == FALSE);
   CMLTRACE(CMLIB_HCELL_DEBUG, "%s - Hive %p, Size %x, %s, Vicinity %08lx\n",
       __FUNCTION__, RegistryHive, Size, (Storage == 0) ? "Stable" : "Volatile", Vicinity);
   /* Round to 16 bytes multiple. */
   Size = ROUND_UP(Size + sizeof(HCELL), 16);
   /* First search in free blocks. */
   FreeCellOffset = HvpFindFree(RegistryHive, Size, Storage);
   /* If no free cell was found we need to extend the hive file. */
   if (FreeCellOffset == HCELL_NIL)
   {
      Bin = HvpAddBin(RegistryHive, Size, Storage);
      if (Bin == NULL)
         return HCELL_NIL;
      FreeCellOffset = Bin->FileOffset + sizeof(HBIN);
      FreeCellOffset |= Storage << HCELL_TYPE_SHIFT;
   }
   FreeCell = HvpGetCellHeader(RegistryHive, FreeCellOffset);
   /* Split the block in two parts */
   /* The free block that is created has to be at least
      sizeof(HCELL) + sizeof(HCELL_INDEX) big, so that free
      cell list code can work. Moreover we round cell sizes
      to 16 bytes, so creating a smaller block would result in
      a cell that would never be allocated. */
   if ((ULONG)FreeCell->Size > Size + 16)
   {
      NewCell = (PHCELL)((ULONG_PTR)FreeCell + Size);
      NewCell->Size = FreeCell->Size - Size;
      FreeCell->Size = Size;
      HvpAddFree(RegistryHive, NewCell, FreeCellOffset + Size);
      if (Storage == Stable)
         HvMarkCellDirty(RegistryHive, FreeCellOffset + Size, FALSE);
   }
   if (Storage == Stable)
      HvMarkCellDirty(RegistryHive, FreeCellOffset, FALSE);
   FreeCell->Size = -FreeCell->Size;
   RtlZeroMemory(FreeCell + 1, Size - sizeof(HCELL));
   CMLTRACE(CMLIB_HCELL_DEBUG, "%s - CellIndex %08lx\n",
       __FUNCTION__, FreeCellOffset);
   return FreeCellOffset;
}

对于cell的申请,我们先是将cell需要的大小按16位对齐,然后我们查找空闲的bin,如果没有找到我们要在hive中添加bin,HvpAddBin具体来实现如何添加bin,然后我们通过bin的首地址,如下计算获得cell的偏移
FreeCellOffset = Bin->FileOffset + sizeof(HBIN);
FreeCellOffset |= Storage << HCELL_TYPE_SHIFT;
然后我们获得cell的实际大小,看看是否已经超过我们需要的大小,如果超过了,我们去掉超出的部分,将超出的部分放入bin中。然后判断这块cell存储是stable还是volatile,如果是stable的,我们要设置标志位。

bin 的申请

对于bin的申请,我们可以从中知道注册表的存储方式,这里面使用的是bin的这种数据结构。

typedef struct _HHIVE
{
    ULONG Signature;
    PGET_CELL_ROUTINE GetCellRoutine;
    PRELEASE_CELL_ROUTINE ReleaseCellRoutine;
    PALLOCATE_ROUTINE Allocate;
    PFREE_ROUTINE Free;
    PFILE_READ_ROUTINE FileRead;
    PFILE_WRITE_ROUTINE FileWrite;
    PFILE_SET_SIZE_ROUTINE FileSetSize;
    PFILE_FLUSH_ROUTINE FileFlush;
    PHBASE_BLOCK BaseBlock;
    RTL_BITMAP DirtyVector;
    ULONG DirtyCount;
    ULONG DirtyAlloc;
    ULONG BaseBlockAlloc;
    ULONG Cluster;
    BOOLEAN Flat;
    BOOLEAN ReadOnly;
    BOOLEAN Log;
    BOOLEAN DirtyFlag;
    ULONG HvBinHeadersUse;
    ULONG HvFreeCellsUse;
    ULONG HvUsedcellsUse;
    ULONG CmUsedCellsUse;
    ULONG HiveFlags;
    ULONG LogSize;
    ULONG RefreshCount;
    ULONG StorageTypeCount;
    ULONG Version;
    DUAL Storage[HTYPE_COUNT];
} HHIVE, *PHHIVE;

其实对于bin的申请,这里面主要操作的是hhive这个数据结构,其位于cmhive中第一个数据成员里,我们可以看一下如下这个截图就可以知道。

这里介绍的是config manager 的核心数据结构。我们接着讨论hhive,其中红色标示的是数据的申请释放函数操作,蓝色标示的是bin的实际数据存储地址和表示。当然不例外的是一切内存地址都有个数据结构来表示,更优化的是还有数据头。我们看一下bin申请的代码就可以知道,bin所在空间的数据结构表示和算法申请。

PHBIN CMAPI
HvpAddBin(
   PHHIVE RegistryHive,
   ULONG Size,
   HSTORAGE_TYPE Storage)
{
   PHMAP_ENTRY BlockList;
   PHBIN Bin;
   SIZE_T BinSize;
   ULONG i;
   ULONG BitmapSize;
   ULONG BlockCount;
   ULONG OldBlockListSize;
   PHCELL Block;
   BinSize = ROUND_UP(Size + sizeof(HBIN), HV_BLOCK_SIZE);
   BlockCount = (ULONG)(BinSize / HV_BLOCK_SIZE);
   Bin = RegistryHive->Allocate(BinSize, TRUE, TAG_CM);
   if (Bin == NULL)
      return NULL;
   RtlZeroMemory(Bin, BinSize);
   Bin->Signature = HV_BIN_SIGNATURE;
   Bin->FileOffset = RegistryHive->Storage[Storage].Length *
                    HV_BLOCK_SIZE;
   Bin->Size = (ULONG)BinSize;
   /* Allocate new block list */
   OldBlockListSize = RegistryHive->Storage[Storage].Length;
   BlockList = RegistryHive->Allocate(sizeof(HMAP_ENTRY) *
                                      (OldBlockListSize + BlockCount),
                                      TRUE,
                                      TAG_CM);
   if (BlockList == NULL)
   {
      RegistryHive->Free(Bin, 0);
      return NULL;
   }
   if (OldBlockListSize > 0)
   {
      RtlCopyMemory(BlockList, RegistryHive->Storage[Storage].BlockList,
                    OldBlockListSize * sizeof(HMAP_ENTRY));
      RegistryHive->Free(RegistryHive->Storage[Storage].BlockList, 0);
   }
   RegistryHive->Storage[Storage].BlockList = BlockList;
   RegistryHive->Storage[Storage].Length += BlockCount;
   for (i = 0; i < BlockCount; i++)
   {
      RegistryHive->Storage[Storage].BlockList[OldBlockListSize + i].BlockAddress =
         ((ULONG_PTR)Bin + (i * HV_BLOCK_SIZE));
      RegistryHive->Storage[Storage].BlockList[OldBlockListSize + i].BinAddress = (ULONG_PTR)Bin;
   }
   /* Initialize a free block in this heap. */
   Block = (PHCELL)(Bin + 1);
   Block->Size = (LONG)(BinSize - sizeof(HBIN));
   if (Storage == Stable)
   {
      /* Calculate bitmap size in bytes (always a multiple of 32 bits). */
      BitmapSize = ROUND_UP(RegistryHive->Storage[Stable].Length,
                            sizeof(ULONG) * 8) / 8;
      /* Grow bitmap if necessary. */
      if (BitmapSize > RegistryHive->DirtyVector.SizeOfBitMap / 8)
      {
         PULONG BitmapBuffer;
         BitmapBuffer = RegistryHive->Allocate(BitmapSize, TRUE, TAG_CM);
         RtlZeroMemory(BitmapBuffer, BitmapSize);
         if (RegistryHive->DirtyVector.SizeOfBitMap > 0)
         {
            ASSERT(RegistryHive->DirtyVector.Buffer);
            RtlCopyMemory(BitmapBuffer,
            RegistryHive->DirtyVector.Buffer,
            RegistryHive->DirtyVector.SizeOfBitMap / 8);
            RegistryHive->Free(RegistryHive->DirtyVector.Buffer, 0);
         }
         RtlInitializeBitMap(&RegistryHive->DirtyVector, BitmapBuffer,
                             BitmapSize * 8);
      }
      /* Mark new bin dirty. */
      RtlSetBits(&RegistryHive->DirtyVector,
                 Bin->FileOffset / HV_BLOCK_SIZE,
                 BlockCount);
      /* Update size in the base block */
      RegistryHive->BaseBlock->Length += BinSize;
   }
   return Bin;
}

bin的申请函数开始处,先在用户要申请的大小加上bin头的大小后做个HV_BLOCK_SIZE大小的对齐。然后呢,我们使用hive已经初始化过的申请空间函数来申请内存,实际上这个函数就是内存管理器的内存申请函数,当然我们申请完空间后判断是否申请成功,之后将这块空间置零。然后我们获得到原来bin块列表的大小,需要重新申请bin块列表,这里仅仅是重新申请表示bin块的列表的空间而不是bin块。然后我们修改bin块列表,使其新增加块的地址指向我们新申请的bin地址,如下操作。

   for (i = 0; i < BlockCount; i++)
   {
      RegistryHive->Storage[Storage].BlockList[OldBlockListSize + i].BlockAddress =
         ((ULONG_PTR)Bin + (i * HV_BLOCK_SIZE));
      RegistryHive->Storage[Storage].BlockList[OldBlockListSize + i].BinAddress = (ULONG_PTR)Bin;
   }

如果我们申请的bin是Stable类型的,我们还要设置标志位。

空闲cell的添加

好的,我们回到cell申请处,去看看我们如何在bin中添加一个空闲的cell,当我们申请完一个bin的时候,我们就可以在这个bin上切出一块我们需要的空间当成cell,然而多数时候我们的cell要比bin小很多,这样bin中就会留有空余空间,我们此时就就这空余空间放置到空闲列表中,当我们下次要申请cell的时候优先查找空闲列表,如果空闲列表中没有可用cell空间时,我们再去申请bin。
so,我们来看看怎么添加空闲内存到bin的空闲列表中吧。

static NTSTATUS CMAPI
HvpAddFree(
   PHHIVE RegistryHive,
   PHCELL FreeBlock,
   HCELL_INDEX FreeIndex)
{
   PHCELL_INDEX FreeBlockData;
   HSTORAGE_TYPE Storage;
   ULONG Index;
   ASSERT(RegistryHive != NULL);
   ASSERT(FreeBlock != NULL);
   Storage = (FreeIndex & HCELL_TYPE_MASK) >> HCELL_TYPE_SHIFT;
   Index = HvpComputeFreeListIndex((ULONG)FreeBlock->Size);
   FreeBlockData = (PHCELL_INDEX)(FreeBlock + 1);
   *FreeBlockData = RegistryHive->Storage[Storage].FreeDisplay[Index];
   RegistryHive->Storage[Storage].FreeDisplay[Index] = FreeIndex;
   /* FIXME: Eventually get rid of free bins. */
   return STATUS_SUCCESS;
}

函数中我们根据块大小计算出索引位置。

   FreeBlockData = (PHCELL_INDEX)(FreeBlock + 1);
   *FreeBlockData = RegistryHive->Storage[Storage].FreeDisplay[Index];
   RegistryHive->Storage[Storage].FreeDisplay[Index] = FreeIndex;

hive 内存与文件的同步

BOOLEAN CMAPI
HvSyncHive(
   PHHIVE RegistryHive)
{
   ASSERT(RegistryHive->ReadOnly == FALSE);
   if (RtlFindSetBits(&RegistryHive->DirtyVector, 1, 0) == ~0U)
   {
      return TRUE;
   }
   /* Update hive header modification time */
   KeQuerySystemTime(&RegistryHive->BaseBlock->TimeStamp);
   /* Update log file */
   if (!HvpWriteLog(RegistryHive))
   {
      return FALSE;
   }
   /* Update hive file */
   if (!HvpWriteHive(RegistryHive, TRUE))
   {
      return FALSE;
   }
   /* Clear dirty bitmap. */
   RtlClearAllBits(&RegistryHive->DirtyVector);
   RegistryHive->DirtyCount = 0;
   return TRUE;
}

我们遍历hivelist,然后判断出hive是否符合刷新的条件,如果符合,那么我们刷新,调用上述函数进行刷新,将内存数据刷新到文件上
为了防止在刷新的时候出现崩溃,我们先将数据存储到log中进行备份,然后再刷新数据。

static BOOLEAN CMAPI
HvpWriteHive(
   PHHIVE RegistryHive,
   BOOLEAN OnlyDirty)
{
   ULONG FileOffset;
   ULONG BlockIndex;
   ULONG LastIndex;
   PVOID BlockPtr;
   BOOLEAN Success;
   ASSERT(RegistryHive->ReadOnly == FALSE);
   ASSERT(RegistryHive->BaseBlock->Length ==
          RegistryHive->Storage[Stable].Length * HV_BLOCK_SIZE);
   DPRINT("HvpWriteHive called\n");
   if (RegistryHive->BaseBlock->Sequence1 !=
       RegistryHive->BaseBlock->Sequence2)
   {
      return FALSE;
   }
   /* Update first update counter and CheckSum */
   RegistryHive->BaseBlock->Type = HFILE_TYPE_PRIMARY;
   RegistryHive->BaseBlock->Sequence1++;
   RegistryHive->BaseBlock->CheckSum =
   HvpHiveHeaderChecksum(RegistryHive->BaseBlock);
   /* Write hive block */
   FileOffset = 0;
   Success = RegistryHive->FileWrite(RegistryHive, HFILE_TYPE_PRIMARY,
                                     &FileOffset, RegistryHive->BaseBlock,
                                     sizeof(HBASE_BLOCK));
   if (!Success)
   {
      return FALSE;
   }
   BlockIndex = 0;
   while (BlockIndex < RegistryHive->Storage[Stable].Length)
   {
      if (OnlyDirty)
      {
         LastIndex = BlockIndex;
         BlockIndex = RtlFindSetBits(&RegistryHive->DirtyVector, 1, BlockIndex);
         if (BlockIndex == ~0U || BlockIndex < LastIndex)
         {
            break;
         }
      }
      BlockPtr = (PVOID)RegistryHive->Storage[Stable].BlockList[BlockIndex].BlockAddress;
      FileOffset = (BlockIndex + 1) * HV_BLOCK_SIZE;
      /* Write hive block */
      Success = RegistryHive->FileWrite(RegistryHive, HFILE_TYPE_PRIMARY,
                                        &FileOffset, BlockPtr,
                                        HV_BLOCK_SIZE);
      if (!Success)
      {
         return FALSE;
      }
      BlockIndex++;
   }
   Success = RegistryHive->FileFlush(RegistryHive, HFILE_TYPE_PRIMARY, NULL, 0);
   if (!Success)
   {
      DPRINT("FileFlush failed\n");
   }
   /* Update second update counter and CheckSum */
   RegistryHive->BaseBlock->Sequence2++;
   RegistryHive->BaseBlock->CheckSum =
      HvpHiveHeaderChecksum(RegistryHive->BaseBlock);
   /* Write hive block */
   FileOffset = 0;
   Success = RegistryHive->FileWrite(RegistryHive, HFILE_TYPE_PRIMARY,
                                     &FileOffset, RegistryHive->BaseBlock,
                                     sizeof(HBASE_BLOCK));
   if (!Success)
   {
      return FALSE;
   }
   Success = RegistryHive->FileFlush(RegistryHive, HFILE_TYPE_PRIMARY, NULL, 0);
   if (!Success)
   {
      DPRINT("FileFlush failed\n");
   }
   return TRUE;
}

我们主要重点看如何同步到文件中的。通过如下方式,我们就能知道在log的备份中是否发生了崩溃。
RegistryHive->BaseBlock->Sequence1 !=
RegistryHive->BaseBlock->Sequence2
如果没有的话,我们计算检验和,递增计数Sequence1 ,设置同步类型,然后调用操作系统的写函数,首先将basebock拷贝到文件系统的缓冲区中,然后我们逐一读取(PVOID)RegistryHive->Storage[Stable].BlockList[BlockIndex].BlockAddress,block的地址,分别复制到文件系统的缓冲区中,当我们把所有的block和基本baseblock复制到系统缓冲区的时候,我们Flush,强制将缓冲区内容刷入到文件中,然后重新计算baseblock的检验和,递增Sequence2,将新的baseblock刷新到文件中,这里使用的是Sequence2,所以在刷新前检查计数是否相等,就可以知道是否中途发生过崩溃。
而这里面查看是否是脏页,并设置标志位的时候,设置的成员变量位于RegistryHive->DirtyVector,即hhive中的DirtyVector。

config manager 文件系统接口

既然注册表是从文件中读取出来的,那么不可避免的要访问文件系统,那么怎么方便的访问呢,这里面cm其实做的还算不错,C语言的系统却是面向对象的思维。
我们去看一下HHIVE结构就能明白其中的奥秘,其实我们在HHIVE中记录了文件系统函数指针,我们在访问文件系统的接口上进行自己的包装,然后将这些基本函数地址在cm初始化的时候记录到HHIVE中,当要使用这些文件函数地址的时候,我们通过hhive去获取就可以了。

typedef struct _HHIVE
{
    ULONG Signature;
    PGET_CELL_ROUTINE GetCellRoutine;
    PRELEASE_CELL_ROUTINE ReleaseCellRoutine;
    PALLOCATE_ROUTINE Allocate;
    PFREE_ROUTINE Free;
    PFILE_READ_ROUTINE FileRead;
    PFILE_WRITE_ROUTINE FileWrite;
    PFILE_SET_SIZE_ROUTINE FileSetSize;
    PFILE_FLUSH_ROUTINE FileFlush;
    。。。。。。。
}

我们如下初始化,记录函数地址到hive中
/* Initialize it */
Status = HvInitialize(&Hive->Hive,
OperationType,
FileType,
HiveFlags,
HiveData,
CmpAllocate,
CmpFree,
CmpFileSetSize,
CmpFileWrite,
CmpFileRead,
CmpFileFlush,
Cluster,
FileName);
下面是两个例子函数,包装系统调用接口

PVOID
NTAPI
CmpAllocate(IN SIZE_T Size,
            IN BOOLEAN Paged,
            IN ULONG Tag)
{
    return ExAllocatePoolWithTag(Paged ? PagedPool : NonPagedPool,
                                 Size,
                                 Tag);
}
BOOLEAN
NTAPI
CmpFileRead(IN PHHIVE RegistryHive,
            IN ULONG FileType,
            IN PULONG FileOffset,
            OUT PVOID Buffer,
            IN SIZE_T BufferLength)
{
    PCMHIVE CmHive = (PCMHIVE)RegistryHive;
    HANDLE HiveHandle = CmHive->FileHandles[FileType];
    LARGE_INTEGER _FileOffset;
    IO_STATUS_BLOCK IoStatusBlock;
    NTSTATUS Status;
    _FileOffset.QuadPart = *FileOffset;
    Status = ZwReadFile(HiveHandle, 0, 0, 0, &IoStatusBlock,
                       Buffer, (ULONG)BufferLength, &_FileOffset, 0);
    return NT_SUCCESS(Status) ? TRUE : FALSE;
}

windbg config manager dbg

下面我们看看windbg中如何调试系统的reg。
我们首先显示出系统的hivelist,这样打印出所有的hive,有了hive的地址,我们相当于有了一切。
然后我们可以使用openkeys 来查询在一个hive中已经被打开的keys。

kd> !reg hivelist

-------------------------------------------------------------------------------------------------------------
| HiveAddr |Stable Length|Stable Map|Volatile Length|Volatile Map|MappedViews|PinnedViews|U(Cnt)| BaseBlock | FileName
-------------------------------------------------------------------------------------------------------------
| 832414e8 |       5000  | 83241564 |       1000    |  832416a0  |        0  |        0  |     0| 83242000  | emRoot\System32\Config\SECURITY
| 832ae100 |       4000  | 832ae17c |          0    |  00000000  |        0  |        0  |     0| 832bb000  | \SystemRoot\System32\Config\SAM
| 8333b3f8 |      3b000  | 8333b474 |          0    |  00000000  |        0  |        0  |     0| 8333c000  | files\NetworkService\NTUSER.DAT
| 833c9008 |      3a000  | 833c9084 |          0    |  00000000  |        0  |        0  |     0| 833ca000  | rofiles\LocalService\NTUSER.DAT
| 8940c440 |       1000  | 8940c4bc |       1000    |  8940c5f8  |        0  |        0  |     0| 8940e000  | <NONAME>
| 8941c008 |     b17000  | 89422000 |      46000    |  8941c1c0  |        0  |        0  |     0| 8941d000  | SYSTEM
| 89444008 |      23000  | 89444084 |       b000    |  894441c0  |        0  |        0  |     0| 89445000  | <NONAME>
| 894d48d0 |     2ba000  | 92e13000 |       1000    |  894d4a88  |        0  |        0  |     0| 894d5000  | temRoot\System32\Config\DEFAULT
| 89b229c8 |     388000  | 89b4c000 |       3000    |  89b22b80  |        0  |        0  |     0| 89aff000  | \Users\Administrator\ntuser.dat
| 8b242008 |      47000  | 8b242084 |          0    |  00000000  |        0  |        0  |     0| 8b243000  | \Microsoft\Windows\UsrClass.dat
| 8ec9d900 |       7000  | 8ec9d97c |          0    |  00000000  |        0  |        0  |     0| 8ec9e000  | Device\HarddiskVolume1\Boot\BCD
| 8ecb29c8 |    20d1000  | 8fdde000 |       2000    |  8ecb2b80  |        0  |        0  |     0| 8ecb3000  | emRoot\System32\Config\SOFTWARE
-------------------------------------------------------------------------------------------------------------

kd> !reg openkeys 832414e8

Hive: \REGISTRY\MACHINE\SECURITY
===========================================================================================
Index 13e: 4359d65c kcb=990c4a08 cell=000000a8 f=00200000 \REGISTRY\MACHINE\SECURITY\RXACT
Index 13f: ddd42113 kcb=894f5968 cell=80000020 f=00300008 \REGISTRY\MACHINE\SECURITY\SAM
Index 15c: b2bf03a2 kcb=89506828 cell=000007f8 f=00200000 \REGISTRY\MACHINE\SECURITY\POLICY
Index 1f6: e09d303e kcb=8959b828 cell=00000020 f=002c0004 \REGISTRY\MACHINE\SECURITY

0x4 keys found
==========================================================================================

在这里我们可以使用hive的地址显示出hive的数据,我们也可以进一步的查看HHIVE的数据。

kd> dt nt!_cmhive 832414e8=
   +0x000 Hive             : _HHIVE
   +0x2ec FileHandles      : [6] 0x8000027c Void
   +0x304 NotifyList       : _LIST_ENTRY [ 0x0 - 0x0 ]
   +0x30c HiveList         : _LIST_ENTRY [ 0x832ae40c - 0x841bc44c ]
   +0x314 PreloadedHiveList : _LIST_ENTRY [ 0x832417fc - 0x832417fc ]
   +0x31c HiveRundown      : _EX_RUNDOWN_REF
   +0x320 ParseCacheEntries : _LIST_ENTRY [ 0x8323cb4c - 0x83243cfc ]
   +0x328 KcbCacheTable    : 0x8324a000 _CM_KEY_HASH_TABLE_ENTRY
   +0x32c KcbCacheTableSize : 0x200
   +0x330 Identity         : 6
   +0x334 HiveLock         : 0x86cdb3c8 _FAST_MUTEX
   +0x338 ViewLock         : _EX_PUSH_LOCK
   +0x33c ViewLockOwner    : (null)
   +0x340 ViewLockLast     : 0xd
   +0x344 ViewUnLockLast   : 0x19
   +0x348 WriterLock       : 0x86cdb408 _FAST_MUTEX
   +0x34c FlusherLock      : 0x86cdb428 _ERESOURCE
   +0x350 FlushDirtyVector : _RTL_BITMAP
   +0x358 FlushOffsetArray : (null)
   +0x35c FlushOffsetArrayCount : 0
   +0x360 FlushHiveTruncated : 0
   +0x364 FlushLock2       : 0x86cdb3e8 _FAST_MUTEX
   +0x368 SecurityLock     : _EX_PUSH_LOCK
   +0x36c MappedViewList   : _LIST_ENTRY [ 0x83241854 - 0x83241854 ]
   +0x374 PinnedViewList   : _LIST_ENTRY [ 0x8324185c - 0x8324185c ]
   +0x37c FlushedViewList  : _LIST_ENTRY [ 0x83241864 - 0x83241864 ]
   +0x384 MappedViewCount  : 0
   +0x386 PinnedViewCount  : 0
   +0x388 UseCount         : 0
   +0x38c ViewsPerHive     : 0x100
   +0x390 FileObject       : (null)
   +0x394 LastShrinkHiveSize : 0
   +0x398 ActualFileSize   : _LARGE_INTEGER 0x40000
   +0x3a0 FileFullPath     : _UNICODE_STRING ""
   +0x3a8 FileUserName     : _UNICODE_STRING "\SystemRoot\System32\Config\SECURITY"
   +0x3b0 HiveRootPath     : _UNICODE_STRING "\REGISTRY\MACHINE\SECURITY"
   +0x3b8 SecurityCount    : 3
   +0x3bc SecurityCacheSize : 0x12
   +0x3c0 SecurityHitHint  : 0n2
   +0x3c4 SecurityCache    : 0x83248398 _CM_KEY_SECURITY_CACHE_ENTRY
   +0x3c8 SecurityHash     : [64] _LIST_ENTRY [ 0x832418b0 - 0x832418b0 ]
   +0x5c8 UnloadEventCount : 0
   +0x5cc UnloadEventArray : (null)
   +0x5d0 RootKcb          : (null)
   +0x5d4 Frozen           : 0 ''
   +0x5d8 UnloadWorkItem   : (null)
   +0x5dc UnloadWorkItemHolder : _CM_WORKITEM
   +0x5f0 GrowOnlyMode     : 0 ''
   +0x5f4 GrowOffset       : 0
   +0x5f8 KcbConvertListHead : _LIST_ENTRY [ 0x83241ae0 - 0x83241ae0 ]
   +0x600 KnodeConvertListHead : _LIST_ENTRY [ 0x83241ae8 - 0x83241ae8 ]
   +0x608 CellRemapArray   : (null)
   +0x60c Flags            : 0x10c
   +0x610 TrustClassEntry  : _LIST_ENTRY [ 0x83241af8 - 0x83241af8 ]
   +0x618 FlushCount       : 0
   +0x61c CmRm             : 0x8ec96300 _CM_RM
   +0x620 CmRmInitFailPoint : 0
   +0x624 CmRmInitFailStatus : 0n0
   +0x628 CreatorOwner     : (null)
   +0x62c RundownThread    : (null)
   +0x630 LastWriteTime    : _LARGE_INTEGER 0x1d065fa`73e7bdad

kd> dt nt!_hhive 832414e8
   +0x000 Signature        : 0xbee0bee0
   +0x004 GetCellRoutine   : 0x8409b292     _CELL_DATA*  nt!HvpGetCellPaged+0
   +0x008 ReleaseCellRoutine : (null)
   +0x00c Allocate         : 0x8405ca31     void*  nt!CmpAllocate+0
   +0x010 Free             : 0x8405cc0b     void  nt!CmpFree+0
   +0x014 FileSetSize      : 0x840328ae     unsigned char  nt!CmpFileSetSize+0
   +0x018 FileWrite        : 0x84060914     unsigned char  nt!CmpFileWrite+0
   +0x01c FileRead         : 0x8401d4c7     unsigned char  nt!CmpFileRead+0
   +0x020 FileFlush        : 0x84060951     unsigned char  nt!CmpFileFlush+0
   +0x024 HiveLoadFailure  : (null)
   +0x028 BaseBlock        : 0x83242000 _HBASE_BLOCK
   +0x02c DirtyVector      : _RTL_BITMAP
   +0x034 DirtyCount       : 8
   +0x038 DirtyAlloc       : 8
   +0x03c BaseBlockAlloc   : 0x1000
   +0x040 Cluster          : 1
   +0x044 Flat             : 0 ''
   +0x045 ReadOnly         : 0 ''
   +0x046 DirtyFlag        : 0x1 ''
   +0x048 HvBinHeadersUse  : 0xa0
   +0x04c HvFreeCellsUse   : 0x498
   +0x050 HvUsedCellsUse   : 0x4ac8
   +0x054 CmUsedCellsUse   : 0
   +0x058 HiveFlags        : 0x10200
   +0x05c CurrentLog       : 4
   +0x060 LogSize          : [2] 0x40000
   +0x068 RefreshCount     : 0
   +0x06c StorageTypeCount : 2
   +0x070 Version          : 3
   +0x074 Storage          : [2] _DUAL
kd> dt -b nt!_hhive 832414e8
   +0x000 Signature        : 0xbee0bee0
   +0x004 GetCellRoutine   : 0x8409b292
   +0x008 ReleaseCellRoutine : (null)
   +0x00c Allocate         : 0x8405ca31
   +0x010 Free             : 0x8405cc0b
   +0x014 FileSetSize      : 0x840328ae
   +0x018 FileWrite        : 0x84060914
   +0x01c FileRead         : 0x8401d4c7
   +0x020 FileFlush        : 0x84060951
   +0x024 HiveLoadFailure  : (null)
   +0x028 BaseBlock        : 0x83242000
   +0x02c DirtyVector      : _RTL_BITMAP
      +0x000 SizeOfBitMap     : 0x28
      +0x004 Buffer           : 0x832408c0
   +0x034 DirtyCount       : 8
   +0x038 DirtyAlloc       : 8
   +0x03c BaseBlockAlloc   : 0x1000
   +0x040 Cluster          : 1
   +0x044 Flat             : 0 ''
   +0x045 ReadOnly         : 0 ''
   +0x046 DirtyFlag        : 0x1 ''
   +0x048 HvBinHeadersUse  : 0xa0
   +0x04c HvFreeCellsUse   : 0x498
   +0x050 HvUsedCellsUse   : 0x4ac8
   +0x054 CmUsedCellsUse   : 0
   +0x058 HiveFlags        : 0x10200
   +0x05c CurrentLog       : 4
   +0x060 LogSize          :
    [00] 0x40000
    [01] 0x40000
   +0x068 RefreshCount     : 0
   +0x06c StorageTypeCount : 2
   +0x070 Version          : 3
   +0x074 Storage          :
    [00] _DUAL
      +0x000 Length           : 0x5000
      +0x004 Map              : 0x83241564
      +0x008 SmallDir         : 0x83246000
      +0x00c Guard            : 0xffffffff
      +0x010 FreeDisplay      :
       [00] _FREE_DISPLAY
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 5
            +0x004 Buffer           : 0x83243b00
       [01]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 5
            +0x004 Buffer           : 0x83243980
       [02]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 5
            +0x004 Buffer           : 0x83243800
       [03]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 5
            +0x004 Buffer           : 0x83243680
       [04]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 5
            +0x004 Buffer           : 0x83243500
       [05]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 5
            +0x004 Buffer           : 0x83243380
       [06]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 5
            +0x004 Buffer           : 0x83243200
       [07]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 5
            +0x004 Buffer           : 0x83243080
       [08]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 5
            +0x004 Buffer           : 0x83244040
       [09]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 5
            +0x004 Buffer           : 0x83244ec0
       [10]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 5
            +0x004 Buffer           : 0x83244d40
       [11]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 5
            +0x004 Buffer           : 0x83244bc0
       [12]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 5
            +0x004 Buffer           : 0x83244a40
       [13]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 5
            +0x004 Buffer           : 0x832448c0
       [14]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 5
            +0x004 Buffer           : 0x83244740
       [15]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 5
            +0x004 Buffer           : 0x832445c0
       [16]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 5
            +0x004 Buffer           : 0x83244440
       [17]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 5
            +0x004 Buffer           : 0x832442c0
       [18]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 5
            +0x004 Buffer           : 0x83245040
       [19]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 5
            +0x004 Buffer           : 0x83245ec0
       [20]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 5
            +0x004 Buffer           : 0x83245d40
       [21]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 5
            +0x004 Buffer           : 0x83245bc0
       [22]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 5
            +0x004 Buffer           : 0x83245a40
       [23]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 5
            +0x004 Buffer           : 0x832458c0
      +0x130 FreeSummary      : 0x80003
      +0x134 FreeBins         : _LIST_ENTRY [ 0x83241690 - 0x83241690 ]
         +0x000 Flink            : 0x83241690
         +0x004 Blink            : 0x83241690
    [01]
      +0x000 Length           : 0x1000
      +0x004 Map              : 0x832416a0
      +0x008 SmallDir         : 0x83252000
      +0x00c Guard            : 0xffffffff
      +0x010 FreeDisplay      :
       [00] _FREE_DISPLAY
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 1
            +0x004 Buffer           : 0x8324b880
       [01]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 1
            +0x004 Buffer           : 0x83248a80
       [02]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 1
            +0x004 Buffer           : 0x83248900
       [03]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 1
            +0x004 Buffer           : 0x83248780
       [04]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 1
            +0x004 Buffer           : 0x83248600
       [05]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 1
            +0x004 Buffer           : 0x83248480
       [06]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 1
            +0x004 Buffer           : 0x83254040
       [07]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 1
            +0x004 Buffer           : 0x83254ec0
       [08]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 1
            +0x004 Buffer           : 0x83254d40
       [09]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 1
            +0x004 Buffer           : 0x83254bc0
       [10]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 1
            +0x004 Buffer           : 0x83254a40
       [11]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 1
            +0x004 Buffer           : 0x832548c0
       [12]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 1
            +0x004 Buffer           : 0x83254740
       [13]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 1
            +0x004 Buffer           : 0x832545c0
       [14]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 1
            +0x004 Buffer           : 0x83254440
       [15]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 1
            +0x004 Buffer           : 0x832542c0
       [16]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 1
            +0x004 Buffer           : 0x83255040
       [17]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 1
            +0x004 Buffer           : 0x83255ec0
       [18]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 1
            +0x004 Buffer           : 0x83255d40
       [19]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 1
            +0x004 Buffer           : 0x83255bc0
       [20]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 1
            +0x004 Buffer           : 0x83255a40
       [21]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 1
            +0x004 Buffer           : 0x832558c0
       [22]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 1
            +0x004 Buffer           : 0x83255740
       [23]
         +0x000 RealVectorSize   : 0x100
         +0x004 Display          : _RTL_BITMAP
            +0x000 SizeOfBitMap     : 1
            +0x004 Buffer           : 0x832555c0
      +0x130 FreeSummary      : 0x100000
      +0x134 FreeBins         : _LIST_ENTRY [ 0x832417cc - 0x832417cc ]
         +0x000 Flink            : 0x832417cc
         +0x004 Blink            : 0x832417cc
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值