You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何仅KERNEL32.DLL的BaseNameHashValue与计算哈希匹配?

问题:为何仅KERNEL32.DLL的BaseNameHashValue与计算得到的哈希值一致?

BaseNameHashValue是LDR_DATA_TABLE_ENTRY结构中的字段,用于存储BaseDllName通过RtlHashUnicodeString计算得到的哈希值(参考《Windows Internals 第七版 第一部分》)。

运行下方x86程序后,输出结果显示仅KERNEL32.DLL的BaseNameHashValue与通过RtlHashUnicodeString计算出的结果匹配,其余模块两者均不匹配:

Name:       peb2.exe
Hash:       0x29413AE9 Calculated: 0x264362E9
Address:    0x00F10000
---------------------------------------------
Name:       ntdll.dll
Hash:       0xF46857D4 Calculated: 0xD22E2014
Address:    0x77AD0000
---------------------------------------------
Name:       KERNEL32.DLL
Hash:       0x536CD652 Calculated: 0x536CD652
Address:    0x75C80000
---------------------------------------------
Name:       KERNELBASE.dll
Hash:       0x0235BEC4 Calculated: 0x1217B6E4
Address:    0x77160000
---------------------------------------------
Name:       ucrtbase.dll
Hash:       0xAEF34AF7 Calculated: 0x2E1D6317
Address:    0x77430000
---------------------------------------------
Name:       VCRUNTIME140.dll
Hash:       0xB61775F8 Calculated: 0xC5F96E18
Address:    0x75710000
---------------------------------------------

对应的测试代码如下(peb2.cpp):

#include <stdio.h>
#include <stdint.h>
#include <windows.h>
#include <ntstatus.h>
#include <winternl.h>

#define HASH_STRING_ALGORITHM_DEFAULT 0

typedef struct {
    PVOID Reserved1[2];
    LIST_ENTRY InMemoryOrderLinks;
    PVOID Reserved2[2];
    PVOID DllBase;
    PVOID EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
    uint8_t pad[92];
    /*
    union _union_9066 field_0x24;
    ushort ObsoleteLoadCount;
    ushort TlsIndex;
    struct _LIST_ENTRY HashLinks;
    ulong TimeDateStamp;
    struct _ACTIVATION_CONTEXT* EntryPointActivationContext;
    void* Lock;
    struct _LDR_DDAG_NODE* DdagNode;
    struct _LIST_ENTRY NodeModuleLink;
    struct _LDRP_LOAD_CONTEXT* LoadContext;
    void* ParentDllBase;
    void* SwitchBackContext;
    struct _RTL_BALANCED_NODE BaseAddressIndexNode;
    struct _RTL_BALANCED_NODE MappingInfoIndexNode;
    ulong OriginalBase;
    long Padding_84;
    union _LARGE_INTEGER LoadTime;
    */
    ULONG BaseNameHashValue;
    /*
    enum _LDR_DLL_LOAD_REASON LoadReason;
    ulong ImplicitPathOptions;
    ulong ReferenceCount;
    ulong DependentLoadFlags;
    uchar SigningLevel;
    char __PADDING__[3];    
    */
} MY_LDR_DATA_TABLE_ENTRY;

typedef VOID(NTAPI* pRtlInitUnicodeString)(PUNICODE_STRING, PCWSTR);
typedef NTSTATUS(NTAPI* pRtlHashUnicodeString)(const UNICODE_STRING*, BOOLEAN, ULONG, PULONG);

ULONG GetHashFromBaseName(PCWSTR BaseName) {
    ULONG hash = 0;
    HMODULE hNtdll = GetModuleHandle(L"ntdll.dll");
    pRtlInitUnicodeString RtlInitUnicodeString = (pRtlInitUnicodeString)GetProcAddress(hNtdll, "RtlInitUnicodeString");
    pRtlHashUnicodeString RtlHashUnicodeString = (pRtlHashUnicodeString)GetProcAddress(hNtdll, "RtlHashUnicodeString");
    UNICODE_STRING uStr;
    RtlInitUnicodeString(&uStr, BaseName);
    RtlHashUnicodeString(&uStr, FALSE, HASH_STRING_ALGORITHM_DEFAULT, &hash);
    return hash;
}

int main()
{
    PPEB peb = (PPEB)__readfsdword(0x30);
    PLIST_ENTRY ptr = peb->Ldr->InMemoryOrderModuleList.Flink;

    while (ptr != &peb->Ldr->InMemoryOrderModuleList)
    {
        MY_LDR_DATA_TABLE_ENTRY* e = CONTAINING_RECORD(ptr, MY_LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);

        if (e->BaseDllName.Buffer != NULL) {
            int len = e->BaseDllName.Length / sizeof(wchar_t);
            wchar_t BaseName[256];
            wcsncpy_s(BaseName, sizeof(BaseName) / sizeof(wchar_t), e->BaseDllName.Buffer, len);
            BaseName[len] = L'\0';
            // BaseDllName - The name of the module itself, without the full path.
            wprintf(L"Name:       %s\n", BaseName);
            // BaseNameHashValue - Hash of BaseDllName using RtlHashUnicodeString.
            wprintf(L"Hash:       0x%08X Calculated: 0x%08X\n", e->BaseNameHashValue, GetHashFromBaseName(BaseName));
            // Holds the base address at which the module was loaded.
            wprintf(L"Address:    0x%p\n", (void*)e->DllBase);
            wprintf(L"---------------------------------------------\n");
        }

        ptr = ptr->Flink;
    }

    return 0;
}

原因分析

出现这个问题的核心原因有两点:

1. 手动定义的结构体偏移错误

你手动定义的MY_LDR_DATA_TABLE_ENTRY结构中,用uint8_t pad[92]占位的方式完全不可靠。Windows不同版本(甚至同版本的更新补丁)中,LDR_DATA_TABLE_ENTRY的内部成员布局、大小都会发生变化,硬编码的pad[92]无法匹配真实的内存偏移,导致你读取的BaseNameHashValue并非结构中真正的哈希字段,而是其他无关内存位置的值。

KERNEL32.DLL的哈希匹配只是巧合——读取到的错误内存值刚好和计算结果一致,其他模块没有这种巧合,所以出现哈希不匹配的情况。

正确做法是使用系统公开的结构定义,或通过动态计算偏移的方式获取BaseNameHashValue的位置,避免硬编码占位长度。

2. 哈希计算的大小写敏感性差异

系统计算BaseNameHashValue时,调用RtlHashUnicodeString传入的大小写敏感参数可能为TRUE(忽略大小写),而你的代码中传入的是FALSE(大小写敏感)。

例如,对于ntdll.dll,系统可能基于大写的NTDLL.DLL计算忽略大小写的哈希,而你的代码用小写的ntdll.dll计算大小写敏感的哈希,结果自然不同。KERNEL32.DLL的名称本身是大写,无论是否忽略大小写,计算出的哈希值都一致,这也进一步导致它的结果看起来匹配。


内容的提问来源于stack exchange,提问作者vengy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 08:27:34