mirror of
https://github.com/reactos/reactos.git
synced 2024-11-01 20:32:36 +00:00
24a56f89ab
This removes all fake apiset forwarders, and handles apisets inside ntdll. This is not 100% compatible with how windows does it, but it should be good enough for us.
1217 lines
40 KiB
C
1217 lines
40 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS kernel
|
|
* FILE: lib/ntdll/ldr/ldrpe.c
|
|
* PURPOSE: Loader Functions dealing low-level PE Format structures
|
|
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include <ntdll.h>
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* GLOBALS *******************************************************************/
|
|
|
|
PLDR_MANIFEST_PROBER_ROUTINE LdrpManifestProberRoutine;
|
|
ULONG LdrpNormalSnap;
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
LdrpSnapIAT(IN PLDR_DATA_TABLE_ENTRY ExportLdrEntry,
|
|
IN PLDR_DATA_TABLE_ENTRY ImportLdrEntry,
|
|
IN PIMAGE_IMPORT_DESCRIPTOR IatEntry,
|
|
IN BOOLEAN EntriesValid)
|
|
{
|
|
PVOID Iat;
|
|
NTSTATUS Status;
|
|
PIMAGE_THUNK_DATA OriginalThunk, FirstThunk;
|
|
PIMAGE_NT_HEADERS NtHeader;
|
|
PIMAGE_SECTION_HEADER SectionHeader;
|
|
PIMAGE_EXPORT_DIRECTORY ExportDirectory;
|
|
LPSTR ImportName;
|
|
ULONG ForwarderChain, i, Rva, OldProtect, IatSize, ExportSize;
|
|
SIZE_T ImportSize;
|
|
DPRINT("LdrpSnapIAT(%wZ %wZ %p %u)\n", &ExportLdrEntry->BaseDllName, &ImportLdrEntry->BaseDllName, IatEntry, EntriesValid);
|
|
|
|
/* Get export directory */
|
|
ExportDirectory = RtlImageDirectoryEntryToData(ExportLdrEntry->DllBase,
|
|
TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_EXPORT,
|
|
&ExportSize);
|
|
|
|
/* Make sure it has one */
|
|
if (!ExportDirectory)
|
|
{
|
|
/* Fail */
|
|
DbgPrint("LDR: %wZ doesn't contain an EXPORT table\n",
|
|
&ExportLdrEntry->BaseDllName);
|
|
return STATUS_INVALID_IMAGE_FORMAT;
|
|
}
|
|
|
|
/* Get the IAT */
|
|
Iat = RtlImageDirectoryEntryToData(ImportLdrEntry->DllBase,
|
|
TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_IAT,
|
|
&IatSize);
|
|
ImportSize = IatSize;
|
|
|
|
/* Check if we don't have one */
|
|
if (!Iat)
|
|
{
|
|
/* Get the NT Header and the first section */
|
|
NtHeader = RtlImageNtHeader(ImportLdrEntry->DllBase);
|
|
if (!NtHeader) return STATUS_INVALID_IMAGE_FORMAT;
|
|
SectionHeader = IMAGE_FIRST_SECTION(NtHeader);
|
|
|
|
/* Get the RVA of the import directory */
|
|
Rva = NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
|
|
|
|
/* Make sure we got one */
|
|
if (Rva)
|
|
{
|
|
/* Loop all the sections */
|
|
for (i = 0; i < NtHeader->FileHeader.NumberOfSections; i++)
|
|
{
|
|
/* Check if we are inside this section */
|
|
if ((Rva >= SectionHeader->VirtualAddress) &&
|
|
(Rva < (SectionHeader->VirtualAddress +
|
|
SectionHeader->SizeOfRawData)))
|
|
{
|
|
/* We are, so set the IAT here */
|
|
Iat = (PVOID)((ULONG_PTR)(ImportLdrEntry->DllBase) +
|
|
SectionHeader->VirtualAddress);
|
|
|
|
/* Set the size */
|
|
IatSize = SectionHeader->Misc.VirtualSize;
|
|
|
|
/* Deal with Watcom and other retarded compilers */
|
|
if (!IatSize) IatSize = SectionHeader->SizeOfRawData;
|
|
|
|
/* Found it, get out */
|
|
break;
|
|
}
|
|
|
|
/* No match, move to the next section */
|
|
SectionHeader++;
|
|
}
|
|
}
|
|
|
|
/* If we still don't have an IAT, that's bad */
|
|
if (!Iat)
|
|
{
|
|
/* Fail */
|
|
DbgPrint("LDR: Unable to unprotect IAT for %wZ (Image Base %p)\n",
|
|
&ImportLdrEntry->BaseDllName,
|
|
ImportLdrEntry->DllBase);
|
|
return STATUS_INVALID_IMAGE_FORMAT;
|
|
}
|
|
|
|
/* Set the right size */
|
|
ImportSize = IatSize;
|
|
}
|
|
|
|
/* Unprotect the IAT */
|
|
Status = NtProtectVirtualMemory(NtCurrentProcess(),
|
|
&Iat,
|
|
&ImportSize,
|
|
PAGE_READWRITE,
|
|
&OldProtect);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Fail */
|
|
DbgPrint("LDR: Unable to unprotect IAT for %wZ (Status %x)\n",
|
|
&ImportLdrEntry->BaseDllName,
|
|
Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Check if the Thunks are already valid */
|
|
if (EntriesValid)
|
|
{
|
|
/* We'll only do forwarders. Get the import name */
|
|
ImportName = (LPSTR)((ULONG_PTR)ImportLdrEntry->DllBase + IatEntry->Name);
|
|
|
|
/* Get the list of forwarders */
|
|
ForwarderChain = IatEntry->ForwarderChain;
|
|
|
|
/* Loop them */
|
|
while (ForwarderChain != -1)
|
|
{
|
|
/* Get the cached thunk VA*/
|
|
OriginalThunk = (PIMAGE_THUNK_DATA)
|
|
((ULONG_PTR)ImportLdrEntry->DllBase +
|
|
IatEntry->OriginalFirstThunk +
|
|
(ForwarderChain * sizeof(IMAGE_THUNK_DATA)));
|
|
|
|
/* Get the first thunk */
|
|
FirstThunk = (PIMAGE_THUNK_DATA)
|
|
((ULONG_PTR)ImportLdrEntry->DllBase +
|
|
IatEntry->FirstThunk +
|
|
(ForwarderChain * sizeof(IMAGE_THUNK_DATA)));
|
|
|
|
/* Get the Forwarder from the thunk */
|
|
ForwarderChain = (ULONG)FirstThunk->u1.Ordinal;
|
|
|
|
/* Snap the thunk */
|
|
_SEH2_TRY
|
|
{
|
|
Status = LdrpSnapThunk(ExportLdrEntry->DllBase,
|
|
ImportLdrEntry->DllBase,
|
|
OriginalThunk,
|
|
FirstThunk,
|
|
ExportDirectory,
|
|
ExportSize,
|
|
TRUE,
|
|
ImportName);
|
|
|
|
/* Move to the next thunk */
|
|
FirstThunk++;
|
|
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Fail with the SEH error */
|
|
Status = _SEH2_GetExceptionCode();
|
|
} _SEH2_END;
|
|
|
|
/* If we messed up, exit */
|
|
if (!NT_SUCCESS(Status)) break;
|
|
}
|
|
}
|
|
else if (IatEntry->FirstThunk)
|
|
{
|
|
/* Full snapping. Get the First thunk */
|
|
FirstThunk = (PIMAGE_THUNK_DATA)
|
|
((ULONG_PTR)ImportLdrEntry->DllBase +
|
|
IatEntry->FirstThunk);
|
|
|
|
/* Get the NT Header */
|
|
NtHeader = RtlImageNtHeader(ImportLdrEntry->DllBase);
|
|
|
|
/* Get the Original thunk VA, watch out for weird images */
|
|
if ((IatEntry->Characteristics < NtHeader->OptionalHeader.SizeOfHeaders) ||
|
|
(IatEntry->Characteristics >= NtHeader->OptionalHeader.SizeOfImage))
|
|
{
|
|
/* Refuse it, this is a strange linked file */
|
|
OriginalThunk = FirstThunk;
|
|
}
|
|
else
|
|
{
|
|
/* Get the address from the field and convert to VA */
|
|
OriginalThunk = (PIMAGE_THUNK_DATA)
|
|
((ULONG_PTR)ImportLdrEntry->DllBase +
|
|
IatEntry->OriginalFirstThunk);
|
|
}
|
|
|
|
/* Get the Import name VA */
|
|
ImportName = (LPSTR)((ULONG_PTR)ImportLdrEntry->DllBase +
|
|
IatEntry->Name);
|
|
|
|
/* Loop while it's valid */
|
|
while (OriginalThunk->u1.AddressOfData)
|
|
{
|
|
/* Snap the Thunk */
|
|
_SEH2_TRY
|
|
{
|
|
Status = LdrpSnapThunk(ExportLdrEntry->DllBase,
|
|
ImportLdrEntry->DllBase,
|
|
OriginalThunk,
|
|
FirstThunk,
|
|
ExportDirectory,
|
|
ExportSize,
|
|
TRUE,
|
|
ImportName);
|
|
|
|
/* Next thunks */
|
|
OriginalThunk++;
|
|
FirstThunk++;
|
|
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Fail with the SEH error */
|
|
Status = _SEH2_GetExceptionCode();
|
|
} _SEH2_END;
|
|
|
|
/* If we failed the snap, break out */
|
|
if (!NT_SUCCESS(Status)) break;
|
|
}
|
|
}
|
|
|
|
/* Protect the IAT again */
|
|
NtProtectVirtualMemory(NtCurrentProcess(),
|
|
&Iat,
|
|
&ImportSize,
|
|
OldProtect,
|
|
&OldProtect);
|
|
|
|
/* Also flush out the cache */
|
|
NtFlushInstructionCache(NtCurrentProcess(), Iat, IatSize);
|
|
|
|
/* Return to Caller */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
LdrpHandleOneNewFormatImportDescriptor(IN LPWSTR DllPath OPTIONAL,
|
|
IN PLDR_DATA_TABLE_ENTRY LdrEntry,
|
|
IN PIMAGE_BOUND_IMPORT_DESCRIPTOR *BoundEntryPtr,
|
|
IN PIMAGE_BOUND_IMPORT_DESCRIPTOR FirstEntry)
|
|
{
|
|
LPSTR ImportName = NULL, BoundImportName, ForwarderName;
|
|
NTSTATUS Status;
|
|
BOOLEAN AlreadyLoaded = FALSE, Stale;
|
|
PIMAGE_IMPORT_DESCRIPTOR ImportEntry;
|
|
PLDR_DATA_TABLE_ENTRY DllLdrEntry, ForwarderLdrEntry;
|
|
PIMAGE_BOUND_FORWARDER_REF ForwarderEntry;
|
|
PIMAGE_BOUND_IMPORT_DESCRIPTOR BoundEntry;
|
|
PPEB Peb = NtCurrentPeb();
|
|
ULONG i, IatSize;
|
|
|
|
/* Get the pointer to the bound entry */
|
|
BoundEntry = *BoundEntryPtr;
|
|
|
|
/* Get the name's VA */
|
|
BoundImportName = (LPSTR)FirstEntry + BoundEntry->OffsetModuleName;
|
|
|
|
/* Show debug message */
|
|
if (ShowSnaps)
|
|
{
|
|
DPRINT1("LDR: %wZ bound to %s\n", &LdrEntry->BaseDllName, BoundImportName);
|
|
}
|
|
|
|
/* Load the module for this entry */
|
|
Status = LdrpLoadImportModule(DllPath,
|
|
BoundImportName,
|
|
&DllLdrEntry,
|
|
&AlreadyLoaded);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Show debug message */
|
|
if (ShowSnaps)
|
|
{
|
|
DPRINT1("LDR: %wZ failed to load import module %s; status = %x\n",
|
|
&LdrEntry->BaseDllName,
|
|
BoundImportName,
|
|
Status);
|
|
}
|
|
goto Quickie;
|
|
}
|
|
|
|
/* Check if it wasn't already loaded */
|
|
if (!AlreadyLoaded)
|
|
{
|
|
/* Add it to our list */
|
|
InsertTailList(&Peb->Ldr->InInitializationOrderModuleList,
|
|
&DllLdrEntry->InInitializationOrderLinks);
|
|
}
|
|
|
|
/* Check if the Bound Entry is now invalid */
|
|
if ((BoundEntry->TimeDateStamp != DllLdrEntry->TimeDateStamp) ||
|
|
(DllLdrEntry->Flags & LDRP_IMAGE_NOT_AT_BASE))
|
|
{
|
|
/* Show debug message */
|
|
if (ShowSnaps)
|
|
{
|
|
DPRINT1("LDR: %wZ has stale binding to %s\n",
|
|
&LdrEntry->BaseDllName,
|
|
BoundImportName);
|
|
}
|
|
|
|
/* Remember it's become stale */
|
|
Stale = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* Show debug message */
|
|
if (ShowSnaps)
|
|
{
|
|
DPRINT1("LDR: %wZ has correct binding to %s\n",
|
|
&LdrEntry->BaseDllName,
|
|
BoundImportName);
|
|
}
|
|
|
|
/* Remember it's valid */
|
|
Stale = FALSE;
|
|
}
|
|
|
|
/* Get the forwarders */
|
|
ForwarderEntry = (PIMAGE_BOUND_FORWARDER_REF)(BoundEntry + 1);
|
|
|
|
/* Loop them */
|
|
for (i = 0; i < BoundEntry->NumberOfModuleForwarderRefs; i++)
|
|
{
|
|
/* Get the name */
|
|
ForwarderName = (LPSTR)FirstEntry + ForwarderEntry->OffsetModuleName;
|
|
|
|
/* Show debug message */
|
|
if (ShowSnaps)
|
|
{
|
|
DPRINT1("LDR: %wZ bound to %s via forwarder(s) from %wZ\n",
|
|
&LdrEntry->BaseDllName,
|
|
ForwarderName,
|
|
&DllLdrEntry->BaseDllName);
|
|
}
|
|
|
|
/* Load the module */
|
|
Status = LdrpLoadImportModule(DllPath,
|
|
ForwarderName,
|
|
&ForwarderLdrEntry,
|
|
&AlreadyLoaded);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Loaded it, was it already loaded? */
|
|
if (!AlreadyLoaded)
|
|
{
|
|
/* Add it to our list */
|
|
InsertTailList(&Peb->Ldr->InInitializationOrderModuleList,
|
|
&ForwarderLdrEntry->InInitializationOrderLinks);
|
|
}
|
|
}
|
|
|
|
/* Check if the Bound Entry is now invalid */
|
|
if (!(NT_SUCCESS(Status)) ||
|
|
(ForwarderEntry->TimeDateStamp != ForwarderLdrEntry->TimeDateStamp) ||
|
|
(ForwarderLdrEntry->Flags & LDRP_IMAGE_NOT_AT_BASE))
|
|
{
|
|
/* Show debug message */
|
|
if (ShowSnaps)
|
|
{
|
|
DPRINT1("LDR: %wZ has stale binding to %s\n",
|
|
&LdrEntry->BaseDllName,
|
|
ForwarderName);
|
|
}
|
|
|
|
/* Remember it's become stale */
|
|
Stale = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* Show debug message */
|
|
if (ShowSnaps)
|
|
{
|
|
DPRINT1("LDR: %wZ has correct binding to %s\n",
|
|
&LdrEntry->BaseDllName,
|
|
ForwarderName);
|
|
}
|
|
|
|
/* Remember it's valid */
|
|
Stale = FALSE;
|
|
}
|
|
|
|
/* Move to the next one */
|
|
ForwarderEntry++;
|
|
}
|
|
|
|
/* Set the next bound entry to the forwarder */
|
|
FirstEntry = (PIMAGE_BOUND_IMPORT_DESCRIPTOR)ForwarderEntry;
|
|
|
|
/* Check if the binding was stale */
|
|
if (Stale)
|
|
{
|
|
/* It was, so find the IAT entry for it */
|
|
++LdrpNormalSnap;
|
|
ImportEntry = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
|
|
TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_IMPORT,
|
|
&IatSize);
|
|
|
|
/* Make sure it has a name */
|
|
while (ImportEntry->Name)
|
|
{
|
|
/* Get the name */
|
|
ImportName = (LPSTR)((ULONG_PTR)LdrEntry->DllBase + ImportEntry->Name);
|
|
|
|
/* Compare it */
|
|
if (!_stricmp(ImportName, BoundImportName)) break;
|
|
|
|
/* Move to next entry */
|
|
ImportEntry++;
|
|
}
|
|
|
|
/* If we didn't find a name, fail */
|
|
if (!ImportEntry->Name)
|
|
{
|
|
/* Show debug message */
|
|
if (ShowSnaps)
|
|
{
|
|
DPRINT1("LDR: LdrpWalkImportTable - failing with"
|
|
"STATUS_OBJECT_NAME_INVALID due to no import descriptor name\n");
|
|
}
|
|
|
|
/* Return error */
|
|
Status = STATUS_OBJECT_NAME_INVALID;
|
|
goto Quickie;
|
|
}
|
|
|
|
/* Show debug message */
|
|
if (ShowSnaps)
|
|
{
|
|
DPRINT1("LDR: Stale Bind %s from %wZ\n",
|
|
ImportName,
|
|
&LdrEntry->BaseDllName);
|
|
}
|
|
|
|
/* Snap the IAT Entry*/
|
|
Status = LdrpSnapIAT(DllLdrEntry,
|
|
LdrEntry,
|
|
ImportEntry,
|
|
FALSE);
|
|
|
|
/* Make sure we didn't fail */
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Show debug message */
|
|
if (ShowSnaps)
|
|
{
|
|
DPRINT1("LDR: %wZ failed to load import module %s; status = %x\n",
|
|
&LdrEntry->BaseDllName,
|
|
BoundImportName,
|
|
Status);
|
|
}
|
|
|
|
/* Return */
|
|
goto Quickie;
|
|
}
|
|
}
|
|
|
|
/* All done */
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Quickie:
|
|
/* Write where we are now and return */
|
|
*BoundEntryPtr = FirstEntry;
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
LdrpHandleNewFormatImportDescriptors(IN LPWSTR DllPath OPTIONAL,
|
|
IN PLDR_DATA_TABLE_ENTRY LdrEntry,
|
|
IN PIMAGE_BOUND_IMPORT_DESCRIPTOR BoundEntry)
|
|
{
|
|
PIMAGE_BOUND_IMPORT_DESCRIPTOR FirstEntry = BoundEntry;
|
|
NTSTATUS Status;
|
|
|
|
/* Make sure we have a name */
|
|
while (BoundEntry->OffsetModuleName)
|
|
{
|
|
/* Parse this descriptor */
|
|
Status = LdrpHandleOneNewFormatImportDescriptor(DllPath,
|
|
LdrEntry,
|
|
&BoundEntry,
|
|
FirstEntry);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
}
|
|
|
|
/* Done */
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
LdrpHandleOneOldFormatImportDescriptor(IN LPWSTR DllPath OPTIONAL,
|
|
IN PLDR_DATA_TABLE_ENTRY LdrEntry,
|
|
IN PIMAGE_IMPORT_DESCRIPTOR *ImportEntry)
|
|
{
|
|
LPSTR ImportName;
|
|
NTSTATUS Status;
|
|
BOOLEAN AlreadyLoaded = FALSE;
|
|
PLDR_DATA_TABLE_ENTRY DllLdrEntry;
|
|
PIMAGE_THUNK_DATA FirstThunk;
|
|
PPEB Peb = NtCurrentPeb();
|
|
|
|
/* Get the import name's VA */
|
|
ImportName = (LPSTR)((ULONG_PTR)LdrEntry->DllBase + (*ImportEntry)->Name);
|
|
|
|
/* Get the first thunk */
|
|
FirstThunk = (PIMAGE_THUNK_DATA)((ULONG_PTR)LdrEntry->DllBase +
|
|
(*ImportEntry)->FirstThunk);
|
|
|
|
/* Make sure it's valid */
|
|
if (!FirstThunk->u1.Function) goto SkipEntry;
|
|
|
|
/* Show debug message */
|
|
if (ShowSnaps)
|
|
{
|
|
DPRINT1("LDR: %s used by %wZ\n",
|
|
ImportName,
|
|
&LdrEntry->BaseDllName);
|
|
}
|
|
|
|
/* Load the module associated to it */
|
|
Status = LdrpLoadImportModule(DllPath,
|
|
ImportName,
|
|
&DllLdrEntry,
|
|
&AlreadyLoaded);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Fail */
|
|
if (ShowSnaps)
|
|
{
|
|
DbgPrint("LDR: LdrpWalkImportTable - LdrpLoadImportModule failed "
|
|
"on import %s with status %x\n",
|
|
ImportName,
|
|
Status);
|
|
}
|
|
|
|
/* Return */
|
|
return Status;
|
|
}
|
|
|
|
/* Show debug message */
|
|
if (ShowSnaps)
|
|
{
|
|
DPRINT1("LDR: Snapping imports for %wZ from %s\n",
|
|
&LdrEntry->BaseDllName,
|
|
ImportName);
|
|
}
|
|
|
|
/* Check if it wasn't already loaded */
|
|
++LdrpNormalSnap;
|
|
if (!AlreadyLoaded)
|
|
{
|
|
/* Add the DLL to our list */
|
|
InsertTailList(&Peb->Ldr->InInitializationOrderModuleList,
|
|
&DllLdrEntry->InInitializationOrderLinks);
|
|
}
|
|
|
|
/* Now snap the IAT Entry */
|
|
Status = LdrpSnapIAT(DllLdrEntry, LdrEntry, *ImportEntry, FALSE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Fail */
|
|
if (ShowSnaps)
|
|
{
|
|
DbgPrint("LDR: LdrpWalkImportTable - LdrpSnapIAT #2 failed with "
|
|
"status %x\n",
|
|
Status);
|
|
}
|
|
|
|
/* Return */
|
|
return Status;
|
|
}
|
|
|
|
SkipEntry:
|
|
/* Move on */
|
|
(*ImportEntry)++;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
LdrpHandleOldFormatImportDescriptors(IN LPWSTR DllPath OPTIONAL,
|
|
IN PLDR_DATA_TABLE_ENTRY LdrEntry,
|
|
IN PIMAGE_IMPORT_DESCRIPTOR ImportEntry)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
/* Check for Name and Thunk */
|
|
while ((ImportEntry->Name) && (ImportEntry->FirstThunk))
|
|
{
|
|
/* Parse this descriptor */
|
|
Status = LdrpHandleOneOldFormatImportDescriptor(DllPath,
|
|
LdrEntry,
|
|
&ImportEntry);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
}
|
|
|
|
/* Done */
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
USHORT
|
|
NTAPI
|
|
LdrpNameToOrdinal(IN LPSTR ImportName,
|
|
IN ULONG NumberOfNames,
|
|
IN PVOID ExportBase,
|
|
IN PULONG NameTable,
|
|
IN PUSHORT OrdinalTable)
|
|
{
|
|
LONG Start, End, Next, CmpResult;
|
|
|
|
/* Use classical binary search to find the ordinal */
|
|
Start = Next = 0;
|
|
End = NumberOfNames - 1;
|
|
while (End >= Start)
|
|
{
|
|
/* Next will be exactly between Start and End */
|
|
Next = (Start + End) >> 1;
|
|
|
|
/* Compare this name with the one we need to find */
|
|
CmpResult = strcmp(ImportName, (PCHAR)((ULONG_PTR)ExportBase + NameTable[Next]));
|
|
|
|
/* We found our entry if result is 0 */
|
|
if (!CmpResult) break;
|
|
|
|
/* We didn't find, update our range then */
|
|
if (CmpResult < 0)
|
|
{
|
|
End = Next - 1;
|
|
}
|
|
else if (CmpResult > 0)
|
|
{
|
|
Start = Next + 1;
|
|
}
|
|
}
|
|
|
|
/* If end is before start, then the search failed */
|
|
if (End < Start) return -1;
|
|
|
|
/* Return found name */
|
|
return OrdinalTable[Next];
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
LdrpWalkImportDescriptor(IN LPWSTR DllPath OPTIONAL,
|
|
IN PLDR_DATA_TABLE_ENTRY LdrEntry)
|
|
{
|
|
RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_EXTENDED ActCtx;
|
|
PPEB Peb = NtCurrentPeb();
|
|
NTSTATUS Status = STATUS_SUCCESS, Status2;
|
|
PIMAGE_BOUND_IMPORT_DESCRIPTOR BoundEntry = NULL;
|
|
PIMAGE_IMPORT_DESCRIPTOR ImportEntry;
|
|
ULONG BoundSize, IatSize;
|
|
|
|
DPRINT("LdrpWalkImportDescriptor - BEGIN (%wZ %p '%S')\n", &LdrEntry->BaseDllName, LdrEntry, DllPath);
|
|
|
|
/* Set up the Act Ctx */
|
|
RtlZeroMemory(&ActCtx, sizeof(ActCtx));
|
|
ActCtx.Size = sizeof(ActCtx);
|
|
ActCtx.Format = RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER;
|
|
|
|
/* Check if we have a manifest prober routine */
|
|
if (LdrpManifestProberRoutine)
|
|
{
|
|
/* Probe the DLL for its manifest. Some details are omitted */
|
|
Status2 = LdrpManifestProberRoutine(LdrEntry->DllBase, LdrEntry->FullDllName.Buffer, &LdrEntry->EntryPointActivationContext);
|
|
|
|
if (!NT_SUCCESS(Status2) &&
|
|
Status2 != STATUS_NO_SUCH_FILE &&
|
|
Status2 != STATUS_RESOURCE_DATA_NOT_FOUND &&
|
|
Status2 != STATUS_RESOURCE_TYPE_NOT_FOUND &&
|
|
Status2 != STATUS_RESOURCE_NAME_NOT_FOUND &&
|
|
Status2 != STATUS_RESOURCE_LANG_NOT_FOUND)
|
|
{
|
|
/* Some serious issue */
|
|
//Status = Status2; // FIXME: Ignore that error for now
|
|
DbgPrintEx(DPFLTR_SXS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"LDR: LdrpWalkImportDescriptor() failed to probe %wZ for its "
|
|
"manifest, ntstatus = 0x%08lx\n",
|
|
&LdrEntry->FullDllName, Status2);
|
|
}
|
|
}
|
|
|
|
/* Check if we failed above */
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Get the Active ActCtx */
|
|
if (!LdrEntry->EntryPointActivationContext)
|
|
{
|
|
Status = RtlGetActiveActivationContext(&LdrEntry->EntryPointActivationContext);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Exit */
|
|
DbgPrintEx(DPFLTR_SXS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"LDR: RtlGetActiveActivationContext() failed; ntstatus = "
|
|
"0x%08lx\n",
|
|
Status);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
/* Activate the ActCtx */
|
|
RtlActivateActivationContextUnsafeFast(&ActCtx,
|
|
LdrEntry->EntryPointActivationContext);
|
|
|
|
/* Check if we were redirected */
|
|
if (!(LdrEntry->Flags & LDRP_REDIRECTED))
|
|
{
|
|
/* Get the Bound IAT */
|
|
BoundEntry = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
|
|
TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT,
|
|
&BoundSize);
|
|
}
|
|
|
|
/* Get the regular IAT, for fallback */
|
|
ImportEntry = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
|
|
TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_IMPORT,
|
|
&IatSize);
|
|
|
|
/* Check if we got at least one */
|
|
if ((BoundEntry) || (ImportEntry))
|
|
{
|
|
/* Do we have a Bound IAT */
|
|
if (BoundEntry)
|
|
{
|
|
/* Handle the descriptor */
|
|
Status = LdrpHandleNewFormatImportDescriptors(DllPath,
|
|
LdrEntry,
|
|
BoundEntry);
|
|
}
|
|
else
|
|
{
|
|
/* Handle the descriptor */
|
|
Status = LdrpHandleOldFormatImportDescriptors(DllPath,
|
|
LdrEntry,
|
|
ImportEntry);
|
|
}
|
|
|
|
/* Check the status of the handlers */
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Check for Per-DLL Heap Tagging */
|
|
if (Peb->NtGlobalFlag & FLG_HEAP_ENABLE_TAG_BY_DLL)
|
|
{
|
|
/* FIXME */
|
|
DPRINT1("We don't support Per-DLL Heap Tagging yet!\n");
|
|
}
|
|
|
|
/* Check if Page Heap was enabled */
|
|
if (Peb->NtGlobalFlag & FLG_HEAP_PAGE_ALLOCS)
|
|
{
|
|
/* Initialize target DLL */
|
|
AVrfPageHeapDllNotification(LdrEntry);
|
|
}
|
|
|
|
/* Check if Application Verifier was enabled */
|
|
if (Peb->NtGlobalFlag & FLG_APPLICATION_VERIFIER)
|
|
{
|
|
AVrfDllLoadNotification(LdrEntry);
|
|
}
|
|
|
|
/* Just to be safe */
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
/* Release the activation context */
|
|
RtlDeactivateActivationContextUnsafeFast(&ActCtx);
|
|
|
|
DPRINT("LdrpWalkImportDescriptor - END (%wZ %p)\n", &LdrEntry->BaseDllName, LdrEntry);
|
|
|
|
/* Return status */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
LdrpLoadImportModule(IN PWSTR DllPath OPTIONAL,
|
|
IN LPSTR ImportName,
|
|
OUT PLDR_DATA_TABLE_ENTRY *DataTableEntry,
|
|
OUT PBOOLEAN Existing)
|
|
{
|
|
ANSI_STRING AnsiString;
|
|
PUNICODE_STRING ImpDescName;
|
|
const WCHAR *p;
|
|
BOOLEAN GotExtension;
|
|
WCHAR c;
|
|
NTSTATUS Status;
|
|
PPEB Peb = RtlGetCurrentPeb();
|
|
PTEB Teb = NtCurrentTeb();
|
|
UNICODE_STRING RedirectedImpDescName;
|
|
BOOLEAN RedirectedDll;
|
|
|
|
DPRINT("LdrpLoadImportModule('%S' '%s' %p %p)\n", DllPath, ImportName, DataTableEntry, Existing);
|
|
|
|
RedirectedDll = FALSE;
|
|
RtlInitEmptyUnicodeString(&RedirectedImpDescName, NULL, 0);
|
|
|
|
/* Convert import descriptor name to unicode string */
|
|
ImpDescName = &Teb->StaticUnicodeString;
|
|
RtlInitAnsiString(&AnsiString, ImportName);
|
|
Status = RtlAnsiStringToUnicodeString(ImpDescName, &AnsiString, FALSE);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Find the extension, if present */
|
|
p = ImpDescName->Buffer + ImpDescName->Length / sizeof(WCHAR) - 1;
|
|
GotExtension = FALSE;
|
|
while (p >= ImpDescName->Buffer)
|
|
{
|
|
c = *p--;
|
|
if (c == L'.')
|
|
{
|
|
GotExtension = TRUE;
|
|
break;
|
|
}
|
|
else if (c == L'\\')
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* If no extension was found, add the default extension */
|
|
if (!GotExtension)
|
|
{
|
|
/* Check that we have space to add one */
|
|
if ((ImpDescName->Length + LdrApiDefaultExtension.Length + sizeof(UNICODE_NULL)) >=
|
|
sizeof(Teb->StaticUnicodeBuffer))
|
|
{
|
|
/* No space to add the extension */
|
|
DbgPrintEx(DPFLTR_LDR_ID,
|
|
DPFLTR_ERROR_LEVEL,
|
|
"LDR: %s - Dll name missing extension; with extension "
|
|
"added the name is too long\n"
|
|
" ImpDescName: (@ %p) \"%wZ\"\n"
|
|
" ImpDescName->Length: %u\n",
|
|
__FUNCTION__,
|
|
ImpDescName,
|
|
ImpDescName,
|
|
ImpDescName->Length);
|
|
return STATUS_NAME_TOO_LONG;
|
|
}
|
|
|
|
/* Add it. Needs to be null terminated, thus the length check above */
|
|
(VOID)RtlAppendUnicodeStringToString(ImpDescName,
|
|
&LdrApiDefaultExtension);
|
|
}
|
|
|
|
/* Check if the SxS Assemblies specify another file */
|
|
Status = LdrpApplyFileNameRedirection(
|
|
ImpDescName, &LdrApiDefaultExtension, NULL, &RedirectedImpDescName, &ImpDescName, &RedirectedDll);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Unrecoverable SxS failure */
|
|
DPRINT1("LDR: LdrpApplyFileNameRedirection failed with status %x for dll %wZ\n", Status, ImpDescName);
|
|
goto done;
|
|
}
|
|
|
|
/* Check if it's loaded */
|
|
if (LdrpCheckForLoadedDll(DllPath,
|
|
ImpDescName,
|
|
TRUE,
|
|
RedirectedDll,
|
|
DataTableEntry))
|
|
{
|
|
/* It's already existing in the list */
|
|
*Existing = TRUE;
|
|
Status = STATUS_SUCCESS;
|
|
goto done;
|
|
}
|
|
|
|
/* We're loading it for the first time */
|
|
*Existing = FALSE;
|
|
|
|
/* Map it */
|
|
Status = LdrpMapDll(DllPath,
|
|
NULL,
|
|
ImpDescName->Buffer,
|
|
NULL,
|
|
TRUE,
|
|
RedirectedDll,
|
|
DataTableEntry);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("LDR: LdrpMapDll failed with status %x for dll %wZ\n", Status, ImpDescName);
|
|
goto done;
|
|
}
|
|
|
|
/* Walk its import descriptor table */
|
|
Status = LdrpWalkImportDescriptor(DllPath,
|
|
*DataTableEntry);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Add it to the in-init-order list in case of failure */
|
|
InsertTailList(&Peb->Ldr->InInitializationOrderModuleList,
|
|
&(*DataTableEntry)->InInitializationOrderLinks);
|
|
}
|
|
|
|
done:
|
|
RtlFreeUnicodeString(&RedirectedImpDescName);
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
LdrpSnapThunk(IN PVOID ExportBase,
|
|
IN PVOID ImportBase,
|
|
IN PIMAGE_THUNK_DATA OriginalThunk,
|
|
IN OUT PIMAGE_THUNK_DATA Thunk,
|
|
IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
|
|
IN ULONG ExportSize,
|
|
IN BOOLEAN Static,
|
|
IN LPSTR DllName)
|
|
{
|
|
BOOLEAN IsOrdinal;
|
|
USHORT Ordinal;
|
|
ULONG OriginalOrdinal = 0;
|
|
PIMAGE_IMPORT_BY_NAME AddressOfData;
|
|
PULONG NameTable;
|
|
PUSHORT OrdinalTable;
|
|
LPSTR ImportName = NULL, DotPosition;
|
|
USHORT Hint;
|
|
NTSTATUS Status;
|
|
ULONG_PTR HardErrorParameters[3];
|
|
UNICODE_STRING HardErrorDllName, HardErrorEntryPointName;
|
|
ANSI_STRING TempString;
|
|
ULONG Mask;
|
|
ULONG Response;
|
|
PULONG AddressOfFunctions;
|
|
UNICODE_STRING TempUString;
|
|
ANSI_STRING ForwarderName;
|
|
PANSI_STRING ForwardName;
|
|
PVOID ForwarderHandle;
|
|
ULONG ForwardOrdinal;
|
|
|
|
/* Check if the snap is by ordinal */
|
|
if ((IsOrdinal = IMAGE_SNAP_BY_ORDINAL(OriginalThunk->u1.Ordinal)))
|
|
{
|
|
/* Get the ordinal number, and its normalized version */
|
|
OriginalOrdinal = IMAGE_ORDINAL(OriginalThunk->u1.Ordinal);
|
|
Ordinal = (USHORT)(OriginalOrdinal - ExportDirectory->Base);
|
|
}
|
|
else
|
|
{
|
|
/* First get the data VA */
|
|
AddressOfData = (PIMAGE_IMPORT_BY_NAME)
|
|
((ULONG_PTR)ImportBase +
|
|
((ULONG_PTR)OriginalThunk->u1.AddressOfData & 0xffffffff));
|
|
|
|
/* Get the name */
|
|
ImportName = (LPSTR)AddressOfData->Name;
|
|
|
|
/* Now get the VA of the Name and Ordinal Tables */
|
|
NameTable = (PULONG)((ULONG_PTR)ExportBase +
|
|
(ULONG_PTR)ExportDirectory->AddressOfNames);
|
|
OrdinalTable = (PUSHORT)((ULONG_PTR)ExportBase +
|
|
(ULONG_PTR)ExportDirectory->AddressOfNameOrdinals);
|
|
|
|
/* Get the hint */
|
|
Hint = AddressOfData->Hint;
|
|
|
|
/* Try to get a match by using the hint */
|
|
if (((ULONG)Hint < ExportDirectory->NumberOfNames) &&
|
|
(!strcmp(ImportName, ((LPSTR)((ULONG_PTR)ExportBase + NameTable[Hint])))))
|
|
{
|
|
/* We got a match, get the Ordinal from the hint */
|
|
Ordinal = OrdinalTable[Hint];
|
|
}
|
|
else
|
|
{
|
|
/* Well bummer, hint didn't work, do it the long way */
|
|
Ordinal = LdrpNameToOrdinal(ImportName,
|
|
ExportDirectory->NumberOfNames,
|
|
ExportBase,
|
|
NameTable,
|
|
OrdinalTable);
|
|
}
|
|
}
|
|
|
|
/* Check if the ordinal is invalid */
|
|
if ((ULONG)Ordinal >= ExportDirectory->NumberOfFunctions)
|
|
{
|
|
FailurePath:
|
|
/* Is this a static snap? */
|
|
if (Static)
|
|
{
|
|
UNICODE_STRING SnapTarget;
|
|
PLDR_DATA_TABLE_ENTRY LdrEntry;
|
|
|
|
/* What was the module we were searching in */
|
|
RtlInitAnsiString(&TempString, DllName ? DllName : "Unknown");
|
|
|
|
/* What was the module we were searching for */
|
|
if (LdrpCheckForLoadedDllHandle(ImportBase, &LdrEntry))
|
|
SnapTarget = LdrEntry->BaseDllName;
|
|
else
|
|
RtlInitUnicodeString(&SnapTarget, L"Unknown");
|
|
|
|
/* Inform the debug log */
|
|
if (IsOrdinal)
|
|
DPRINT1("Failed to snap ordinal %Z!0x%x for %wZ\n", &TempString, OriginalOrdinal, &SnapTarget);
|
|
else
|
|
DPRINT1("Failed to snap %Z!%s for %wZ\n", &TempString, ImportName, &SnapTarget);
|
|
|
|
/* These are critical errors. Setup a string for the DLL name */
|
|
RtlAnsiStringToUnicodeString(&HardErrorDllName, &TempString, TRUE);
|
|
|
|
/* Set it as the parameter */
|
|
HardErrorParameters[1] = (ULONG_PTR)&HardErrorDllName;
|
|
Mask = 2;
|
|
|
|
/* Check if we have an ordinal */
|
|
if (IsOrdinal)
|
|
{
|
|
/* Then set the ordinal as the 1st parameter */
|
|
HardErrorParameters[0] = OriginalOrdinal;
|
|
}
|
|
else
|
|
{
|
|
/* We don't, use the entrypoint. Set up a string for it */
|
|
RtlInitAnsiString(&TempString, ImportName);
|
|
RtlAnsiStringToUnicodeString(&HardErrorEntryPointName,
|
|
&TempString,
|
|
TRUE);
|
|
|
|
/* Set it as the parameter */
|
|
HardErrorParameters[0] = (ULONG_PTR)&HardErrorEntryPointName;
|
|
Mask = 3;
|
|
}
|
|
|
|
/* Raise the error */
|
|
NtRaiseHardError(IsOrdinal ? STATUS_ORDINAL_NOT_FOUND :
|
|
STATUS_ENTRYPOINT_NOT_FOUND,
|
|
2,
|
|
Mask,
|
|
HardErrorParameters,
|
|
OptionOk,
|
|
&Response);
|
|
|
|
/* Increase the error count */
|
|
if (LdrpInLdrInit) LdrpFatalHardErrorCount++;
|
|
|
|
/* Free our string */
|
|
RtlFreeUnicodeString(&HardErrorDllName);
|
|
if (!IsOrdinal)
|
|
{
|
|
/* Free our second string. Return entrypoint error */
|
|
RtlFreeUnicodeString(&HardErrorEntryPointName);
|
|
RtlRaiseStatus(STATUS_ENTRYPOINT_NOT_FOUND);
|
|
}
|
|
|
|
/* Return ordinal error */
|
|
RtlRaiseStatus(STATUS_ORDINAL_NOT_FOUND);
|
|
}
|
|
else
|
|
{
|
|
/* Inform the debug log */
|
|
if (IsOrdinal)
|
|
DPRINT("Non-fatal: Failed to snap ordinal 0x%x\n", OriginalOrdinal);
|
|
else
|
|
DPRINT("Non-fatal: Failed to snap %s\n", ImportName);
|
|
}
|
|
|
|
/* Set this as a bad DLL */
|
|
Thunk->u1.Function = (ULONG_PTR)0xffbadd11;
|
|
|
|
/* Return the right error code */
|
|
Status = IsOrdinal ? STATUS_ORDINAL_NOT_FOUND :
|
|
STATUS_ENTRYPOINT_NOT_FOUND;
|
|
}
|
|
else
|
|
{
|
|
/* The ordinal seems correct, get the AddressOfFunctions VA */
|
|
AddressOfFunctions = (PULONG)
|
|
((ULONG_PTR)ExportBase +
|
|
(ULONG_PTR)ExportDirectory->AddressOfFunctions);
|
|
|
|
/* Write the function pointer*/
|
|
Thunk->u1.Function = (ULONG_PTR)ExportBase + AddressOfFunctions[Ordinal];
|
|
|
|
/* Make sure it's within the exports */
|
|
if ((Thunk->u1.Function > (ULONG_PTR)ExportDirectory) &&
|
|
(Thunk->u1.Function < ((ULONG_PTR)ExportDirectory + ExportSize)))
|
|
{
|
|
/* Get the Import and Forwarder Names */
|
|
ImportName = (LPSTR)Thunk->u1.Function;
|
|
|
|
DotPosition = strchr(ImportName, '.');
|
|
ASSERT(DotPosition != NULL);
|
|
if (!DotPosition)
|
|
goto FailurePath;
|
|
|
|
ForwarderName.Buffer = ImportName;
|
|
ForwarderName.Length = (USHORT)(DotPosition - ImportName);
|
|
ForwarderName.MaximumLength = ForwarderName.Length;
|
|
Status = RtlAnsiStringToUnicodeString(&TempUString,
|
|
&ForwarderName,
|
|
TRUE);
|
|
|
|
/* Make sure the conversion was OK */
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
WCHAR StringBuffer[MAX_PATH];
|
|
UNICODE_STRING StaticString, *RedirectedImportName;
|
|
BOOLEAN Redirected = FALSE;
|
|
|
|
RtlInitEmptyUnicodeString(&StaticString, StringBuffer, sizeof(StringBuffer));
|
|
|
|
/* Check if the SxS Assemblies specify another file */
|
|
Status = LdrpApplyFileNameRedirection(
|
|
&TempUString, &LdrApiDefaultExtension, &StaticString, NULL, &RedirectedImportName, &Redirected);
|
|
|
|
if (NT_SUCCESS(Status) && Redirected)
|
|
{
|
|
if (ShowSnaps)
|
|
DPRINT1("LDR: %Z got redirected to %wZ\n", &ForwarderName, RedirectedImportName);
|
|
}
|
|
else
|
|
{
|
|
RedirectedImportName = &TempUString;
|
|
}
|
|
|
|
/* Load the forwarder */
|
|
Status = LdrpLoadDll(Redirected,
|
|
NULL,
|
|
NULL,
|
|
RedirectedImportName,
|
|
&ForwarderHandle,
|
|
FALSE);
|
|
|
|
RtlFreeUnicodeString(&TempUString);
|
|
}
|
|
|
|
/* If the load or conversion failed, use the failure path */
|
|
if (!NT_SUCCESS(Status)) goto FailurePath;
|
|
|
|
/* Now set up a name for the actual forwarder dll */
|
|
RtlInitAnsiString(&ForwarderName,
|
|
ImportName + ForwarderName.Length + sizeof(CHAR));
|
|
|
|
/* Check if it's an ordinal forward */
|
|
if ((ForwarderName.Length > 1) && (*ForwarderName.Buffer == '#'))
|
|
{
|
|
/* We don't have an actual function name */
|
|
ForwardName = NULL;
|
|
|
|
/* Convert the string into an ordinal */
|
|
Status = RtlCharToInteger(ForwarderName.Buffer + sizeof(CHAR),
|
|
0,
|
|
&ForwardOrdinal);
|
|
|
|
/* If this fails, then error out */
|
|
if (!NT_SUCCESS(Status)) goto FailurePath;
|
|
}
|
|
else
|
|
{
|
|
/* Import by name */
|
|
ForwardName = &ForwarderName;
|
|
ForwardOrdinal = 0;
|
|
}
|
|
|
|
/* Get the pointer */
|
|
Status = LdrpGetProcedureAddress(ForwarderHandle,
|
|
ForwardName,
|
|
ForwardOrdinal,
|
|
(PVOID*)&Thunk->u1.Function,
|
|
FALSE);
|
|
/* If this fails, then error out */
|
|
if (!NT_SUCCESS(Status)) goto FailurePath;
|
|
}
|
|
else
|
|
{
|
|
/* It's not within the exports, let's hope it's valid */
|
|
if (!AddressOfFunctions[Ordinal]) goto FailurePath;
|
|
}
|
|
|
|
/* If we got here, then it's success */
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Return status */
|
|
return Status;
|
|
}
|
|
|
|
/* EOF */
|