Windows2000/private/ntos/dll/ldrwx86.c
2020-09-30 17:12:32 +02:00

2298 lines
80 KiB
C

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
ldrwx86.c
Abstract:
This module implements the wx86 specific ldr functions.
Author:
13-Jan-1995 Jonle , created
Revision History:
15-Oct-1998 CBiks Modified the code that throws the architecture
mismatch exception so the exception is only
thrown for NT 3,0 and lower executables. This was
changed to make the Wx86 loader behave like the
real loader, which does not throw this exception.
Also added a call to the cleanup function when LdrpWx86LoadDll() fails.
There were cases where the CPU failed to initialize but the Wx86 global pointers
were not cleared and pointed to a invalid memory because wx86.dll was unloaded.
--*/
#include "ntos.h"
#include "ldrp.h"
#define PAGE_SIZE_X86 (0x1000)
#if defined (WX86)
BOOLEAN(*Wx86ProcessInit)(PVOID, BOOLEAN) = NULL;
BOOLEAN(*Wx86DllMapNotify)(PVOID, BOOLEAN) = NULL;
BOOLEAN(*Wx86DllEntryPoint)(PDLL_INIT_ROUTINE, PVOID, ULONG, PCONTEXT) = NULL;
ULONG(*Wx86ProcessStartRoutine)(VOID) = NULL;
ULONG(*Wx86ThreadStartRoutine)(PVOID) = NULL;
BOOLEAN(*Wx86KnownDllName)(PUNICODE_STRING, PUNICODE_STRING) = NULL;
BOOLEAN(*Wx86KnownNativeDll)(PUNICODE_STRING) = NULL;
BOOLEAN(*Wx86KnownRedistDll)(PUNICODE_STRING) = NULL;
BOOLEAN Wx86OnTheFly = FALSE;
ULONG Wx86ProviderUnloadCount = 0;
WCHAR Wx86Dir[] = L"\\sys32x86";
UNICODE_STRING Wx86SystemDir = {0,0,NULL};
UNICODE_STRING NtSystemRoot = {0,0,NULL};
#if defined(BUILD_WOW6432)
// We want to accept only Alpha32 images as native. USER_SHARED_DATA
// will only include AXP64.
#define IsNativeMachineType(m) ((m) == IMAGE_FILE_MACHINE_ALPHA)
#else
#define IsNativeMachineType(m) ((m) >= USER_SHARED_DATA->ImageNumberLow && (m) <= USER_SHARED_DATA->ImageNumberHigh)
#endif
// Wx86 plugin support
typedef struct _Wx86Plugin {
LIST_ENTRY Links; // plugin list links
PVOID DllBase; // plugin dll base
ULONG Count; // number of providers
PVOID Provider[WX86PLUGIN_MAXPROVIDER]; // dll base of providers for this plugin
} WX86PLUGIN, * PWX86PLUGIN; // plugin list entry
LIST_ENTRY Wx86PluginList = { // plugin list head
&Wx86PluginList,
&Wx86PluginList
};
// Prototypes for plugin provider interfaces
typedef BOOLEAN(*WX86IDENTIFYPLUGIN)(PVOID PluginDll, WCHAR * FullFileName, BOOLEAN NativeToX86);
typedef BOOLEAN(*WX86THUNKEXPORT)(PVOID PluginDll,
PCHAR ExportName,
ULONG Ordinal,
PVOID ExportAddress,
PVOID * ExportThunk,
BOOLEAN NativeToX86
);
BOOLEAN DllHasExports(PVOID DllBase)
{
ULONG ExportSize;
PIMAGE_EXPORT_DIRECTORY ExportDir;
ExportDir = RtlImageDirectoryEntryToData(DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &ExportSize);
return ExportDir && ExportSize && (ExportDir->NumberOfFunctions || ExportDir->NumberOfNames);
}
BOOLEAN DllNameMatchesLdrEntry(PUNICODE_STRING BaseDllName,
PUNICODE_STRING FullDllName,
PLDR_DATA_TABLE_ENTRY LdrEntry,
BOOLEAN ImporterX86
)
/*++
Routine Description:
Verifies that the LdrEntry matches the specifed dll.
Arguments:
BaseDllName - Unicode string describing Base Name of the Dll.
FullDllName - Unicode string describing full path Name of the Dll.
Set FullDllName length to zero for no full path matching.
LdrEntry - loader information for dll found by basename compare.
ImporterX86 - TRUE if Importer is X86.
Return Value:
TRUE if any of the following conditions are met.
- FullDllName is same as LdrEntry FullDllName.
- Machine Type is the same.
- x86 importer AND LdrEntry is a Wx86 Risc thunk dll.
--*/
{
USHORT MachineType;
BOOLEAN FullNameMatches = FALSE;
PIMAGE_NT_HEADERS NtHeaders;
// The Base name must match.
if (!RtlEqualUnicodeString(BaseDllName, &LdrEntry->BaseDllName, TRUE)) {
return FALSE;
}
if (!FullDllName->Length ||
(FullDllName->Length && RtlEqualUnicodeString(FullDllName, &LdrEntry->FullDllName, TRUE))) {
FullNameMatches = TRUE;
}
// if we are not checking Machine Type, return based on FullName matching.
// LDRP_WX86_IGNORE_MACHINETYPE is used for images with no exports
// LDRP_WX86_PLUGIN is used when there's a plug provider that will
// dynamically generate thunks when GetProcAddress() is called.
if ((LdrEntry->Flags & LDRP_WX86_IGNORE_MACHINETYPE) || (LdrEntry->Flags & LDRP_WX86_PLUGIN)) {
return FullNameMatches;
}
NtHeaders = RtlImageNtHeader(LdrEntry->DllBase);
MachineType = NtHeaders->FileHeader.Machine;
if (ImporterX86) {
if (MachineType == IMAGE_FILE_MACHINE_I386) {
return FullNameMatches;
}
// Allow cross platform linking for x86 to risc Wx86 thunk
// dlls. All risc Wx86 Thunk dlls are marked as Wx86 Thunk dlls in the ntheader.
if (FullNameMatches) {
return (NtHeaders->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_X86_THUNK) != 0;
}
// The full name doesn't match, we can still allow matches
// for loads which were redirected from system32 to wx86 system dir (See LdrpWx86MapDll).
else {
UNICODE_STRING PathPart;
PathPart = LdrEntry->FullDllName;
PathPart.Length = LdrEntry->FullDllName.Length - LdrEntry->BaseDllName.Length - sizeof(WCHAR);
if (!RtlEqualUnicodeString(&PathPart, &Wx86SystemDir, TRUE)) {
return FALSE;
}
PathPart = *FullDllName;
PathPart.Length = FullDllName->Length - BaseDllName->Length - sizeof(WCHAR);
if (!RtlEqualUnicodeString(&PathPart, &LdrpKnownDllPath, TRUE)) {
return FALSE;
}
RtlCopyUnicodeString(FullDllName, &LdrEntry->FullDllName);
return TRUE;
}
}
// Importer is Risc.
if (IsNativeMachineType(MachineType)) {
return FullNameMatches;
}
return FALSE;
}
BOOLEAN SearchWx86Dll(IN PWSTR DllPath,
IN PUNICODE_STRING BaseName,
OUT PUNICODE_STRING FileName,
OUT PWSTR * pNextDllPath
)
/*++
Routine Description:
Search the path for a dll, based on Wx86 altered search path rules.
Arguments:
DllPath - search path to use.
BaseName - Name of dll to search for.
FileName - addr of Unicode string to fill in the found dll path name.
pNextDllPath - addr to fill in next path component to be searched.
--*/
{
PWCHAR pwch;
ULONG Length;
// formulate the name for each path component, and see if it exists.
Length = BaseName->Length + 2 * sizeof(WCHAR);
do {
pwch = FileName->Buffer;
// copy up till next semicolon
FileName->Length = 0;
while (*DllPath) {
if (FileName->MaximumLength <= FileName->Length + Length) {
return FALSE;
}
if (*DllPath == (WCHAR)';') {
DllPath++;
break;
}
*pwch++ = *DllPath++;
FileName->Length += sizeof(WCHAR);
}
// if we got a path component, append the basename and return if it exists.
if (FileName->Length) {
if (*(pwch - 1) != L'\\') {
*pwch = L'\\';
FileName->Length += sizeof(WCHAR);
}
}
RtlAppendUnicodeStringToString(FileName, BaseName);
if (RtlDoesFileExists_U(FileName->Buffer)) {
*pNextDllPath = DllPath;
return TRUE;
}
} while (*DllPath);
*pNextDllPath = DllPath;
return FALSE;
}
BOOLEAN LdrpWx86DllMapNotify(PVOID DllBase, BOOLEAN Mapped)
/*++
Routine Description:
Invoked by the nt loader immediately after an x86 Dll is Mapped or unmapped from memory.
This routine is not called at a point where
it is safe to load other dlls. That work should be deferred till
as late as possible before the x86 code is actually going to be executed.
Arguments:
DllBase - Base address of the DLL
Mapped - TRUE if the DLL is being mapped, FALSE if it is being unmapped.
Return Value:
FALSE on failure, TRUE on success.
--*/
{
return Wx86DllMapNotify && (*Wx86DllMapNotify)(DllBase, Mapped);
}
NTSTATUS LdrpWx86MapDll(
IN PWSTR DllPath OPTIONAL,
IN PULONG DllCharacteristics OPTIONAL,
IN BOOLEAN Wx86KnownDll,
IN BOOLEAN StaticLink,
OUT PUNICODE_STRING DllName,
OUT PLDR_DATA_TABLE_ENTRY * pEntry,
OUT ULONG_PTR * pViewSize,
OUT HANDLE * pSection
)
/*++
Routine Description:
Resolves dll name, creates image section and maps image into memory.
Arguments:
DllPath - Supplies the DLL search path.
DllCharacteristics - Supplies an optional DLL characteristics flag, that if specified is used to match against the dll being loaded.
(IMAGE_FILE_HEADER Characteristics)
Wx86KnownDll - if true, Importer is x86.
StaticLink - TRUE, if static link and not dynamic.
DllName - Name of Dll to map.
pEntry - returns filled LdrEntry allocated off of the process heap.
pViewSize - returns the View Size of mapped image.
pSection - returns the section handle.
Return Value:
Status
--*/
{
NTSTATUS st;
NTSTATUS stMapSection;
PWCHAR pwch;
PVOID ViewBase = NULL;
PTEB Teb = NtCurrentTeb();
PIMAGE_NT_HEADERS NtHeaders;
PVOID ArbitraryUserPointer;
PLDR_DATA_TABLE_ENTRY Entry;
BOOLEAN Wx86DirOverride = FALSE;
BOOLEAN Wx86DirUndone = FALSE;
BOOLEAN ContainsNoExports = FALSE;
UNICODE_STRING NameUnicode;
UNICODE_STRING FreeUnicode;
UNICODE_STRING FullNameUnicode;
UNICODE_STRING BaseNameUnicode;
UNICODE_STRING FullName;
HANDLE MismatchSection;
WCHAR FullNameBuffer[((DOS_MAX_PATH_LENGTH * 2 + 20) + sizeof(UNICODE_NULL)) / 2];
BOOLEAN Wx86Plugin;
BOOLEAN MismatchEncountered;
UNICODE_STRING ThunkDllName;
SECTION_IMAGE_INFORMATION SectionInfo;
UNICODE_STRING LocalDllName;
PUNICODE_STRING pLocalDllName;
PWSTR DllSearchPath;
PUNICODE_STRING ForcedDllName = NULL;
PUNICODE_STRING ForcedDllPath = NULL;
MismatchEncountered = FALSE;
FullName.Buffer = NULL;
Wx86Plugin = FALSE;
Entry = NULL;
FullNameUnicode.Buffer = FullNameBuffer;
FullNameUnicode.MaximumLength = sizeof(FullNameBuffer);
FullNameUnicode.Length = 0;
// If DllPath is not supplied, use the loader's default path
if (!DllPath) {
DllPath = LdrpDefaultPath.Buffer;
}
// DllPath can be ignored if:
// 1) DllPath is the null string
// or 2) DllName contains a hard coded path
if (!*DllPath || RtlDetermineDosPathNameType_U(DllName->Buffer) != RtlPathTypeRelative) {
DllPath = NULL;
}
// Alloc a chunk of memory to use in constructing the full
// dll name from the path and file name. Note that because
// a path component may contain relative references, it may exceed MAX_PATH.
FreeUnicode.Length = 0;
FreeUnicode.MaximumLength = (DOS_MAX_PATH_LENGTH * 2 + 20) + sizeof(UNICODE_NULL);
if (DllPath) {
FreeUnicode.MaximumLength += wcslen(DllPath) * sizeof(WCHAR);
}
FreeUnicode.Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TEMP_TAG), FreeUnicode.MaximumLength);
if (!FreeUnicode.Buffer) {
return STATUS_NO_MEMORY;
}
*pSection = NULL;
MismatchSection = NULL;
pLocalDllName = &LocalDllName;
DllSearchPath = DllPath;
// If we are looking for an x86 dll and it's base name is one of the
// Wx86 thunk dlls, map the Wx86 thunk dll instead
// X86 importers: force Wx86 system32 path before NtSystem32 path.
LocalDllName.Buffer = wcsrchr(DllName->Buffer, L'\\');
LocalDllName.Buffer = LocalDllName.Buffer ? &LocalDllName.Buffer[1] : DllName->Buffer;
LocalDllName.Length = wcslen(LocalDllName.Buffer) * sizeof(WCHAR);
LocalDllName.MaximumLength = LocalDllName.Length + sizeof(WCHAR);
pLocalDllName = DllName;
if (Wx86KnownDll) {
// Looking for x86 dll ...
if (Wx86KnownDllName(&LocalDllName, &ThunkDllName)) {
// It's a thunked dll. Redirect to system32\<thunk dll>
ForcedDllPath = &LdrpKnownDllPath;
ForcedDllName = &ThunkDllName; // use the thunked dll
} else if (Wx86KnownRedistDll(&LocalDllName)) {
// It's a redistributed x86 dll. Redirect to sys32x86\<dll name>
ForcedDllPath = &Wx86SystemDir;
ForcedDllName = &LocalDllName; // use the input dll name
}
}
// if ForcedDllPath is not NULL, disable path searching and retries.
if (ForcedDllPath) {
if (ShowSnaps) {
DbgPrint("LDRWX86: %s %wZ - force load from %wZ\\%wZ\n",
Wx86KnownDll ? "x86" : "native", DllName, ForcedDllPath, ForcedDllName);
}
RtlCopyUnicodeString(&FreeUnicode, ForcedDllPath);
FreeUnicode.Buffer[FreeUnicode.Length >> 1] = L'\\';
FreeUnicode.Length += sizeof(WCHAR);
RtlAppendUnicodeStringToString(&FreeUnicode, ForcedDllName);
pLocalDllName = &FreeUnicode;
DllSearchPath = NULL; // disable path searching
Wx86DirOverride = TRUE; // don't retry the load
}
// Loop as long as there are more directories in DllSearchPath
// or at least once if path searching is disabled.
while (TRUE) {
// Find the next occurance of the dll in the DllPath directories.
// If no DllPath then use DllName as provided
if (DllSearchPath) {
if (!SearchWx86Dll(DllSearchPath, pLocalDllName, &FreeUnicode, &DllSearchPath)) {
st = STATUS_DLL_NOT_FOUND;
break;
}
pwch = FreeUnicode.Buffer;
} else {
pwch = pLocalDllName->Buffer;
}
// Setup FullNameUnicode, BaseNameUnicode strings
FullNameUnicode.Length = (USHORT)RtlGetFullPathName_U(
pwch,
FullNameUnicode.MaximumLength,
FullNameUnicode.Buffer,
&BaseNameUnicode.Buffer
);
if (!FullNameUnicode.Length || FullNameUnicode.Length >= FullNameUnicode.MaximumLength) {
st = STATUS_OBJECT_PATH_SYNTAX_BAD;
break;
}
BaseNameUnicode.Length = FullNameUnicode.Length -
(USHORT)((ULONG_PTR)BaseNameUnicode.Buffer - (ULONG_PTR)FullNameUnicode.Buffer);
BaseNameUnicode.MaximumLength = BaseNameUnicode.Length + sizeof(WCHAR);
if (DllSearchPath && Wx86KnownDll && !Wx86DirOverride) {
NameUnicode = FullNameUnicode;
NameUnicode.Length -= BaseNameUnicode.Length + sizeof(WCHAR);
if (RtlEqualUnicodeString(&NameUnicode, &LdrpKnownDllPath, TRUE)) {
RtlCopyUnicodeString(&FreeUnicode, &Wx86SystemDir);
FreeUnicode.Buffer[FreeUnicode.Length >> 1] = L'\\';
FreeUnicode.Length += sizeof(WCHAR);
pwch = &FullNameUnicode.Buffer[FreeUnicode.Length >> 1];
RtlAppendUnicodeStringToString(&FreeUnicode, &BaseNameUnicode);
Wx86DirOverride = TRUE;
if (RtlDoesFileExists_U(FreeUnicode.Buffer)) {
RtlCopyUnicodeString(&FullNameUnicode, &FreeUnicode);
BaseNameUnicode.Buffer = pwch;
} else {
Wx86DirUndone = TRUE;
}
}
}
RetryWx86SystemDir:
// Create the image section.
if (!RtlDosPathNameToNtPathName_U(FullNameUnicode.Buffer, &NameUnicode, NULL, NULL)) {
st = STATUS_OBJECT_PATH_SYNTAX_BAD;
break;
}
if (ShowSnaps) {
DbgPrint("LDR: Loading (%s) %wZ\n", StaticLink ? "STATIC" : "DYNAMIC", &FullNameUnicode);
}
st = LdrpCreateDllSection(&NameUnicode, NULL, pLocalDllName, DllCharacteristics, pSection);
RtlFreeHeap(RtlProcessHeap(), 0, NameUnicode.Buffer);
if (!NT_SUCCESS(st)) {
break;
}
// Query the section info to discover its attributes
st = NtQuerySection(*pSection, SectionImageInformation, &SectionInfo, sizeof(SectionInfo), NULL);
if (!NT_SUCCESS(st)) {
break;
}
// MachineType is native type, allow:
// - if risc importer
// - if Wx86 thunk dlls
// - if image contains no exports,
// since Wx86 thunk dll not required (richedt32.dll).
if (IsNativeMachineType(SectionInfo.Machine)) {
if (!Wx86KnownDll || (SectionInfo.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_X86_THUNK)) {
break;
}
if (!SectionInfo.ImageContainsCode) {
ContainsNoExports = TRUE;
break;
}
}
// Machine Type is not native, allow:
// - if x86 importer, and machine type is x86
// - if image doesn't contain code,
// since its probably a resource\data dll only.
else {
if (SectionInfo.Machine == IMAGE_FILE_MACHINE_I386) {
if (Wx86KnownDll) {
break;
}
}
if (!SectionInfo.ImageContainsCode) {
ContainsNoExports = TRUE;
break;
}
}
// Failure because of an image machine mismatch.
// Save the mapped dll information for plugin processing or a hard
// error in case we can't find an image with matching machine type.
if (!MismatchEncountered) {
FullName.MaximumLength = FullNameUnicode.MaximumLength;
FullName.Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TEMP_TAG), FullName.MaximumLength);
if (!FullName.Buffer) {
st = STATUS_NO_MEMORY;
break;
}
st = NtDuplicateObject(NtCurrentProcess(),
*pSection,
NtCurrentProcess(),
&MismatchSection,
0,
FALSE,
DUPLICATE_SAME_ACCESS);
if (!NT_SUCCESS(st)) {
break;
}
RtlCopyUnicodeString(&FullName, &FullNameUnicode);
st = STATUS_INVALID_IMAGE_FORMAT;
MismatchEncountered = TRUE;
}
NtClose(*pSection);
*pSection = NULL;
// If we previously overid system32 with wx86 sys dir
// undo the override by retrying with system32.
if (DllSearchPath) {
if (Wx86DirOverride && !Wx86DirUndone) {
RtlCopyUnicodeString(&FullNameUnicode, &LdrpKnownDllPath);
FullNameUnicode.Buffer[FullNameUnicode.Length >> 1] = L'\\';
FullNameUnicode.Length += sizeof(WCHAR);
pwch = &FullNameUnicode.Buffer[FullNameUnicode.Length >> 1];
RtlAppendUnicodeStringToString(&FullNameUnicode, &BaseNameUnicode);
BaseNameUnicode.Buffer = pwch;
Wx86DirUndone = TRUE;
goto RetryWx86SystemDir;
}
}
// if x86 Importer, with hardcoded path to system32, retry with
// the Wx86 system directory. This is because some apps, erroneously
// derive the system32 path by appending system32 to WinDir, instead
// of calling GetSystemDir().
else if (Wx86KnownDll && !Wx86DirOverride) {
NameUnicode = FullNameUnicode;
NameUnicode.Length -= BaseNameUnicode.Length + sizeof(WCHAR);
if (RtlEqualUnicodeString(&NameUnicode, &LdrpKnownDllPath, TRUE)) {
RtlCopyUnicodeString(&FreeUnicode, &BaseNameUnicode);
RtlCopyUnicodeString(&FullNameUnicode, &Wx86SystemDir);
FullNameUnicode.Buffer[FullNameUnicode.Length >> 1] = L'\\';
FullNameUnicode.Length += sizeof(WCHAR);
BaseNameUnicode.Buffer = &FullNameUnicode.Buffer[FullNameUnicode.Length >> 1];
RtlAppendUnicodeStringToString(&FullNameUnicode, &FreeUnicode);
Wx86DirUndone = Wx86DirOverride = TRUE;
goto RetryWx86SystemDir;
}
}
// Try further down the path, for a matching machine type
// if no more path to search, we fail.
if (!DllSearchPath || !*DllSearchPath) {
break;
}
} // while (TRUE)
// Cleanup the temporary allocated buffers.
if (FreeUnicode.Buffer) {
RtlFreeHeap(RtlProcessHeap(), 0, FreeUnicode.Buffer);
}
if (MismatchEncountered && !StaticLink) {
if (NT_SUCCESS(st)) {
// Mismatch was encountered, but a DLL further along the path
// did match. Cleanup the mismatch stuff.
NtClose(MismatchSection);
MismatchSection = NULL;
MismatchEncountered = FALSE;
} else {
// No matching DLL found. Revert back to the image that gave the
// image type mismatch.
if (pSection) NtClose(*pSection);
*pSection = MismatchSection;
MismatchSection = NULL;
st = STATUS_SUCCESS;
}
}
if (NT_SUCCESS(st)) {
// Map the section into memory
*pViewSize = 0;
ViewBase = NULL;
ArbitraryUserPointer = Teb->NtTib.ArbitraryUserPointer;
Teb->NtTib.ArbitraryUserPointer = FullNameUnicode.Buffer;
st = NtMapViewOfSection(*pSection, NtCurrentProcess(), &ViewBase, 0L, 0L, NULL, pViewSize, ViewShare, 0L, PAGE_READWRITE);
// Save this return code because it may be STATUS_IMAGE_NOT_AT_BASE.
// This status must be returned so the dll will be relocated by the caller.
stMapSection = st;
Teb->NtTib.ArbitraryUserPointer = ArbitraryUserPointer;
if (!NT_SUCCESS(st)) {
goto LWMDGiveUp;
}
NtHeaders = RtlImageNtHeader(ViewBase);
if (NtHeaders == NULL) {
st = STATUS_INVALID_IMAGE_FORMAT;
goto LWMDGiveUp;
}
#if defined (_ALPHA_)
// Fix up non alpha compatible images
if (NtHeaders->OptionalHeader.SectionAlignment < PAGE_SIZE) {
NTSTATUS formatStatus = LdrpWx86FormatVirtualImage((PIMAGE_NT_HEADERS32)NtHeaders, ViewBase);
if (!NT_SUCCESS(formatStatus)) {
st = formatStatus;
goto LWMDGiveUp;
}
}
#endif
}
if (MismatchEncountered) {
#if defined (_ALPHA_)
if (!StaticLink) {
// Encountered a mismatch image type and didn't find another
// that matches. Attempt to find a plugin provider dll that
// can thunk this interface.
// If there are no errors in plugin processing:
// st = STATUS_SUCCESS if plugin was thunked
// st = STATUS_IMAGE_MACHINE_TYPE_MISMATCH if not thunked
// Otherwise st will contain the error from the plugin code
PLDR_DATA_TABLE_ENTRY Temp;
// temporarily insert the image in the loaded-module-list
Temp = LdrpAllocateDataTableEntry(ViewBase);
if (!Temp) {
st = STATUS_NO_MEMORY;
} else {
Temp->Flags = 0;
Temp->LoadCount = 0;
Temp->FullDllName = FullNameUnicode;
Temp->BaseDllName = BaseNameUnicode;
Temp->EntryPoint = LdrpFetchAddressOfEntryPoint(Temp->DllBase);
LdrpInsertMemoryTableEntry(Temp);
// Determine if this dll can be thunked using a plugin
st = Wx86IdentifyPlugin(ViewBase, &FullNameUnicode);
// remove the image from the loaded-module-list.
// It will be added again in the main-line path.
RemoveEntryList(&Temp->InLoadOrderLinks);
RemoveEntryList(&Temp->InMemoryOrderLinks);
RemoveEntryList(&Temp->HashLinks);
RtlFreeHeap(RtlProcessHeap(), 0, Temp);
Temp = NULL;
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", &FullNameUnicode, Action);
}
// If plug provider supports this dll flag it and restore
// status to the return code from original NtMapViewOfSection.
// Howver, don't revert to STATUS_IMAGE_MACHINE_TYPE_MISMATCH -
// that's what we just fixed up.
if (st == STATUS_SUCCESS) {
Wx86Plugin = TRUE;
if (stMapSection != STATUS_IMAGE_MACHINE_TYPE_MISMATCH) {
st = stMapSection;
}
}
}
}
#endif
if (st == STATUS_IMAGE_MACHINE_TYPE_MISMATCH) {
// Encountered a mismatch image type.
// Raise a Hard Error for the machine mismatch.
if (ShowSnaps) {
DbgPrint("Wx86 image type mismatch loading %ws (expected %s)\n",
FullName.Buffer,
Wx86KnownDll ? "x86" : "RISC"
);
}
if (NtHeaders->OptionalHeader.MajorSubsystemVersion <= 3) {
ULONG_PTR ErrorParameters[2];
ULONG ErrorResponse;
ErrorResponse = ResponseOk;
ErrorParameters[0] = (ULONG_PTR)&FullName;
NtRaiseHardError(STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE, 1, 1, ErrorParameters, OptionOk, &ErrorResponse);
}
st = STATUS_INVALID_IMAGE_FORMAT;
}
}
// if we were successfull,
// allocate and fill FullDllName, BaseDllName for the caller.
if (NT_SUCCESS(st)) {
PUNICODE_STRING Unicode;
if (st == STATUS_IMAGE_MACHINE_TYPE_MISMATCH) {
st = NtHeaders->OptionalHeader.ImageBase == (ULONG_PTR)ViewBase ? STATUS_SUCCESS : STATUS_IMAGE_NOT_AT_BASE;
}
*pEntry = Entry = LdrpAllocateDataTableEntry(ViewBase);
if (!Entry) {
st = STATUS_NO_MEMORY;
goto LWMDGiveUp;
}
// Fil in loader entry
Entry->Flags = StaticLink ? LDRP_STATIC_LINK : 0;
if (ContainsNoExports) {
Entry->Flags |= LDRP_WX86_IGNORE_MACHINETYPE;
}
if (Wx86Plugin) {
Entry->Flags |= LDRP_WX86_PLUGIN;
}
Entry->LoadCount = 0;
Entry->EntryPoint = LdrpFetchAddressOfEntryPoint(ViewBase);
Entry->FullDllName.Buffer = NULL;
Entry->BaseDllName.Buffer = NULL;
// Copy in the full dll name
Unicode = &Entry->FullDllName;
Unicode->Length = FullNameUnicode.Length;
Unicode->MaximumLength = Unicode->Length + sizeof(UNICODE_NULL);
Unicode->Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(LDR_TAG), Unicode->MaximumLength);
if (!Unicode->Buffer) {
st = STATUS_NO_MEMORY;
goto LWMDGiveUp;
}
RtlCopyMemory(Unicode->Buffer, FullNameUnicode.Buffer, Unicode->MaximumLength);
// Copy in the basename
Unicode = &Entry->BaseDllName;
Unicode->Length = BaseNameUnicode.Length;
Unicode->MaximumLength = Unicode->Length + sizeof(UNICODE_NULL);
Unicode->Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(LDR_TAG), Unicode->MaximumLength);
if (Unicode->Buffer) {
RtlCopyMemory(Unicode->Buffer, BaseNameUnicode.Buffer, Unicode->MaximumLength);
} else {
st = STATUS_NO_MEMORY;
}
}
LWMDGiveUp:
// cleanup items saved from mismatched images
if (MismatchSection) {
NtClose(MismatchSection);
}
if (FullName.Buffer) {
RtlFreeHeap(RtlProcessHeap(), 0, FullName.Buffer);
}
// If failure, cleanup mapview and section.
if (!NT_SUCCESS(st)) {
if (ViewBase) {
NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
}
if (*pSection) {
NtClose(*pSection);
}
if (Entry) {
if (Entry->FullDllName.Buffer) {
RtlFreeHeap(RtlProcessHeap(), 0, Entry->FullDllName.Buffer);
}
if (Entry->BaseDllName.Buffer) {
RtlFreeHeap(RtlProcessHeap(), 0, Entry->BaseDllName.Buffer);
}
RtlFreeHeap(RtlProcessHeap(), 0, Entry);
*pEntry = NULL;
}
}
return st;
}
PLDR_DATA_TABLE_ENTRY LdrpWx86CheckForLoadedDll(IN PWSTR DllPath OPTIONAL,
IN PUNICODE_STRING DllName,
IN BOOLEAN Wx86KnownDll,
OUT PUNICODE_STRING FullDllName
)
/*++
Routine Description:
Checks for loaded dlls, ensuring that duplicate module
base names are resolved correctly
Arguments:
DllPath - optional search path used to locate the DLL.
DllName - Name of Dll
Wx86KnownDll - if true, Importer is x86.
FullDllName - buffer to receive full path name, assumes STATIC_UNICODE_BUFFER_LENGTH
Return Value:
LdrEntry for dllname if found, otherwise NULL.
--*/
{
NTSTATUS Status;
int Index, Length;
PWCHAR pwch;
PLIST_ENTRY Head, Next;
PLDR_DATA_TABLE_ENTRY LdrEntry;
BOOLEAN HardCodedPath = FALSE;
UNICODE_STRING BaseDllName;
UNICODE_STRING FreeUnicode;
UNICODE_STRING ThunkDllName;
UNICODE_STRING LocalDllName;
PUNICODE_STRING ForcedDllPath;
PUNICODE_STRING ForcedDllName;
// If DllPath is not supplied, use the loader's default path
if (!DllPath) {
DllPath = LdrpDefaultPath.Buffer;
}
// DllPath can be ignored if:
// 1) DllPath is unusable (=1)
// or 2) DllPath is the null string
// or 3) DllName contains a hard coded path
if ((UINT_PTR)DllPath == 1 || !*DllPath || RtlDetermineDosPathNameType_U(DllName->Buffer) != RtlPathTypeRelative) {
DllPath = NULL;
}
// Alloc a chunk of memory to use in constructing the full
// dll name from the path and file name. Note that because
// a path component may contain relative references, it may
// exceed MAX_PATH.
FreeUnicode.Length = 0;
FreeUnicode.MaximumLength = (DOS_MAX_PATH_LENGTH * 2 + 20) * sizeof(UNICODE_NULL);
if (DllPath) {
FreeUnicode.MaximumLength = wcslen(DllPath) * sizeof(WCHAR);
}
FreeUnicode.Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TEMP_TAG), FreeUnicode.MaximumLength);
if (!FreeUnicode.Buffer) {
return NULL;
}
// Checking for known dlls before searching for the file improves performance
// This code was moved from the do loop.
FullDllName->Length = 0;
BaseDllName = *DllName;
LocalDllName.Buffer = wcsrchr(BaseDllName.Buffer, L'\\');
LocalDllName.Buffer = LocalDllName.Buffer ? &LocalDllName.Buffer[1] : BaseDllName.Buffer;
LocalDllName.Length = wcslen(LocalDllName.Buffer) * sizeof(WCHAR);
LocalDllName.MaximumLength = LocalDllName.Length + sizeof(WCHAR);
ForcedDllPath = NULL;
ForcedDllName = NULL;
if (Wx86KnownDll) { // if the call is from x86 apps
// Looking for x86 dll ...
if (Wx86KnownDllName(&LocalDllName, &ThunkDllName)) {
// It's a thunked dll. Redirect to system32\<thunk dll>
ForcedDllPath = &LdrpKnownDllPath;
ForcedDllName = &ThunkDllName; // use the thunked dll
} else if (Wx86KnownRedistDll(&LocalDllName)) {
// It's a redistributed x86 dll. Redirect to sys32x86\<dll name>
ForcedDllPath = &Wx86SystemDir;
ForcedDllName = &LocalDllName; // use the input dll name
}
} else if (Wx86KnownNativeDll(&LocalDllName)) {
// It's a native known dll. Redirect to system32\<dll name>
ForcedDllPath = &LdrpKnownDllPath;
ForcedDllName = &LocalDllName;
}
// If ForcedDllPath is not null, then we either need to find the dll at the
// predetermined path or we return failed.
if (ForcedDllPath) {
RtlCopyUnicodeString(FullDllName, ForcedDllPath);
FullDllName->Buffer[FullDllName->Length / 2] = L'\\';
FullDllName->Length += sizeof(WCHAR);
BaseDllName.Buffer = &FullDllName->Buffer[FullDllName->Length / 2];
BaseDllName.Length = ForcedDllName->Length;
BaseDllName.MaximumLength = BaseDllName.Length + sizeof(WCHAR);
RtlAppendUnicodeStringToString(FullDllName, ForcedDllName);
Index = LDRP_COMPUTE_HASH_INDEX(BaseDllName.Buffer[0]);
Head = &LdrpHashTable[Index];
Next = Head->Flink;
while (Next != Head) {
LdrEntry = CONTAINING_RECORD(Next, LDR_DATA_TABLE_ENTRY, HashLinks);
if (DllNameMatchesLdrEntry(&BaseDllName, FullDllName, LdrEntry, Wx86KnownDll)) {
goto FoundMatch;
}
Next = Next->Flink;
// not a Wx86 Known dll or Wx86 Known dll is not loaded.
}
return NULL;
}
// Search the DllPath, and verify that fullpath and machine type match.
do {
if (DllPath) {
if (!SearchWx86Dll(DllPath, DllName, &FreeUnicode, &DllPath)) {
FullDllName->Length = 0;
FullDllName->Buffer[0] = L'\0';
BaseDllName = *DllName;
Index = LDRP_COMPUTE_HASH_INDEX(BaseDllName.Buffer[0]);
Head = &LdrpHashTable[Index];
Next = Head->Flink;
while (Next != Head) {
LdrEntry = CONTAINING_RECORD(Next, LDR_DATA_TABLE_ENTRY, HashLinks);
if (DllNameMatchesLdrEntry(&BaseDllName, FullDllName, LdrEntry, Wx86KnownDll)) {
goto FoundMatch;
}
Next = Next->Flink;
}
break;
}
pwch = FreeUnicode.Buffer;
} else {
pwch = DllName->Buffer;
}
// Form the fullpathname
FullDllName->Length = 0;
Length = RtlGetFullPathName_U(pwch,
FullDllName->MaximumLength,
FullDllName->Buffer,
&pwch // receives address of file name portion
);
if (Length && Length < FullDllName->MaximumLength) {
UNICODE_STRING PathPart;
// Setup BaseDllName as the file name portion of FullDllName
FullDllName->Length = (USHORT)Length;
RtlInitUnicodeString(&BaseDllName, pwch);
// Setup PathPart as the path portion of FullDllName
PathPart = *FullDllName;
PathPart.Length = (USHORT)((ULONG_PTR)BaseDllName.Buffer - (ULONG_PTR)FullDllName->Buffer - sizeof(WCHAR));
// Search Loader HashTable by BaseName.
// For each matching basename, verify the full path and machine type.
Index = LDRP_COMPUTE_HASH_INDEX(BaseDllName.Buffer[0]);
Head = &LdrpHashTable[Index];
Next = Head->Flink;
while (Next != Head) {
LdrEntry = CONTAINING_RECORD(Next, LDR_DATA_TABLE_ENTRY, HashLinks);
if (DllNameMatchesLdrEntry(&BaseDllName, FullDllName, LdrEntry, Wx86KnownDll)) {
goto FoundMatch;
}
Next = Next->Flink;
}
// TBD: need to add code from LdrpCheckForLoadedDll
// 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
}
} while (DllPath && *DllPath);
LdrEntry = NULL;
FoundMatch:
RtlFreeHeap(RtlProcessHeap(), 0, FreeUnicode.Buffer);
return LdrEntry;
}
VOID LdrpWx86DllProcessDetach(IN PLDR_DATA_TABLE_ENTRY LdrDataTableEntry)
/*++
Routine Description:
Handles process detach for LdrUnload
Arguments:
InitRoutine - address of i386 dll entry point
DllBase - standard dll entry point parameters
Return Value:
SUCCESS or reason
--*/
{
PIMAGE_NT_HEADERS NtHeader;
PDLL_INIT_ROUTINE InitRoutine;
// check for all x86dlls unloaded
NtHeader = RtlImageNtHeader(LdrDataTableEntry->DllBase);
InitRoutine = (PDLL_INIT_ROUTINE)LdrDataTableEntry->EntryPoint;
if (InitRoutine && (LdrDataTableEntry->Flags & LDRP_PROCESS_ATTACH_CALLED)) {
if (ShowSnaps) {
DbgPrint("WX86LDR: Calling deinit %lx\n", InitRoutine);
}
if (NtHeader->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) {
(Wx86DllEntryPoint)(InitRoutine, LdrDataTableEntry->DllBase, DLL_PROCESS_DETACH, NULL);
} else {
LdrpCallInitRoutine(InitRoutine, LdrDataTableEntry->DllBase, DLL_PROCESS_DETACH, NULL);
}
}
}
NTSTATUS LdrpRunWx86DllEntryPoint(
IN PDLL_INIT_ROUTINE InitRoutine,
OUT BOOLEAN * pInitStatus,
IN PVOID DllBase,
IN ULONG Reason,
IN PCONTEXT Context
)
/*++
Routine Description:
Invokes the i386 emulator (wx86.dll) to run dll entry points.
Arguments:
InitRoutine - address of i386 dll entry point
pInitStatus - receives return code from the InitRoutine
DllBase - standard dll entry point parameters
Reason
Context
Return Value:
SUCCESS or reason
--*/
{
PIMAGE_NT_HEADERS NtHeader = NULL;
BOOLEAN InitStatus;
PWX86TIB Wx86Tib;
NtHeader = RtlImageNtHeader(DllBase);
if (NtHeader && NtHeader->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) {
InitStatus = (Wx86DllEntryPoint)(InitRoutine, DllBase, Reason, Context);
if (pInitStatus) {
*pInitStatus = InitStatus;
}
return STATUS_SUCCESS;
}
return STATUS_IMAGE_MACHINE_TYPE_MISMATCH;
}
NTSTATUS LoaderWx86Unload(VOID)
/*++
Routine Description:
When Wx86.dll process detach routine executes, Wx86.dll invokes this routine
so that the loader can cleanup Wx86 specific stuff.
Arguments:
none
Return Value:
SUCCESS or reason
--*/
{
Wx86ProcessInit = NULL;
Wx86DllMapNotify = NULL;
Wx86DllEntryPoint = NULL;
Wx86ProcessStartRoutine = NULL;
Wx86ThreadStartRoutine = NULL;
Wx86KnownDllName = NULL;
Wx86KnownNativeDll = NULL;
Wx86KnownRedistDll = NULL;
Wx86OnTheFly = FALSE;
if (Wx86SystemDir.Buffer) {
RtlFreeHeap(RtlProcessHeap(), 0, Wx86SystemDir.Buffer);
Wx86SystemDir.Buffer = NULL;
}
return STATUS_SUCCESS;
}
NTSTATUS LdrpLoadWx86Dll(PCONTEXT Context)
/*++
Routine Description:
Loads in the i386 emulator (wx86.dll) and performs process initialization for wx86 specific ldr code.
Arguments:
Context, initial process context,
if hibit set this is ofly init after process initialzation is complete
(Wx86 on the fly), and the PCONTEXT == Wx86DllHandle | 0x80000000
Return Value:
SUCCESS or reason
--*/
{
NTSTATUS st;
ULONG Length;
PVOID DllHandle;
ANSI_STRING ProcName;
UNICODE_STRING DllName;
WCHAR Buffer[STATIC_UNICODE_BUFFER_LENGTH];
// Retrieve the dll handle for the ofly case
DllHandle = (PVOID)((UINT_PTR)Context & ~0x80000000);
if (DllHandle != Context) {
Context = NULL;
} else {
DllHandle = NULL;
}
// initialize Wx86SystemDir
RtlInitUnicodeString(&NtSystemRoot, USER_SHARED_DATA->NtSystemRoot);
Wx86SystemDir.MaximumLength = NtSystemRoot.Length + sizeof(Wx86Dir);
Wx86SystemDir.Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(LDR_TAG), Wx86SystemDir.MaximumLength);
if (!Wx86SystemDir.Buffer) {
st = STATUS_NO_MEMORY;
goto LWx86DllError;
}
RtlCopyUnicodeString(&Wx86SystemDir, &NtSystemRoot);
st = RtlAppendUnicodeToString(&Wx86SystemDir, Wx86Dir);
if (!NT_SUCCESS(st)) {
goto LWx86DllError;
}
// Load Wx86.dll, wintdll.dll. This must be done before the app binary is snapped to ensure wx86.dll is ready for emulation.
if (!DllHandle) {
DllName.Buffer = Buffer;
DllName.MaximumLength = sizeof(Buffer);
RtlCopyUnicodeString(&DllName, &LdrpKnownDllPath);
DllName.Buffer[DllName.Length / sizeof(WCHAR)] = L'\\';
DllName.Length += sizeof(WCHAR);
RtlAppendUnicodeToString(&DllName, L"wx86.dll");
st = LdrpLoadDll(NULL, NULL, &DllName, &DllHandle, TRUE);
if (!NT_SUCCESS(st)) {
goto LWx86DllError;
}
}
// Get fn address from Wx86.dll
RtlInitAnsiString(&ProcName, "Wx86KnownDllName");
st = LdrGetProcedureAddress(DllHandle, &ProcName, 0, (PVOID *)&Wx86KnownDllName);
if (!NT_SUCCESS(st)) {
goto LWx86DllError;
}
RtlInitAnsiString(&ProcName, "Wx86KnownNativeDll");
st = LdrGetProcedureAddress(DllHandle, &ProcName, 0, (PVOID *)&Wx86KnownNativeDll);
if (!NT_SUCCESS(st)) {
goto LWx86DllError;
}
RtlInitAnsiString(&ProcName, "Wx86KnownRedistDll");
st = LdrGetProcedureAddress(DllHandle, &ProcName, 0, (PVOID *)&Wx86KnownRedistDll);
if (!NT_SUCCESS(st)) {
goto LWx86DllError;
}
RtlInitAnsiString(&ProcName, "RunWx86DllEntryPoint");
st = LdrGetProcedureAddress(DllHandle, &ProcName, 0, (PVOID *)&Wx86DllEntryPoint);
if (!NT_SUCCESS(st)) {
goto LWx86DllError;
}
RtlInitAnsiString(&ProcName, "Wx86ThreadStartRoutine");
st = LdrGetProcedureAddress(DllHandle, &ProcName, 0, (PVOID *)&Wx86ThreadStartRoutine);
if (!NT_SUCCESS(st)) {
goto LWx86DllError;
}
RtlInitAnsiString(&ProcName, "Wx86ProcessStartRoutine");
st = LdrGetProcedureAddress(DllHandle, &ProcName, 0, (PVOID *)&Wx86ProcessStartRoutine);
if (!NT_SUCCESS(st)) {
goto LWx86DllError;
}
RtlInitAnsiString(&ProcName, "Wx86DllMapNotify");
st = LdrGetProcedureAddress(DllHandle, &ProcName, 0, (PVOID *)&Wx86DllMapNotify);
if (!NT_SUCCESS(st)) {
goto LWx86DllError;
}
RtlInitAnsiString(&ProcName, "Wx86ProcessInit");
st = LdrGetProcedureAddress(DllHandle, &ProcName, 0, (PVOID *)&Wx86ProcessInit);
if (!NT_SUCCESS(st)) {
goto LWx86DllError;
}
if (Context) {
st = LdrpInitWx86(NtCurrentTeb()->Vdm, Context, FALSE);
if (!NT_SUCCESS(st)) {
goto LWx86DllError;
}
} else {
Wx86OnTheFly = TRUE;
}
if (!(*Wx86ProcessInit)(LoaderWx86Unload, Wx86OnTheFly)) {
st = STATUS_ENTRYPOINT_NOT_FOUND;
}
LWx86DllError:
if (!NT_SUCCESS(st)) {
LoaderWx86Unload();// If the load failed make sure we clean-up.
}
return st;
}
NTSTATUS LdrpInitWx86(PWX86TIB Wx86Tib, PCONTEXT Context, BOOLEAN NewThread)
/*++
Routine Description:
Per thread wx86 specific initialization.
Return Value:
SUCCESS or reason
--*/
{
PTEB Teb;
MEMORY_BASIC_INFORMATION MemBasicInfo;
if (Wx86Tib != Wx86CurrentTib()) {
return STATUS_APP_INIT_FAILURE;
}
if (ShowSnaps) {
DbgPrint("LDRWX86: %x Pc %x Base %x Limit %x DeallocationStack %x\n",
Wx86Tib,
Wx86Tib->InitialPc,
Wx86Tib->StackBase,
Wx86Tib->StackLimit,
Wx86Tib->DeallocationStack
);
}
if (Wx86Tib->EmulateInitialPc) {
Wx86Tib->EmulateInitialPc = FALSE;
if (NewThread) {
#if defined(_MIPS_)
Context->XIntA0 = (LONG)Wx86ThreadStartRoutine;
#elif defined(_ALPHA_)
Context->IntA0 = (ULONG_PTR)Wx86ThreadStartRoutine;
#elif defined(_PPC_)
Context->Gpr3 = (ULONG)Wx86ThreadStartRoutine;
#elif defined(_IA64_)
Context->IntS0 = Context->StIIP = (ULONG_PTR)Wx86ThreadStartRoutine;
#else
#error Need to set instruction pointer to Wx86ThreadStartRoutine
#endif
} else {
#if defined(_MIPS_)
Context->XIntA1 = (LONG)Wx86ProcessStartRoutine;
#elif defined(_ALPHA_)
Context->IntA0 = (ULONG_PTR)Wx86ProcessStartRoutine;
#elif defined(_PPC_)
Context->Gpr3 = (ULONG)Wx86ProcessStartRoutine;
#elif defined(_IA64_)
Context->IntS0 = Context->StIIP = (ULONG_PTR)Wx86ProcessStartRoutine;
#else
#error Need to set instruction pointer to Wx86ProcessStartRoutine
#endif
}
}
return STATUS_SUCCESS;
}
#endif
#if defined (_ALPHA_) || defined(BUILD_WOW6432)
// From mi\mi.h:
#define MI_ROUND_TO_SIZE(LENGTH,ALIGNMENT) \
(((LENGTH) + ((ALIGNMENT) - 1)) & ~((ALIGNMENT) - 1))
NTSTATUS Wx86SetRelocatedSharedProtection(IN PVOID Base, IN BOOLEAN Reset)
/*++
Routine Description:
This function loops thru the images sections/objects, setting
all relocated shared 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.
Return Value:
SUCCESS or reason NtProtectVirtualMemory failed.
--*/
{
HANDLE CurrentProcessHandle;
SIZE_T RegionSize;
ULONG NewProtect, OldProtect;
PVOID VirtualAddress;
ULONG i;
PIMAGE_NT_HEADERS NtHeaders;
PIMAGE_SECTION_HEADER SectionHeader;
NTSTATUS st;
ULONG NumberOfSharedDataPages;
SIZE_T NumberOfNativePagesForImage;
CurrentProcessHandle = NtCurrentProcess();
NtHeaders = RtlImageNtHeader(Base);
SectionHeader = (PIMAGE_SECTION_HEADER)((ULONG_PTR)NtHeaders + sizeof(ULONG) +
sizeof(IMAGE_FILE_HEADER) +
NtHeaders->FileHeader.SizeOfOptionalHeader
);
NumberOfSharedDataPages = 0;
NumberOfNativePagesForImage = NATIVE_BYTES_TO_PAGES(NtHeaders->OptionalHeader.SizeOfImage);
for (i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++, SectionHeader++) {
if ((SectionHeader->Characteristics & IMAGE_SCN_MEM_SHARED) &&
(!(SectionHeader->Characteristics & IMAGE_SCN_MEM_EXECUTE) ||
(SectionHeader->Characteristics & IMAGE_SCN_MEM_WRITE))) {
RegionSize = SectionHeader->SizeOfRawData;
VirtualAddress = (PVOID)((ULONG_PTR)Base +
((NumberOfNativePagesForImage + NumberOfSharedDataPages) << NATIVE_PAGE_SHIFT));
NumberOfNativePagesForImage += MI_ROUND_TO_SIZE(RegionSize, NATIVE_PAGE_SIZE) >> NATIVE_PAGE_SHIFT;
if (!(SectionHeader->Characteristics & IMAGE_SCN_MEM_WRITE)) {
// Object isn't writeable, so 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;
}
st = NtProtectVirtualMemory(CurrentProcessHandle, &VirtualAddress, &RegionSize, NewProtect, &OldProtect);
if (!NT_SUCCESS(st)) {
return st;
}
}
}
}
if (Reset) {
NtFlushInstructionCache(NtCurrentProcess(), NULL, 0);
}
return STATUS_SUCCESS;
}
PIMAGE_BASE_RELOCATION LdrpWx86ProcessRelocationBlock(IN ULONG_PTR VA,
IN PUCHAR ImageBase,
IN ULONG SizeOfBlock,
IN PUSHORT NextOffset,
IN ULONG Diff,
IN ULONG_PTR SectionStartVA,
IN ULONG_PTR SectionEndVA);
NTSTATUS FixupBlockList(IN PUCHAR ImageBase);
VOID FixupSectionHeader(IN PUCHAR ImageBase);
NTSTATUS LdrpWx86FormatVirtualImage(IN PIMAGE_NT_HEADERS32 NtHeaders, IN PVOID DllBase)
{
PIMAGE_SECTION_HEADER SectionTable, Section, LastSection, FirstSection;
ULONG VirtualImageSize;
PUCHAR NextVirtualAddress, SrcVirtualAddress, DestVirtualAddress;
PUCHAR ImageBase = DllBase;
LONG Size;
ULONG NumberOfSharedDataPages;
ULONG NumberOfNativePagesForImage;
ULONG NumberOfExtraPagesForImage;
ULONG_PTR PreferredImageBase;
BOOLEAN ImageHasRelocatedSharedSection = FALSE;
ULONG SubSectionSize;
NTSTATUS st = Wx86SetRelocatedSharedProtection(DllBase, FALSE);
if (!NT_SUCCESS(st)) {
DbgPrint("Wx86SetRelocatedSharedProtection failed with return status %x\n", st);
Wx86SetRelocatedSharedProtection(DllBase, TRUE);
return st;
}
// Copy each section from its raw file address to its virtual address
SectionTable = IMAGE_FIRST_SECTION(NtHeaders);
LastSection = SectionTable + NtHeaders->FileHeader.NumberOfSections;
if (SectionTable->PointerToRawData == SectionTable->VirtualAddress) {
// If the first section does not need to be moved then we exclude it
// from condideration in passes 1 and 2
FirstSection = SectionTable + 1;
} else {
FirstSection = SectionTable;
}
// First pass starts at the top and works down moving up each section that
// is to be moved up.
Section = FirstSection;
while (Section < LastSection) {
SrcVirtualAddress = ImageBase + Section->PointerToRawData;
DestVirtualAddress = Section->VirtualAddress + ImageBase;
if (DestVirtualAddress > SrcVirtualAddress) {
// Section needs to be moved down
break;
}
// Section needs to be moved up
if (Section->SizeOfRawData != 0) {
if (Section->PointerToRawData != 0) {
RtlMoveMemory(DestVirtualAddress, SrcVirtualAddress, Section->SizeOfRawData);
}
} else {
Section->PointerToRawData = 0;
}
Section++;
}
// Second pass is from the end of the image and work backwards since src and dst overlap
Section = --LastSection;
NextVirtualAddress = ImageBase + NtHeaders->OptionalHeader.SizeOfImage;
while (Section >= FirstSection) {
SrcVirtualAddress = ImageBase + Section->PointerToRawData;
DestVirtualAddress = Section->VirtualAddress + ImageBase;
// Compute the subsection size. The mm is really flexible here...
// it will allow a SizeOfRawData that far exceeds the virtual size,
// so we can't trust that. If that happens, just use the page-aligned
// virtual size, since that is all that the mm will map in.
SubSectionSize = Section->SizeOfRawData;
if (Section->Misc.VirtualSize && SubSectionSize > MI_ROUND_TO_SIZE(Section->Misc.VirtualSize, PAGE_SIZE_X86)) {
SubSectionSize = MI_ROUND_TO_SIZE(Section->Misc.VirtualSize, PAGE_SIZE_X86);
}
// ensure Virtual section doesn't overlap the next section
if (DestVirtualAddress + SubSectionSize > NextVirtualAddress) {
Wx86SetRelocatedSharedProtection(DllBase, TRUE);
return STATUS_INVALID_IMAGE_FORMAT;
}
if (DestVirtualAddress < SrcVirtualAddress) {
// Section needs to be moved up
break;
}
// Section needs to be moved down
if (Section->SizeOfRawData != 0) {
if (Section->PointerToRawData != 0) {
RtlMoveMemory(DestVirtualAddress, SrcVirtualAddress, SubSectionSize);
}
} else {
Section->PointerToRawData = 0;
}
NextVirtualAddress = DestVirtualAddress;
Section--;
}
// Third pass is for zeroing out any memory left between the end of a
// section and the end of the page. We'll do this from end to top
Section = LastSection;
NextVirtualAddress = ImageBase + NtHeaders->OptionalHeader.SizeOfImage;
NumberOfSharedDataPages = 0;
while (Section >= SectionTable) {
DestVirtualAddress = Section->VirtualAddress + ImageBase;
// Shared Data sections cannot be shared, because of
// page misalignment, and are treated as Exec- Copy on Write.
if ((Section->Characteristics & IMAGE_SCN_MEM_SHARED) &&
(!(Section->Characteristics & IMAGE_SCN_MEM_EXECUTE) || (Section->Characteristics & IMAGE_SCN_MEM_WRITE))) {
ImageHasRelocatedSharedSection = TRUE;
#if 0
DbgPrint("Unsuported IMAGE_SCN_MEM_SHARED %x\n", Section->Characteristics);
#endif
}
// If section was empty zero it out
if (Section->SizeOfRawData != 0) {
if (Section->PointerToRawData == 0) {
RtlZeroMemory(DestVirtualAddress, Section->SizeOfRawData);
}
}
// Zero out remaining bytes up to the next section
RtlZeroMemory(DestVirtualAddress + Section->SizeOfRawData,
(ULONG)(NextVirtualAddress - DestVirtualAddress - Section->SizeOfRawData)
);
NextVirtualAddress = DestVirtualAddress;
Section--;
}
// Pass 4: if the dll has any shared sections, change the shared data
// references to point to additional shared pages at the end of the image.
// Note that our fixups are applied assuming that the dll is loaded at
// its preferred base; if it is loaded at some other address, it will
// be relocated again along will al other addresses.
if (!ImageHasRelocatedSharedSection) {
goto LdrwWx86FormatVirtualImageDone;
}
st = FixupBlockList(DllBase);
if (!NT_SUCCESS(st)) {
Wx86SetRelocatedSharedProtection(DllBase, TRUE);
return st;
}
NumberOfNativePagesForImage = NATIVE_BYTES_TO_PAGES(NtHeaders->OptionalHeader.SizeOfImage);
NumberOfExtraPagesForImage = 0;
// Account for raw data that extends beyond SizeOfImage
for (Section = SectionTable; Section <= LastSection; Section++) {
ULONG EndOfSection;
ULONG ExtraPages;
EndOfSection = Section->PointerToRawData + Section->SizeOfRawData;
if (EndOfSection > NtHeaders->OptionalHeader.SizeOfImage) {
ExtraPages = NATIVE_BYTES_TO_PAGES(EndOfSection - NtHeaders->OptionalHeader.SizeOfImage);
if (ExtraPages > NumberOfExtraPagesForImage) {
NumberOfExtraPagesForImage = ExtraPages;
}
}
}
PreferredImageBase = NtHeaders->OptionalHeader.ImageBase;
NumberOfNativePagesForImage += NumberOfExtraPagesForImage;
NumberOfSharedDataPages = 0;
for (Section = SectionTable; Section <= LastSection; Section++) {
ULONG bFirst = 1;
if ((Section->Characteristics & IMAGE_SCN_MEM_SHARED) &&
(!(Section->Characteristics & IMAGE_SCN_MEM_EXECUTE) || (Section->Characteristics & IMAGE_SCN_MEM_WRITE))) {
PIMAGE_BASE_RELOCATION NextBlock;
PUSHORT NextOffset;
ULONG TotalBytes;
ULONG SizeOfBlock;
ULONG_PTR VA;
ULONG_PTR SectionStartVA;
ULONG_PTR SectionEndVA;
ULONG SectionVirtualSize;
ULONG Diff;
SectionVirtualSize = Section->Misc.VirtualSize;
if (SectionVirtualSize == 0) {
SectionVirtualSize = Section->SizeOfRawData;
}
SectionStartVA = PreferredImageBase + Section->VirtualAddress;
SectionEndVA = SectionStartVA + SectionVirtualSize;
NextBlock = RtlImageDirectoryEntryToData(DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_BASERELOC, &TotalBytes);
if (!NextBlock || !TotalBytes) {
// Note that if this fails, it should fail in the very
// first iteration and no fixups would have been performed
if (!bFirst) {
// Trouble
if (ShowSnaps) {
DbgPrint("LdrpWx86FormatVirtualImage: failure "
"after relocating some sections for image at %x\n",
DllBase);
}
Wx86SetRelocatedSharedProtection(DllBase, TRUE);
return STATUS_INVALID_IMAGE_FORMAT;
}
if (ShowSnaps) {
DbgPrint("LdrpWx86FormatVirtualImage: No fixup info "
"for image at %x; private sections will be "
"used for shared data sections.\n",
DllBase);
}
break;
}
bFirst = 0;
Diff = (NumberOfNativePagesForImage + NumberOfSharedDataPages) << NATIVE_PAGE_SHIFT;
Diff -= (ULONG)(SectionStartVA - PreferredImageBase);
if (ShowSnaps) {
DbgPrint("LdrpWx86FormatVirtualImage: Relocating shared "
"data for shared data section 0x%x of image "
"at %x by 0x%lx bytes\n",
Section - SectionTable + 1, DllBase, Diff);
}
while (TotalBytes) {
SizeOfBlock = NextBlock->SizeOfBlock;
TotalBytes -= SizeOfBlock;
SizeOfBlock -= sizeof(IMAGE_BASE_RELOCATION);
SizeOfBlock /= sizeof(USHORT);
NextOffset = (PUSHORT)((PCHAR)NextBlock + sizeof(IMAGE_BASE_RELOCATION));
VA = (ULONG_PTR)DllBase + NextBlock->VirtualAddress;
NextBlock = LdrpWx86ProcessRelocationBlock(VA, DllBase, SizeOfBlock, NextOffset, Diff, SectionStartVA, SectionEndVA);
if (NextBlock == NULL) {
// Trouble
if (ShowSnaps) {
DbgPrint("LdrpWx86FormatVirtualImage: failure "
"after relocating some sections for image at %x; "
"Relocation information invalid\n",
DllBase);
}
Wx86SetRelocatedSharedProtection(DllBase, TRUE);
return STATUS_INVALID_IMAGE_FORMAT;
}
}
NumberOfSharedDataPages += MI_ROUND_TO_SIZE(SectionVirtualSize, NATIVE_PAGE_SIZE) >> NATIVE_PAGE_SHIFT;
}
}
LdrwWx86FormatVirtualImageDone:
// Zero out first section's Raw Data up to its VirtualAddress
if (SectionTable->PointerToRawData != 0) {
DestVirtualAddress = SectionTable->PointerToRawData + ImageBase;
Size = (LONG)(NextVirtualAddress - DestVirtualAddress);
if (Size > 0) {
RtlZeroMemory(DestVirtualAddress, (ULONG)Size);
}
}
Wx86SetRelocatedSharedProtection(DllBase, TRUE);
return STATUS_SUCCESS;
}
ULONG LdrpWx86RelocatedFixupDiff(IN PUCHAR ImageBase, IN ULONG Offset)
{
PIMAGE_SECTION_HEADER SectionHeader;
ULONG i;
ULONG NumberOfSharedDataPages;
ULONG NumberOfNativePagesForImage;
PIMAGE_NT_HEADERS32 NtHeaders = (PIMAGE_NT_HEADERS32)RtlImageNtHeader(ImageBase);
ULONG Diff = 0;
ULONG_PTR FixupAddr = (ULONG_PTR)(ImageBase + Offset);
SectionHeader = (PIMAGE_SECTION_HEADER)((ULONG_PTR)NtHeaders + sizeof(ULONG) +
sizeof(IMAGE_FILE_HEADER) +
NtHeaders->FileHeader.SizeOfOptionalHeader
);
NumberOfNativePagesForImage = NATIVE_BYTES_TO_PAGES(NtHeaders->OptionalHeader.SizeOfImage);
NumberOfSharedDataPages = 0;
for (i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++, SectionHeader++) {
ULONG_PTR SectionStartVA;
ULONG_PTR SectionEndVA;
ULONG SectionVirtualSize;
SectionVirtualSize = SectionHeader->Misc.VirtualSize;
if (SectionVirtualSize == 0) {
SectionVirtualSize = SectionHeader->SizeOfRawData;
}
SectionStartVA = (ULONG_PTR)ImageBase + SectionHeader->VirtualAddress;
SectionEndVA = SectionStartVA + SectionVirtualSize;
if (((ULONG_PTR)FixupAddr >= SectionStartVA) && ((ULONG_PTR)FixupAddr <= SectionEndVA)) {
if ((SectionHeader->Characteristics & IMAGE_SCN_MEM_SHARED) &&
(!(SectionHeader->Characteristics & IMAGE_SCN_MEM_EXECUTE) ||
(SectionHeader->Characteristics & IMAGE_SCN_MEM_WRITE))) {
Diff = (NumberOfNativePagesForImage + NumberOfSharedDataPages) << NATIVE_PAGE_SHIFT;
Diff -= (ULONG)SectionHeader->VirtualAddress;
}
break;
}
if ((SectionHeader->Characteristics & IMAGE_SCN_MEM_SHARED) &&
(!(SectionHeader->Characteristics & IMAGE_SCN_MEM_EXECUTE) ||
(SectionHeader->Characteristics & IMAGE_SCN_MEM_WRITE))) {
NumberOfSharedDataPages += MI_ROUND_TO_SIZE(SectionVirtualSize, NATIVE_PAGE_SIZE) >> NATIVE_PAGE_SHIFT;
}
}
return Diff;
}
NTSTATUS FixupBlockList(IN PUCHAR ImageBase)
{
PIMAGE_BASE_RELOCATION NextBlock;
PUSHORT NextOffset;
ULONG TotalBytes;
ULONG SizeOfBlock;
NTSTATUS st;
NextBlock = RtlImageDirectoryEntryToData(ImageBase, TRUE, IMAGE_DIRECTORY_ENTRY_BASERELOC, &TotalBytes);
if (!NextBlock || !TotalBytes) {
if (ShowSnaps) {
DbgPrint("LdrpWx86FixupBlockList: No fixup info "
"for image at %x; private sections will be "
"used for shared data sections.\n",
ImageBase);
}
return STATUS_SUCCESS;
}
while (TotalBytes) {
SizeOfBlock = NextBlock->SizeOfBlock;
TotalBytes -= SizeOfBlock;
SizeOfBlock -= sizeof(IMAGE_BASE_RELOCATION);
SizeOfBlock /= sizeof(USHORT);
NextOffset = (PUSHORT)((PCHAR)NextBlock + sizeof(IMAGE_BASE_RELOCATION));
NextBlock->VirtualAddress += LdrpWx86RelocatedFixupDiff(ImageBase, NextBlock->VirtualAddress);
while (SizeOfBlock--) {
switch ((*NextOffset) >> 12) {
case IMAGE_REL_BASED_HIGHLOW:
case IMAGE_REL_BASED_HIGH:
case IMAGE_REL_BASED_LOW:
break;
case IMAGE_REL_BASED_HIGHADJ:
++NextOffset;
--SizeOfBlock;
break;
case IMAGE_REL_BASED_IA64_IMM64:
case IMAGE_REL_BASED_DIR64:
case IMAGE_REL_BASED_MIPS_JMPADDR:
case IMAGE_REL_BASED_ABSOLUTE:
case IMAGE_REL_BASED_SECTION:
case IMAGE_REL_BASED_REL32:
break;
case IMAGE_REL_BASED_HIGH3ADJ:
++NextOffset;
--SizeOfBlock;
++NextOffset;
--SizeOfBlock;
break;
default:
return STATUS_INVALID_IMAGE_FORMAT;
}
++NextOffset;
}
NextBlock = (PIMAGE_BASE_RELOCATION)NextOffset;
if (NextBlock == NULL) {
// Trouble
if (ShowSnaps) {
DbgPrint("LdrpWx86FixupBlockList: failure "
"after relocating some sections for image at %x; "
"Relocation information invalid\n",
ImageBase);
}
return STATUS_INVALID_IMAGE_FORMAT;
}
}
return STATUS_SUCCESS;
}
BOOLEAN LdrpWx86DllHasRelocatedSharedSection(IN PUCHAR ImageBase)
{
PIMAGE_SECTION_HEADER SectionHeader;
ULONG i;
PIMAGE_NT_HEADERS32 NtHeaders = (PIMAGE_NT_HEADERS32)RtlImageNtHeader(ImageBase);
SectionHeader = (PIMAGE_SECTION_HEADER)((ULONG_PTR)NtHeaders +
sizeof(ULONG) +
sizeof(IMAGE_FILE_HEADER) +
NtHeaders->FileHeader.SizeOfOptionalHeader);
for (i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++, SectionHeader++) {
if ((SectionHeader->Characteristics & IMAGE_SCN_MEM_SHARED) &&
(!(SectionHeader->Characteristics & IMAGE_SCN_MEM_EXECUTE) ||
(SectionHeader->Characteristics & IMAGE_SCN_MEM_WRITE))) {
return TRUE;
}
}
return FALSE;
}
// Following fn is adapted from rtl\ldrreloc.c; it should be updated when
// that function changes. Eliminated 64 bit address relocations.
// Note: Instead of calling this routine, we could call
// LdrpProcessRelocationBlock(VA, 1, NextOffset, Diff)
// but we should do that only if the address to be relocated is between
// SectionStartVA and SectionEndVA. So we would have to replicate all the
// code in the switch stmt below that computes the address of the data item -
// which is pretty much the entire function. So we chose to replicate the
// function as it was and change it to make the test.
PIMAGE_BASE_RELOCATION LdrpWx86ProcessRelocationBlock(IN ULONG_PTR VA,
IN PUCHAR ImageBase,
IN ULONG SizeOfBlock,
IN PUSHORT NextOffset,
IN ULONG Diff,
IN ULONG_PTR SectionStartVA,
IN ULONG_PTR SectionEndVA)
{
PUCHAR FixupVA;
USHORT Offset;
LONG Temp;
ULONG_PTR DataVA;
while (SizeOfBlock--) {
Offset = *NextOffset & (USHORT)0xfff;
FixupVA = (PUCHAR)(VA + Offset);
// Apply the fixups.
switch ((*NextOffset) >> 12) {
case IMAGE_REL_BASED_HIGHLOW:
// HighLow - (32-bits) relocate the high and low half
// of an address.
Temp = *(LONG UNALIGNED *)FixupVA;
DataVA = (ULONG_PTR)Temp;
if (DataVA >= SectionStartVA && DataVA <= SectionEndVA) {
Temp += (ULONG)Diff;
*(LONG UNALIGNED *)FixupVA = Temp;
}
break;
case IMAGE_REL_BASED_HIGH:
// High - (16-bits) relocate the high half of an address.
Temp = *(PUSHORT)FixupVA << 16;
DataVA = (ULONG_PTR)Temp;
if (DataVA >= SectionStartVA && DataVA <= SectionEndVA) {
Temp += (ULONG)Diff;
*(PUSHORT)FixupVA = (USHORT)(Temp >> 16);
}
break;
case IMAGE_REL_BASED_HIGHADJ:
// Adjust high - (16-bits) relocate the high half of an
// address and adjust for sign extension of low half.
Temp = *(PUSHORT)FixupVA << 16;
++NextOffset;
--SizeOfBlock;
Temp += (LONG)(*(PSHORT)NextOffset);
DataVA = (ULONG_PTR)Temp;
if (DataVA >= SectionStartVA && DataVA <= SectionEndVA) {
Temp += (ULONG)Diff;
Temp += 0x8000;
*(PUSHORT)FixupVA = (USHORT)(Temp >> 16);
}
break;
case IMAGE_REL_BASED_LOW:
// Low - (16-bit) relocate the low half of an address.
Temp = *(PSHORT)FixupVA;
DataVA = (ULONG_PTR)Temp;
if (DataVA >= SectionStartVA && DataVA <= SectionEndVA) {
Temp += (ULONG)Diff;
*(PUSHORT)FixupVA = (USHORT)Temp;
}
break;
case IMAGE_REL_BASED_IA64_IMM64:
// Align it to bundle address before fixing up the
// 64-bit immediate value of the movl instruction.
// No need to support
break;
case IMAGE_REL_BASED_DIR64:
// Update 32-bit address
// No need to support
break;
case IMAGE_REL_BASED_MIPS_JMPADDR:
// JumpAddress - (32-bits) relocate a MIPS jump address.
// No need to support
break;
case IMAGE_REL_BASED_ABSOLUTE:
break;// Absolute - no fixup required.
case IMAGE_REL_BASED_SECTION:
break;// Section Relative reloc. Ignore for now.
case IMAGE_REL_BASED_REL32:
break;// Relative intrasection. Ignore for now.
case IMAGE_REL_BASED_HIGH3ADJ:
// Similar to HIGHADJ except this is the third word.
// Adjust low half of high dword of an address and adjust for
// sign extension of the low dword.
// No need to support
++NextOffset;
--SizeOfBlock;
++NextOffset;
--SizeOfBlock;
break;
default:
return (PIMAGE_BASE_RELOCATION)NULL;// Illegal - illegal relocation type.
}
++NextOffset;
}
return (PIMAGE_BASE_RELOCATION)NextOffset;
}
#endif // ALPHA or BUILD_WOW6432
#if defined (_ALPHA_) && defined (WX86)
NTSTATUS Wx86IdentifyPlugin(IN PVOID DllBase, IN PUNICODE_STRING FullDllName)
/*++
Routine Description:
Determine which (if any) plugin provider dlls support this plugin.
All registered plugin provider dlls are loaded sequentially and given an opportunity to examine the the plugin dll.
Provider dlls that support the plugin are stored in a WX86PLUGIN object and linked into Wx86PluginList.
Note: This routine may be invoked before Wx86 has been loaded so the global values initialized by LdrpLoadWx86 cannot be used.
Return Value:
STATUS_SUCCESS - The dll is supported by at least one plugin provider.
STATUS_IMAGE_MACHINE_TYPE_MISMATCH - There are no plugin providers that support this dll.
Other - failure in Wx86IdentifyPlugIn
--*/
{
NTSTATUS st;
PVOID Provider[WX86PLUGIN_MAXPROVIDER];
ULONG Count;
ULONG Length;
ULONG Index;
ULONG NumProviders;
ULONG Disposition;
HANDLE hProviderKey = NULL;
HANDLE hProviderIdKey = NULL;
USHORT ProviderLength;
USHORT MachineType;
BOOLEAN CanThunk;
UNICODE_STRING KeyName;
UNICODE_STRING DllName;
UNICODE_STRING ProviderName;
ANSI_STRING ProcName;
OBJECT_ATTRIBUTES Obja;
PKEY_VALUE_PARTIAL_INFORMATION ValPartInfo;
PKEY_FULL_INFORMATION KeyFullInfo;
PKEY_BASIC_INFORMATION KeyBasicInfo;
WCHAR ProviderPath[DOS_MAX_PATH_LENGTH];
WCHAR DataBuffer[STATIC_UNICODE_BUFFER_LENGTH];
WX86IDENTIFYPLUGIN IdentifyPlugin = NULL;
PIMAGE_NT_HEADERS NtHeaders;
NtHeaders = RtlImageNtHeader(DllBase);
if (NtHeaders == NULL) {
return STATUS_INVALID_IMAGE_FORMAT;
}
MachineType = NtHeaders->FileHeader.Machine;
// Identify the plugin by trying each plugin provider in turn.
// The absence of the Provider registry key is a switch to turn off plugin support.
Count = 0;
RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Wx86\\Provider");
InitializeObjectAttributes(&Obja, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL);
st = NtOpenKey(&hProviderKey, KEY_READ | KEY_WRITE, &Obja);
if (st == STATUS_OBJECT_NAME_NOT_FOUND) {
st = STATUS_IMAGE_MACHINE_TYPE_MISMATCH;
goto Wx86IdentifyDone;
}
if (NT_ERROR(st)) {
goto Wx86IdentifyDone;
}
ValPartInfo = (PKEY_VALUE_PARTIAL_INFORMATION)DataBuffer;
KeyFullInfo = (PKEY_FULL_INFORMATION)DataBuffer;
st = NtQueryKey(hProviderKey, KeyFullInformation, KeyFullInfo, sizeof(DataBuffer), &Length);
if (NT_ERROR(st)) {
goto Wx86IdentifyDone;
}
NumProviders = KeyFullInfo->SubKeys;
// Initialize the base path of the provider dlls
ProviderName.Buffer = ProviderPath;
ProviderName.MaximumLength = sizeof(ProviderPath);
ProviderName.Length = 0;
RtlAppendUnicodeToString(&ProviderName, USER_SHARED_DATA->NtSystemRoot);
if (NT_SUCCESS(st)) {
st = RtlAppendUnicodeToString(&ProviderName, Wx86Dir);
}
if (NT_SUCCESS(st)) {
st = RtlAppendUnicodeToString(&ProviderName, L"\\Provider\\");
}
if (NT_ERROR(st)) {
goto Wx86IdentifyDone;
}
ProviderLength = ProviderName.Length;
Index = 0;
st = STATUS_SUCCESS;
KeyBasicInfo = (PKEY_BASIC_INFORMATION)DataBuffer;
do {
st = NtEnumerateKey(hProviderKey, Index, KeyBasicInformation, DataBuffer, sizeof(DataBuffer) - 2, &Length);
if (st == STATUS_NO_MORE_ENTRIES) {
st = STATUS_SUCCESS;
break;
}
if (NT_ERROR(st)) {
goto Wx86IdentifyDone;
}
// Get the name of the next plugin provider dll
KeyBasicInfo->Name[KeyBasicInfo->NameLength / 2] = UNICODE_NULL;
RtlInitUnicodeString(&KeyName, KeyBasicInfo->Name);
InitializeObjectAttributes(&Obja, &KeyName, OBJ_CASE_INSENSITIVE, hProviderKey, NULL);
st = NtOpenKey(&hProviderIdKey, KEY_READ, &Obja);
if (st == STATUS_OBJECT_NAME_NOT_FOUND) {
goto Wx86IdentifyDone;
}
RtlInitUnicodeString(&KeyName, L"DllName");
st = NtQueryValueKey(hProviderIdKey, &KeyName, KeyValuePartialInformation, DataBuffer, sizeof(DataBuffer) - 2, &Length);
if (st == STATUS_SUCCESS) {
// Get the full provider dll path by appending the name to the base provider path
*(PWCHAR)(&ValPartInfo->Data[ValPartInfo->DataLength]) = UNICODE_NULL;
ProviderName.Length = ProviderLength;
if (NT_SUCCESS(st)) {
st = RtlAppendUnicodeToString(&ProviderName, (PWSTR)ValPartInfo->Data);
}
if (NT_ERROR(st)) {
goto Wx86IdentifyDone;
}
// Get the name of an export this provider requires (if any).
// If the plugin dll doesn't have this export then it's not possible for this provider to support it.
// This is a quick check which excludes most false tests for plug providers.
RtlInitUnicodeString(&KeyName, L"Export");
st = NtQueryValueKey(hProviderIdKey, &KeyName, KeyValuePartialInformation, DataBuffer, sizeof(DataBuffer) - 2, &Length);
if (st == STATUS_SUCCESS) {
ANSI_STRING ExportName;
UNICODE_STRING UnicodeExportName;
PVOID ExportAddress;
CHAR ExportNameBuffer[64];
ExportName.Buffer = ExportNameBuffer;
ExportName.MaximumLength = sizeof(ExportNameBuffer);
ExportName.Length = 0;
*(PWCHAR)(&ValPartInfo->Data[ValPartInfo->DataLength]) = UNICODE_NULL;
RtlInitUnicodeString(&UnicodeExportName, (PWCHAR)(ValPartInfo->Data));
st = RtlUnicodeStringToAnsiString(&ExportName, &UnicodeExportName, FALSE);
if (NT_ERROR(st)) {
goto Wx86IdentifyDone;
}
st = LdrGetProcedureAddress(DllBase, &ExportName, 0, &ExportAddress);
if (NT_ERROR(st)) {
st = STATUS_IMAGE_MACHINE_TYPE_MISMATCH;
continue;
}
}
// The required export exists so it's time to load the provider and do the full check
// On error skip to the next provider
st = LdrpLoadDll(NULL, NULL, &ProviderName, &Provider[Count], TRUE);
if (NT_SUCCESS(st)) {
RtlInitAnsiString(&ProcName, "Wx86IdentifyPlugin");
st = LdrGetProcedureAddress(Provider[Count], &ProcName, 0, (PVOID *)&IdentifyPlugin);
CanThunk = FALSE;
if (NT_SUCCESS(st) && IdentifyPlugin) {
try {
CanThunk = IdentifyPlugin(DllBase, FullDllName->Buffer, MachineType == IMAGE_FILE_MACHINE_I386);
} except(EXCEPTION_EXECUTE_HANDLER) {
}
}
if (CanThunk) {
Count++;
} else {
LdrUnloadDll(Provider[Count]);
Provider[Count] = NULL;
}
} // loaded provder dll
} // opened provider key
} while ((++Index < NumProviders) && (Count < WX86PLUGIN_MAXPROVIDER));
// Return success if we found at least on plugin provider.
// Allocate a WX86PLUGIN list entry to keep track of which provders were loaded for this plugin
if (Count) {
PWX86PLUGIN Wx86Plugin;
Wx86Plugin = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(LDR_TAG), sizeof(WX86PLUGIN));
if (!Wx86Plugin) {
st = STATUS_NO_MEMORY;
goto Wx86IdentifyDone;
}
Wx86Plugin->DllBase = DllBase;
Wx86Plugin->Count = Count;
RtlMoveMemory(Wx86Plugin->Provider, Provider, Count * sizeof(PVOID));
InsertTailList(&Wx86PluginList, &Wx86Plugin->Links);
st = STATUS_SUCCESS;
} else {
st = STATUS_IMAGE_MACHINE_TYPE_MISMATCH;
}
st = Count ? STATUS_SUCCESS : STATUS_IMAGE_MACHINE_TYPE_MISMATCH;
Wx86IdentifyDone:
// On error exit release any provider dlls that were loaded
if (NT_ERROR(st)) {
for (Index = 0; Index < Count; Index++) {
LdrUnloadDll(Provider[Count]);
}
}
if (hProviderKey) {
NtClose(hProviderKey);
}
if (hProviderIdKey) {
NtClose(hProviderIdKey);
}
return st;
}
NTSTATUS Wx86ThunkPluginExport(
IN PVOID DllBase,
IN PCHAR ExportName,
IN ULONG Ordinal,
IN PVOID ExportAddress,
OUT PVOID * ExportThunk
)
/*++
Routine Description:
This procedure is called during GetProcAddress() processing when the request if for a cross-architecure procedure address in an
image which was loaded as a plugin dll (LDRP_WX86_PLUGIN flag).
The providers associated with this plugin dll are called in order to provide a thunk for the specified plugin export.
The thunk attempt stops when a provider successfully thunks the export.
Return Value:
STATUS_SUCCESS - if export is successfully thunked
STATUS_PROCEDURE_NOT_FOUND - if export cannot be thunked
STATUS_INVALID_IMAGE_FORMAT - DllBase is invalid
--*/
{
PLIST_ENTRY Head, Next;
PWX86PLUGIN Wx86Plugin;
ULONG Index;
WX86THUNKEXPORT ThunkExport;
ANSI_STRING ProcName;
PIMAGE_NT_HEADERS NtHeaders;
USHORT MachineType;
BOOLEAN Thunked;
NTSTATUS st;
NtHeaders = RtlImageNtHeader(DllBase);
if (NtHeaders == NULL) {
return STATUS_INVALID_IMAGE_FORMAT;
}
MachineType = NtHeaders->FileHeader.Machine;
// Find the Wx86Plugin entry with a matching DllBase
Head = &Wx86PluginList;
Next = Head->Flink;
while (Next != Head) {
Wx86Plugin = CONTAINING_RECORD(Next, WX86PLUGIN, Links);
if (Wx86Plugin->DllBase == DllBase) {
break;
}
Next = Next->Flink;
}
// No Wx86Plugin entry for this Dll
if (Next == Head) {
return STATUS_PROCEDURE_NOT_FOUND;
}
// Get the address of the routine to thunk exports for each provider
RtlInitAnsiString(&ProcName, "Wx86ThunkExport");
for (Index = 0; Index < Wx86Plugin->Count; Index++) {
st = LdrGetProcedureAddress(Wx86Plugin->Provider[Index], &ProcName, 0, (PVOID *)&ThunkExport);
if (NT_SUCCESS(st) && ThunkExport != NULL) {
try {
Thunked = ThunkExport(DllBase, ExportName, Ordinal, ExportAddress, ExportThunk, MachineType == IMAGE_FILE_MACHINE_I386);
} except(EXCEPTION_EXECUTE_HANDLER) {
Thunked = FALSE;
}
if (Thunked) {
if (ShowSnaps) {
DbgPrint("LDRWx86: thunk export for %08X Ord=%04X Addr=%08X Name=%s Thunk=%08X\n",
DllBase, Ordinal, ExportAddress, ExportName ? ExportName : "<noname>", *ExportThunk);
break;
}
}
}
}
return st;
}
BOOLEAN Wx86UnloadProviders(IN PVOID DllBase)
/*++
Routine Description:
Handle unloading of plugin dlls.
The DllBase passed in may have already been unloaded.
However, it is used here only to find the associated plugin provider dll.
Return Value:
FALSE on failure, TRUE on success.
--*/
{
PLDR_DATA_TABLE_ENTRY PluginEntry;
NTSTATUS Status = STATUS_SUCCESS;
PLIST_ENTRY Head, Next;
PWX86PLUGIN Wx86Plugin;
ULONG Index;
// Prevent recursion - Only perform this at the top level.
// This is relevant when being called from LdrUnloadDll
if (Wx86ProviderUnloadCount == 0) {
try {
Wx86ProviderUnloadCount++;
// Find the the entry for this plugin dll
Head = &Wx86PluginList;
Next = Head->Flink;
while (Next != Head) {
Wx86Plugin = CONTAINING_RECORD(Next, WX86PLUGIN, Links);
if (Wx86Plugin->DllBase == DllBase) {
// If the plugin dll is no longer mapped then unload the providers and free the Wx86PluginList entry
if (!LdrpCheckForLoadedDllHandle(DllBase, &PluginEntry)) {
for (Index = 0; Index < Wx86Plugin->Count; Index++) {
Status = LdrUnloadDll(Wx86Plugin->Provider[Index]);
}
RemoveEntryList(&Wx86Plugin->Links);
RtlFreeHeap(RtlProcessHeap(), 0, Wx86Plugin);
break;
}
}
Next = Next->Flink;
}
} finally {
Wx86ProviderUnloadCount--;
}
}
return NT_SUCCESS(Status);
}
#endif