mirror of
https://github.com/winterknife/SILVERPICK
synced 2026-06-08 18:21:15 +00:00
121 lines
4.9 KiB
C++
121 lines
4.9 KiB
C++
// ========================================================================
|
|
// File: UserModuleBase.cpp
|
|
//
|
|
// Author: winterknife
|
|
//
|
|
// Description: This source file contains routine(s) to query the
|
|
// image base address of user modules mapped in UVAS without any
|
|
// external dependencies including CRT, Win32, or Native API routines
|
|
//
|
|
// Modifications:
|
|
// 2025-09-23 Created
|
|
// 2025-09-25 Updated
|
|
// ========================================================================
|
|
|
|
// ========================================================================
|
|
// Includes
|
|
// ========================================================================
|
|
|
|
#include "UserModuleBase.h"
|
|
#include <algorithm>
|
|
|
|
// ========================================================================
|
|
// Routines
|
|
// ========================================================================
|
|
|
|
#pragma region ROUTINES
|
|
|
|
_Use_decl_annotations_
|
|
PVOID __stdcall get_user_module_base_by_hash(
|
|
QWORD qwTargetModuleHash
|
|
) {
|
|
// Init local variables
|
|
PTEB pTeb = nullptr;
|
|
PPEB pPeb = nullptr;
|
|
PVOID pImageBase = nullptr;
|
|
PPEB_LDR_DATA pPebLdrData = nullptr;
|
|
PLIST_ENTRY pListHead = nullptr;
|
|
PLIST_ENTRY pNextEntry = nullptr;
|
|
PLDR_DATA_TABLE_ENTRY pLdrDataTableEntry = nullptr;
|
|
PUNICODE_STRING pUnicodeString = nullptr;
|
|
CHAR strModuleName[MAX_BASE_DLL_NAME_LEN]; ZERO_MEMORY(strModuleName, sizeof(strModuleName));
|
|
DWORD_PTR dwptrModuleNameLength = 0;
|
|
DWORD dwIndex = 0;
|
|
WCHAR wchLetter = 0;
|
|
QWORD qwModuleHash = 0;
|
|
|
|
// Get the base address of the current thread's Thread Environment Block (TEB)
|
|
pTeb = get_current_teb();
|
|
if (pTeb == nullptr)
|
|
return nullptr;
|
|
|
|
// Get the base address of the current process's Process Environment Block (PEB)
|
|
pPeb = pTeb->ProcessEnvironmentBlock;
|
|
if (pPeb == nullptr)
|
|
return nullptr;
|
|
|
|
// Check if the hash of the target module name is 0, if true then return the base address of the calling module
|
|
if (qwTargetModuleHash == 0) {
|
|
pImageBase = pPeb->Reserved3[1]; // ntdll!_PEB.ImageBaseAddress (0x10)
|
|
return pImageBase;
|
|
}
|
|
|
|
// Get the address of the PEB loader data - ntdll!_PEB_LDR_DATA structure (0x18)
|
|
// Caution: race condition below since we cannot acquire the loader lock here before walking the loaded module list
|
|
pPebLdrData = pPeb->Ldr;
|
|
|
|
// Get the address of the circular doubly linked list head - ntdll!_LIST_ENTRY structure (0x20)
|
|
pListHead = &(pPebLdrData->InMemoryOrderModuleList);
|
|
|
|
// Get the address of the first entry - ntdll!_LIST_ENTRY structure (0x00)
|
|
pNextEntry = pListHead->Flink;
|
|
|
|
// Loop through all the list entries/loaded modules till we reach the head again
|
|
while (pNextEntry != pListHead) {
|
|
// Get the address of the current loader module entry - ntdll!_LDR_DATA_TABLE_ENTRY structure (0x10)
|
|
pLdrDataTableEntry = CONTAINING_RECORD(pNextEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
|
|
|
|
// Get the address of the Unicode string representing the current module name - ntdll!_UNICODE_STRING structure
|
|
pUnicodeString = BIT_CAST(PUNICODE_STRING, &pLdrDataTableEntry->Reserved4); // ntdll!_LDR_DATA_TABLE_ENTRY.BaseDllName (0x58)
|
|
|
|
// Reset the ANSI string buffer for the current module
|
|
ZERO_MEMORY(strModuleName, sizeof(strModuleName));
|
|
|
|
// Get the length of the Unicode string representing the current module name
|
|
dwptrModuleNameLength = std::min(pUnicodeString->Length / sizeof(WCHAR), sizeof(strModuleName));
|
|
|
|
// Convert the current module name from Unicode string to ANSI string
|
|
for (dwIndex = 0; dwIndex < dwptrModuleNameLength; dwIndex++) {
|
|
// Extract each wide character letter
|
|
wchLetter = *(pUnicodeString->Buffer + dwIndex);
|
|
|
|
// Convert each wide character letter to lowercase
|
|
if (wchLetter != L'_')
|
|
wchLetter = wchLetter | 0x20U;
|
|
|
|
// Check if each wide character letter is a valid US-ASCII character, else replace with "?"
|
|
if (wchLetter > 0x7FU)
|
|
wchLetter = '?';
|
|
|
|
// Put each wide character letter into the ANSI string buffer
|
|
strModuleName[dwIndex] = STATIC_CAST(CHAR, wchLetter);
|
|
}
|
|
|
|
// Get the 64-bit FNV-1a hash of the current module name
|
|
qwModuleHash = HASH_STRING_RUN_TIME(strModuleName, dwptrModuleNameLength);
|
|
|
|
// Check if the hash of the current module name matches the hash of the target module name
|
|
if (qwModuleHash == qwTargetModuleHash) {
|
|
// Return the base address of the current module
|
|
pImageBase = pLdrDataTableEntry->DllBase;
|
|
return pImageBase;
|
|
}
|
|
|
|
// Get the address of the next entry
|
|
pNextEntry = pNextEntry->Flink;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
#pragma endregion |