BaseNameHashValue is a loader data table entry in LDR_DATA_TABLE_ENTRY that stores the hash of BaseDllName using RtlHashUnicodeString. (Ref: Windows Internals Seventh Edition Part 1 System architecture, processes, threads, memory management, and more.)
Running the x86 program below outputs these values
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
---------------------------------------------
However, notice the only hash value that matches the calculated value using RtlHashUnicodeString is
Name: KERNEL32.DLL
Hash: 0x536CD652 Calculated: 0x536CD652
Address: 0x75C80000
Why does KERNEL32.DLL have the same hash values but none of the other modules?
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;
}
Change the second parameter in this call
RtlHashUnicodeString(&uStr, FALSE, HASH_STRING_ALGORITHM_DEFAULT, &hash);
to TRUE:
RtlHashUnicodeString(&uStr, TRUE, HASH_STRING_ALGORITHM_DEFAULT, &hash);
Looking at ntdll.dll, the loader subsystem wraps RtlHashUnicodeString
with a private function LdrpHashUnicodeString
. This function always uses the case insensitive hash generation.