3165 lines
112 KiB
C
3165 lines
112 KiB
C
/*++
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
ldrsnap.c
|
|
|
|
Abstract:
|
|
This module implements the guts of the Ldr Dll Snap Routine.
|
|
This code is system code that is part of the executive, but is executed from both user and system space.
|
|
|
|
Author:
|
|
Mike O'Leary (mikeol) 23-Mar-1990
|
|
--*/
|
|
|
|
#define LDRDBG 0
|
|
|
|
#include "ntos.h"
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <heap.h>
|
|
#include "ldrp.h"
|
|
|
|
#if DBG // DBG
|
|
PUCHAR MonthOfYear[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
|
PUCHAR DaysOfWeek[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
|
|
LARGE_INTEGER MapBeginTime, MapEndTime, MapElapsedTime;
|
|
#endif // DBG
|
|
|
|
#if defined (_ALPHA_)
|
|
VOID AlphaFindArchitectureFixups(PIMAGE_NT_HEADERS NtHeaders, PVOID ViewBase, BOOLEAN StaticLink);
|
|
#endif
|
|
|
|
#if defined (_X86_)
|
|
extern PVOID LdrpLockPrefixTable;
|
|
|
|
|
|
// Specify address of kernel32 lock prefixes
|
|
|
|
IMAGE_LOAD_CONFIG_DIRECTORY _load_config_used = {
|
|
0, // Reserved
|
|
0, // Reserved
|
|
0, // Reserved
|
|
0, // Reserved
|
|
0, // GlobalFlagsClear
|
|
0, // GlobalFlagsSet
|
|
0, // CriticalSectionTimeout (milliseconds)
|
|
0, // DeCommitFreeBlockThreshold
|
|
0, // DeCommitTotalFreeThreshold
|
|
(ULONG_PTR)&LdrpLockPrefixTable, // LockPrefixTable
|
|
0, 0, 0, 0, 0, 0, 0 // Reserved
|
|
};
|
|
|
|
void LdrpValidateImageForMp(IN PLDR_DATA_TABLE_ENTRY LdrDataTableEntry)
|
|
{
|
|
PIMAGE_LOAD_CONFIG_DIRECTORY ImageConfigData;
|
|
ULONG i;
|
|
PUCHAR * pb;
|
|
ULONG ErrorParameters;
|
|
ULONG ErrorResponse;
|
|
|
|
|
|
// If we are on an MP system and the DLL has image config info, check to see
|
|
// if it has a lock prefix table and make sure the locks have not been converted
|
|
// to NOPs
|
|
|
|
|
|
ImageConfigData = RtlImageDirectoryEntryToData(LdrDataTableEntry->DllBase,
|
|
TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG,
|
|
&i
|
|
);
|
|
|
|
if (ImageConfigData != NULL &&
|
|
i == sizeof(*ImageConfigData) &&
|
|
ImageConfigData->LockPrefixTable) {
|
|
pb = (PUCHAR *)ImageConfigData->LockPrefixTable;
|
|
while (*pb) {
|
|
if (**pb == (UCHAR)0x90) {
|
|
if (LdrpNumberOfProcessors > 1) {
|
|
// Hard error time. One of the know DLL's is corrupt !
|
|
ErrorParameters = (ULONG)&LdrDataTableEntry->BaseDllName;
|
|
NtRaiseHardError(
|
|
STATUS_IMAGE_MP_UP_MISMATCH,
|
|
1,
|
|
1,
|
|
&ErrorParameters,
|
|
OptionOk,
|
|
&ErrorResponse
|
|
);
|
|
|
|
if (LdrpInLdrInit) {
|
|
LdrpFatalHardErrorCount++;
|
|
}
|
|
|
|
}
|
|
}
|
|
pb++;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
NTSTATUS
|
|
LdrpWalkImportDescriptor(
|
|
IN PWSTR DllPath OPTIONAL,
|
|
IN PLDR_DATA_TABLE_ENTRY LdrDataTableEntry
|
|
);
|
|
|
|
VOID
|
|
LdrpDphInitializeTargetDll(
|
|
PLDR_DATA_TABLE_ENTRY LoadedDllData
|
|
);
|
|
|
|
NTSTATUS
|
|
LdrpLoadImportModule(
|
|
IN PWSTR DllPath OPTIONAL,
|
|
IN LPSTR ImportName,
|
|
IN PVOID DllBaseImporter,
|
|
OUT PLDR_DATA_TABLE_ENTRY * DataTableEntry,
|
|
OUT PBOOLEAN AlreadyLoaded
|
|
)
|
|
{
|
|
NTSTATUS st;
|
|
ANSI_STRING AnsiString;
|
|
PUNICODE_STRING ImportDescriptorName_U;
|
|
BOOLEAN Wx86KnownDll = FALSE;
|
|
|
|
ImportDescriptorName_U = &NtCurrentTeb()->StaticUnicodeString;
|
|
RtlInitAnsiString(&AnsiString, ImportName);
|
|
st = RtlAnsiStringToUnicodeString(ImportDescriptorName_U, &AnsiString, FALSE);
|
|
if (!NT_SUCCESS(st)) {
|
|
return st;
|
|
}
|
|
|
|
#if defined (WX86)
|
|
if (Wx86ProcessInit) {
|
|
Wx86KnownDll = RtlImageNtHeader(DllBaseImporter)->FileHeader.Machine == IMAGE_FILE_MACHINE_I386;
|
|
}
|
|
#endif
|
|
|
|
|
|
// BUGBUG If a DLL refers to itself we will recurse and blow up
|
|
|
|
|
|
// Check the LdrTable to see if Dll has already been mapped into this image. If not, map it.
|
|
if (LdrpCheckForLoadedDll(DllPath, ImportDescriptorName_U, TRUE, Wx86KnownDll, DataTableEntry)) {
|
|
*AlreadyLoaded = TRUE;
|
|
} else {
|
|
*AlreadyLoaded = FALSE;
|
|
|
|
st = LdrpMapDll(DllPath, ImportDescriptorName_U->Buffer, NULL, TRUE, Wx86KnownDll, DataTableEntry);
|
|
if (!NT_SUCCESS(st)) {
|
|
return st;
|
|
}
|
|
st = LdrpWalkImportDescriptor(DllPath, *DataTableEntry);
|
|
if (!NT_SUCCESS(st)) {
|
|
InsertTailList(&NtCurrentPeb()->Ldr->InInitializationOrderModuleList, &(*DataTableEntry)->InInitializationOrderLinks);
|
|
}
|
|
}
|
|
|
|
return st;
|
|
}
|
|
|
|
|
|
NTSTATUS LdrpWalkImportDescriptor(IN PWSTR DllPath OPTIONAL, IN PLDR_DATA_TABLE_ENTRY LdrDataTableEntry)
|
|
/*++
|
|
Routine Description:
|
|
This is a recursive routine which walks the Import Descriptor Table and loads each DLL that is referenced.
|
|
Arguments:
|
|
DllPath - Supplies an optional search path to be used to locate the DLL.
|
|
LdrDataTableEntry - Supplies the address of the data table entry to initialize.
|
|
Return Value:
|
|
Status value.
|
|
--*/
|
|
{
|
|
ULONG i, ImportSize, NewImportSize;
|
|
BOOLEAN AlreadyLoaded, SnapForwardersOnly, StaleBinding;
|
|
PLDR_DATA_TABLE_ENTRY DataTableEntry, FwdDataTableEntry;
|
|
PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
|
|
PIMAGE_BOUND_IMPORT_DESCRIPTOR NewImportDescriptor;
|
|
PIMAGE_BOUND_FORWARDER_REF NewImportForwarder;
|
|
PSZ ImportName, NewImportName, NewFwdImportName, NewImportStringBase;
|
|
NTSTATUS st;
|
|
PIMAGE_NT_HEADERS ExportNtHeaders;
|
|
PVOID ImportBase;
|
|
ULONG RegionSize;
|
|
PIMAGE_THUNK_DATA Thunk, Name;
|
|
PIMAGE_THUNK_DATA FirstThunk;
|
|
PPEB Peb = NtCurrentPeb();
|
|
|
|
|
|
// See if there is a bound import table. If so, walk that to
|
|
// verify if the binding is good. If so, then succeed with
|
|
// having touched the .idata section, as all the information
|
|
// in the bound imports table is stored in the header. If any
|
|
// are stale, then fall out into the unbound case.
|
|
|
|
NewImportDescriptor = (PIMAGE_BOUND_IMPORT_DESCRIPTOR)RtlImageDirectoryEntryToData(
|
|
LdrDataTableEntry->DllBase,
|
|
TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT,
|
|
&NewImportSize
|
|
);
|
|
|
|
ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)RtlImageDirectoryEntryToData(
|
|
LdrDataTableEntry->DllBase,
|
|
TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_IMPORT,
|
|
&ImportSize
|
|
);
|
|
|
|
if (NewImportDescriptor) {
|
|
NewImportStringBase = (LPSTR)NewImportDescriptor;
|
|
while (NewImportDescriptor->OffsetModuleName) {
|
|
NewImportName = NewImportStringBase + NewImportDescriptor->OffsetModuleName;
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: %wZ bound to %s\n",
|
|
&LdrDataTableEntry->BaseDllName,
|
|
NewImportName
|
|
);
|
|
}
|
|
|
|
st = LdrpLoadImportModule(DllPath,
|
|
NewImportName,
|
|
LdrDataTableEntry->DllBase,
|
|
&DataTableEntry,
|
|
&AlreadyLoaded
|
|
);
|
|
if (!NT_SUCCESS(st)) {
|
|
return st;
|
|
}
|
|
|
|
// Add to initialization list.
|
|
if (!AlreadyLoaded) {
|
|
InsertTailList(&Peb->Ldr->InInitializationOrderModuleList,
|
|
&DataTableEntry->InInitializationOrderLinks);
|
|
}
|
|
|
|
#if defined (_ALPHA_) && defined (WX86)
|
|
ExportNtHeaders = RtlImageNtHeader(LdrDataTableEntry->DllBase);
|
|
|
|
if ((ExportNtHeaders->OptionalHeader.SectionAlignment < PAGE_SIZE) &&
|
|
(ExportNtHeaders->FileHeader.Machine == IMAGE_FILE_MACHINE_I386)) {
|
|
if (LdrpWx86DllHasRelocatedSharedSection(LdrDataTableEntry->DllBase)) {
|
|
DataTableEntry->Flags |= LDRP_IMAGE_NOT_AT_BASE;
|
|
}
|
|
}
|
|
#endif // defined (_ALPHA_) && defined (WX86)
|
|
|
|
if (NewImportDescriptor->TimeDateStamp != DataTableEntry->TimeDateStamp ||
|
|
(DataTableEntry->Flags & LDRP_IMAGE_NOT_AT_BASE)) {
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: %wZ has stale binding to %s\n",
|
|
&LdrDataTableEntry->BaseDllName,
|
|
NewImportName
|
|
);
|
|
}
|
|
StaleBinding = TRUE;
|
|
} else {
|
|
#if DBG
|
|
LdrpSnapBypass++;
|
|
#endif
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: %wZ has correct binding to %s\n",
|
|
&LdrDataTableEntry->BaseDllName,
|
|
NewImportName
|
|
);
|
|
}
|
|
StaleBinding = FALSE;
|
|
}
|
|
|
|
NewImportForwarder = (PIMAGE_BOUND_FORWARDER_REF)(NewImportDescriptor + 1);
|
|
for (i = 0; i < NewImportDescriptor->NumberOfModuleForwarderRefs; i++) {
|
|
NewFwdImportName = NewImportStringBase + NewImportForwarder->OffsetModuleName;
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: %wZ bound to %s via forwarder(s) from %wZ\n",
|
|
&LdrDataTableEntry->BaseDllName,
|
|
NewFwdImportName,
|
|
&DataTableEntry->BaseDllName
|
|
);
|
|
}
|
|
|
|
st = LdrpLoadImportModule(DllPath,
|
|
NewFwdImportName,
|
|
LdrDataTableEntry->DllBase,
|
|
&FwdDataTableEntry,
|
|
&AlreadyLoaded
|
|
);
|
|
if (NT_SUCCESS(st)) {
|
|
if (!AlreadyLoaded) {
|
|
InsertTailList(&Peb->Ldr->InInitializationOrderModuleList,
|
|
&FwdDataTableEntry->InInitializationOrderLinks);
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(st) ||
|
|
NewImportForwarder->TimeDateStamp != FwdDataTableEntry->TimeDateStamp ||
|
|
(FwdDataTableEntry->Flags & LDRP_IMAGE_NOT_AT_BASE)) {
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: %wZ has stale binding to %s\n",
|
|
&LdrDataTableEntry->BaseDllName,
|
|
NewFwdImportName
|
|
);
|
|
}
|
|
StaleBinding = TRUE;
|
|
} else {
|
|
#if DBG
|
|
LdrpSnapBypass++;
|
|
#endif
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: %wZ has correct binding to %s\n",
|
|
&LdrDataTableEntry->BaseDllName,
|
|
NewFwdImportName
|
|
);
|
|
}
|
|
}
|
|
|
|
NewImportForwarder += 1;
|
|
}
|
|
NewImportDescriptor = (PIMAGE_BOUND_IMPORT_DESCRIPTOR)NewImportForwarder;
|
|
|
|
if (StaleBinding) {
|
|
#if DBG
|
|
LdrpNormalSnap++;
|
|
#endif
|
|
|
|
// Find the unbound import descriptor that matches this bound
|
|
// import descriptor
|
|
|
|
|
|
ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)RtlImageDirectoryEntryToData(
|
|
LdrDataTableEntry->DllBase,
|
|
TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_IMPORT,
|
|
&ImportSize
|
|
);
|
|
|
|
while (ImportDescriptor->Name) {
|
|
ImportName = (PSZ)((ULONG_PTR)LdrDataTableEntry->DllBase + ImportDescriptor->Name);
|
|
if (!_stricmp(ImportName, NewImportName)) {
|
|
break;
|
|
}
|
|
|
|
ImportDescriptor += 1;
|
|
}
|
|
|
|
if (!ImportDescriptor->Name) {
|
|
return STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: Stale Bind %s from %wZ\n", ImportName, &LdrDataTableEntry->BaseDllName);
|
|
}
|
|
|
|
st = LdrpSnapIAT(DataTableEntry, LdrDataTableEntry, ImportDescriptor, FALSE);
|
|
if (!NT_SUCCESS(st)) {
|
|
return st;
|
|
}
|
|
}
|
|
}
|
|
} else
|
|
if (ImportDescriptor) {
|
|
|
|
// For each DLL used by this DLL, load the dll. Then snap
|
|
// the IAT, and call the DLL's init routine.
|
|
|
|
|
|
while (ImportDescriptor->Name && ImportDescriptor->FirstThunk) {
|
|
ImportName = (PSZ)((ULONG_PTR)LdrDataTableEntry->DllBase + ImportDescriptor->Name);
|
|
|
|
|
|
// check for import that has no references
|
|
|
|
FirstThunk = (PIMAGE_THUNK_DATA)((ULONG_PTR)LdrDataTableEntry->DllBase + ImportDescriptor->FirstThunk);
|
|
if (!FirstThunk->u1.Function) {
|
|
goto skipimport;
|
|
}
|
|
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: %s used by %wZ\n", ImportName, &LdrDataTableEntry->BaseDllName);
|
|
}
|
|
|
|
|
|
st = LdrpLoadImportModule(DllPath, ImportName, LdrDataTableEntry->DllBase, &DataTableEntry, &AlreadyLoaded);
|
|
if (!NT_SUCCESS(st)) {
|
|
return st;
|
|
}
|
|
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: Snapping imports for %wZ from %s\n", &LdrDataTableEntry->BaseDllName, ImportName);
|
|
}
|
|
|
|
|
|
// If the image has been bound and the import date stamp
|
|
// matches the date time stamp in the export modules header,
|
|
// and the image was mapped at it's prefered base address,
|
|
// then we are done.
|
|
|
|
|
|
SnapForwardersOnly = FALSE;
|
|
|
|
if (ImportDescriptor->OriginalFirstThunk) {
|
|
if (ImportDescriptor->TimeDateStamp &&
|
|
ImportDescriptor->TimeDateStamp == DataTableEntry->TimeDateStamp &&
|
|
(!DataTableEntry->Flags & LDRP_IMAGE_NOT_AT_BASE)) {
|
|
#if DBG
|
|
LdrpSnapBypass++;
|
|
#endif
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: Snap bypass %s from %wZ\n", ImportName, &LdrDataTableEntry->BaseDllName);
|
|
}
|
|
|
|
if (ImportDescriptor->ForwarderChain == -1) {
|
|
goto bypass_snap;
|
|
}
|
|
|
|
SnapForwardersOnly = TRUE;
|
|
|
|
}
|
|
}
|
|
#if DBG
|
|
LdrpNormalSnap++;
|
|
#endif
|
|
|
|
// Add to initialization list.
|
|
if (!AlreadyLoaded) {
|
|
InsertTailList(&Peb->Ldr->InInitializationOrderModuleList, &DataTableEntry->InInitializationOrderLinks);
|
|
}
|
|
st = LdrpSnapIAT(DataTableEntry, LdrDataTableEntry, ImportDescriptor, SnapForwardersOnly);
|
|
if (!NT_SUCCESS(st)) {
|
|
return st;
|
|
}
|
|
AlreadyLoaded = TRUE;
|
|
bypass_snap:
|
|
|
|
// Add to initialization list.
|
|
if (!AlreadyLoaded) {
|
|
InsertTailList(&Peb->Ldr->InInitializationOrderModuleList, &DataTableEntry->InInitializationOrderLinks);
|
|
}
|
|
|
|
skipimport:
|
|
++ImportDescriptor;
|
|
}
|
|
}
|
|
|
|
if (Peb->NtGlobalFlag & FLG_HEAP_ENABLE_TAG_BY_DLL) {
|
|
PVOID IATBase;
|
|
SIZE_T BigIATSize;
|
|
ULONG LittleIATSize;
|
|
PVOID * ProcAddresses;
|
|
ULONG NumberOfProcAddresses;
|
|
ULONG OldProtect;
|
|
USHORT TagIndex;
|
|
|
|
// Determine the location and size of the IAT. If found, scan the
|
|
// IAT address to see if any are pointing to RtlAllocateHeap. If so
|
|
// replace when with a pointer to a unique thunk function that will
|
|
// replace the tag with a unique tag for this image.
|
|
|
|
IATBase = RtlImageDirectoryEntryToData(LdrDataTableEntry->DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_IAT, &LittleIATSize);
|
|
BigIATSize = LittleIATSize;
|
|
if (IATBase != NULL) {
|
|
st = NtProtectVirtualMemory(NtCurrentProcess(), &IATBase, &BigIATSize, PAGE_READWRITE, &OldProtect);
|
|
if (!NT_SUCCESS(st)) {
|
|
DbgPrint("LDR: Unable to unprotect IAT to enable tagging by DLL.\n");
|
|
} else {
|
|
ProcAddresses = (PVOID *)IATBase;
|
|
NumberOfProcAddresses = (ULONG)(BigIATSize / sizeof(PVOID));
|
|
while (NumberOfProcAddresses--) {
|
|
if (*ProcAddresses == RtlAllocateHeap) {
|
|
*ProcAddresses = LdrpDefineDllTag(LdrDataTableEntry->BaseDllName.Buffer, &TagIndex);
|
|
if (*ProcAddresses == NULL) {
|
|
*ProcAddresses = RtlAllocateHeap;
|
|
}
|
|
}
|
|
|
|
ProcAddresses += 1;
|
|
}
|
|
|
|
NtProtectVirtualMemory(NtCurrentProcess(), &IATBase, &BigIATSize, OldProtect, &OldProtect);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Check if we need to redirect some imports in case page heap
|
|
// is enabled and we target specific dlls.
|
|
|
|
if (Peb->NtGlobalFlag & FLG_HEAP_PAGE_ALLOCS) {
|
|
LdrpDphInitializeTargetDll(LdrDataTableEntry);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
ULONG LdrpClearLoadInProgress(VOID)
|
|
{
|
|
PLIST_ENTRY Head, Next;
|
|
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
|
|
ULONG i;
|
|
|
|
Head = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList;
|
|
Next = Head->Flink;
|
|
i = 0;
|
|
while (Next != Head) {
|
|
LdrDataTableEntry = CONTAINING_RECORD(Next, LDR_DATA_TABLE_ENTRY, InInitializationOrderLinks);
|
|
LdrDataTableEntry->Flags &= ~LDRP_LOAD_IN_PROGRESS;
|
|
|
|
// return the number of entries that have not been processed, but have init routines
|
|
if (!(LdrDataTableEntry->Flags & LDRP_ENTRY_PROCESSED) && LdrDataTableEntry->EntryPoint) {
|
|
i++;
|
|
}
|
|
|
|
Next = Next->Flink;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
|
|
NTSTATUS LdrpRunInitializeRoutines(IN PCONTEXT Context OPTIONAL)
|
|
{
|
|
PLIST_ENTRY Head, Next;
|
|
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
|
|
PLDR_DATA_TABLE_ENTRY * LdrDataTableBase;
|
|
PDLL_INIT_ROUTINE InitRoutine;
|
|
BOOLEAN InitStatus;
|
|
ULONG NumberOfRoutines;
|
|
ULONG i;
|
|
NTSTATUS Status;
|
|
ULONG BreakOnDllLoad;
|
|
|
|
// Run the Init routines
|
|
|
|
// capture the entries that have init routines
|
|
NumberOfRoutines = LdrpClearLoadInProgress();
|
|
if (NumberOfRoutines) {
|
|
LdrDataTableBase = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TEMP_TAG), NumberOfRoutines * sizeof(LdrDataTableBase));
|
|
if (!LdrDataTableBase) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
} else {
|
|
LdrDataTableBase = NULL;
|
|
}
|
|
|
|
Head = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList;
|
|
Next = Head->Flink;
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: Real INIT LIST\n");
|
|
}
|
|
|
|
i = 0;
|
|
while (Next != Head) {
|
|
LdrDataTableEntry = CONTAINING_RECORD(Next, LDR_DATA_TABLE_ENTRY, InInitializationOrderLinks);
|
|
|
|
#if defined (WX86)
|
|
if (Wx86ProcessInit && !(LdrDataTableEntry->Flags & LDRP_ENTRY_PROCESSED)) {
|
|
PIMAGE_NT_HEADERS NtHeaders;
|
|
NtHeaders = RtlImageNtHeader(LdrDataTableEntry->DllBase);
|
|
if (NtHeaders->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) {
|
|
LdrpWx86DllMapNotify(LdrDataTableEntry->DllBase, TRUE);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!(LdrDataTableEntry->Flags & LDRP_ENTRY_PROCESSED) && LdrDataTableEntry->EntryPoint) {
|
|
LdrDataTableBase[i] = LdrDataTableEntry;
|
|
if (ShowSnaps) {
|
|
DbgPrint(" %wZ init routine %x\n", &LdrDataTableEntry->FullDllName, LdrDataTableEntry->EntryPoint);
|
|
}
|
|
|
|
i++;
|
|
}
|
|
LdrDataTableEntry->Flags |= LDRP_ENTRY_PROCESSED;
|
|
|
|
Next = Next->Flink;
|
|
}
|
|
|
|
if (!LdrDataTableBase) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
try {
|
|
i = 0;
|
|
while (i < NumberOfRoutines) {
|
|
LdrDataTableEntry = LdrDataTableBase[i];
|
|
i++;
|
|
InitRoutine = (PDLL_INIT_ROUTINE)LdrDataTableEntry->EntryPoint;
|
|
|
|
// Walk through the entire list looking for un-processed entries.
|
|
// For each entry, set the processed flag and optionally call it's init routine
|
|
|
|
BreakOnDllLoad = 0;
|
|
#if DBG
|
|
if (TRUE) {
|
|
#else
|
|
if (NtCurrentPeb()->BeingDebugged || NtCurrentPeb()->ReadImageFileExecOptions) {
|
|
#endif
|
|
Status = LdrQueryImageFileExecutionOptions(&LdrDataTableEntry->BaseDllName,
|
|
L"BreakOnDllLoad",
|
|
REG_DWORD,
|
|
&BreakOnDllLoad,
|
|
sizeof(BreakOnDllLoad),
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
BreakOnDllLoad = 0;
|
|
}
|
|
}
|
|
|
|
if (BreakOnDllLoad) {
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: %wZ loaded.", &LdrDataTableEntry->BaseDllName);
|
|
DbgPrint(" - About to call init routine at %lx\n", InitRoutine);
|
|
}
|
|
DbgBreakPoint();
|
|
} else if (ShowSnaps) {
|
|
if (InitRoutine) {
|
|
DbgPrint("LDR: %wZ loaded.", &LdrDataTableEntry->BaseDllName);
|
|
DbgPrint(" - Calling init routine at %lx\n", InitRoutine);
|
|
}
|
|
}
|
|
|
|
if (InitRoutine) {
|
|
// If the DLL has TLS data, then call the optional initializers
|
|
if (LdrDataTableEntry->TlsIndex && Context) {
|
|
LdrpCallTlsInitializers(LdrDataTableEntry->DllBase, DLL_PROCESS_ATTACH);
|
|
}
|
|
|
|
#if defined (WX86)
|
|
if (!Wx86ProcessInit ||
|
|
LdrpRunWx86DllEntryPoint(InitRoutine, &InitStatus, LdrDataTableEntry->DllBase, DLL_PROCESS_ATTACH, Context) == STATUS_IMAGE_MACHINE_TYPE_MISMATCH) {
|
|
InitStatus = LdrpCallInitRoutine(InitRoutine, LdrDataTableEntry->DllBase, DLL_PROCESS_ATTACH, Context);
|
|
}
|
|
#else
|
|
InitStatus = LdrpCallInitRoutine(InitRoutine, LdrDataTableEntry->DllBase, DLL_PROCESS_ATTACH, Context);
|
|
#endif
|
|
|
|
LdrDataTableEntry->Flags |= LDRP_PROCESS_ATTACH_CALLED;
|
|
|
|
if (!InitStatus) {
|
|
return STATUS_DLL_INIT_FAILED;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (LdrpImageHasTls && Context) {
|
|
LdrpCallTlsInitializers(NtCurrentPeb()->ImageBaseAddress, DLL_PROCESS_ATTACH);// If the image has tls than call its initializers
|
|
}
|
|
} finally {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, LdrDataTableBase);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
BOOLEAN LdrpCheckForLoadedDll(IN PWSTR DllPath OPTIONAL,
|
|
IN PUNICODE_STRING DllName,
|
|
IN BOOLEAN StaticLink,
|
|
IN BOOLEAN Wx86KnownDll,
|
|
OUT PLDR_DATA_TABLE_ENTRY * LdrDataTableEntry)
|
|
/*++
|
|
Routine Description:
|
|
This function scans the loader data table looking to see if the specified DLL has already been mapped into the image.
|
|
If the dll has been loaded, the address of its data table entry is returned.
|
|
Arguments:
|
|
DllPath - Supplies an optional search path used to locate the DLL.
|
|
DllName - Supplies the name to search for.
|
|
StaticLink - TRUE if performing a static link.
|
|
Wx86KnownDll - TRUE, treat Importer as x86
|
|
LdrDataTableEntry - Returns the address of the loader data table entry that describes the first dll section that implements the dll.
|
|
Return Value:
|
|
TRUE- The dll is already loaded. The address of the data table entries that implement the dll, and the number of data table entries are returned.
|
|
FALSE - The dll is not already mapped.
|
|
--*/
|
|
{
|
|
BOOLEAN Result;
|
|
PLDR_DATA_TABLE_ENTRY Entry;
|
|
PLIST_ENTRY Head, Next;
|
|
UNICODE_STRING FullDllName;
|
|
HANDLE DllFile;
|
|
BOOLEAN HardCodedPath;
|
|
PWCH p;
|
|
WCHAR wch;
|
|
ULONG i;
|
|
CHAR FullDllNameBuffer[530 + sizeof(UNICODE_NULL)];
|
|
ULONG Length = 0;
|
|
PWCH src, dest;
|
|
|
|
if (!DllName->Buffer || !DllName->Buffer[0]) {
|
|
return FALSE;
|
|
}
|
|
|
|
#if defined (WX86)
|
|
if (Wx86ProcessInit) {
|
|
FullDllName.Buffer = (PWCHAR)FullDllNameBuffer;
|
|
FullDllName.MaximumLength = sizeof(FullDllNameBuffer);
|
|
FullDllName.Length = 0;
|
|
Entry = LdrpWx86CheckForLoadedDll(DllPath, DllName, Wx86KnownDll, &FullDllName);
|
|
if (Entry) {
|
|
RtlCopyUnicodeString(DllName, &FullDllName);
|
|
*LdrDataTableEntry = Entry;
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// for static links, just go to the hash table
|
|
staticlink:
|
|
if (StaticLink) {
|
|
i = LDRP_COMPUTE_HASH_INDEX(DllName->Buffer[0]);
|
|
Head = &LdrpHashTable[i];
|
|
Next = Head->Flink;
|
|
while (Next != Head) {
|
|
Entry = CONTAINING_RECORD(Next, LDR_DATA_TABLE_ENTRY, HashLinks);
|
|
#if DBG
|
|
LdrpCompareCount++;
|
|
#endif
|
|
if (RtlEqualUnicodeString(DllName, &Entry->BaseDllName, TRUE)) {
|
|
*LdrDataTableEntry = Entry;
|
|
return TRUE;
|
|
}
|
|
Next = Next->Flink;
|
|
}
|
|
}
|
|
|
|
if (StaticLink) {
|
|
return FALSE;
|
|
}
|
|
|
|
// If the dll name contained a hard coded path (dynamic link only),
|
|
// then the fully qualified name needs to be compared to make sure we have the correct dll.
|
|
p = DllName->Buffer;
|
|
HardCodedPath = FALSE;
|
|
while (*p) {
|
|
wch = *p++;
|
|
if (wch == (WCHAR)'\\' || wch == (WCHAR)'/') {
|
|
HardCodedPath = TRUE;
|
|
|
|
// We have a hard coded path, so we have to search path for the DLL. We need the full DLL name so we can
|
|
FullDllName.Buffer = (WCHAR *)FullDllNameBuffer;
|
|
Length = RtlDosSearchPath_U(ARGUMENT_PRESENT(DllPath) ? DllPath : LdrpDefaultPath.Buffer,
|
|
DllName->Buffer,
|
|
NULL,
|
|
sizeof(FullDllNameBuffer) - sizeof(UNICODE_NULL),
|
|
FullDllName.Buffer,
|
|
NULL);
|
|
if (!Length || Length > sizeof(FullDllNameBuffer) - sizeof(UNICODE_NULL)) {
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: LdrpCheckForLoadedDll - Unable To Locate ");
|
|
DbgPrint("%ws from %ws\n", DllName->Buffer, ARGUMENT_PRESENT(DllPath) ? DllPath : LdrpDefaultPath.Buffer);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
FullDllName.Length = (USHORT)Length;
|
|
FullDllName.MaximumLength = FullDllName.Length + (USHORT)sizeof(UNICODE_NULL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if this is a dynamic load lib, and there is not a hard coded path, then go to the static lib hash table for resolution
|
|
if (!HardCodedPath) {
|
|
StaticLink = TRUE;
|
|
goto staticlink;
|
|
}
|
|
|
|
Result = FALSE;
|
|
Head = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
|
|
Next = Head->Flink;
|
|
|
|
while (Next != Head) {
|
|
Entry = CONTAINING_RECORD(Next, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
|
|
Next = Next->Flink;
|
|
|
|
// when we unload, the memory order links flink field is nulled.
|
|
// this is used to skip the entry pending list removal.
|
|
if (!Entry->InMemoryOrderLinks.Flink) {
|
|
continue;
|
|
}
|
|
|
|
if (RtlEqualUnicodeString(&FullDllName, &Entry->FullDllName, TRUE)) {
|
|
Result = TRUE;
|
|
*LdrDataTableEntry = Entry;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!Result) {
|
|
// no names matched. This might be a long short name mismatch or any kind of alias pathname.
|
|
// Deal with this by opening and mapping full dll name and then repeat the scan this time checking for timedatestamp matches
|
|
HANDLE File;
|
|
HANDLE Section;
|
|
NTSTATUS st;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
PVOID ViewBase;
|
|
SIZE_T ViewSize;
|
|
PIMAGE_NT_HEADERS NtHeadersSrc, NtHeadersE;
|
|
UNICODE_STRING NtFileName;
|
|
|
|
if (!RtlDosPathNameToNtPathName_U(FullDllName.Buffer, &NtFileName, NULL, NULL)) {
|
|
goto alldone;
|
|
}
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes, &NtFileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
st = NtOpenFile(&File,
|
|
SYNCHRONIZE | FILE_EXECUTE,
|
|
&ObjectAttributes,
|
|
&IoStatus,
|
|
FILE_SHARE_READ | FILE_SHARE_DELETE,
|
|
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
|
|
RtlFreeHeap(RtlProcessHeap(), 0, NtFileName.Buffer);
|
|
if (!NT_SUCCESS(st)) {
|
|
goto alldone;
|
|
}
|
|
|
|
st = NtCreateSection(&Section,
|
|
SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_MAP_WRITE,
|
|
NULL,
|
|
NULL,
|
|
PAGE_EXECUTE,
|
|
SEC_COMMIT,
|
|
File);
|
|
NtClose(File);
|
|
if (!NT_SUCCESS(st)) {
|
|
goto alldone;
|
|
}
|
|
|
|
ViewBase = NULL;
|
|
ViewSize = 0;
|
|
st = NtMapViewOfSection(Section, NtCurrentProcess(), (PVOID *)&ViewBase, 0L, 0L, NULL, &ViewSize, ViewShare, 0L, PAGE_EXECUTE);
|
|
NtClose(Section);
|
|
if (!NT_SUCCESS(st)) {
|
|
goto alldone;
|
|
}
|
|
|
|
// The section is mapped. Now find the headers
|
|
NtHeadersSrc = RtlImageNtHeader(ViewBase);
|
|
if (!NtHeadersSrc) {
|
|
NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
|
|
goto alldone;
|
|
}
|
|
Head = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
|
|
Next = Head->Flink;
|
|
|
|
while (Next != Head) {
|
|
Entry = CONTAINING_RECORD(Next, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
|
|
Next = Next->Flink;
|
|
|
|
// when we unload, the memory order links flink field is nulled.
|
|
// this is used to skip the entry pending list removal.
|
|
if (!Entry->InMemoryOrderLinks.Flink) {
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
if (Entry->TimeDateStamp == NtHeadersSrc->FileHeader.TimeDateStamp &&
|
|
Entry->SizeOfImage == NtHeadersSrc->OptionalHeader.SizeOfImage) {
|
|
// there is a very good chance we have an image match.
|
|
// Check the entire file header and optional header. If they match, declare this a match
|
|
NtHeadersE = RtlImageNtHeader(Entry->DllBase);
|
|
|
|
// Now that it looks like we have a match, compare volume serial number's and file index's
|
|
if (RtlCompareMemory(NtHeadersE, NtHeadersSrc, sizeof(*NtHeadersE))) {
|
|
st = NtAreMappedFilesTheSame(Entry->DllBase, ViewBase);
|
|
if (!NT_SUCCESS(st)) {
|
|
continue;
|
|
} else {
|
|
Result = TRUE;
|
|
*LdrDataTableEntry = Entry;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
break;
|
|
}
|
|
}
|
|
NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
|
|
}
|
|
|
|
alldone:
|
|
return Result;
|
|
}
|
|
|
|
|
|
BOOLEAN LdrpCheckForLoadedDllHandle(IN PVOID DllHandle, OUT PLDR_DATA_TABLE_ENTRY * LdrDataTableEntry)
|
|
/*++
|
|
Routine Description:
|
|
This function scans the loader data table looking to see if the specified DLL has already been mapped into the image address space.
|
|
If the dll has been loaded, the address of its data table entry that describes the dll is returned.
|
|
Arguments:
|
|
DllHandle - Supplies the DllHandle of the DLL being searched for.
|
|
LdrDataTableEntry - Returns the address of the loader data table entry that describes the dll.
|
|
Return Value:
|
|
TRUE- The dll is loaded. The address of the data table entry is returned.
|
|
FALSE - The dll is not loaded.
|
|
--*/
|
|
{
|
|
PLDR_DATA_TABLE_ENTRY Entry;
|
|
PLIST_ENTRY Head, Next;
|
|
|
|
if (LdrpLoadedDllHandleCache && (PVOID)LdrpLoadedDllHandleCache->DllBase == DllHandle) {
|
|
*LdrDataTableEntry = LdrpLoadedDllHandleCache;
|
|
return TRUE;
|
|
}
|
|
|
|
Head = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
|
|
Next = Head->Flink;
|
|
|
|
while (Next != Head) {
|
|
Entry = CONTAINING_RECORD(Next, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
|
|
Next = Next->Flink;
|
|
|
|
// when we unload, the memory order links flink field is nulled.
|
|
// this is used to skip the entry pending list removal.
|
|
if (!Entry->InMemoryOrderLinks.Flink) {
|
|
continue;
|
|
}
|
|
|
|
if (DllHandle == (PVOID)Entry->DllBase) {
|
|
LdrpLoadedDllHandleCache = Entry;
|
|
*LdrDataTableEntry = Entry;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LdrpMapDll(
|
|
IN PWSTR DllPath OPTIONAL,
|
|
IN PWSTR DllName,
|
|
IN PULONG DllCharacteristics OPTIONAL,
|
|
IN BOOLEAN StaticLink,
|
|
IN BOOLEAN Wx86KnownDll,
|
|
OUT PLDR_DATA_TABLE_ENTRY * LdrDataTableEntry
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine maps the DLL into the users address space.
|
|
Arguments:
|
|
DllPath - Supplies an optional search path to be used to locate the DLL.
|
|
DllName - Supplies the name of the DLL to load.
|
|
StaticLink - TRUE if this DLL has a static link to it.
|
|
Wx86KnownDll - TRUE, treat Importer as x86
|
|
LdrDataTableEntry - Supplies the address of the data table entry.
|
|
Return Value:
|
|
Status value.
|
|
--*/
|
|
{
|
|
NTSTATUS st;
|
|
PVOID ViewBase;
|
|
PTEB Teb = NtCurrentTeb();
|
|
SIZE_T ViewSize;
|
|
HANDLE Section, DllFile;
|
|
UNICODE_STRING FullDllName, BaseDllName;
|
|
UNICODE_STRING NtFileName;
|
|
PLDR_DATA_TABLE_ENTRY Entry;
|
|
PIMAGE_NT_HEADERS NtHeaders;
|
|
PVOID ArbitraryUserPointer;
|
|
BOOLEAN KnownDll;
|
|
UNICODE_STRING CollidingDll;
|
|
PUCHAR ImageBase, ImageBounds, ScanBase, ScanTop;
|
|
PLDR_DATA_TABLE_ENTRY ScanEntry;
|
|
PLIST_ENTRY ScanHead, ScanNext;
|
|
BOOLEAN CollidingDllFound;
|
|
NTSTATUS ErrorStatus;
|
|
ULONG_PTR ErrorParameters[2];
|
|
ULONG ErrorResponse;
|
|
#if defined (WX86)
|
|
BOOLEAN Wx86Plugin = FALSE;
|
|
#endif
|
|
|
|
// Get section handle of DLL being snapped
|
|
|
|
#if LDRDBG
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: LdrpMapDll: Image Name %ws, Search Path %ws\n", DllName, ARGUMENT_PRESENT(DllPath) ? DllPath : L"");
|
|
}
|
|
#endif
|
|
|
|
Entry = NULL;
|
|
KnownDll = FALSE;
|
|
Section = NULL;
|
|
|
|
if (LdrpKnownDllObjectDirectory && !Wx86KnownDll) {
|
|
PWCH p = DllName;
|
|
WCHAR wch;
|
|
|
|
// Skip the KnownDll check if this is an explicit path.
|
|
while (*p) {
|
|
wch = *p++;
|
|
if (wch == (WCHAR)'\\' || wch == (WCHAR)'/') {
|
|
goto SkipKnownDllCheck;
|
|
}
|
|
}
|
|
Section = LdrpCheckForKnownDll(DllName, &FullDllName, &BaseDllName);
|
|
}
|
|
|
|
SkipKnownDllCheck:
|
|
|
|
if (!Section) {
|
|
#if defined (WX86)
|
|
if (Wx86ProcessInit) {
|
|
RtlInitUnicodeString(&BaseDllName, DllName);
|
|
st = LdrpWx86MapDll(DllPath, DllCharacteristics, Wx86KnownDll, StaticLink, &BaseDllName, &Entry, &ViewSize, &Section);
|
|
if (st == STATUS_DLL_NOT_FOUND) {
|
|
goto Wx86MapDllNotFound;
|
|
}
|
|
if (!NT_SUCCESS(st)) {
|
|
return st;
|
|
}
|
|
|
|
ViewBase = Entry->DllBase;
|
|
FullDllName = Entry->FullDllName;
|
|
BaseDllName = Entry->BaseDllName;
|
|
NtHeaders = RtlImageNtHeader(ViewBase);
|
|
|
|
goto Wx86MapComplete;
|
|
} else
|
|
#endif
|
|
|
|
if (LdrpResolveDllName(DllPath, DllName, &FullDllName, &BaseDllName, &DllFile)) {
|
|
if (ShowSnaps) {
|
|
PSZ type;
|
|
type = StaticLink ? "STATIC" : "DYNAMIC";
|
|
DbgPrint("LDR: Loading (%s) %wZ\n", type, &FullDllName);
|
|
}
|
|
|
|
if (!RtlDosPathNameToNtPathName_U(FullDllName.Buffer, &NtFileName, NULL, NULL)) {
|
|
return STATUS_OBJECT_PATH_SYNTAX_BAD;
|
|
}
|
|
|
|
st = LdrpCreateDllSection(&NtFileName, DllFile, &BaseDllName, DllCharacteristics, &Section);
|
|
RtlFreeHeap(RtlProcessHeap(), 0, NtFileName.Buffer);
|
|
if (!NT_SUCCESS(st)) {
|
|
RtlFreeUnicodeString(&FullDllName);
|
|
RtlFreeUnicodeString(&BaseDllName);
|
|
return st;
|
|
}
|
|
#if DBG
|
|
LdrpSectionCreates++;
|
|
#endif
|
|
} else {
|
|
#ifdef WX86
|
|
Wx86MapDllNotFound :
|
|
#endif
|
|
if (StaticLink) {
|
|
PUNICODE_STRING ErrorStrings[2];
|
|
UNICODE_STRING ErrorDllName, ErrorDllPath;
|
|
ULONG ErrorResponse;
|
|
|
|
ErrorStrings[0] = &ErrorDllName;
|
|
ErrorStrings[1] = &ErrorDllPath;
|
|
RtlInitUnicodeString(&ErrorDllName, DllName);
|
|
RtlInitUnicodeString(&ErrorDllPath, ARGUMENT_PRESENT(DllPath) ? DllPath : LdrpDefaultPath.Buffer);
|
|
NtRaiseHardError((NTSTATUS)STATUS_DLL_NOT_FOUND, 2, 0x00000003, (PULONG_PTR)ErrorStrings, OptionOk, &ErrorResponse);
|
|
if (LdrpInLdrInit) {
|
|
LdrpFatalHardErrorCount++;
|
|
}
|
|
}
|
|
|
|
return STATUS_DLL_NOT_FOUND;
|
|
}
|
|
} else {
|
|
KnownDll = TRUE;
|
|
}
|
|
|
|
ViewBase = NULL;
|
|
ViewSize = 0;
|
|
|
|
#if DBG
|
|
LdrpSectionMaps++;
|
|
if (LdrpDisplayLoadTime) {
|
|
NtQueryPerformanceCounter(&MapBeginTime, NULL);
|
|
}
|
|
#endif
|
|
|
|
// arrange for debugger to pick up the image name
|
|
ArbitraryUserPointer = Teb->NtTib.ArbitraryUserPointer;
|
|
Teb->NtTib.ArbitraryUserPointer = (PVOID)FullDllName.Buffer;
|
|
st = NtMapViewOfSection(Section, NtCurrentProcess(), (PVOID *)&ViewBase, 0L, 0L, NULL, &ViewSize, ViewShare, 0L, PAGE_READWRITE);
|
|
Teb->NtTib.ArbitraryUserPointer = ArbitraryUserPointer;
|
|
if (!NT_SUCCESS(st)) {
|
|
NtClose(Section);
|
|
return st;
|
|
}
|
|
|
|
NtHeaders = RtlImageNtHeader(ViewBase);
|
|
if (!NtHeaders) {
|
|
NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
|
|
NtClose(Section);
|
|
return STATUS_INVALID_IMAGE_FORMAT;
|
|
}
|
|
|
|
#if DBG
|
|
if (LdrpDisplayLoadTime) {
|
|
NtQueryPerformanceCounter(&MapEndTime, NULL);
|
|
MapElapsedTime.QuadPart = MapEndTime.QuadPart - MapBeginTime.QuadPart;
|
|
DbgPrint("Map View of Section Time %ld %ws\n", MapElapsedTime.LowPart, DllName);
|
|
}
|
|
#endif
|
|
|
|
#if defined (BUILD_WOW6432)
|
|
if (NtHeaders->OptionalHeader.SectionAlignment < NATIVE_PAGE_SIZE &&
|
|
!NT_SUCCESS(LdrpWx86FormatVirtualImage((PIMAGE_NT_HEADERS32)NtHeaders, ViewBase))) {
|
|
NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
|
|
NtClose(Section);
|
|
return st;
|
|
}
|
|
#elif defined (_ALPHA_) && defined (WX86)
|
|
if ((NtHeaders->OptionalHeader.SectionAlignment < PAGE_SIZE) &&
|
|
!NT_SUCCESS(LdrpWx86FormatVirtualImage((PIMAGE_NT_HEADERS32)NtHeaders, ViewBase))) {
|
|
NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
|
|
NtClose(Section);
|
|
return STATUS_INVALID_IMAGE_FORMAT;
|
|
}
|
|
|
|
if (st == STATUS_IMAGE_MACHINE_TYPE_MISMATCH && !StaticLink) {
|
|
// Attempt to find a plugin provider dll that can thunk this interface temporarily insert the image in the loaded-module-list
|
|
Entry = LdrpAllocateDataTableEntry(ViewBase);
|
|
if (!Entry) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
Entry->Flags = 0;
|
|
Entry->LoadCount = 0;
|
|
Entry->FullDllName = FullDllName;
|
|
Entry->BaseDllName = BaseDllName;
|
|
Entry->EntryPoint = LdrpFetchAddressOfEntryPoint(Entry->DllBase);
|
|
LdrpInsertMemoryTableEntry(Entry);
|
|
|
|
st = Wx86IdentifyPlugin(ViewBase, &FullDllName);// Determine if this dll can be thunked using a plugin
|
|
|
|
// remove the image from the loaded-module-list
|
|
RemoveEntryList(&Entry->InLoadOrderLinks);
|
|
RemoveEntryList(&Entry->InMemoryOrderLinks);
|
|
RemoveEntryList(&Entry->HashLinks);
|
|
RtlFreeHeap(RtlProcessHeap(), 0, Entry);
|
|
Entry = NULL;
|
|
|
|
if (st == STATUS_SUCCESS) {
|
|
// Release the previous mapping because it's going to be reloaded via Wx86.
|
|
|
|
NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
|
|
ViewBase = NULL;
|
|
|
|
// The plugin providers must be statically bound to Wx86 or dynamically load it in their DllInit.
|
|
if (Wx86ProcessInit) {// Load the dll again using Wx86.
|
|
st = LdrpWx86MapDll(NULL,
|
|
DllCharacteristics,
|
|
!Wx86KnownDll, // swap sense so we don't get mismatch
|
|
StaticLink,
|
|
&FullDllName,
|
|
&Entry,
|
|
&ViewSize,
|
|
&Section
|
|
);
|
|
if (NT_SUCCESS(st)) {// Load and thunk the plugin dll
|
|
ViewBase = Entry->DllBase;
|
|
FullDllName = Entry->FullDllName;
|
|
BaseDllName = Entry->BaseDllName;
|
|
NtHeaders = RtlImageNtHeader(ViewBase);
|
|
}
|
|
}
|
|
|
|
if (!Wx86ProcessInit || NT_ERROR(st)) {
|
|
Wx86UnloadProviders(ViewBase);
|
|
st = STATUS_IMAGE_MACHINE_TYPE_MISMATCH;
|
|
}
|
|
}
|
|
|
|
// Need to save the status here to put in loader ENTRY after it's allocated.
|
|
|
|
Wx86Plugin = (st == STATUS_SUCCESS);
|
|
|
|
if (ShowSnaps) {
|
|
PCHAR Action;
|
|
|
|
if (st == STATUS_SUCCESS) {
|
|
Action = "Loaded";
|
|
} else if (st == STATUS_IMAGE_MACHINE_TYPE_MISMATCH) {
|
|
Action = "Unsupported";
|
|
} else {
|
|
Action = "Failed";
|
|
}
|
|
|
|
DbgPrint("LDRWx86: Plugin: %wZ %s.\n", &FullDllName, Action);
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(st) || (ViewBase == NULL)) {
|
|
if (ViewBase) NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
|
|
NtClose(Section);
|
|
return st;
|
|
} else if (Entry) {
|
|
goto Wx86MapComplete;
|
|
}
|
|
#endif
|
|
|
|
// Allocate a data table entry.
|
|
Entry = LdrpAllocateDataTableEntry(ViewBase);
|
|
if (!Entry) {
|
|
NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
|
|
NtClose(Section);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
Entry->Flags = (USHORT)(StaticLink ? LDRP_STATIC_LINK : 0);
|
|
Entry->LoadCount = 0;
|
|
Entry->FullDllName = FullDllName;
|
|
Entry->BaseDllName = BaseDllName;
|
|
Entry->EntryPoint = LdrpFetchAddressOfEntryPoint(Entry->DllBase);
|
|
|
|
#ifdef WX86
|
|
Wx86MapComplete :
|
|
if (Wx86Plugin) {
|
|
Entry->Flags |= LDRP_WX86_PLUGIN;
|
|
}
|
|
#endif
|
|
|
|
#if LDRDBG
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: LdrpMapDll: Full Name %wZ, Base Name %wZ\n", &FullDllName, &BaseDllName);
|
|
}
|
|
#endif
|
|
|
|
LdrpInsertMemoryTableEntry(Entry);
|
|
|
|
if (st == STATUS_IMAGE_MACHINE_TYPE_MISMATCH) {
|
|
PIMAGE_NT_HEADERS ImageHeader = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
|
|
|
|
// apps compiled for NT 3.x and below can load cross architecture images
|
|
ErrorStatus = STATUS_SUCCESS;
|
|
ErrorResponse = ResponseCancel;
|
|
|
|
if (ImageHeader->OptionalHeader.MajorSubsystemVersion <= 3) {
|
|
Entry->EntryPoint = 0;
|
|
|
|
// Hard Error Time
|
|
|
|
// Its error time...
|
|
|
|
ErrorParameters[0] = (ULONG_PTR)&FullDllName;
|
|
ErrorStatus = NtRaiseHardError(STATUS_IMAGE_MACHINE_TYPE_MISMATCH, 1, 1, ErrorParameters, OptionOkCancel, &ErrorResponse);
|
|
}
|
|
|
|
if (NT_SUCCESS(ErrorStatus) && ErrorResponse == ResponseCancel) {
|
|
RemoveEntryList(&Entry->InLoadOrderLinks);
|
|
RemoveEntryList(&Entry->InMemoryOrderLinks);
|
|
RemoveEntryList(&Entry->HashLinks);
|
|
RtlFreeHeap(RtlProcessHeap(), 0, Entry);
|
|
NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
|
|
NtClose(Section);
|
|
|
|
if (ImageHeader->OptionalHeader.MajorSubsystemVersion <= 3) {
|
|
if (LdrpInLdrInit) {
|
|
LdrpFatalHardErrorCount++;
|
|
}
|
|
}
|
|
|
|
return STATUS_INVALID_IMAGE_FORMAT;
|
|
}
|
|
} else {
|
|
if (NtHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL) {
|
|
Entry->Flags |= LDRP_IMAGE_DLL;
|
|
}
|
|
|
|
if (!(Entry->Flags & LDRP_IMAGE_DLL)) {
|
|
Entry->EntryPoint = 0;
|
|
}
|
|
}
|
|
|
|
*LdrDataTableEntry = Entry;
|
|
|
|
if (st == STATUS_IMAGE_NOT_AT_BASE) {
|
|
Entry->Flags |= LDRP_IMAGE_NOT_AT_BASE;
|
|
|
|
// now find the colliding dll. If we can not find a dll, then the colliding dll must be dynamic memory
|
|
ImageBase = (PUCHAR)NtHeaders->OptionalHeader.ImageBase;
|
|
ImageBounds = ImageBase + ViewSize;
|
|
|
|
CollidingDllFound = FALSE;
|
|
ScanHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
|
|
ScanNext = ScanHead->Flink;
|
|
while (ScanNext != ScanHead) {
|
|
ScanEntry = CONTAINING_RECORD(ScanNext, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
|
|
ScanNext = ScanNext->Flink;
|
|
|
|
ScanBase = (PUCHAR)ScanEntry->DllBase;
|
|
ScanTop = ScanBase + ScanEntry->SizeOfImage;
|
|
|
|
// when we unload, the memory order links flink field is nulled.
|
|
// this is used to skip the entry pending list removal.
|
|
if (!ScanEntry->InMemoryOrderLinks.Flink) {
|
|
continue;
|
|
}
|
|
|
|
// See if the base address of the scan image is within the relocating dll or if the top address of the scan image is within the relocating dll
|
|
if ((ImageBase >= ScanBase && ImageBase <= ScanTop) ||
|
|
(ImageBounds >= ScanBase && ImageBounds <= ScanTop) ||
|
|
(ScanBase >= ImageBase && ScanBase <= ImageBounds)) {
|
|
CollidingDllFound = TRUE;
|
|
CollidingDll = ScanEntry->FullDllName;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!CollidingDllFound) {
|
|
RtlInitUnicodeString(&CollidingDll, L"Dynamically Allocated Memory");
|
|
}
|
|
|
|
#if DBG
|
|
if (BeginTime.LowPart || BeginTime.HighPart) {
|
|
DbgPrint("\nLDR: LdrpMapDll Relocateing Image Name %ws\n", DllName);
|
|
}
|
|
LdrpSectionRelocates++;
|
|
#endif
|
|
|
|
if (Entry->Flags & LDRP_IMAGE_DLL) {
|
|
BOOLEAN AllowRelocation;
|
|
UNICODE_STRING SystemDll;
|
|
|
|
if (!(NtHeaders->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED)) {
|
|
PVOID pBaseRelocs;
|
|
ULONG BaseRelocCountBytes = 0;
|
|
|
|
// If the iamge doesn't have the reloc stripped bit set and there's no relocs in the data directory, allow this through.
|
|
// This is probably a pure forwarder dll or data w/o relocs.
|
|
pBaseRelocs = RtlImageDirectoryEntryToData(ViewBase, TRUE, IMAGE_DIRECTORY_ENTRY_BASERELOC, &BaseRelocCountBytes);
|
|
if (!pBaseRelocs && !BaseRelocCountBytes) {
|
|
goto NoRelocNeeded;
|
|
}
|
|
}
|
|
|
|
// decide whether or not to allow the relocation certain system dll's like user32 and kernel32 are not relocatable since addresses within these dll's are not always stored per process
|
|
// do not allow these dll's to be relocated
|
|
AllowRelocation = TRUE;
|
|
RtlInitUnicodeString(&SystemDll, L"user32.dll");
|
|
if (RtlEqualUnicodeString(&BaseDllName, &SystemDll, TRUE)) {
|
|
AllowRelocation = FALSE;
|
|
} else {
|
|
RtlInitUnicodeString(&SystemDll, L"kernel32.dll");
|
|
if (RtlEqualUnicodeString(&BaseDllName, &SystemDll, TRUE)) {
|
|
AllowRelocation = FALSE;
|
|
}
|
|
}
|
|
|
|
if (!AllowRelocation && KnownDll) {
|
|
// totally disallow the relocation since this is a knowndll that matches our system binaries and is being relocated
|
|
|
|
// Hard Error Time
|
|
|
|
ErrorParameters[0] = (ULONG_PTR)&SystemDll;
|
|
ErrorParameters[1] = (ULONG_PTR)&CollidingDll;
|
|
|
|
NtRaiseHardError(STATUS_ILLEGAL_DLL_RELOCATION, 2, 3, ErrorParameters, OptionOk, &ErrorResponse);
|
|
if (LdrpInLdrInit) {
|
|
LdrpFatalHardErrorCount++;
|
|
}
|
|
|
|
st = STATUS_CONFLICTING_ADDRESSES;
|
|
goto skipreloc;
|
|
}
|
|
|
|
st = LdrpSetProtection(ViewBase, FALSE, StaticLink);
|
|
if (NT_SUCCESS(st)) {
|
|
st = (NTSTATUS)LdrRelocateImage(ViewBase, "LDR", (ULONG)STATUS_SUCCESS, (ULONG)STATUS_CONFLICTING_ADDRESSES, (ULONG)STATUS_INVALID_IMAGE_FORMAT);
|
|
if (NT_SUCCESS(st)) {
|
|
// If we did relocations, then map the section again.
|
|
// this will force the debug event
|
|
|
|
// arrange for debugger to pick up the image name
|
|
ArbitraryUserPointer = Teb->NtTib.ArbitraryUserPointer;
|
|
Teb->NtTib.ArbitraryUserPointer = (PVOID)FullDllName.Buffer;
|
|
NtMapViewOfSection(Section, NtCurrentProcess(), (PVOID *)&ViewBase, 0L, 0L, NULL, &ViewSize, ViewShare, 0L, PAGE_READWRITE);
|
|
Teb->NtTib.ArbitraryUserPointer = ArbitraryUserPointer;
|
|
st = LdrpSetProtection(ViewBase, TRUE, StaticLink);
|
|
}
|
|
}
|
|
|
|
skipreloc:
|
|
// if the set protection failed, or if the relocation failed,
|
|
// then remove the partially loaded dll from the lists and clear entry that it has been freed.
|
|
if (!NT_SUCCESS(st)) {
|
|
RemoveEntryList(&Entry->InLoadOrderLinks);
|
|
RemoveEntryList(&Entry->InMemoryOrderLinks);
|
|
RemoveEntryList(&Entry->HashLinks);
|
|
NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
|
|
Entry = NULL;
|
|
}
|
|
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: Fixups %successfully re-applied @ %lx\n", NT_SUCCESS(st) ? "s" : "uns", ViewBase);
|
|
}
|
|
} else {
|
|
NoRelocNeeded:
|
|
st = STATUS_SUCCESS;
|
|
|
|
// arrange for debugger to pick up the image name
|
|
ArbitraryUserPointer = Teb->NtTib.ArbitraryUserPointer;
|
|
Teb->NtTib.ArbitraryUserPointer = (PVOID)FullDllName.Buffer;
|
|
NtMapViewOfSection(Section, NtCurrentProcess(), (PVOID *)&ViewBase, 0L, 0L, NULL, &ViewSize, ViewShare, 0L, PAGE_READWRITE);
|
|
Teb->NtTib.ArbitraryUserPointer = ArbitraryUserPointer;
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: Fixups won't be re-applied to non-Dll @ %lx\n", ViewBase);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined (_ALPHA_)
|
|
// Find and apply Alpha architecture fixups for this dll
|
|
if (Entry->Flags & LDRP_IMAGE_DLL)
|
|
AlphaFindArchitectureFixups(NtHeaders, ViewBase, StaticLink);
|
|
#endif
|
|
|
|
#if defined(_X86_)
|
|
if (LdrpNumberOfProcessors > 1 && Entry && (Entry->Flags & LDRP_IMAGE_DLL)) {
|
|
LdrpValidateImageForMp(Entry);
|
|
}
|
|
#endif
|
|
|
|
NtClose(Section);
|
|
return st;
|
|
}
|
|
|
|
#if defined (_ALPHA_)
|
|
|
|
#define asm __asm
|
|
#pragma intrinsic (__asm)
|
|
|
|
VOID
|
|
AlphaApplyArchitectureFixups(
|
|
IN PIMAGE_ARCHITECTURE_HEADER ArchHeader,
|
|
IN ULONG ArchHeaderByteSize,
|
|
IN ULONG_PTR BaseAddr
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This applies the Alpha architecture enhancement fixups. If an image contains
|
|
a .arch section, then the image/dll, if running on a newer chip, may
|
|
be eligible to take advantage of new instructions. Check the replacement
|
|
constraints vs. chip capabilities and apply fixup if warranted.
|
|
Arguments:
|
|
ArchHeader - Supplies a pointer to the first architecture specific fixup header record.
|
|
ArchHeaderByteSize - Supplies the size (in bytes) of the Architecture headers to be processed.
|
|
BaseAddr - Base address to be added to RVA's in architecture section
|
|
--*/
|
|
{
|
|
ULONG Count, Entry;
|
|
ULONG SystemAmask;
|
|
ULONG ReplacementInstruction;
|
|
PIMAGE_ARCHITECTURE_HEADER ArchHeaderEnd;
|
|
PIMAGE_ARCHITECTURE_ENTRY ArchEntry;
|
|
|
|
// Figure out the end value for ArchHeader
|
|
|
|
ArchHeaderEnd = (PIMAGE_ARCHITECTURE_HEADER)((PCHAR)ArchHeader + ArchHeaderByteSize);
|
|
|
|
// Get the architecture mask and implementation version
|
|
// of the running system
|
|
|
|
|
|
while (*(PULONGLONG)ArchHeader == 0L)
|
|
ArchHeader++; // skip over quadwords of zero
|
|
|
|
SystemAmask = ~asm("amask $16, $0", -1);
|
|
|
|
while ((*(PULONGLONG)ArchHeader != 0xffffffffL) && (ArchHeader < ArchHeaderEnd)) {
|
|
if (*(PULONGLONG)ArchHeader != 0L) { // maybe zero's intermixed
|
|
#if DBG && ARCH_DBG
|
|
DbgPrint("ARCH: Amask=0x%x, Current Header=0x%08x, Shift=%d, Value=%d\n", SystemAmask, *ArchHeader, ArchHeader->AmaskShift, ArchHeader->AmaskValue);
|
|
#endif
|
|
if (((SystemAmask >> ArchHeader->AmaskShift) & 1) != ArchHeader->AmaskValue) {
|
|
// fixup needed
|
|
PIMAGE_ARCHITECTURE_ENTRY ArchEntry;
|
|
ArchEntry = (PIMAGE_ARCHITECTURE_ENTRY)(ArchHeader->FirstEntryRVA + BaseAddr);
|
|
while (*(PULONGLONG)ArchEntry != 0xffffffffL) {
|
|
if (*(PULONGLONG)ArchEntry != 0L) { // maybe zero's intermixed
|
|
#if DBG && ARCH_DBG
|
|
DbgPrint("ARCH: 0x%08x -> 0x%08x\n", *(PALPHA_INSTRUCTION)(ArchEntry->FixupInstRVA + BaseAddr), ArchEntry->NewInst);
|
|
#endif
|
|
* (ULONG *)(ArchEntry->FixupInstRVA + BaseAddr) = ArchEntry->NewInst;
|
|
}
|
|
ArchEntry++;
|
|
}
|
|
} // if this header describes fixups for this machine
|
|
}
|
|
ArchHeader++;
|
|
}
|
|
}
|
|
|
|
|
|
VOID AlphaFindArchitectureFixups(PIMAGE_NT_HEADERS NtHeaders, PVOID ViewBase, BOOLEAN StaticLink)
|
|
/*++
|
|
Routine Description:
|
|
This routine runs through a mapped image/dll looking for an architecture enhancement section (.arch).
|
|
If found, it applies the architecture fixups.
|
|
Arguments:
|
|
NtHeaders - the NT header for this image or dll.
|
|
ViewBase - the Mapped view base used for page protection munging.
|
|
StaticLink - TRUE if this DLL has a static link to it
|
|
--*/
|
|
{
|
|
ULONG Size;
|
|
PIMAGE_ARCHITECTURE_HEADER ImageArchData;
|
|
|
|
if (NtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_ALPHA) {
|
|
// If the image isnt Alpha, don't try to find/apply Alpha fixups.
|
|
// The image may be an x86 image linked with the x86 Watcom
|
|
// linker which stuffed its version number in the .arch directory
|
|
// entry in the image, fooling the Alpha .arch checker.
|
|
|
|
return;
|
|
}
|
|
|
|
ImageArchData =
|
|
(PIMAGE_ARCHITECTURE_HEADER)RtlImageDirectoryEntryToData(
|
|
ViewBase,
|
|
TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE,
|
|
&Size);
|
|
if (ImageArchData) {
|
|
NTSTATUS st;
|
|
|
|
st = LdrpSetProtection(ViewBase, FALSE, StaticLink); // access to r/w
|
|
if (NT_SUCCESS(st)) {
|
|
AlphaApplyArchitectureFixups(ImageArchData, Size, (ULONG_PTR)ViewBase);
|
|
LdrpSetProtection(ViewBase, TRUE, StaticLink); // back to r/o
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
LdrpCreateDllSection(
|
|
IN PUNICODE_STRING NtFullDllName,
|
|
IN HANDLE DllFile,
|
|
IN PUNICODE_STRING BaseName,
|
|
IN PULONG DllCharacteristics OPTIONAL,
|
|
OUT PHANDLE SectionHandle
|
|
)
|
|
{
|
|
HANDLE File;
|
|
HANDLE Section;
|
|
NTSTATUS st;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
|
|
if (!DllFile) {
|
|
// Since ntsdk does not search paths well, we can't use
|
|
// relative object names
|
|
InitializeObjectAttributes(&ObjectAttributes, NtFullDllName, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
st = NtOpenFile(
|
|
&File,
|
|
SYNCHRONIZE | FILE_EXECUTE,
|
|
&ObjectAttributes,
|
|
&IoStatus,
|
|
FILE_SHARE_READ | FILE_SHARE_DELETE,
|
|
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
|
|
);
|
|
if (!NT_SUCCESS(st)) {
|
|
#if DBG
|
|
DbgPrint("LDR: LdrCreateDllSection - NtOpenFile( %wZ ) failed. Status == %X\n", NtFullDllName, st);
|
|
#endif
|
|
* SectionHandle = NULL;
|
|
return st;
|
|
}
|
|
} else {
|
|
File = DllFile;
|
|
}
|
|
|
|
st = NtCreateSection(
|
|
SectionHandle,
|
|
SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_MAP_WRITE | SECTION_QUERY,
|
|
NULL,
|
|
NULL,
|
|
PAGE_EXECUTE,
|
|
SEC_IMAGE,
|
|
File
|
|
);
|
|
NtClose(File);
|
|
if (!NT_SUCCESS(st)) {
|
|
// hard error time
|
|
ULONG_PTR ErrorParameters[1];
|
|
ULONG ErrorResponse;
|
|
|
|
*SectionHandle = NULL;
|
|
ErrorParameters[0] = (ULONG_PTR)NtFullDllName;
|
|
|
|
NtRaiseHardError(STATUS_INVALID_IMAGE_FORMAT, 1, 1, ErrorParameters, OptionOk, &ErrorResponse);
|
|
|
|
if (LdrpInLdrInit) {
|
|
LdrpFatalHardErrorCount++;
|
|
}
|
|
|
|
#if DBG
|
|
if (st != STATUS_INVALID_IMAGE_NE_FORMAT &&
|
|
st != STATUS_INVALID_IMAGE_LE_FORMAT &&
|
|
st != STATUS_INVALID_IMAGE_WIN_16
|
|
) {
|
|
DbgPrint("LDR: LdrCreateDllSection - NtCreateSection %wZ failed. Status == %X\n", NtFullDllName, st);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return st;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LdrpSnapIAT(
|
|
IN PLDR_DATA_TABLE_ENTRY LdrDataTableEntry_Export,
|
|
IN PLDR_DATA_TABLE_ENTRY LdrDataTableEntry_Import,
|
|
IN PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor,
|
|
IN BOOLEAN SnapForwardersOnly
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This function snaps the Import Address Table for this Import Descriptor.
|
|
Arguments:
|
|
LdrDataTableEntry_Export - Information about the image to import from.
|
|
LdrDataTableEntry_Import - Information about the image to import to.
|
|
ImportDescriptor - Contains a pointer to the IAT to snap.
|
|
SnapForwardersOnly - TRUE if just snapping forwarders only.
|
|
Return Value:
|
|
Status value
|
|
--*/
|
|
{
|
|
PPEB Peb;
|
|
NTSTATUS st;
|
|
ULONG ExportSize;
|
|
PIMAGE_EXPORT_DIRECTORY ExportDirectory;
|
|
PIMAGE_THUNK_DATA Thunk, OriginalThunk;
|
|
PSZ ImportName;
|
|
ULONG ForwarderChain;
|
|
PIMAGE_NT_HEADERS NtHeaders;
|
|
PIMAGE_SECTION_HEADER NtSection;
|
|
ULONG i, Rva;
|
|
PVOID IATBase;
|
|
SIZE_T IATSize;
|
|
ULONG LittleIATSize;
|
|
ULONG OldProtect;
|
|
|
|
Peb = NtCurrentPeb();
|
|
|
|
ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(LdrDataTableEntry_Export->DllBase,
|
|
TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_EXPORT,
|
|
&ExportSize);
|
|
if (!ExportDirectory) {
|
|
KdPrint(("LDR: %wZ doesn't contain an EXPORT table\n", &LdrDataTableEntry_Export->BaseDllName));
|
|
return STATUS_INVALID_IMAGE_FORMAT;
|
|
}
|
|
|
|
// Determine the location and size of the IAT.
|
|
// If the linker did not tell use explicitly, then use the location and size of the image section that contains the import table.
|
|
IATBase = RtlImageDirectoryEntryToData(LdrDataTableEntry_Import->DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_IAT, &LittleIATSize);
|
|
IATSize = LittleIATSize;
|
|
if (IATBase == NULL) {
|
|
NtHeaders = RtlImageNtHeader(LdrDataTableEntry_Import->DllBase);
|
|
NtSection = IMAGE_FIRST_SECTION(NtHeaders);
|
|
Rva = NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
|
|
if (Rva != 0) {
|
|
for (i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++) {
|
|
if (Rva >= NtSection->VirtualAddress &&
|
|
Rva < (NtSection->VirtualAddress + NtSection->SizeOfRawData)) {
|
|
IATBase = (PVOID)((ULONG_PTR)(LdrDataTableEntry_Import->DllBase) + NtSection->VirtualAddress);
|
|
|
|
LittleIATSize = NtSection->Misc.VirtualSize;
|
|
if (LittleIATSize == 0) {
|
|
LittleIATSize = NtSection->SizeOfRawData;
|
|
}
|
|
break;
|
|
}
|
|
|
|
++NtSection;
|
|
}
|
|
}
|
|
|
|
if (IATBase == NULL) {
|
|
KdPrint(("LDR: Unable to unprotect IAT for %wZ (Image Base %x)\n",
|
|
&LdrDataTableEntry_Import->BaseDllName,
|
|
LdrDataTableEntry_Import->DllBase));
|
|
return STATUS_INVALID_IMAGE_FORMAT;
|
|
}
|
|
IATSize = LittleIATSize;
|
|
}
|
|
|
|
st = NtProtectVirtualMemory(NtCurrentProcess(), &IATBase, &IATSize, PAGE_READWRITE, &OldProtect);
|
|
if (!NT_SUCCESS(st)) {
|
|
KdPrint(("LDR: Unable to unprotect IAT for %wZ (Status %x)\n", &LdrDataTableEntry_Import->BaseDllName, st));
|
|
return st;
|
|
}
|
|
|
|
// If just snapping forwarded entries, walk that list
|
|
|
|
if (SnapForwardersOnly) {
|
|
ImportName = (PSZ)((ULONG_PTR)LdrDataTableEntry_Import->DllBase + ImportDescriptor->Name);
|
|
ForwarderChain = ImportDescriptor->ForwarderChain;
|
|
while (ForwarderChain != -1) {
|
|
OriginalThunk = (PIMAGE_THUNK_DATA)((ULONG_PTR)LdrDataTableEntry_Import->DllBase +
|
|
ImportDescriptor->OriginalFirstThunk +
|
|
(ForwarderChain * sizeof(IMAGE_THUNK_DATA)));
|
|
Thunk = (PIMAGE_THUNK_DATA)((ULONG_PTR)LdrDataTableEntry_Import->DllBase +
|
|
ImportDescriptor->FirstThunk +
|
|
(ForwarderChain * sizeof(IMAGE_THUNK_DATA)));
|
|
ForwarderChain = (ULONG)Thunk->u1.Ordinal;
|
|
try {
|
|
st = LdrpSnapThunk(LdrDataTableEntry_Export->DllBase,
|
|
LdrDataTableEntry_Import->DllBase,
|
|
OriginalThunk,
|
|
Thunk,
|
|
ExportDirectory,
|
|
ExportSize,
|
|
TRUE,
|
|
ImportName);
|
|
Thunk++;
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER) {
|
|
st = GetExceptionCode();
|
|
}
|
|
if (!NT_SUCCESS(st)) {
|
|
break;
|
|
}
|
|
}
|
|
} else if (ImportDescriptor->FirstThunk) {// Otherwise, walk through the IAT and snap all the thunks.
|
|
Thunk = (PIMAGE_THUNK_DATA)((ULONG_PTR)LdrDataTableEntry_Import->DllBase + ImportDescriptor->FirstThunk);
|
|
NtHeaders = RtlImageNtHeader(LdrDataTableEntry_Import->DllBase);
|
|
|
|
// If the OriginalFirstThunk field does not point inside the image, then ignore it.
|
|
// This is will detect bogus Borland Linker 2.25 images that did not fill this field in.
|
|
if (ImportDescriptor->Characteristics < NtHeaders->OptionalHeader.SizeOfHeaders ||
|
|
ImportDescriptor->Characteristics >= NtHeaders->OptionalHeader.SizeOfImage) {
|
|
OriginalThunk = Thunk;
|
|
} else {
|
|
OriginalThunk = (PIMAGE_THUNK_DATA)((ULONG_PTR)LdrDataTableEntry_Import->DllBase + ImportDescriptor->OriginalFirstThunk);
|
|
}
|
|
ImportName = (PSZ)((ULONG_PTR)LdrDataTableEntry_Import->DllBase + ImportDescriptor->Name);
|
|
while (OriginalThunk->u1.AddressOfData) {
|
|
try {
|
|
st = LdrpSnapThunk(LdrDataTableEntry_Export->DllBase,
|
|
LdrDataTableEntry_Import->DllBase,
|
|
OriginalThunk,
|
|
Thunk,
|
|
ExportDirectory,
|
|
ExportSize,
|
|
TRUE,
|
|
ImportName);
|
|
OriginalThunk++;
|
|
Thunk++;
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER) {
|
|
st = GetExceptionCode();
|
|
}
|
|
|
|
if (!NT_SUCCESS(st)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Restore protection for IAT and flush instruction cache.
|
|
NtProtectVirtualMemory(NtCurrentProcess(), &IATBase, &IATSize, OldProtect, &OldProtect);
|
|
NtFlushInstructionCache(NtCurrentProcess(), IATBase, LittleIATSize);
|
|
return st;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LdrpSnapThunk(
|
|
IN PVOID DllBase,
|
|
IN PVOID ImageBase,
|
|
IN PIMAGE_THUNK_DATA OriginalThunk,
|
|
IN OUT PIMAGE_THUNK_DATA Thunk,
|
|
IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
|
|
IN ULONG ExportSize,
|
|
IN BOOLEAN StaticSnap,
|
|
IN PSZ DllName
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This function snaps a thunk using the specified Export Section data.
|
|
If the section data does not support the thunk, then the thunk is partially snapped (Dll field is still non-null, but snap address is set).
|
|
Arguments:
|
|
DllBase - Base of Dll.
|
|
ImageBase - Base of image that contains the thunks to snap.
|
|
Thunk - On input, supplies the thunk to snap. When successfully snapped, the function field is set to point to the address in the DLL, and the DLL field is set to NULL.
|
|
ExportDirectory - Supplies the Export Section data from a DLL.
|
|
StaticSnap - If TRUE, then loader is attempting a static snap, and any ordinal/name lookup failure will be reported.
|
|
Return Value:
|
|
STATUS_SUCCESS or STATUS_PROCEDURE_NOT_FOUND
|
|
--*/
|
|
{
|
|
BOOLEAN Ordinal;
|
|
USHORT OrdinalNumber;
|
|
ULONG OriginalOrdinalNumber;
|
|
PIMAGE_IMPORT_BY_NAME AddressOfData;
|
|
PULONG NameTableBase;
|
|
PUSHORT NameOrdinalTableBase;
|
|
PULONG Addr;
|
|
USHORT HintIndex;
|
|
NTSTATUS st;
|
|
PSZ ImportString;
|
|
|
|
// Determine if snap is by name, or by ordinal
|
|
Ordinal = (BOOLEAN)IMAGE_SNAP_BY_ORDINAL(OriginalThunk->u1.Ordinal);
|
|
if (Ordinal) {
|
|
OriginalOrdinalNumber = (ULONG)IMAGE_ORDINAL(OriginalThunk->u1.Ordinal);
|
|
OrdinalNumber = (USHORT)(OriginalOrdinalNumber - ExportDirectory->Base);
|
|
} else {
|
|
// Change AddressOfData from an RVA to a VA.
|
|
AddressOfData = (PIMAGE_IMPORT_BY_NAME)((ULONG_PTR)ImageBase + ((ULONG_PTR)OriginalThunk->u1.AddressOfData & 0xffffffff));
|
|
ImportString = (PSZ)AddressOfData->Name;
|
|
|
|
// Lookup Name in NameTable
|
|
NameTableBase = (PULONG)((ULONG_PTR)DllBase + (ULONG)ExportDirectory->AddressOfNames);
|
|
NameOrdinalTableBase = (PUSHORT)((ULONG_PTR)DllBase + (ULONG)ExportDirectory->AddressOfNameOrdinals);
|
|
|
|
// Before dropping into binary search, see if the hint index results in a successful match.
|
|
// If the hint index is zero, then drop into binary search.
|
|
HintIndex = AddressOfData->Hint;
|
|
if ((ULONG)HintIndex < ExportDirectory->NumberOfNames && !strcmp(ImportString, (PSZ)((ULONG_PTR)DllBase + NameTableBase[HintIndex]))) {
|
|
OrdinalNumber = NameOrdinalTableBase[HintIndex];
|
|
#if LDRDBG
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: Snapping %s\n", ImportString);
|
|
}
|
|
#endif
|
|
} else {
|
|
#if LDRDBG
|
|
if (HintIndex) {
|
|
DbgPrint("LDR: Warning HintIndex Failure. Name %s (%lx) Hint 0x%lx\n", ImportString, (ULONG)ImportString, (ULONG)HintIndex);
|
|
}
|
|
#endif
|
|
OrdinalNumber = LdrpNameToOrdinal(ImportString, ExportDirectory->NumberOfNames, DllBase, NameTableBase, NameOrdinalTableBase);
|
|
}
|
|
}
|
|
|
|
// If OrdinalNumber is not within the Export Address Table, then DLL does not implement function. Snap to LDRP_BAD_DLL.
|
|
if ((ULONG)OrdinalNumber >= ExportDirectory->NumberOfFunctions) {
|
|
baddllref:
|
|
#if DBG
|
|
if (StaticSnap) {
|
|
if (Ordinal) {
|
|
DbgPrint("LDR: Can't locate ordinal 0x%lx\n", OriginalOrdinalNumber);
|
|
} else {
|
|
DbgPrint("LDR: Can't locate %s\n", ImportString);
|
|
}
|
|
}
|
|
#endif
|
|
if (StaticSnap) {
|
|
// Hard Error Time
|
|
ULONG_PTR ErrorParameters[3];
|
|
UNICODE_STRING ErrorDllName, ErrorEntryPointName;
|
|
ANSI_STRING AnsiScratch;
|
|
ULONG ParameterStringMask;
|
|
ULONG ErrorResponse;
|
|
|
|
RtlInitAnsiString(&AnsiScratch, DllName ? DllName : "Unknown");
|
|
RtlAnsiStringToUnicodeString(&ErrorDllName, &AnsiScratch, TRUE);
|
|
ErrorParameters[1] = (ULONG_PTR)&ErrorDllName;
|
|
ParameterStringMask = 2;
|
|
|
|
if (Ordinal) {
|
|
ErrorParameters[0] = OriginalOrdinalNumber;
|
|
} else {
|
|
RtlInitAnsiString(&AnsiScratch, ImportString);
|
|
RtlAnsiStringToUnicodeString(&ErrorEntryPointName, &AnsiScratch, TRUE);
|
|
ErrorParameters[0] = (ULONG_PTR)&ErrorEntryPointName;
|
|
ParameterStringMask = 3;
|
|
}
|
|
|
|
NtRaiseHardError(Ordinal ? STATUS_ORDINAL_NOT_FOUND : STATUS_ENTRYPOINT_NOT_FOUND,
|
|
2,
|
|
ParameterStringMask,
|
|
ErrorParameters,
|
|
OptionOk,
|
|
&ErrorResponse);
|
|
|
|
if (LdrpInLdrInit) {
|
|
LdrpFatalHardErrorCount++;
|
|
}
|
|
RtlFreeUnicodeString(&ErrorDllName);
|
|
if (!Ordinal) {
|
|
RtlFreeUnicodeString(&ErrorEntryPointName);
|
|
RtlRaiseStatus(STATUS_ENTRYPOINT_NOT_FOUND);
|
|
}
|
|
RtlRaiseStatus(STATUS_ORDINAL_NOT_FOUND);
|
|
}
|
|
Thunk->u1.Function = (ULONG_PTR)LDRP_BAD_DLL;
|
|
st = Ordinal ? STATUS_ORDINAL_NOT_FOUND : STATUS_ENTRYPOINT_NOT_FOUND;
|
|
} else {
|
|
Addr = (PULONG)((ULONG_PTR)DllBase + (ULONG)ExportDirectory->AddressOfFunctions);
|
|
Thunk->u1.Function = ((ULONG_PTR)DllBase + Addr[OrdinalNumber]);
|
|
if (Thunk->u1.Function > (ULONG_PTR)ExportDirectory &&
|
|
Thunk->u1.Function < ((ULONG_PTR)ExportDirectory + ExportSize)) {
|
|
UNICODE_STRING UnicodeString;
|
|
ANSI_STRING ForwardDllName;
|
|
PVOID ForwardDllHandle;
|
|
PUNICODE_STRING ForwardProcName;
|
|
ULONG ForwardProcOrdinal;
|
|
|
|
ImportString = (PSZ)Thunk->u1.Function;
|
|
ForwardDllName.Buffer = ImportString, ForwardDllName.Length = (USHORT)(strchr(ImportString, '.') - ImportString);
|
|
ForwardDllName.MaximumLength = ForwardDllName.Length;
|
|
st = RtlAnsiStringToUnicodeString(&UnicodeString, &ForwardDllName, TRUE);
|
|
if (NT_SUCCESS(st)) {
|
|
#if defined (WX86)
|
|
if (Wx86ProcessInit) {
|
|
NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll =
|
|
RtlImageNtHeader(DllBase)->FileHeader.Machine == IMAGE_FILE_MACHINE_I386;
|
|
}
|
|
#endif
|
|
|
|
st = LdrpLoadDll(NULL, NULL, &UnicodeString, &ForwardDllHandle, FALSE);
|
|
RtlFreeUnicodeString(&UnicodeString);
|
|
}
|
|
|
|
if (!NT_SUCCESS(st)) {
|
|
goto baddllref;
|
|
}
|
|
|
|
RtlInitAnsiString(&ForwardDllName, ImportString + ForwardDllName.Length + 1);
|
|
if (ForwardDllName.Length > 1 && *ForwardDllName.Buffer == '#') {
|
|
ForwardProcName = NULL;
|
|
st = RtlCharToInteger(ForwardDllName.Buffer + 1, 0, &ForwardProcOrdinal);
|
|
if (!NT_SUCCESS(st)) {
|
|
goto baddllref;
|
|
}
|
|
} else {
|
|
ForwardProcName = (PUNICODE_STRING)&ForwardDllName;
|
|
|
|
// Following line is not needed since this is a by name lookup
|
|
|
|
//ForwardProcOrdinal = (ULONG)&ForwardDllName;
|
|
}
|
|
|
|
st = LdrpGetProcedureAddress(ForwardDllHandle,
|
|
(PANSI_STRING)ForwardProcName,
|
|
ForwardProcOrdinal,
|
|
&(PVOID)Thunk->u1.Function,
|
|
FALSE);
|
|
if (!NT_SUCCESS(st)) {
|
|
goto baddllref;
|
|
}
|
|
} else {
|
|
if (!Addr[OrdinalNumber]) {
|
|
goto baddllref;
|
|
}
|
|
#if defined (_ALPHA_) && defined (WX86)
|
|
else {
|
|
PIMAGE_NT_HEADERS ExportNtHeaders = RtlImageNtHeader(DllBase);
|
|
if ((ExportNtHeaders->OptionalHeader.SectionAlignment < PAGE_SIZE) &&
|
|
(ExportNtHeaders->FileHeader.Machine == IMAGE_FILE_MACHINE_I386)) {
|
|
Thunk->u1.Function += LdrpWx86RelocatedFixupDiff(DllBase, Addr[OrdinalNumber]);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
st = STATUS_SUCCESS;
|
|
}
|
|
|
|
return st;
|
|
}
|
|
|
|
|
|
USHORT
|
|
LdrpNameToOrdinal(
|
|
IN PSZ Name,
|
|
IN ULONG NumberOfNames,
|
|
IN PVOID DllBase,
|
|
IN PULONG NameTableBase,
|
|
IN PUSHORT NameOrdinalTableBase
|
|
)
|
|
{
|
|
LONG High;
|
|
LONG Low;
|
|
LONG Middle;
|
|
LONG Result;
|
|
|
|
// Lookup the import name in the name table using a binary search.
|
|
Low = 0;
|
|
High = NumberOfNames - 1;
|
|
while (High >= Low) {
|
|
// Compute the next probe index and compare the import name
|
|
// with the export name entry.
|
|
Middle = (Low + High) >> 1;
|
|
Result = strcmp(Name, (PCHAR)((ULONG_PTR)DllBase + NameTableBase[Middle]));
|
|
if (Result < 0) {
|
|
High = Middle - 1;
|
|
} else if (Result > 0) {
|
|
Low = Middle + 1;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If the high index is less than the low index, then a matching
|
|
// table entry was not found. Otherwise, get the ordinal number
|
|
// from the ordinal table.
|
|
if (High < Low) {
|
|
return (USHORT)-1;
|
|
} else {
|
|
return NameOrdinalTableBase[Middle];
|
|
}
|
|
}
|
|
|
|
|
|
VOID LdrpUpdateLoadCount(IN PLDR_DATA_TABLE_ENTRY LdrDataTableEntry, IN BOOLEAN IncrementCount)
|
|
/*++
|
|
Routine Description:
|
|
This function dereferences a loaded DLL adjusting its reference count. It then dereferences each dll referenced by this dll.
|
|
Arguments:
|
|
LdrDataTableEntry - Supplies the address of the DLL to dereference
|
|
IncrementCount - TRUE if adding one to LoadCount, O.W. subtracting one
|
|
--*/
|
|
{
|
|
PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
|
|
PIMAGE_BOUND_IMPORT_DESCRIPTOR NewImportDescriptor;
|
|
PIMAGE_BOUND_FORWARDER_REF NewImportForwarder;
|
|
PSZ ImportName, NewImportStringBase;
|
|
ULONG i, ImportSize, NewImportSize;
|
|
ANSI_STRING AnsiString;
|
|
PUNICODE_STRING ImportDescriptorName_U;
|
|
PLDR_DATA_TABLE_ENTRY Entry;
|
|
NTSTATUS st;
|
|
BOOLEAN Wx86KnownDll = FALSE;
|
|
PIMAGE_THUNK_DATA FirstThunk;
|
|
|
|
if (IncrementCount)
|
|
if (LdrDataTableEntry->Flags & LDRP_LOAD_IN_PROGRESS) {
|
|
return;
|
|
} else {
|
|
LdrDataTableEntry->Flags |= LDRP_LOAD_IN_PROGRESS;
|
|
} else
|
|
if (LdrDataTableEntry->Flags & LDRP_UNLOAD_IN_PROGRESS) {
|
|
return;
|
|
} else {
|
|
LdrDataTableEntry->Flags |= LDRP_UNLOAD_IN_PROGRESS;
|
|
}
|
|
|
|
|
|
// For each DLL used by this DLL, reference or dereference the DLL.
|
|
ImportDescriptorName_U = &NtCurrentTeb()->StaticUnicodeString;
|
|
|
|
#if defined (WX86)
|
|
if (Wx86ProcessInit) {
|
|
Wx86KnownDll = RtlImageNtHeader(LdrDataTableEntry->DllBase)->FileHeader.Machine == IMAGE_FILE_MACHINE_I386;
|
|
}
|
|
#endif
|
|
|
|
|
|
// See if there is a bound import table. If so, walk that to
|
|
// determine DLL names to reference or dereference. Avoids touching
|
|
// the .idata section
|
|
|
|
NewImportDescriptor = (PIMAGE_BOUND_IMPORT_DESCRIPTOR)RtlImageDirectoryEntryToData(
|
|
LdrDataTableEntry->DllBase,
|
|
TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT,
|
|
&NewImportSize
|
|
);
|
|
if (NewImportDescriptor) {
|
|
if (IncrementCount) {
|
|
LdrDataTableEntry->Flags |= LDRP_LOAD_IN_PROGRESS;
|
|
} else {
|
|
LdrDataTableEntry->Flags |= LDRP_UNLOAD_IN_PROGRESS;
|
|
}
|
|
|
|
NewImportStringBase = (LPSTR)NewImportDescriptor;
|
|
while (NewImportDescriptor->OffsetModuleName) {
|
|
ImportName = NewImportStringBase +
|
|
NewImportDescriptor->OffsetModuleName;
|
|
RtlInitAnsiString(&AnsiString, ImportName);
|
|
st = RtlAnsiStringToUnicodeString(ImportDescriptorName_U, &AnsiString, FALSE);
|
|
if (NT_SUCCESS(st)) {
|
|
if (LdrpCheckForLoadedDll(NULL, ImportDescriptorName_U, TRUE, Wx86KnownDll, &Entry)) {
|
|
if (Entry->LoadCount != 0xffff) {
|
|
if (IncrementCount) {
|
|
Entry->LoadCount++;
|
|
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: Refcount %wZ (%lx)\n", ImportDescriptorName_U, (ULONG)Entry->LoadCount);
|
|
}
|
|
} else {
|
|
Entry->LoadCount--;
|
|
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: Derefcount %wZ (%lx)\n", ImportDescriptorName_U, (ULONG)Entry->LoadCount);
|
|
}
|
|
}
|
|
}
|
|
LdrpUpdateLoadCount(Entry, IncrementCount);
|
|
}
|
|
}
|
|
|
|
NewImportForwarder = (PIMAGE_BOUND_FORWARDER_REF)(NewImportDescriptor + 1);
|
|
for (i = 0; i < NewImportDescriptor->NumberOfModuleForwarderRefs; i++) {
|
|
ImportName = NewImportStringBase + NewImportForwarder->OffsetModuleName;
|
|
RtlInitAnsiString(&AnsiString, ImportName);
|
|
st = RtlAnsiStringToUnicodeString(ImportDescriptorName_U, &AnsiString, FALSE);
|
|
if (NT_SUCCESS(st)) {
|
|
if (LdrpCheckForLoadedDll(NULL, ImportDescriptorName_U, TRUE, Wx86KnownDll, &Entry)) {
|
|
if (Entry->LoadCount != 0xffff) {
|
|
if (IncrementCount) {
|
|
Entry->LoadCount++;
|
|
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: Refcount %wZ (%lx)\n", ImportDescriptorName_U, (ULONG)Entry->LoadCount);
|
|
}
|
|
} else {
|
|
Entry->LoadCount--;
|
|
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: Derefcount %wZ (%lx)\n", ImportDescriptorName_U, (ULONG)Entry->LoadCount);
|
|
}
|
|
}
|
|
}
|
|
LdrpUpdateLoadCount(Entry, IncrementCount);
|
|
}
|
|
}
|
|
|
|
NewImportForwarder += 1;
|
|
}
|
|
|
|
NewImportDescriptor = (PIMAGE_BOUND_IMPORT_DESCRIPTOR)NewImportForwarder;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)RtlImageDirectoryEntryToData(
|
|
LdrDataTableEntry->DllBase,
|
|
TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_IMPORT,
|
|
&ImportSize
|
|
);
|
|
if (ImportDescriptor) {
|
|
while (ImportDescriptor->Name && ImportDescriptor->FirstThunk) {
|
|
// Match code in walk that skips references like this. IE3 had
|
|
// some dll's with these bogus links to url.dll. On load, the url.dll
|
|
// ref was skipped. On unload, it was not skipped because
|
|
// this code was missing.
|
|
|
|
// Since the skip logic is only in the old style import
|
|
// descriptor path, it is only duplicated here.
|
|
|
|
// check for import that has no references
|
|
|
|
FirstThunk = (PIMAGE_THUNK_DATA)((ULONG_PTR)LdrDataTableEntry->DllBase + ImportDescriptor->FirstThunk);
|
|
if (!FirstThunk->u1.Function) {
|
|
goto skipskippedimport;
|
|
}
|
|
|
|
ImportName = (PSZ)((ULONG_PTR)LdrDataTableEntry->DllBase + ImportDescriptor->Name);
|
|
|
|
RtlInitAnsiString(&AnsiString, ImportName);
|
|
st = RtlAnsiStringToUnicodeString(ImportDescriptorName_U, &AnsiString, FALSE);
|
|
if (NT_SUCCESS(st)) {
|
|
if (LdrpCheckForLoadedDll(NULL, ImportDescriptorName_U, TRUE, Wx86KnownDll, &Entry)) {
|
|
if (Entry->LoadCount != 0xffff) {
|
|
if (IncrementCount) {
|
|
Entry->LoadCount++;
|
|
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: Refcount %wZ (%lx)\n", ImportDescriptorName_U, (ULONG)Entry->LoadCount);
|
|
}
|
|
} else {
|
|
Entry->LoadCount--;
|
|
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: Derefcount %wZ (%lx)\n", ImportDescriptorName_U, (ULONG)Entry->LoadCount);
|
|
}
|
|
}
|
|
}
|
|
LdrpUpdateLoadCount(Entry, IncrementCount);
|
|
}
|
|
}
|
|
skipskippedimport:
|
|
++ImportDescriptor;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
PLDR_DATA_TABLE_ENTRY LdrpAllocateDataTableEntry(IN PVOID DllBase)
|
|
/*++
|
|
Routine Description:
|
|
This function allocates an entry in the loader data table. If the table is going to overflow, then a new table is allocated.
|
|
Arguments:
|
|
DllBase - Supplies the address of the base of the DLL Image. be added to the loader data table.
|
|
Return Value:
|
|
Returns the address of the allocated loader data table entry
|
|
--*/
|
|
{
|
|
PLDR_DATA_TABLE_ENTRY Entry;
|
|
PIMAGE_NT_HEADERS NtHeaders;
|
|
|
|
NtHeaders = RtlImageNtHeader(DllBase);
|
|
|
|
Entry = NULL;
|
|
if (NtHeaders) {
|
|
Entry = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(LDR_TAG) | HEAP_ZERO_MEMORY, sizeof(*Entry));
|
|
if (Entry) {
|
|
Entry->DllBase = DllBase;
|
|
Entry->SizeOfImage = NtHeaders->OptionalHeader.SizeOfImage;
|
|
Entry->TimeDateStamp = NtHeaders->FileHeader.TimeDateStamp;
|
|
}
|
|
}
|
|
|
|
return Entry;
|
|
}
|
|
|
|
|
|
VOID LdrpInsertMemoryTableEntry(IN PLDR_DATA_TABLE_ENTRY LdrDataTableEntry)
|
|
/*++
|
|
Routine Description:
|
|
This function inserts a loader data table entry into the
|
|
list of loaded modules for this process. The insertion is
|
|
done in "image memory base order".
|
|
Arguments:
|
|
LdrDataTableEntry - Supplies the address of the loader data table entry to insert in the list of loaded modules for this process.
|
|
--*/
|
|
{
|
|
PPEB_LDR_DATA Ldr;
|
|
ULONG i;
|
|
|
|
Ldr = NtCurrentPeb()->Ldr;
|
|
|
|
i = LDRP_COMPUTE_HASH_INDEX(LdrDataTableEntry->BaseDllName.Buffer[0]);
|
|
InsertTailList(&LdrpHashTable[i], &LdrDataTableEntry->HashLinks);
|
|
InsertTailList(&Ldr->InLoadOrderModuleList, &LdrDataTableEntry->InLoadOrderLinks);
|
|
InsertTailList(&Ldr->InMemoryOrderModuleList, &LdrDataTableEntry->InMemoryOrderLinks);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
LdrpResolveDllName(
|
|
IN PWSTR DllPath OPTIONAL,
|
|
IN PWSTR DllName,
|
|
OUT PUNICODE_STRING FullDllName,
|
|
OUT PUNICODE_STRING BaseDllName,
|
|
OUT PHANDLE DllFile
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This function computes the DLL pathname and base dll name (the unqualified, extensionless portion of the file name) for the specified DLL.
|
|
Arguments:
|
|
DllPath - Supplies the DLL search path.
|
|
DllName - Supplies the name of the DLL.
|
|
FullDllName - Returns the fully qualified pathname of the DLL.
|
|
The Buffer field of this string is dynamically allocated from the processes heap.
|
|
BaseDLLName - Returns the base dll name of the dll. The base name
|
|
is the file name portion of the dll path without the trailing
|
|
extension. The Buffer field of this string is dynamically
|
|
allocated from the processes heap.
|
|
DllFile - Returns an open handle to the DLL file. This parameter may still be NULL even upon success.
|
|
Return Value:
|
|
TRUE - The operation was successful.
|
|
A DLL file was found, and the FullDllName->Buffer & BaseDllName->Buffer field points to the base of process heap allocated memory.
|
|
FALSE - The DLL could not be found.
|
|
--*/
|
|
{
|
|
ULONG Length;
|
|
PWCH p, pp;
|
|
PWCH FullBuffer;
|
|
|
|
*DllFile = NULL;
|
|
FullDllName->Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TEMP_TAG), 530 + sizeof(UNICODE_NULL));
|
|
if (FullDllName->Buffer == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
Length = RtlDosSearchPath_U(
|
|
ARGUMENT_PRESENT(DllPath) ? DllPath : LdrpDefaultPath.Buffer,
|
|
DllName,
|
|
NULL,
|
|
530,
|
|
FullDllName->Buffer,
|
|
&BaseDllName->Buffer
|
|
);
|
|
if (!Length || Length > 530) {
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: LdrResolveDllName - Unable To Locate ");
|
|
DbgPrint("%ws from %ws\n", DllName, ARGUMENT_PRESENT(DllPath) ? DllPath : LdrpDefaultPath.Buffer);
|
|
}
|
|
|
|
RtlFreeUnicodeString(FullDllName);
|
|
return FALSE;
|
|
}
|
|
|
|
FullDllName->Length = (USHORT)Length;
|
|
FullDllName->MaximumLength = FullDllName->Length + (USHORT)sizeof(UNICODE_NULL);
|
|
FullBuffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(LDR_TAG), FullDllName->MaximumLength);
|
|
if (FullBuffer) {
|
|
RtlCopyMemory(FullBuffer, FullDllName->Buffer, FullDllName->MaximumLength);
|
|
RtlFreeHeap(RtlProcessHeap(), 0, FullDllName->Buffer);
|
|
FullDllName->Buffer = FullBuffer;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
|
|
// Compute Length of base dll name
|
|
|
|
pp = UNICODE_NULL;
|
|
p = FullDllName->Buffer;
|
|
while (*p) {
|
|
if (*p++ == (WCHAR)'\\') {
|
|
pp = p;
|
|
}
|
|
}
|
|
|
|
p = pp ? pp : DllName;
|
|
pp = p;
|
|
|
|
while (*p) {
|
|
++p;
|
|
}
|
|
|
|
BaseDllName->Length = (USHORT)((ULONG_PTR)p - (ULONG_PTR)pp);
|
|
BaseDllName->MaximumLength = BaseDllName->Length + (USHORT)sizeof(UNICODE_NULL);
|
|
BaseDllName->Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(LDR_TAG), BaseDllName->MaximumLength);
|
|
if (BaseDllName->Buffer) {
|
|
RtlMoveMemory(BaseDllName->Buffer, pp, BaseDllName->Length);
|
|
BaseDllName->Buffer[BaseDllName->Length >> 1] = UNICODE_NULL;
|
|
} else {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, FullBuffer);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
PVOID LdrpFetchAddressOfEntryPoint(IN PVOID Base)
|
|
/*++
|
|
Routine Description:
|
|
This function returns the address of the initialization routine.
|
|
Arguments:
|
|
Base - Base of image.
|
|
Return Value:
|
|
Status value
|
|
--*/
|
|
{
|
|
PIMAGE_NT_HEADERS NtHeaders;
|
|
ULONG_PTR ep;
|
|
|
|
NtHeaders = RtlImageNtHeader(Base);
|
|
ep = NtHeaders->OptionalHeader.AddressOfEntryPoint;
|
|
if (ep) {
|
|
ep += (ULONG_PTR)Base;
|
|
}
|
|
|
|
return (PVOID)ep;
|
|
}
|
|
|
|
|
|
HANDLE
|
|
LdrpCheckForKnownDll(
|
|
IN PWSTR DllName,
|
|
OUT PUNICODE_STRING FullDllName,
|
|
OUT PUNICODE_STRING BaseDllName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function checks to see if the specified DLL is a known DLL.
|
|
It assumes it is only called for static DLL's, and when
|
|
the know DLL directory structure has been set up.
|
|
|
|
Arguments:
|
|
|
|
DllName - Supplies the name of the DLL.
|
|
|
|
FullDllName - Returns the fully qualified pathname of the
|
|
DLL. The Buffer field of this string is dynamically
|
|
allocated from the processes heap.
|
|
|
|
BaseDLLName - Returns the base dll name of the dll. The base name
|
|
is the file name portion of the dll path without the trailing
|
|
extension. The Buffer field of this string is dynamically
|
|
allocated from the processes heap.
|
|
|
|
Return Value:
|
|
|
|
NON-NULL - Returns an open handle to the section associated with
|
|
the DLL.
|
|
|
|
NULL - The DLL is not known.
|
|
|
|
--*/
|
|
{
|
|
UNICODE_STRING Unicode;
|
|
HANDLE Section;
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
PSZ p;
|
|
PWSTR pw;
|
|
|
|
Section = NULL;
|
|
|
|
// calculate base name
|
|
RtlInitUnicodeString(&Unicode, DllName);
|
|
|
|
|
|
BaseDllName->Length = Unicode.Length;
|
|
BaseDllName->MaximumLength = Unicode.MaximumLength;
|
|
BaseDllName->Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(LDR_TAG), Unicode.MaximumLength);
|
|
if (!BaseDllName->Buffer) {
|
|
return NULL;
|
|
}
|
|
RtlMoveMemory(BaseDllName->Buffer, Unicode.Buffer, Unicode.MaximumLength);
|
|
|
|
|
|
// now compute the full name for the dll
|
|
FullDllName->Length = (USHORT)(LdrpKnownDllPath.Length + // path prefix
|
|
(USHORT)sizeof(WCHAR) + // seperator
|
|
BaseDllName->Length // base
|
|
);
|
|
|
|
FullDllName->MaximumLength = FullDllName->Length + (USHORT)sizeof(UNICODE_NULL);
|
|
FullDllName->Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(LDR_TAG), FullDllName->MaximumLength);
|
|
if (!FullDllName->Buffer) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, BaseDllName->Buffer);
|
|
return NULL;
|
|
}
|
|
|
|
p = (PSZ)FullDllName->Buffer;
|
|
RtlMoveMemory(p, LdrpKnownDllPath.Buffer, LdrpKnownDllPath.Length);
|
|
p += LdrpKnownDllPath.Length;
|
|
pw = (PWSTR)p;
|
|
*pw++ = (WCHAR)'\\';
|
|
p = (PSZ)pw;
|
|
|
|
|
|
// This is the relative name of the section
|
|
Unicode.Buffer = (PWSTR)p;
|
|
Unicode.Length = BaseDllName->Length; // base
|
|
Unicode.MaximumLength = Unicode.Length + (USHORT)sizeof(UNICODE_NULL);
|
|
|
|
RtlMoveMemory(p, BaseDllName->Buffer, BaseDllName->MaximumLength);
|
|
|
|
// open the section object
|
|
InitializeObjectAttributes(&Obja, &Unicode, OBJ_CASE_INSENSITIVE, LdrpKnownDllObjectDirectory, NULL);
|
|
Status = NtOpenSection(&Section, SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_MAP_WRITE, &Obja);
|
|
if (!NT_SUCCESS(Status)) {
|
|
Section = NULL;
|
|
RtlFreeHeap(RtlProcessHeap(), 0, BaseDllName->Buffer);
|
|
RtlFreeHeap(RtlProcessHeap(), 0, FullDllName->Buffer);
|
|
}
|
|
#if DBG
|
|
else {
|
|
LdrpSectionOpens++;
|
|
}
|
|
#endif // DBG
|
|
|
|
return Section;
|
|
}
|
|
|
|
|
|
NTSTATUS LdrpSetProtection(IN PVOID Base, IN BOOLEAN Reset, IN BOOLEAN StaticLink)
|
|
/*++
|
|
Routine Description:
|
|
This function loops thru the images sections/objects, setting all sections/objects marked r/o to r/w.
|
|
It also resets the original section/object protections.
|
|
Arguments:
|
|
Base - Base of image.
|
|
Reset - If TRUE, reset section/object protection to original
|
|
protection described by the section/object headers.
|
|
If FALSE, then set all sections/objects to r/w.
|
|
StaticLink - TRUE if this is a static link.
|
|
Return Value:
|
|
SUCCESS or reason NtProtectVirtualMemory failed.
|
|
--*/
|
|
{
|
|
HANDLE CurrentProcessHandle;
|
|
SIZE_T RegionSize;
|
|
ULONG NewProtect, OldProtect;
|
|
PVOID VirtualAddress;
|
|
ULONG i;
|
|
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
|
|
PIMAGE_NT_HEADERS NtHeaders;
|
|
PIMAGE_SECTION_HEADER SectionHeader;
|
|
NTSTATUS st;
|
|
|
|
CurrentProcessHandle = NtCurrentProcess();
|
|
|
|
NtHeaders = RtlImageNtHeader(Base);
|
|
|
|
#if defined (_ALPHA_)
|
|
if (NtHeaders->OptionalHeader.SectionAlignment < PAGE_SIZE) {
|
|
|
|
// if SectionAlignment < PAGE_SIZE the entire image is
|
|
// exec-copy on write, so we have nothing to do.
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
SectionHeader = (PIMAGE_SECTION_HEADER)((ULONG_PTR)NtHeaders + sizeof(ULONG) +
|
|
sizeof(IMAGE_FILE_HEADER) +
|
|
NtHeaders->FileHeader.SizeOfOptionalHeader
|
|
);
|
|
|
|
for (i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++) {
|
|
if (!(SectionHeader->Characteristics & IMAGE_SCN_MEM_WRITE) &&
|
|
(SectionHeader->SizeOfRawData)) {
|
|
// Object isn't writeable and has a non-zero on disk size, change it.
|
|
if (Reset) {
|
|
if (SectionHeader->Characteristics & IMAGE_SCN_MEM_EXECUTE) {
|
|
NewProtect = PAGE_EXECUTE;
|
|
} else {
|
|
NewProtect = PAGE_READONLY;
|
|
}
|
|
NewProtect |= (SectionHeader->Characteristics & IMAGE_SCN_MEM_NOT_CACHED) ? PAGE_NOCACHE : 0;
|
|
} else {
|
|
NewProtect = PAGE_READWRITE;
|
|
}
|
|
VirtualAddress = (PVOID)((ULONG_PTR)Base + SectionHeader->VirtualAddress);
|
|
RegionSize = SectionHeader->SizeOfRawData;
|
|
|
|
if (RegionSize != 0) {
|
|
st = NtProtectVirtualMemory(CurrentProcessHandle, &VirtualAddress, &RegionSize, NewProtect, &OldProtect);
|
|
if (!NT_SUCCESS(st)) {
|
|
return st;
|
|
}
|
|
}
|
|
|
|
}
|
|
++SectionHeader;
|
|
}
|
|
|
|
if (Reset) {
|
|
NtFlushInstructionCache(NtCurrentProcess(), NULL, 0);
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
// Page heap target dll logic
|
|
|
|
|
|
BOOLEAN LdrpDphKernel32Snapped;
|
|
BOOLEAN LdrpDphMsvcrtSnapped;
|
|
|
|
#define SNAP_ROUTINE_GLOBALALLOC 0
|
|
#define SNAP_ROUTINE_GLOBALREALLOC 1
|
|
#define SNAP_ROUTINE_GLOBALFREE 2
|
|
#define SNAP_ROUTINE_LOCALALLOC 3
|
|
#define SNAP_ROUTINE_LOCALREALLOC 4
|
|
#define SNAP_ROUTINE_LOCALFREE 5
|
|
#define SNAP_ROUTINE_HEAPALLOC 6
|
|
#define SNAP_ROUTINE_HEAPREALLOC 7
|
|
#define SNAP_ROUTINE_HEAPFREE 8
|
|
#define SNAP_ROUTINE_HEAPCREATE 9
|
|
#define SNAP_ROUTINE_MALLOC 10
|
|
#define SNAP_ROUTINE_CALLOC 11
|
|
#define SNAP_ROUTINE_REALLOC 12
|
|
#define SNAP_ROUTINE_FREE 13
|
|
#define SNAP_ROUTINE_NEW 14
|
|
#define SNAP_ROUTINE_DELETE 15
|
|
#define SNAP_ROUTINE_NEW_ARRAY 16
|
|
#define SNAP_ROUTINE_DELETE_ARRAY 17
|
|
#define SNAP_ROUTINE_MAX_INDEX 18
|
|
|
|
PVOID LdrpDphSnapRoutines[SNAP_ROUTINE_MAX_INDEX];
|
|
|
|
typedef struct _DPH_SNAP_NAME {
|
|
PSTR Name;
|
|
ULONG Index;
|
|
} DPH_SNAP_NAME, * PDPH_SNAP_NAME;
|
|
|
|
DPH_SNAP_NAME LdrpDphSnapNamesForKernel32[] = {
|
|
|
|
{ "GlobalAlloc", 0 },
|
|
{ "GlobalReAlloc", 1 },
|
|
{ "GlobalFree", 2 },
|
|
{ "LocalAlloc", 3 },
|
|
{ "LocalReAlloc", 4 },
|
|
{ "LocalFree", 5 },
|
|
{ "HeapAlloc", 6 },
|
|
{ "HeapReAlloc", 7 },
|
|
{ "HeapFree", 8 },
|
|
{ "HeapCreate", 9 },
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
DPH_SNAP_NAME LdrpDphSnapNamesForMsvcrt[] = {
|
|
|
|
{ "malloc", 10},
|
|
{ "calloc", 11},
|
|
{ "realloc", 12},
|
|
{ "free", 13},
|
|
{ "??2@YAPAXI@Z", 14}, // operator new
|
|
{ "??3@YAXPAX@Z", 15}, // operator delete
|
|
{ "??_U@YAPAXI@Z", 16}, // operator new[]
|
|
{ "??_V@YAXPAX@Z", 17}, // operator delete[]
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
|
|
// Declarations for replacement functions
|
|
|
|
|
|
PVOID RtlpDphDllHeapAlloc(IN PVOID HeapHandle, IN ULONG Flags, IN SIZE_T Size);
|
|
PVOID RtlpDphDllHeapReAlloc(IN PVOID HeapHandle, IN ULONG Flags, IN PVOID Address, IN SIZE_T Size);
|
|
BOOLEAN RtlpDphDllHeapFree(IN PVOID HeapHandle, IN ULONG Flags, IN PVOID Address);
|
|
PVOID RtlpDphDllLocalAlloc(IN ULONG Flags, IN SIZE_T Size);
|
|
PVOID RtlpDphDllLocalReAlloc(IN PVOID Address, IN SIZE_T Size, IN ULONG Flags);
|
|
PVOID RtlpDphDllLocalFree(IN PVOID Address);
|
|
PVOID RtlpDphDllGlobalAlloc(IN ULONG Flags, IN SIZE_T Size);
|
|
PVOID RtlpDphDllGlobalReAlloc(IN PVOID Address, IN SIZE_T Size, IN ULONG Flags);
|
|
PVOID RtlpDphDllGlobalFree(IN PVOID Address);
|
|
PVOID __cdecl RtlpDphDllmalloc(IN SIZE_T Size);
|
|
PVOID __cdecl RtlpDphDllcalloc(IN SIZE_T Number, IN SIZE_T Size);
|
|
PVOID __cdecl RtlpDphDllrealloc(IN PVOID Address, IN SIZE_T Size);
|
|
VOID __cdecl RtlpDphDllfree(IN PVOID Address);
|
|
PVOID __cdecl RtlpDphDllNew(IN SIZE_T Size);
|
|
VOID __cdecl RtlpDphDllDelete(IN PVOID Address);
|
|
PVOID __cdecl RtlpDphDllNewArray(IN SIZE_T Size);
|
|
VOID __cdecl RtlpDphDllDeleteArray(IN PVOID Address);
|
|
|
|
// Replacement function for msvcrt HeapCreate used to intercept the CRT heap creation.
|
|
PVOID RtlpDphDllHeapCreate(ULONG Options, SIZE_T InitialSize, SIZE_T MaximumSize);
|
|
|
|
// Address of heap created by msvcrt. This is needed by the replacements of malloc/free etc.
|
|
PVOID RtlpDphMsvcrtHeap;
|
|
|
|
|
|
// Snap implementation
|
|
|
|
|
|
BOOLEAN LdrpDphDetectSnapRoutines(PWSTR DllString, PDPH_SNAP_NAME SnapNames)
|
|
{
|
|
PLDR_DATA_TABLE_ENTRY DllData;
|
|
PIMAGE_EXPORT_DIRECTORY Directory;
|
|
ULONG Size;
|
|
PCHAR NameAddress;
|
|
PCHAR FunctionAddress;
|
|
PCHAR Base;
|
|
PCHAR IndexAddress;
|
|
ULONG Index;
|
|
ULONG RealIndex;
|
|
BOOLEAN Result;
|
|
UNICODE_STRING DllName;
|
|
PDPH_SNAP_NAME CurrentSnapName;
|
|
|
|
RtlInitUnicodeString(&DllName, DllString);
|
|
Result = LdrpCheckForLoadedDll(NULL, &DllName, TRUE, FALSE, &DllData);
|
|
if (Result == FALSE) {
|
|
return FALSE;
|
|
}
|
|
|
|
Base = DllData->DllBase;
|
|
|
|
Directory = RtlImageDirectoryEntryToData(DllData->DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &Size);
|
|
if (Directory == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
for (CurrentSnapName = SnapNames; CurrentSnapName->Name; CurrentSnapName += 1) {
|
|
for (Index = 0; Index < Directory->NumberOfFunctions; Index += 1) {
|
|
NameAddress = Base + Directory->AddressOfNames;
|
|
NameAddress = Base + ((ULONG *)NameAddress)[Index];
|
|
|
|
IndexAddress = Base + Directory->AddressOfNameOrdinals;
|
|
RealIndex = (ULONG)(((USHORT *)IndexAddress)[Index]);
|
|
|
|
if (_stricmp(NameAddress, CurrentSnapName->Name) == 0) {
|
|
FunctionAddress = Base + Directory->AddressOfFunctions;
|
|
FunctionAddress = Base + ((ULONG *)FunctionAddress)[RealIndex];
|
|
|
|
LdrpDphSnapRoutines[CurrentSnapName->Index] = FunctionAddress;
|
|
DbgPrint("Page heap: found %s @ address %p \n", NameAddress, FunctionAddress);
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOLEAN LdrpDphSnapImports(PLDR_DATA_TABLE_ENTRY LdrDataTableEntry, BOOLEAN CallToDetectCrtHeap)
|
|
{
|
|
PVOID IATBase;
|
|
SIZE_T BigIATSize;
|
|
ULONG LittleIATSize;
|
|
PVOID * ProcAddresses;
|
|
ULONG NumberOfProcAddresses;
|
|
ULONG OldProtect;
|
|
USHORT TagIndex;
|
|
NTSTATUS st;
|
|
|
|
// Determine the location and size of the IAT. If found, scan the
|
|
// IAT address to see if any are pointing to alloc/free functions and replace those thunks.
|
|
IATBase = RtlImageDirectoryEntryToData(LdrDataTableEntry->DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_IAT, &LittleIATSize);
|
|
|
|
BigIATSize = LittleIATSize;
|
|
|
|
if (IATBase != NULL) {
|
|
st = NtProtectVirtualMemory(NtCurrentProcess(), &IATBase, &BigIATSize, PAGE_READWRITE, &OldProtect);
|
|
if (!NT_SUCCESS(st)) {
|
|
DbgPrint("LDR: Unable to unprotect IAT to enable per DLL page heap.\n");
|
|
return FALSE;
|
|
} else {
|
|
ProcAddresses = (PVOID *)IATBase;
|
|
NumberOfProcAddresses = (ULONG)(BigIATSize / sizeof(PVOID));
|
|
while (NumberOfProcAddresses--) {
|
|
if (CallToDetectCrtHeap) {
|
|
if (*ProcAddresses == LdrpDphSnapRoutines[SNAP_ROUTINE_HEAPCREATE]) {
|
|
*ProcAddresses = RtlpDphDllHeapCreate;
|
|
DbgPrint("Snapped (%ws) HeapCreate ... \n", LdrDataTableEntry->BaseDllName.Buffer);
|
|
}
|
|
} else {
|
|
// ntdll imports
|
|
if (*ProcAddresses == RtlAllocateHeap) {
|
|
*ProcAddresses = RtlpDphDllHeapAlloc;
|
|
DbgPrint("Snapped (%ws) RtlAllocateHeap ... \n", LdrDataTableEntry->BaseDllName.Buffer);
|
|
} else if (*ProcAddresses == RtlReAllocateHeap) {
|
|
*ProcAddresses = RtlpDphDllHeapReAlloc;
|
|
DbgPrint("Snapped (%ws) RtlReAllocateHeap ... \n", LdrDataTableEntry->BaseDllName.Buffer);
|
|
} else if (*ProcAddresses == RtlFreeHeap) {
|
|
*ProcAddresses = RtlpDphDllHeapFree;
|
|
DbgPrint("Snapped (%ws) RtlFreeHeap ... \n", LdrDataTableEntry->BaseDllName.Buffer);
|
|
}
|
|
|
|
|
|
// kernel32 imports
|
|
else if (*ProcAddresses == LdrpDphSnapRoutines[SNAP_ROUTINE_HEAPALLOC]) {
|
|
*ProcAddresses = RtlpDphDllHeapAlloc;
|
|
DbgPrint("Snapped (%ws) HeapAlloc ... \n", LdrDataTableEntry->BaseDllName.Buffer);
|
|
} else if (*ProcAddresses == LdrpDphSnapRoutines[SNAP_ROUTINE_HEAPREALLOC]) {
|
|
*ProcAddresses = RtlpDphDllHeapReAlloc;
|
|
DbgPrint("Snapped (%ws) HeapReAlloc ... \n", LdrDataTableEntry->BaseDllName.Buffer);
|
|
} else if (*ProcAddresses == LdrpDphSnapRoutines[SNAP_ROUTINE_HEAPFREE]) {
|
|
*ProcAddresses = RtlpDphDllHeapFree;
|
|
DbgPrint("Snapped (%ws) HeapFree ... \n", LdrDataTableEntry->BaseDllName.Buffer);
|
|
} else if (*ProcAddresses == LdrpDphSnapRoutines[SNAP_ROUTINE_LOCALALLOC]) {
|
|
*ProcAddresses = RtlpDphDllLocalAlloc;
|
|
DbgPrint("Snapped (%ws) LocalAlloc ... \n", LdrDataTableEntry->BaseDllName.Buffer);
|
|
} else if (*ProcAddresses == LdrpDphSnapRoutines[SNAP_ROUTINE_LOCALREALLOC]) {
|
|
*ProcAddresses = RtlpDphDllLocalReAlloc;
|
|
DbgPrint("Snapped (%ws) LocalReAlloc ... \n", LdrDataTableEntry->BaseDllName.Buffer);
|
|
} else if (*ProcAddresses == LdrpDphSnapRoutines[SNAP_ROUTINE_LOCALFREE]) {
|
|
*ProcAddresses = RtlpDphDllLocalFree;
|
|
DbgPrint("Snapped (%ws) LocalFree ... \n", LdrDataTableEntry->BaseDllName.Buffer);
|
|
} else if (*ProcAddresses == LdrpDphSnapRoutines[SNAP_ROUTINE_GLOBALALLOC]) {
|
|
*ProcAddresses = RtlpDphDllGlobalAlloc;
|
|
DbgPrint("Snapped (%ws) GlobalAlloc ... \n", LdrDataTableEntry->BaseDllName.Buffer);
|
|
} else if (*ProcAddresses == LdrpDphSnapRoutines[SNAP_ROUTINE_GLOBALREALLOC]) {
|
|
*ProcAddresses = RtlpDphDllGlobalReAlloc;
|
|
DbgPrint("Snapped (%ws) GlobalReAlloc ... \n", LdrDataTableEntry->BaseDllName.Buffer);
|
|
} else if (*ProcAddresses == LdrpDphSnapRoutines[SNAP_ROUTINE_GLOBALFREE]) {
|
|
*ProcAddresses = RtlpDphDllGlobalFree;
|
|
DbgPrint("Snapped (%ws) GlobalFree ... \n", LdrDataTableEntry->BaseDllName.Buffer);
|
|
}
|
|
|
|
// msvcrt imports
|
|
|
|
else if (*ProcAddresses == LdrpDphSnapRoutines[SNAP_ROUTINE_MALLOC]) {
|
|
*ProcAddresses = RtlpDphDllmalloc;
|
|
DbgPrint("Snapped (%ws) malloc ... \n", LdrDataTableEntry->BaseDllName.Buffer);
|
|
} else if (*ProcAddresses == LdrpDphSnapRoutines[SNAP_ROUTINE_REALLOC]) {
|
|
*ProcAddresses = RtlpDphDllrealloc;
|
|
DbgPrint("Snapped (%ws) realloc ... \n", LdrDataTableEntry->BaseDllName.Buffer);
|
|
} else if (*ProcAddresses == LdrpDphSnapRoutines[SNAP_ROUTINE_CALLOC]) {
|
|
*ProcAddresses = RtlpDphDllcalloc;
|
|
DbgPrint("Snapped (%ws) calloc ... \n", LdrDataTableEntry->BaseDllName.Buffer);
|
|
} else if (*ProcAddresses == LdrpDphSnapRoutines[SNAP_ROUTINE_FREE]) {
|
|
*ProcAddresses = RtlpDphDllfree;
|
|
DbgPrint("Snapped (%ws) free ... \n", LdrDataTableEntry->BaseDllName.Buffer);
|
|
}
|
|
|
|
else if (*ProcAddresses == LdrpDphSnapRoutines[SNAP_ROUTINE_NEW]) {
|
|
*ProcAddresses = RtlpDphDllNew;
|
|
DbgPrint("Snapped (%ws) operator new ... \n", LdrDataTableEntry->BaseDllName.Buffer);
|
|
} else if (*ProcAddresses == LdrpDphSnapRoutines[SNAP_ROUTINE_DELETE]) {
|
|
*ProcAddresses = RtlpDphDllDelete;
|
|
DbgPrint("Snapped (%ws) operator delete ... \n", LdrDataTableEntry->BaseDllName.Buffer);
|
|
} else if (*ProcAddresses == LdrpDphSnapRoutines[SNAP_ROUTINE_NEW_ARRAY]) {
|
|
*ProcAddresses = RtlpDphDllNewArray;
|
|
DbgPrint("Snapped (%ws) operator new[] ... \n", LdrDataTableEntry->BaseDllName.Buffer);
|
|
} else if (*ProcAddresses == LdrpDphSnapRoutines[SNAP_ROUTINE_DELETE_ARRAY]) {
|
|
*ProcAddresses = RtlpDphDllDeleteArray;
|
|
DbgPrint("Snapped (%ws) operator delete[] ... \n", LdrDataTableEntry->BaseDllName.Buffer);
|
|
}
|
|
}
|
|
|
|
ProcAddresses += 1;
|
|
}
|
|
|
|
NtProtectVirtualMemory(NtCurrentProcess(), &IATBase, &BigIATSize, OldProtect, &OldProtect);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID LdrpDphInitializeTargetDll(PLDR_DATA_TABLE_ENTRY LoadedDllData)
|
|
{
|
|
BOOLEAN Kernel32JustSnapped = FALSE;
|
|
BOOLEAN MsvcrtJustSnapped = FALSE;
|
|
|
|
// If we do not have per dll page heap feature enabled we return immediately.
|
|
if (!(RtlpDphGlobalFlags & PAGE_HEAP_USE_DLL_NAMES)) {
|
|
return;
|
|
}
|
|
|
|
if (!LdrpDphKernel32Snapped) {
|
|
Kernel32JustSnapped = LdrpDphDetectSnapRoutines(L"kernel32.dll", LdrpDphSnapNamesForKernel32);
|
|
LdrpDphKernel32Snapped = Kernel32JustSnapped;
|
|
}
|
|
|
|
if (!LdrpDphMsvcrtSnapped) {
|
|
MsvcrtJustSnapped = LdrpDphDetectSnapRoutines(L"msvcrt.dll", LdrpDphSnapNamesForMsvcrt);
|
|
LdrpDphMsvcrtSnapped = MsvcrtJustSnapped;
|
|
}
|
|
|
|
// Snap everything already loaded if we just managed to detect snap routines.
|
|
if (Kernel32JustSnapped || MsvcrtJustSnapped) {
|
|
PWSTR Current;
|
|
PWSTR End;
|
|
WCHAR SavedChar;
|
|
PLDR_DATA_TABLE_ENTRY DllData;
|
|
BOOLEAN Result;
|
|
UNICODE_STRING DllName;
|
|
|
|
Current = RtlpDphTargetDlls;
|
|
|
|
while (*Current) {
|
|
while (*Current == L' ') {
|
|
Current += 1;
|
|
}
|
|
|
|
End = Current;
|
|
|
|
while (*End && *End != L' ') {
|
|
End += 1;
|
|
}
|
|
|
|
if (*Current == L'\0') {
|
|
break;
|
|
}
|
|
|
|
SavedChar = *End;
|
|
*End = L'\0';
|
|
|
|
RtlInitUnicodeString(&DllName, Current);
|
|
Result = LdrpCheckForLoadedDll(NULL, &DllName, TRUE, FALSE, &DllData);
|
|
if (Result) {
|
|
if (DllData->DllBase == LoadedDllData->DllBase) {
|
|
#if DBG
|
|
DbgPrint("Page heap: oversnapping %ws \n", DllData->BaseDllName);
|
|
#endif
|
|
}
|
|
|
|
LdrpDphSnapImports(DllData, FALSE);
|
|
}
|
|
|
|
*End = SavedChar;
|
|
Current = End;
|
|
}
|
|
}
|
|
|
|
|
|
// If we just loaded msvcrt.dll we need to redirect HeapCreate call
|
|
// in order to detect when the CRT heap gets created.
|
|
if (_wcsicmp(LoadedDllData->BaseDllName.Buffer, L"msvcrt.dll") == 0) {
|
|
LdrpDphSnapImports(LoadedDllData, TRUE);
|
|
}
|
|
|
|
// Call back into page heap manager to figure out if the
|
|
// currently loaded dll is a target for page heap.
|
|
if (RtlpDphIsDllTargeted(LoadedDllData->BaseDllName.Buffer)) {
|
|
LdrpDphSnapImports(LoadedDllData, FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
// Snap routines
|
|
|
|
|
|
|
|
// A biased heap pointer signals to the page heap manager that
|
|
// this allocation needs to get into page heap (not normal heap).
|
|
// This needs to happen only for allocation function (not free, delete).
|
|
|
|
|
|
#define BIAS_POINTER(p) ((PVOID)((ULONG_PTR)(p) | 0x01))
|
|
|
|
|
|
PVOID RtlpDphDllHeapAlloc(IN PVOID HeapHandle, IN ULONG Flags, IN SIZE_T Size)
|
|
{
|
|
return RtlpDebugPageHeapAllocate(BIAS_POINTER(HeapHandle), Flags, Size);
|
|
}
|
|
|
|
|
|
PVOID RtlpDphDllHeapReAlloc(IN PVOID HeapHandle, IN ULONG Flags, IN PVOID Address, IN SIZE_T Size)
|
|
{
|
|
return RtlpDebugPageHeapReAllocate(BIAS_POINTER(HeapHandle), Flags, Address, Size);
|
|
}
|
|
|
|
|
|
BOOLEAN RtlpDphDllHeapFree(IN PVOID HeapHandle, IN ULONG Flags, IN PVOID Address)
|
|
{
|
|
return RtlpDebugPageHeapFree(HeapHandle, Flags, Address);
|
|
}
|
|
|
|
|
|
// LocalAlloc, LocalReAlloc, LocalFree
|
|
// GlobalAlloc, GlobalReAlloc, GlobalFree
|
|
|
|
// The following macros are copied from sdk\inc\winbase.h
|
|
// There is very low probability that anybody will ever change
|
|
// these values for application compatibility reasons.
|
|
|
|
|
|
#define LMEM_MOVEABLE 0x0002
|
|
#define LMEM_ZEROINIT 0x0040
|
|
|
|
#if defined(_IA64_) || defined(_AXP64_)
|
|
#define BASE_HANDLE_MARK_BIT 0x08
|
|
#else
|
|
#define BASE_HANDLE_MARK_BIT 0x04
|
|
#endif
|
|
|
|
typedef PVOID(*FUN_LOCAL_ALLOC) (IN ULONG Flags, IN SIZE_T Size);
|
|
typedef PVOID(*FUN_LOCAL_REALLOC) (IN PVOID Address, IN SIZE_T Size, IN ULONG Flags);
|
|
typedef PVOID(*FUN_LOCAL_FREE)(IN PVOID Address);
|
|
typedef PVOID(*FUN_GLOBAL_ALLOC) (IN ULONG Flags, IN SIZE_T Size);
|
|
typedef PVOID(*FUN_GLOBAL_REALLOC) (IN PVOID Address, IN SIZE_T Size, IN ULONG Flags);
|
|
typedef PVOID(*FUN_GLOBAL_FREE)(IN PVOID Address);
|
|
|
|
|
|
PVOID RtlpDphDllLocalAlloc(IN ULONG Flags, IN SIZE_T Size)
|
|
{
|
|
PVOID Block;
|
|
FUN_LOCAL_ALLOC Original;
|
|
|
|
if (!(Flags & LMEM_MOVEABLE)) {
|
|
Block = RtlpDebugPageHeapAllocate(BIAS_POINTER(RtlProcessHeap()), 0, Size);
|
|
|
|
if ((Flags & LMEM_ZEROINIT)) {
|
|
RtlZeroMemory(Block, Size);
|
|
}
|
|
|
|
return Block;
|
|
} else {
|
|
Original = (FUN_LOCAL_ALLOC)(LdrpDphSnapRoutines[SNAP_ROUTINE_LOCALALLOC]);
|
|
return (*Original) (Flags, Size);
|
|
}
|
|
}
|
|
|
|
|
|
PVOID RtlpDphDllLocalReAlloc(IN PVOID Address, IN SIZE_T Size, IN ULONG Flags)
|
|
{
|
|
PVOID Block;
|
|
FUN_LOCAL_REALLOC Original;
|
|
|
|
if (!(Flags & LMEM_MOVEABLE)) {
|
|
Block = RtlpDebugPageHeapReAllocate(BIAS_POINTER(RtlProcessHeap()), 0, Address, Size);
|
|
return Block;
|
|
} else {
|
|
Original = (FUN_LOCAL_REALLOC)(LdrpDphSnapRoutines[SNAP_ROUTINE_LOCALREALLOC]);
|
|
return (*Original) (Address, Size, Flags);
|
|
}
|
|
}
|
|
|
|
|
|
PVOID RtlpDphDllLocalFree(IN PVOID Address)
|
|
{
|
|
BOOLEAN Result;
|
|
FUN_LOCAL_FREE Original;
|
|
|
|
if ((ULONG_PTR)Address & BASE_HANDLE_MARK_BIT) {
|
|
Original = (FUN_LOCAL_FREE)(LdrpDphSnapRoutines[SNAP_ROUTINE_LOCALFREE]);
|
|
return (*Original) (Address);
|
|
} else {
|
|
Result = RtlpDebugPageHeapFree(RtlProcessHeap(), 0, Address);
|
|
if (Result) {
|
|
return NULL;
|
|
} else {
|
|
return Address;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
PVOID RtlpDphDllGlobalAlloc(IN ULONG Flags, IN SIZE_T Size)
|
|
{
|
|
PVOID Block;
|
|
FUN_GLOBAL_ALLOC Original;
|
|
|
|
if (!(Flags & LMEM_MOVEABLE)) {
|
|
Block = RtlpDebugPageHeapAllocate(BIAS_POINTER(RtlProcessHeap()), 0, Size);
|
|
|
|
if ((Flags & LMEM_ZEROINIT)) {
|
|
RtlZeroMemory(Block, Size);
|
|
}
|
|
|
|
return Block;
|
|
} else {
|
|
Original = (FUN_GLOBAL_ALLOC)(LdrpDphSnapRoutines[SNAP_ROUTINE_GLOBALALLOC]);
|
|
return (*Original) (Flags, Size);
|
|
}
|
|
}
|
|
|
|
|
|
PVOID RtlpDphDllGlobalReAlloc(IN PVOID Address, IN SIZE_T Size, IN ULONG Flags)
|
|
{
|
|
PVOID Block;
|
|
FUN_GLOBAL_REALLOC Original;
|
|
|
|
if (!(Flags & LMEM_MOVEABLE)) {
|
|
Block = RtlpDebugPageHeapReAllocate(BIAS_POINTER(RtlProcessHeap()), 0, Address, Size);
|
|
return Block;
|
|
} else {
|
|
Original = (FUN_GLOBAL_REALLOC)(LdrpDphSnapRoutines[SNAP_ROUTINE_GLOBALREALLOC]);
|
|
return (*Original) (Address, Size, Flags);
|
|
}
|
|
}
|
|
|
|
|
|
PVOID RtlpDphDllGlobalFree(IN PVOID Address)
|
|
{
|
|
BOOLEAN Result;
|
|
FUN_GLOBAL_FREE Original;
|
|
|
|
if ((ULONG_PTR)Address & BASE_HANDLE_MARK_BIT) {
|
|
Original = (FUN_GLOBAL_FREE)(LdrpDphSnapRoutines[SNAP_ROUTINE_GLOBALFREE]);
|
|
return (*Original) (Address);
|
|
} else {
|
|
Result = RtlpDebugPageHeapFree(RtlProcessHeap(), 0, Address);
|
|
if (Result) {
|
|
return NULL;
|
|
} else {
|
|
return Address;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// malloc, calloc, realloc, free
|
|
|
|
|
|
PVOID __cdecl RtlpDphDllmalloc(IN SIZE_T Size)
|
|
{
|
|
PVOID Block;
|
|
|
|
ASSERT(RtlpDphMsvcrtHeap != NULL);
|
|
|
|
Block = RtlpDebugPageHeapAllocate(BIAS_POINTER(RtlpDphMsvcrtHeap), 0, Size);
|
|
|
|
return Block;
|
|
}
|
|
|
|
|
|
PVOID __cdecl RtlpDphDllcalloc(IN SIZE_T Number, IN SIZE_T Size)
|
|
{
|
|
PVOID Block;
|
|
|
|
ASSERT(RtlpDphMsvcrtHeap != NULL);
|
|
|
|
Block = RtlpDebugPageHeapAllocate(BIAS_POINTER(RtlpDphMsvcrtHeap), 0, Size * Number);
|
|
|
|
RtlZeroMemory(Block, Size * Number);
|
|
return Block;
|
|
}
|
|
|
|
|
|
PVOID __cdecl RtlpDphDllrealloc(IN PVOID Address, IN SIZE_T Size)
|
|
{
|
|
PVOID Block;
|
|
|
|
ASSERT(RtlpDphMsvcrtHeap != NULL);
|
|
|
|
if (Address == NULL) {
|
|
Block = RtlpDebugPageHeapAllocate(BIAS_POINTER(RtlpDphMsvcrtHeap), 0, Size);
|
|
} else {
|
|
Block = RtlpDebugPageHeapReAllocate(BIAS_POINTER(RtlpDphMsvcrtHeap), 0, Address, Size);
|
|
}
|
|
|
|
return Block;
|
|
}
|
|
|
|
|
|
VOID __cdecl RtlpDphDllfree(IN PVOID Address)
|
|
{
|
|
ASSERT(RtlpDphMsvcrtHeap != NULL);
|
|
|
|
RtlpDebugPageHeapFree(RtlpDphMsvcrtHeap, 0, Address);
|
|
}
|
|
|
|
|
|
// operator new, delete
|
|
// operator new[], delete[]
|
|
|
|
|
|
PVOID __cdecl RtlpDphDllNew(IN SIZE_T Size)
|
|
{
|
|
PVOID Block;
|
|
|
|
ASSERT(RtlpDphMsvcrtHeap != NULL);
|
|
|
|
Block = RtlpDebugPageHeapAllocate(BIAS_POINTER(RtlpDphMsvcrtHeap), 0, Size);
|
|
|
|
return Block;
|
|
}
|
|
|
|
|
|
VOID __cdecl RtlpDphDllDelete(IN PVOID Address)
|
|
{
|
|
ASSERT(RtlpDphMsvcrtHeap != NULL);
|
|
|
|
RtlpDebugPageHeapFree(RtlpDphMsvcrtHeap, 0, Address);
|
|
}
|
|
|
|
|
|
PVOID __cdecl RtlpDphDllNewArray(IN SIZE_T Size)
|
|
{
|
|
ASSERT(RtlpDphMsvcrtHeap != NULL);
|
|
|
|
return RtlpDebugPageHeapAllocate(BIAS_POINTER(RtlpDphMsvcrtHeap), 0, Size);
|
|
}
|
|
|
|
|
|
VOID __cdecl RtlpDphDllDeleteArray(IN PVOID Address)
|
|
{
|
|
ASSERT(RtlpDphMsvcrtHeap != NULL);
|
|
|
|
RtlpDebugPageHeapFree(RtlpDphMsvcrtHeap, 0, Address);
|
|
}
|
|
|
|
|
|
// HeapCreate
|
|
|
|
|
|
typedef PVOID(*FUN_HEAP_CREATE) (ULONG Options, SIZE_T InitialSize, SIZE_T MaximumSize);
|
|
|
|
PVOID RtlpDphDllHeapCreate(ULONG Options, SIZE_T InitialSize, SIZE_T MaximumSize)
|
|
{
|
|
PVOID Heap;
|
|
FUN_HEAP_CREATE Original;
|
|
|
|
Original = (FUN_HEAP_CREATE)(LdrpDphSnapRoutines[SNAP_ROUTINE_HEAPCREATE]);
|
|
Heap = (*Original) (Options, InitialSize, MaximumSize);
|
|
|
|
RtlpDphMsvcrtHeap = Heap;
|
|
DbgPrint("Page heap: detected CRT heap @ %p \n", RtlpDphMsvcrtHeap);
|
|
|
|
return Heap;
|
|
} |