mirror of
https://github.com/bb107/MemoryModulePP
synced 2026-06-08 13:15:33 +00:00
771 lines
27 KiB
C++
771 lines
27 KiB
C++
#include "stdafx.h"
|
|
#include <random>
|
|
|
|
#define InsertTailList(ListHead,Entry) {\
|
|
PLIST_ENTRY _EX_Blink;\
|
|
PLIST_ENTRY _EX_ListHead;\
|
|
_EX_ListHead = (ListHead);\
|
|
_EX_Blink = _EX_ListHead->Blink;\
|
|
(Entry)->Flink = _EX_ListHead;\
|
|
(Entry)->Blink = _EX_Blink;\
|
|
_EX_Blink->Flink = (Entry);\
|
|
_EX_ListHead->Blink = (Entry);\
|
|
}
|
|
|
|
static PRTL_RB_TREE NTAPI RtlFindLdrpModuleBaseAddressIndex() {
|
|
static PRTL_RB_TREE LdrpModuleBaseAddressIndex = nullptr;
|
|
if (LdrpModuleBaseAddressIndex)return LdrpModuleBaseAddressIndex;
|
|
|
|
PLDR_DATA_TABLE_ENTRY_WIN10 nt10 = decltype(nt10)(RtlFindNtdllLdrEntry());
|
|
PRTL_BALANCED_NODE node = nullptr;
|
|
if (!nt10 || !RtlIsWindowsVersionOrGreater(6, 2, 0))return nullptr;
|
|
node = &nt10->BaseAddressIndexNode;
|
|
while (node->ParentValue & (~7)) node = decltype(node)(node->ParentValue & (~7));
|
|
|
|
if (!node->Red) {
|
|
BYTE count = 0;
|
|
PRTL_RB_TREE tmp = nullptr;
|
|
SEARCH_CONTEXT SearchContext{};
|
|
SearchContext.MemoryBuffer = &node;
|
|
SearchContext.BufferLength = sizeof(size_t);
|
|
while (NT_SUCCESS(RtlFindMemoryBlockFromModuleSection((HMODULE)nt10->DllBase, ".data", &SearchContext))) {
|
|
if (count++)return nullptr;
|
|
tmp = (decltype(tmp))SearchContext.MemoryBlockInSection;
|
|
}
|
|
if (count && tmp && tmp->Root && tmp->Min) {
|
|
LdrpModuleBaseAddressIndex = tmp;
|
|
}
|
|
}
|
|
|
|
return LdrpModuleBaseAddressIndex;
|
|
}
|
|
static NTSTATUS NTAPI RtlInsertModuleBaseAddressIndexNode(IN PLDR_DATA_TABLE_ENTRY DataTableEntry, IN PVOID BaseAddress) {
|
|
static auto LdrpModuleBaseAddressIndex = RtlFindLdrpModuleBaseAddressIndex();
|
|
if (!LdrpModuleBaseAddressIndex)return STATUS_UNSUCCESSFUL;
|
|
|
|
PLDR_DATA_TABLE_ENTRY_WIN8 LdrNode = decltype(LdrNode)((size_t)LdrpModuleBaseAddressIndex - offsetof(LDR_DATA_TABLE_ENTRY_WIN8, BaseAddressIndexNode));
|
|
bool bRight = false;
|
|
const auto i = offsetof(LDR_DATA_TABLE_ENTRY_WIN8, BaseAddressIndexNode);
|
|
while (true) {
|
|
if (BaseAddress < LdrNode->DllBase) {
|
|
if (!LdrNode->BaseAddressIndexNode.Left)break;
|
|
LdrNode = decltype(LdrNode)((size_t)LdrNode->BaseAddressIndexNode.Left - offsetof(LDR_DATA_TABLE_ENTRY_WIN8, BaseAddressIndexNode));
|
|
}
|
|
else if (BaseAddress > LdrNode->DllBase) {
|
|
if (!LdrNode->BaseAddressIndexNode.Right) {
|
|
bRight = true;
|
|
break;
|
|
}
|
|
LdrNode = decltype(LdrNode)((size_t)LdrNode->BaseAddressIndexNode.Right - offsetof(LDR_DATA_TABLE_ENTRY_WIN8, BaseAddressIndexNode));
|
|
}
|
|
else {
|
|
LdrNode->DdagNode->LoadCount++;
|
|
if (RtlIsWindowsVersionOrGreater(10, 0, 0)) {
|
|
PLDR_DATA_TABLE_ENTRY_WIN10(LdrNode)->ReferenceCount++;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
RtlRbInsertNodeEx(LdrpModuleBaseAddressIndex, &LdrNode->BaseAddressIndexNode, bRight, &PLDR_DATA_TABLE_ENTRY_WIN8(DataTableEntry)->BaseAddressIndexNode);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
static NTSTATUS NTAPI RtlRemoveModuleBaseAddressIndexNode(IN PLDR_DATA_TABLE_ENTRY DataTableEntry) {
|
|
DebugBreak();
|
|
static auto tree{ RtlFindLdrpModuleBaseAddressIndex() };
|
|
if (!tree->Root)return STATUS_UNSUCCESSFUL;
|
|
RtlRbRemoveNode(tree, &PLDR_DATA_TABLE_ENTRY_WIN8(DataTableEntry)->BaseAddressIndexNode);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS NTAPI RtlFreeDependencies(IN PLDR_DATA_TABLE_ENTRY_WIN10 LdrEntry) {
|
|
_LDR_DDAG_NODE* DependentDdgeNode = nullptr;
|
|
PLDR_DATA_TABLE_ENTRY_WIN10 ModuleEntry = nullptr;
|
|
_LDRP_CSLIST* head = (decltype(head))LdrEntry->DdagNode->Dependencies, *entry = head;
|
|
const static bool IsWin8 = RtlIsWindowsVersionInScope(6, 2, 0, 6, 3, -1);
|
|
if (!LdrEntry->DdagNode->Dependencies)return STATUS_SUCCESS;
|
|
|
|
//find all dependencies and free
|
|
do {
|
|
DependentDdgeNode = entry->Dependent.DependentDdagNode;
|
|
if (DependentDdgeNode->Modules.Flink->Flink != &DependentDdgeNode->Modules) __fastfail(FAST_FAIL_CORRUPT_LIST_ENTRY);
|
|
ModuleEntry = decltype(ModuleEntry)((size_t)DependentDdgeNode->Modules.Flink - offsetof(_LDR_DATA_TABLE_ENTRY_WIN8, NodeModuleLink));
|
|
if (ModuleEntry->DdagNode != DependentDdgeNode) __fastfail(FAST_FAIL_CORRUPT_LIST_ENTRY);
|
|
if (!DependentDdgeNode->IncomingDependencies) __fastfail(FAST_FAIL_CORRUPT_LIST_ENTRY);
|
|
_LDRP_CSLIST::_LDRP_CSLIST_INCOMMING* _last = DependentDdgeNode->IncomingDependencies, *_entry = _last;
|
|
_LDR_DDAG_NODE* CurrentDdagNode;
|
|
size_t State = 0, Cookies;
|
|
|
|
//Acquire LoaderLock
|
|
do {
|
|
if (!NT_SUCCESS(LdrLockLoaderLock(LOCK_NO_WAIT_IF_BUSY, &State, &Cookies))) __fastfail(FAST_FAIL_FATAL_APP_EXIT);
|
|
} while (State != LOCK_STATE_ENTERED);
|
|
|
|
do {
|
|
CurrentDdagNode = (decltype(CurrentDdagNode))((size_t)_entry->IncommingDdagNode & ~1);
|
|
if (CurrentDdagNode == LdrEntry->DdagNode) {
|
|
//node is head
|
|
if (_entry == DependentDdgeNode->IncomingDependencies) {
|
|
//only one node in list
|
|
if (_entry->NextIncommingEntry == (PSINGLE_LIST_ENTRY)DependentDdgeNode->IncomingDependencies) {
|
|
DependentDdgeNode->IncomingDependencies = nullptr;
|
|
}
|
|
else {
|
|
//find the last node in the list
|
|
PSINGLE_LIST_ENTRY i = _entry->NextIncommingEntry;
|
|
while (i->Next != (PSINGLE_LIST_ENTRY)_entry)i = i->Next;
|
|
i->Next = _entry->NextIncommingEntry;
|
|
DependentDdgeNode->IncomingDependencies = (_LDRP_CSLIST::_LDRP_CSLIST_INCOMMING*)_entry->NextIncommingEntry;
|
|
}
|
|
}
|
|
//node is not head
|
|
else {
|
|
_last->NextIncommingEntry = _entry->NextIncommingEntry;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//save the last entry
|
|
if (_last != _entry)_last = (decltype(_last))_last->NextIncommingEntry;
|
|
_entry = (decltype(_entry))_entry->NextIncommingEntry;
|
|
} while (_entry != _last);
|
|
//free LoaderLock
|
|
LdrUnlockLoaderLock(0, Cookies);
|
|
entry = (decltype(entry))entry->Dependent.NextDependentEntry;
|
|
|
|
//free it
|
|
if (IsWin8) {
|
|
//Update win8 dep count
|
|
_LDR_DDAG_NODE_WIN8* win8_node = (decltype(win8_node))ModuleEntry->DdagNode;
|
|
if (!win8_node->DependencyCount)__fastfail(FAST_FAIL_CORRUPT_LIST_ENTRY);
|
|
--win8_node->DependencyCount;
|
|
if (!ModuleEntry->DdagNode->LoadCount && win8_node->ReferenceCount == 1 && !win8_node->DependencyCount) {
|
|
win8_node->LoadCount = 1;
|
|
LdrUnloadDll(ModuleEntry->DllBase);
|
|
}
|
|
}
|
|
else {
|
|
LdrUnloadDll(ModuleEntry->DllBase);
|
|
}
|
|
RtlFreeLdrpHeap(LdrEntry->DdagNode->Dependencies);
|
|
|
|
//lookup next dependent.
|
|
LdrEntry->DdagNode->Dependencies = (_LDRP_CSLIST::_LDRP_CSLIST_DEPENDENT*)(entry == head ? nullptr : entry);
|
|
} while (entry != head);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
static bool NTAPI RtlInitializeLdrDataTableEntry(
|
|
OUT PLDR_DATA_TABLE_ENTRY LdrEntry,
|
|
IN DWORD dwFlags,
|
|
IN PVOID BaseAddress,
|
|
IN UNICODE_STRING &DllBaseName,
|
|
IN UNICODE_STRING &DllFullName) {
|
|
RtlZeroMemory(LdrEntry, LdrpDataTableEntrySize());
|
|
PIMAGE_NT_HEADERS headers = RtlImageNtHeader(BaseAddress);
|
|
if (!headers)return false;
|
|
bool FlagsProcessed = false;
|
|
|
|
switch (NtWindowsVersion()) {
|
|
case win10:
|
|
case win10_1:
|
|
case win10_2: {
|
|
auto entry = (PLDR_DATA_TABLE_ENTRY_WIN10)LdrEntry;
|
|
entry->ReferenceCount = 1;
|
|
}
|
|
case win8:
|
|
case win8_1: {
|
|
auto entry = (PLDR_DATA_TABLE_ENTRY_WIN8)LdrEntry;
|
|
const static bool IsWin8 = RtlIsWindowsVersionInScope(6, 2, 0, 6, 3, -1);
|
|
NtQuerySystemTime(&entry->LoadTime);
|
|
entry->OriginalBase = headers->OptionalHeader.ImageBase;
|
|
entry->BaseNameHashValue = LdrHashEntry(DllBaseName, false);
|
|
entry->LoadReason = LoadReasonDynamicLoad;
|
|
if (!NT_SUCCESS(RtlInsertModuleBaseAddressIndexNode(LdrEntry, BaseAddress)))return false;
|
|
if (!(entry->DdagNode = (decltype(entry->DdagNode))
|
|
RtlAllocateLdrpHeap(IsWin8 ? sizeof(_LDR_DDAG_NODE_WIN8) : sizeof(_LDR_DDAG_NODE))))return false;
|
|
//RtlInitializeListEntry(&entry->NodeModuleLink);
|
|
//RtlInitializeListEntry(&entry->DdagNode->Modules);
|
|
//RtlInitializeSingleEntry(&entry->DdagNode->CondenseLink);
|
|
entry->NodeModuleLink.Flink = &entry->DdagNode->Modules;
|
|
entry->NodeModuleLink.Blink = &entry->DdagNode->Modules;
|
|
entry->DdagNode->Modules.Flink = &entry->NodeModuleLink;
|
|
entry->DdagNode->Modules.Blink = &entry->NodeModuleLink;
|
|
entry->DdagNode->State = LdrModulesReadyToRun;
|
|
entry->DdagNode->LoadCount = 1;
|
|
if (IsWin8) ((_LDR_DDAG_NODE_WIN8*)(entry->DdagNode))->ReferenceCount = 1;
|
|
entry->ImageDll = entry->LoadNotificationsSent = entry->EntryProcessed =
|
|
entry->InLegacyLists = entry->InIndexes = entry->ProcessAttachCalled = true;
|
|
entry->InExceptionTable = !(dwFlags & LOAD_FLAGS_NOT_ADD_INVERTED_FUNCTION);
|
|
FlagsProcessed = true;
|
|
}
|
|
|
|
case win7: {
|
|
if (LdrpDataTableEntrySize() == sizeof(LDR_DATA_TABLE_ENTRY_WIN7)) {
|
|
auto entry = (PLDR_DATA_TABLE_ENTRY_WIN7)LdrEntry;
|
|
entry->OriginalBase = headers->OptionalHeader.ImageBase;
|
|
NtQuerySystemTime(&entry->LoadTime);
|
|
}
|
|
}
|
|
case vista: {
|
|
if (LdrpDataTableEntrySize() == sizeof(LDR_DATA_TABLE_ENTRY_VISTA) ||
|
|
LdrpDataTableEntrySize() == sizeof(LDR_DATA_TABLE_ENTRY_WIN7)) {
|
|
auto entry = (PLDR_DATA_TABLE_ENTRY_VISTA)LdrEntry;
|
|
RtlInitializeListEntry(&entry->ForwarderLinks);
|
|
RtlInitializeListEntry(&entry->StaticLinks);
|
|
RtlInitializeListEntry(&entry->ServiceTagLinks);
|
|
}
|
|
}
|
|
case xp: {
|
|
LdrEntry->DllBase = BaseAddress;
|
|
LdrEntry->SizeOfImage = headers->OptionalHeader.SizeOfImage;
|
|
LdrEntry->TimeDateStamp = headers->FileHeader.TimeDateStamp;
|
|
LdrEntry->BaseDllName = DllBaseName;
|
|
LdrEntry->FullDllName = DllFullName;
|
|
LdrEntry->EntryPoint = (PVOID)((size_t)BaseAddress + headers->OptionalHeader.AddressOfEntryPoint);
|
|
LdrEntry->LoadCount = 1;
|
|
if (!FlagsProcessed) LdrEntry->Flags = LDRP_IMAGE_DLL | LDRP_ENTRY_INSERTED | LDRP_ENTRY_PROCESSED | LDRP_PROCESS_ATTACH_CALLED;
|
|
RtlInitializeListEntry(&LdrEntry->HashLinks);
|
|
return true;
|
|
}
|
|
default:return false;
|
|
}
|
|
}
|
|
static bool NTAPI RtlFreeLdrDataTableEntry(IN PLDR_DATA_TABLE_ENTRY LdrEntry) {
|
|
switch (NtWindowsVersion()) {
|
|
case win10:
|
|
case win10_1:
|
|
case win10_2:
|
|
case win8:
|
|
case win8_1: {
|
|
auto entry = (PLDR_DATA_TABLE_ENTRY_WIN10)LdrEntry;
|
|
RtlFreeDependencies(entry);
|
|
RtlFreeLdrpHeap(entry->DdagNode);
|
|
RtlRemoveModuleBaseAddressIndexNode(LdrEntry);
|
|
}
|
|
case win7:
|
|
case vista: {
|
|
if (LdrpDataTableEntrySize() == sizeof(LDR_DATA_TABLE_ENTRY_VISTA) ||
|
|
LdrpDataTableEntrySize() == sizeof(LDR_DATA_TABLE_ENTRY_WIN7)) {
|
|
PLDR_DATA_TABLE_ENTRY_VISTA entry = (decltype(entry))LdrEntry;
|
|
PLIST_ENTRY head = &entry->ForwarderLinks, next = head->Flink;
|
|
while (head != next) {
|
|
PLDR_DATA_TABLE_ENTRY dep = *(decltype(&dep))((size_t*)next + 2);
|
|
LdrUnloadDll(dep->DllBase);
|
|
next = next->Flink;
|
|
RtlFreeLdrpHeap(next->Blink);
|
|
}
|
|
}
|
|
}
|
|
case xp: {
|
|
RtlFreeLdrpHeap(LdrEntry->BaseDllName.Buffer);
|
|
RtlFreeLdrpHeap(LdrEntry->FullDllName.Buffer);
|
|
RemoveEntryList(&LdrEntry->InLoadOrderLinks);
|
|
RemoveEntryList(&LdrEntry->InMemoryOrderLinks);
|
|
RemoveEntryList(&LdrEntry->InInitializationOrderLinks);
|
|
RemoveEntryList(&LdrEntry->HashLinks);
|
|
RtlFreeLdrpHeap(LdrEntry);
|
|
return true;
|
|
}
|
|
default:return false;
|
|
}
|
|
}
|
|
|
|
#define FLAG_REFERENCE 0
|
|
#define FLAG_DEREFERENCE 1
|
|
static NTSTATUS NTAPI RtlUpdateReferenceCount(IN OUT PLDR_DATA_TABLE_ENTRY LdrEntry, IN DWORD Flags) {
|
|
if (Flags != FLAG_REFERENCE && Flags != FLAG_DEREFERENCE)return STATUS_INVALID_PARAMETER_2;
|
|
switch (NtWindowsVersion()) {
|
|
case xp:
|
|
case vista:
|
|
case win7: {
|
|
if (Flags == FLAG_REFERENCE && LdrEntry->LoadCount != 0xffff)
|
|
++LdrEntry->LoadCount;
|
|
if (Flags == FLAG_DEREFERENCE && LdrEntry->LoadCount)
|
|
--LdrEntry->LoadCount;
|
|
break;
|
|
}
|
|
case win8:
|
|
case win8_1:
|
|
case win10:
|
|
case win10_1:
|
|
case win10_2: {
|
|
auto entry = (PLDR_DATA_TABLE_ENTRY_WIN10)LdrEntry;
|
|
if (Flags == FLAG_REFERENCE) {
|
|
if (entry->ObsoleteLoadCount != 0xffff)++entry->ObsoleteLoadCount;
|
|
if (entry->DdagNode->LoadCount != 0xffffffff)++entry->DdagNode->LoadCount;
|
|
}
|
|
if (Flags == FLAG_DEREFERENCE) {
|
|
if (entry->ObsoleteLoadCount)--entry->ObsoleteLoadCount;
|
|
if (entry->DdagNode->LoadCount)--entry->DdagNode->LoadCount;
|
|
}
|
|
break;
|
|
}
|
|
default:return STATUS_UNSUCCESSFUL;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
static NTSTATUS NTAPI RtlGetReferenceCount(IN PLDR_DATA_TABLE_ENTRY LdrEntry, OUT PULONG Count) {
|
|
switch (NtWindowsVersion()) {
|
|
case xp:
|
|
case vista:
|
|
case win7: {
|
|
*Count = LdrEntry->LoadCount;
|
|
break;
|
|
}
|
|
case win8:
|
|
case win8_1:
|
|
case win10:
|
|
case win10_1:
|
|
case win10_2: {
|
|
auto entry = (PLDR_DATA_TABLE_ENTRY_WIN8)LdrEntry;
|
|
*Count = entry->DdagNode->LoadCount == entry->ObsoleteLoadCount ? entry->ObsoleteLoadCount : entry->DdagNode->LoadCount;
|
|
break;
|
|
}
|
|
default:return STATUS_UNSUCCESSFUL;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static bool NTAPI RtlResolveDllNameUnicodeString(
|
|
IN PCWSTR DllName OPTIONAL, IN PCWSTR DllFullName OPTIONAL,
|
|
OUT PUNICODE_STRING BaseDllName, OUT PUNICODE_STRING FullDllName) {
|
|
|
|
std::random_device random;
|
|
size_t Length = 0;
|
|
size_t FullLength = 0;
|
|
PWSTR _DllName = nullptr, _DllFullName = _DllName;
|
|
bool result = false;
|
|
if (DllName) {
|
|
bool add = false;
|
|
if ((Length = wcslen(DllName)) <= 4 || wcsnicmp(DllName + Length - 4, L".dll", 4)) {
|
|
add = true;
|
|
Length += 4;
|
|
}
|
|
_DllName = new wchar_t[++Length];
|
|
wcscpy(_DllName, DllName);
|
|
if (add)wcscat(_DllName, L".DLL");
|
|
}
|
|
else {
|
|
Length = 16 + 4 + 1; //hex(ULONG64) + ".dll" + '\0'
|
|
_DllName = new wchar_t[Length];
|
|
swprintf(_DllName, L"%016llX.DLL", ((ULONG64)random() << 32) | random());
|
|
}
|
|
if (DllFullName) {
|
|
bool add = false;
|
|
FullLength = wcslen(DllFullName);
|
|
if (DllName && !wcsstr(DllFullName, DllName) && wcsnicmp(DllFullName + FullLength - 4, L".dll", 4)) {
|
|
add = true;
|
|
FullLength += Length;
|
|
}
|
|
wcscpy(_DllFullName = new wchar_t[++FullLength], DllFullName);
|
|
if (add) wsprintfW(_DllFullName, L"%s\\%s", _DllFullName, _DllName);
|
|
}
|
|
else {
|
|
FullLength = 16 + 1 + Length; //hex(ULONG64) + '\\' + _DllName
|
|
swprintf(_DllFullName = new wchar_t[FullLength], L"%016llX\\%s", ((ULONG64)random() << 32) | random(), _DllName);
|
|
}
|
|
FullLength *= sizeof(wchar_t);
|
|
Length *= sizeof(wchar_t);
|
|
|
|
/* Allocate space for full DLL name */
|
|
if (!(FullDllName->Buffer = (PWSTR)RtlAllocateLdrpHeap(FullLength))) goto end;
|
|
FullDllName->Length = FullLength - sizeof(wchar_t);
|
|
FullDllName->MaximumLength = FullLength;
|
|
wcscpy(FullDllName->Buffer, _DllFullName);
|
|
|
|
/* Construct base DLL name */
|
|
BaseDllName->Length = Length - sizeof(wchar_t);
|
|
BaseDllName->MaximumLength = Length;
|
|
BaseDllName->Buffer = (PWSTR)RtlAllocateLdrpHeap(Length);
|
|
if (!BaseDllName->Buffer) {
|
|
RtlFreeLdrpHeap(BaseDllName->Buffer);
|
|
goto end;
|
|
}
|
|
wcscpy(BaseDllName->Buffer, _DllName);
|
|
result = true;
|
|
end:
|
|
delete[]_DllName;
|
|
delete[]_DllFullName;
|
|
return result;
|
|
}
|
|
|
|
static PLDR_DATA_TABLE_ENTRY NTAPI RtlAllocateDataTableEntry(IN PVOID BaseAddress) {
|
|
PLDR_DATA_TABLE_ENTRY LdrEntry = nullptr;
|
|
PIMAGE_NT_HEADERS NtHeader;
|
|
|
|
/* Make sure the header is valid */
|
|
if (NtHeader = RtlImageNtHeader(BaseAddress)) {
|
|
/* Allocate an entry */
|
|
LdrEntry = (decltype(LdrEntry))RtlAllocateLdrpHeap(LdrpDataTableEntrySize());
|
|
}
|
|
|
|
/* Return the entry */
|
|
return LdrEntry;
|
|
}
|
|
|
|
static VOID NTAPI RtlInsertMemoryTableEntry(IN PLDR_DATA_TABLE_ENTRY LdrEntry) {
|
|
PPEB_LDR_DATA PebData = NtCurrentPeb()->Ldr;
|
|
PLIST_ENTRY LdrpHashTable = RtlFindLdrpHashTable();
|
|
ULONG i;
|
|
|
|
/* Insert into hash table */
|
|
i = LdrHashEntry(LdrEntry->BaseDllName);
|
|
InsertTailList(&LdrpHashTable[i], &LdrEntry->HashLinks);
|
|
|
|
/* Insert into other lists */
|
|
InsertTailList(&PebData->InLoadOrderModuleList, &LdrEntry->InLoadOrderLinks);
|
|
InsertTailList(&PebData->InMemoryOrderModuleList, &LdrEntry->InMemoryOrderLinks);
|
|
InsertTailList(&PebData->InInitializationOrderModuleList, &LdrEntry->InInitializationOrderLinks);
|
|
}
|
|
|
|
static NTSTATUS NTAPI LdrMapDllMemory(IN HMEMORYMODULE ViewBase, IN DWORD dwFlags, IN PCWSTR DllName OPTIONAL,
|
|
IN PCWSTR lpFullDllName OPTIONAL, OUT PLDR_DATA_TABLE_ENTRY* DataTableEntry OPTIONAL) {
|
|
|
|
UNICODE_STRING FullDllName, BaseDllName;
|
|
PIMAGE_NT_HEADERS NtHeaders;
|
|
PLDR_DATA_TABLE_ENTRY LdrEntry;
|
|
|
|
if (!(NtHeaders = RtlImageNtHeader(ViewBase))) return STATUS_INVALID_IMAGE_FORMAT;
|
|
|
|
if (!(LdrEntry = RtlAllocateDataTableEntry(ViewBase))) return STATUS_NO_MEMORY;
|
|
|
|
if (!RtlResolveDllNameUnicodeString(DllName, lpFullDllName, &BaseDllName, &FullDllName)) {
|
|
RtlFreeLdrpHeap(LdrEntry);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
if (!RtlInitializeLdrDataTableEntry(LdrEntry, dwFlags, ViewBase, BaseDllName, FullDllName)) {
|
|
RtlFreeLdrpHeap(LdrEntry);
|
|
RtlFreeLdrpHeap(BaseDllName.Buffer);
|
|
RtlFreeLdrpHeap(FullDllName.Buffer);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
RtlInsertMemoryTableEntry(LdrEntry);
|
|
if (DataTableEntry)*DataTableEntry = LdrEntry;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#ifndef _INLINE_INTERNALS
|
|
static __forceinline WORD CalcCheckSum(DWORD StartValue, LPVOID BaseAddress, DWORD WordCount) {
|
|
LPWORD Ptr = (LPWORD)BaseAddress;
|
|
DWORD Sum = StartValue;
|
|
for (DWORD i = 0; i < WordCount; i++) {
|
|
Sum += *Ptr;
|
|
if (HIWORD(Sum) != 0) Sum = LOWORD(Sum) + HIWORD(Sum);
|
|
Ptr++;
|
|
}
|
|
return (WORD)(LOWORD(Sum) + HIWORD(Sum));
|
|
}
|
|
BOOLEAN __forceinline WINAPI CheckSumBufferedFile(LPVOID BaseAddress, DWORD BufferLength) {
|
|
PIMAGE_NT_HEADERS header = RtlImageNtHeader(BaseAddress);
|
|
DWORD CalcSum = CalcCheckSum(0, BaseAddress, (BufferLength + 1) / sizeof(WORD));
|
|
DWORD HdrSum = header->OptionalHeader.CheckSum;
|
|
if (!HdrSum)return TRUE;
|
|
|
|
if (!header) return FALSE;
|
|
if (LOWORD(CalcSum) >= LOWORD(HdrSum)) CalcSum -= LOWORD(HdrSum);
|
|
else CalcSum = ((LOWORD(CalcSum) - LOWORD(HdrSum)) & 0xFFFF) - 1;
|
|
if (LOWORD(CalcSum) >= HIWORD(HdrSum)) CalcSum -= HIWORD(HdrSum);
|
|
else CalcSum = ((LOWORD(CalcSum) - HIWORD(HdrSum)) & 0xFFFF) - 1;
|
|
CalcSum += BufferLength;
|
|
return HdrSum == CalcSum;
|
|
}
|
|
#endif
|
|
BOOLEAN NTAPI RtlIsValidImageBuffer(PVOID Buffer) {
|
|
|
|
BOOLEAN result = FALSE;
|
|
__try {
|
|
union {
|
|
PIMAGE_NT_HEADERS32 nt32;
|
|
PIMAGE_NT_HEADERS64 nt64;
|
|
PIMAGE_NT_HEADERS nt;
|
|
}headers;
|
|
headers.nt = RtlImageNtHeader(Buffer);
|
|
PIMAGE_SECTION_HEADER sections = nullptr;
|
|
size_t SizeofImage = 0;
|
|
|
|
if (!headers.nt) {
|
|
return FALSE;
|
|
}
|
|
|
|
switch (headers.nt->OptionalHeader.Magic) {
|
|
case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
|
|
sections = PIMAGE_SECTION_HEADER((char*)&headers.nt32->OptionalHeader + headers.nt32->FileHeader.SizeOfOptionalHeader);
|
|
SizeofImage = headers.nt32->OptionalHeader.SizeOfHeaders;
|
|
ProbeForRead(sections, headers.nt32->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER));
|
|
for (WORD i = 0; i < headers.nt32->FileHeader.NumberOfSections; ++i, ++sections)
|
|
SizeofImage += sections->SizeOfRawData;
|
|
|
|
//Signature size
|
|
SizeofImage += headers.nt32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size;
|
|
break;
|
|
case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
|
|
sections = PIMAGE_SECTION_HEADER((char*)&headers.nt64->OptionalHeader + headers.nt64->FileHeader.SizeOfOptionalHeader);
|
|
SizeofImage = headers.nt64->OptionalHeader.SizeOfHeaders;
|
|
ProbeForRead(sections, headers.nt64->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER));
|
|
for (WORD i = 0; i < headers.nt64->FileHeader.NumberOfSections; ++i, ++sections)
|
|
SizeofImage += sections->SizeOfRawData;
|
|
SizeofImage += headers.nt64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size;
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
IMAGE_FIRST_SECTION(headers.nt32);
|
|
ProbeForRead(Buffer, SizeofImage);
|
|
result = CheckSumBufferedFile(Buffer, SizeofImage);
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER) {
|
|
SetLastError(RtlNtStatusToDosError(GetExceptionCode()));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
NTSTATUS NTAPI LdrLoadDllMemory(OUT HMEMORYMODULE* BaseAddress, IN LPVOID BufferAddress, IN size_t BufferSize) {
|
|
return LdrLoadDllMemoryExW(BaseAddress, nullptr, LOAD_FLAGS_NOT_FAIL_IF_HANDLE_TLS, BufferAddress, BufferSize, nullptr, nullptr);
|
|
}
|
|
|
|
NTSTATUS NTAPI LdrLoadDllMemoryExW(
|
|
OUT HMEMORYMODULE* BaseAddress,
|
|
OUT PVOID* LdrEntry OPTIONAL,
|
|
IN DWORD dwFlags,
|
|
IN LPVOID BufferAddress,
|
|
IN size_t BufferSize,
|
|
IN LPCWSTR DllName OPTIONAL,
|
|
IN LPCWSTR DllFullName OPTIONAL) {
|
|
PMEMORYMODULE module = nullptr;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PLDR_DATA_TABLE_ENTRY ModuleEntry = nullptr;
|
|
PIMAGE_NT_HEADERS headers = nullptr;
|
|
|
|
if (BufferSize)return STATUS_INVALID_PARAMETER_5;
|
|
__try {
|
|
*BaseAddress = nullptr;
|
|
if (LdrEntry)*LdrEntry = nullptr;
|
|
if (!(dwFlags & LOAD_FLAGS_PASS_IMAGE_CHECK) && !RtlIsValidImageBuffer(BufferAddress))status = STATUS_INVALID_IMAGE_FORMAT;
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER) {
|
|
status = GetExceptionCode();
|
|
}
|
|
if (!NT_SUCCESS(status))return status;
|
|
|
|
if (dwFlags & LOAD_FLAGS_NOT_MAP_DLL) {
|
|
dwFlags &= LOAD_FLAGS_NOT_MAP_DLL;
|
|
DllName = DllFullName = nullptr;
|
|
}
|
|
if (dwFlags & LOAD_FLAGS_USE_DLL_NAME && (!DllName || !DllFullName))return STATUS_INVALID_PARAMETER_3;
|
|
|
|
if (DllName) {
|
|
PLIST_ENTRY ListHead, ListEntry;
|
|
PLDR_DATA_TABLE_ENTRY CurEntry;
|
|
PIMAGE_NT_HEADERS h1 = RtlImageNtHeader(BufferAddress), h2 = nullptr;
|
|
if (!h1)return STATUS_INVALID_IMAGE_FORMAT;
|
|
ListEntry = (ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList)->Flink;
|
|
while (ListEntry != ListHead) {
|
|
CurEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
|
|
ListEntry = ListEntry->Flink;
|
|
/* Check if it's being unloaded */
|
|
if (!CurEntry->InMemoryOrderLinks.Flink) continue;
|
|
/* Check if name matches */
|
|
if (!wcsnicmp(DllName, CurEntry->BaseDllName.Buffer, (CurEntry->BaseDllName.Length / sizeof(wchar_t)) - 4) ||
|
|
!wcsnicmp(DllName, CurEntry->BaseDllName.Buffer, CurEntry->BaseDllName.Length / sizeof(wchar_t))) {
|
|
/* Let's compare their headers */
|
|
if (!(h2 = RtlImageNtHeader(CurEntry->DllBase)))continue;
|
|
if (!(module = MapMemoryModuleHandle((HMEMORYMODULE)CurEntry->DllBase)))continue;
|
|
if ((h1->OptionalHeader.SizeOfCode == h2->OptionalHeader.SizeOfCode) &&
|
|
(h1->OptionalHeader.SizeOfHeaders == h2->OptionalHeader.SizeOfHeaders)) {
|
|
/* This is our entry!, update load count and return success */
|
|
if (!module->UseReferenceCount || dwFlags & LOAD_FLAGS_NOT_USE_REFERENCE_COUNT)return STATUS_INVALID_PARAMETER_3;
|
|
RtlUpdateReferenceCount(CurEntry, FLAG_REFERENCE);
|
|
*BaseAddress = (HMEMORYMODULE)CurEntry->DllBase;
|
|
if (LdrEntry)*LdrEntry = CurEntry;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!(*BaseAddress = MemoryLoadLibrary(BufferAddress))) {
|
|
switch (GetLastError()) {
|
|
case ERROR_BAD_EXE_FORMAT:
|
|
return STATUS_INVALID_IMAGE_FORMAT;
|
|
case ERROR_OUTOFMEMORY:
|
|
return STATUS_NO_MEMORY;
|
|
case ERROR_DLL_INIT_FAILED:
|
|
return STATUS_DLL_INIT_FAILED;
|
|
default:
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
if (!(module = MapMemoryModuleHandle(*BaseAddress))) {
|
|
__fastfail(FAST_FAIL_FATAL_APP_EXIT);
|
|
DebugBreak();
|
|
ExitProcess(STATUS_INVALID_ADDRESS);
|
|
TerminateProcess(NtCurrentProcess(), STATUS_INVALID_ADDRESS);
|
|
}
|
|
module->loadFromNtLoadDllMemory = true;
|
|
headers = RtlImageNtHeader(*BaseAddress);
|
|
if (headers->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NO_SEH)dwFlags |= LOAD_FLAGS_NOT_ADD_INVERTED_FUNCTION;
|
|
if (dwFlags & LOAD_FLAGS_NOT_MAP_DLL) return status;
|
|
|
|
status = LdrMapDllMemory(*BaseAddress, dwFlags, DllName, DllFullName, &ModuleEntry);
|
|
if (!NT_SUCCESS(status)) {
|
|
LdrUnloadDllMemory(*BaseAddress);
|
|
*BaseAddress = nullptr;
|
|
return status;
|
|
}
|
|
module->MappedDll = true;
|
|
|
|
if (LdrEntry)*LdrEntry = ModuleEntry;
|
|
|
|
if (!(dwFlags & LOAD_FLAGS_NOT_USE_REFERENCE_COUNT))module->UseReferenceCount = true;
|
|
|
|
if (!(dwFlags & LOAD_FLAGS_NOT_ADD_INVERTED_FUNCTION)) {
|
|
status = RtlInsertInvertedFunctionTable((PVOID)module->codeBase, headers->OptionalHeader.SizeOfImage);
|
|
if (!NT_SUCCESS(status)) {
|
|
LdrUnloadDllMemory(*BaseAddress);
|
|
*BaseAddress = nullptr;
|
|
if (LdrEntry)*LdrEntry = nullptr;
|
|
return status;
|
|
}
|
|
module->InsertInvertedFunctionTableEntry = true;
|
|
}
|
|
|
|
if (!(dwFlags & LOAD_FLAGS_NOT_HANDLE_TLS)) {
|
|
status = LdrpHandleTlsData(ModuleEntry);
|
|
if (!NT_SUCCESS(status)) {
|
|
do {
|
|
if (dwFlags & LOAD_FLAGS_NOT_FAIL_IF_HANDLE_TLS) {
|
|
status = 0x7fffffff;
|
|
break;
|
|
}
|
|
LdrUnloadDllMemory(*BaseAddress);
|
|
*BaseAddress = nullptr;
|
|
if (LdrEntry)*LdrEntry = nullptr;
|
|
return status;
|
|
} while (false);
|
|
}
|
|
else {
|
|
module->TlsHandled = true;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS NTAPI LdrLoadDllMemoryExA(
|
|
OUT HMEMORYMODULE* BaseAddress,
|
|
OUT PVOID* LdrEntry OPTIONAL,
|
|
IN DWORD dwFlags,
|
|
IN LPVOID BufferAddress,
|
|
IN size_t BufferSize,
|
|
IN LPCSTR DllName OPTIONAL,
|
|
IN LPCSTR DllFullName OPTIONAL){
|
|
LPWSTR _DllName = nullptr, _DllFullName = nullptr;
|
|
size_t size;
|
|
NTSTATUS status;
|
|
if (DllName) {
|
|
size = strlen(DllName) + 1;
|
|
_DllName = new wchar_t[size];
|
|
mbstowcs(_DllName, DllName, size);
|
|
}
|
|
if (DllFullName) {
|
|
size = strlen(DllFullName) + 1;
|
|
_DllFullName = new wchar_t[size];
|
|
mbstowcs(_DllFullName, DllFullName, size);
|
|
}
|
|
status = LdrLoadDllMemoryExW(BaseAddress, LdrEntry, dwFlags, BufferAddress, BufferSize, _DllName, _DllFullName);
|
|
if (_DllName)delete[]_DllName;
|
|
if (_DllFullName)delete[]_DllFullName;
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS NTAPI LdrUnloadDllMemory(IN HMEMORYMODULE BaseAddress) {
|
|
__try {
|
|
ProbeForRead(BaseAddress, sizeof(size_t));
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetExceptionCode();
|
|
}
|
|
|
|
PLDR_DATA_TABLE_ENTRY CurEntry;
|
|
ULONG count = 0;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PMEMORYMODULE module = MapMemoryModuleHandle(BaseAddress);
|
|
|
|
//Not a memory module loaded via LdrLoadDllMemory
|
|
if (!module || !module->loadFromNtLoadDllMemory)return STATUS_INVALID_HANDLE;
|
|
|
|
//Mapping dll failed
|
|
if (module->loadFromNtLoadDllMemory && !module->MappedDll) {
|
|
module->underUnload = true;
|
|
return MemoryFreeLibrary(BaseAddress) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
if (CurEntry = RtlFindLdrTableEntryByHandle(BaseAddress)) {
|
|
if (RtlImageNtHeader(BaseAddress)->OptionalHeader.SizeOfImage == CurEntry->SizeOfImage) {
|
|
if (module->UseReferenceCount) {
|
|
status = RtlGetReferenceCount(CurEntry, &count);
|
|
if (!NT_SUCCESS(status))return status;
|
|
}
|
|
if (!(count & ~1)) {
|
|
module->underUnload = true;
|
|
if (module->MappedDll) {
|
|
if (module->InsertInvertedFunctionTableEntry) {
|
|
status = RtlRemoveInvertedFunctionTable(BaseAddress);
|
|
if (!NT_SUCCESS(status))__fastfail(FAST_FAIL_CORRUPT_LIST_ENTRY);
|
|
}
|
|
if (module->TlsHandled) {
|
|
|
|
status = LdrpReleaseTlsEntry(CurEntry);
|
|
if (!NT_SUCCESS(status)) __fastfail(FAST_FAIL_FATAL_APP_EXIT);
|
|
}
|
|
if (!RtlFreeLdrDataTableEntry(CurEntry))__fastfail(FAST_FAIL_FATAL_APP_EXIT);
|
|
}
|
|
if (!MemoryFreeLibrary(BaseAddress))__fastfail(FAST_FAIL_FATAL_APP_EXIT);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
return RtlUpdateReferenceCount(CurEntry, FLAG_DEREFERENCE);
|
|
}
|
|
}
|
|
}
|
|
|
|
return STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
VOID NTAPI LdrUnloadDllMemoryAndExitThread(IN HMEMORYMODULE BaseAddress, IN DWORD dwExitCode) {
|
|
LdrUnloadDllMemory(BaseAddress);
|
|
RtlExitUserThread(dwExitCode);
|
|
}
|
|
|
|
NTSTATUS NTAPI LdrQuerySystemMemoryModuleFeatures(OUT PDWORD pFeatures) {
|
|
static DWORD features = 0;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PVOID pfn = nullptr;
|
|
bool value = false;
|
|
__try {
|
|
if (features) {
|
|
*pFeatures = features;
|
|
return status;
|
|
}
|
|
|
|
if (RtlFindLdrpModuleBaseAddressIndex())features |= MEMORY_FEATURE_MODULE_BASEADDRESS_INDEX;
|
|
if (RtlFindLdrpHeap())features |= MEMORY_FEATURE_LDRP_HEAP;
|
|
if (RtlFindLdrpHashTable())features |= MEMORY_FEATURE_LDRP_HASH_TABLE;
|
|
if (RtlFindLdrpInvertedFunctionTable())features |= MEMORY_FEATURE_INVERTED_FUNCTION_TABLE;
|
|
if (NT_SUCCESS(RtlFindLdrpHandleTlsData(&pfn, &value)) && pfn)features |= MEMORY_FEATURE_LDRP_HANDLE_TLS_DATA;
|
|
if (NT_SUCCESS(RtlFindLdrpReleaseTlsEntry(&pfn, &value) && pfn))features |= MEMORY_FEATURE_LDRP_RELEASE_TLS_ENTRY;
|
|
if (features)features |= MEMORY_FEATURE_SUPPORT_VERSION;
|
|
*pFeatures = features;
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER) {
|
|
status = GetExceptionCode();
|
|
}
|
|
return status;
|
|
}
|
|
|
|
|
|
|