windows-nt/Source/XPSP1/NT/base/ntdll/ldrwx86.c
2020-09-26 16:20:57 +08:00

1251 lines
37 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.
13-Mar-2001 SamerA Fix sharing of varialbes inside a SHARED read/write section.
20-May-2001 SamerA Fix mapping of image sections that have their PointerToRawData
RVA overlap with other sections virtual addresses.
(Fixed corel's WP2002 intro.exe)
24-Oct-2001 SamerA Correct calculation of variables offsets inside the relocated
shared section.
--*/
#include "ntos.h"
#include "ldrp.h"
#define PAGE_SIZE_X86 (0x1000)
#if defined(BUILD_WOW6432)
//
// From mi\mi.h:
//
#define MI_ROUND_TO_SIZE(LENGTH,ALIGNMENT) \
(((LENGTH) + ((ALIGNMENT) - 1)) & ~((ALIGNMENT) - 1))
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
LdrpWx86FixupExportedSharedSection (
IN PVOID ImageBase,
IN PIMAGE_NT_HEADERS NtHeaders
);
BOOLEAN
LdrpWx86DetectSectionOverlap (
IN PVOID ImageBase,
IN PIMAGE_NT_HEADERS NtHeaders
)
{
PIMAGE_SECTION_HEADER SectionHeader;
ULONG SrcRawData;
ULONG SrcEndRawData;
ULONG OverlapData;
ULONG EndOverlapData;
ULONG SrcSize;
ULONG Section;
ULONG SectionCheck;
ULONG Count;
BOOLEAN Result = FALSE;
//
// Run through the section and see if any one need to be moved down (higher in address space),
// then for each one of those, check if it overlap with any section
// that has already been moved up.
//
SectionHeader = IMAGE_FIRST_SECTION (NtHeaders);
for (Section = NtHeaders->FileHeader.NumberOfSections-1, Count=0 ;
Count < NtHeaders->FileHeader.NumberOfSections ; Section--, Count++) {
SrcRawData = SectionHeader[Section].PointerToRawData;
SrcSize = SectionHeader[Section].SizeOfRawData;
if ((SectionHeader[Section].Misc.VirtualSize > 0) &&
(SrcRawData > MI_ROUND_TO_SIZE(SectionHeader[Section].Misc.VirtualSize, PAGE_SIZE_X86))) {
SrcRawData = MI_ROUND_TO_SIZE(SectionHeader[Section].Misc.VirtualSize, PAGE_SIZE_X86);
}
if (SectionHeader[Section].VirtualAddress <= SrcRawData) {
break;
}
SrcEndRawData = SrcRawData + SrcSize;
//
// This section needs to be moved down
//
for (SectionCheck = 0 ; SectionCheck < NtHeaders->FileHeader.NumberOfSections ; SectionCheck++) {
if (Section == SectionCheck) {
continue;
}
OverlapData = SectionHeader[SectionCheck].PointerToRawData;
SrcSize = SectionHeader[SectionCheck].SizeOfRawData;
if ((SectionHeader[SectionCheck].Misc.VirtualSize > 0) &&
(SrcRawData > MI_ROUND_TO_SIZE(SectionHeader[SectionCheck].Misc.VirtualSize, PAGE_SIZE_X86))) {
SrcRawData = MI_ROUND_TO_SIZE(SectionHeader[SectionCheck].Misc.VirtualSize, PAGE_SIZE_X86);
}
if (SectionHeader[SectionCheck].VirtualAddress > OverlapData) {
break;
}
if (((SrcRawData >= SectionHeader[SectionCheck].VirtualAddress) &&
(SrcRawData < (SectionHeader[SectionCheck].VirtualAddress + SrcSize))) ||
((SrcEndRawData >= SectionHeader[SectionCheck].VirtualAddress) &&
(SrcEndRawData < (SectionHeader[SectionCheck].VirtualAddress + SrcSize)))) {
Result = TRUE;
break;
}
}
if (Result == TRUE) {
break;
}
}
return Result;
}
NTSTATUS
LdrpWx86CheckVirtualSectionOverlap (
IN PUNICODE_STRING ImageName OPTIONAL,
IN PVOID ImageBase,
IN PIMAGE_NT_HEADERS NtHeaders,
OUT PVOID *SrcImageMap
)
/*++
Routine Description:
This function goes through the image sections based at ImageBase and looks
for any overlap between the section physical locations and their updated virtual
locations.
Arguments:
ImageName - Unicode string pointer to the full path to the image.
ImageBase - Base of image.
SrcImageMap - Pointer to pointer to receive a base pointer to the image mapped
as read-only pages inside this process. The mapped pages need to be released
when done.
Return Value:
NTSTATUS.
--*/
{
PUNICODE_STRING NtPathName;
PVOID FreeBuffer;
BOOLEAN Result;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
HANDLE FileHandle;
HANDLE MappingHandle;
PVOID ViewBase;
SIZE_T ViewSize;
LARGE_INTEGER SectionOffset;
UCHAR Buffer[ DOS_MAX_PATH_LENGTH ];
NTSTATUS NtStatus = STATUS_UNSUCCESSFUL;
//
// Check for any overlap inside the image.
//
Result = LdrpWx86DetectSectionOverlap (ImageBase, NtHeaders);
if (Result == FALSE) {
return NtStatus;
}
FreeBuffer = NULL;
//
// Make sure we have a path.
//
NtPathName = (PUNICODE_STRING)Buffer;
if (ARGUMENT_PRESENT (ImageName) == 0) {
NtStatus = NtQueryInformationProcess(
NtCurrentProcess(),
ProcessImageFileName,
NtPathName,
sizeof (Buffer),
NULL
);
} else {
Result = RtlDosPathNameToNtPathName_U(
ImageName->Buffer,
NtPathName,
NULL,
NULL
);
if (Result != FALSE) {
FreeBuffer = NtPathName->Buffer;
NtStatus = STATUS_SUCCESS;
}
}
if (NT_SUCCESS (NtStatus)) {
InitializeObjectAttributes(
&ObjectAttributes,
NtPathName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
NtStatus = NtCreateFile(
&FileHandle,
(ACCESS_MASK) GENERIC_READ | SYNCHRONIZE | FILE_READ_ATTRIBUTES,
&ObjectAttributes,
&IoStatusBlock,
NULL,
0L,
FILE_SHARE_READ | FILE_SHARE_DELETE,
FILE_OPEN,
0L,
NULL,
0L
);
if (FreeBuffer != NULL) {
RtlFreeHeap (RtlProcessHeap(), 0, FreeBuffer);
}
if (NT_SUCCESS (NtStatus)) {
NtStatus = NtCreateSection(
&MappingHandle,
STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ,
NULL,
NULL,
PAGE_READONLY,
SEC_COMMIT,
FileHandle
);
NtClose (FileHandle);
if (NT_SUCCESS (NtStatus)) {
SectionOffset.LowPart = 0;
SectionOffset.HighPart = 0;
ViewSize = 0;
ViewBase = NULL;
NtStatus = NtMapViewOfSection(
MappingHandle,
NtCurrentProcess(),
&ViewBase,
0L,
0L,
&SectionOffset,
&ViewSize,
ViewShare,
0L,
PAGE_READONLY
);
NtClose (MappingHandle);
if (NT_SUCCESS (NtStatus)) {
*SrcImageMap = ViewBase;
}
}
}
}
return NtStatus;
}
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++) {
RegionSize = SectionHeader->SizeOfRawData;
if ((SectionHeader->Characteristics & IMAGE_SCN_MEM_SHARED) &&
(!(SectionHeader->Characteristics & IMAGE_SCN_MEM_EXECUTE) ||
(SectionHeader->Characteristics & IMAGE_SCN_MEM_WRITE)) &&
(RegionSize != 0)) {
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(),
Base,
NumberOfNativePagesForImage << NATIVE_PAGE_SHIFT
);
}
return STATUS_SUCCESS;
}
NTSTATUS
LdrpWx86FormatVirtualImage(
IN PUNICODE_STRING DosImagePathName,
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;
PVOID AlternateBase;
NTSTATUS st;
AlternateBase = NULL;
st = LdrpWx86CheckVirtualSectionOverlap (DosImagePathName,
DllBase,
NtHeaders,
&AlternateBase
);
st = Wx86SetRelocatedSharedProtection(DllBase, FALSE);
if (!NT_SUCCESS(st)) {
DbgPrint("Wx86SetRelocatedSharedProtection failed with return status %x\n", st);
Wx86SetRelocatedSharedProtection(DllBase, TRUE);
if (AlternateBase != NULL) {
NtUnmapViewOfSection (NtCurrentProcess(), AlternateBase);
}
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 consideration 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);
if (AlternateBase != NULL) {
NtUnmapViewOfSection (NtCurrentProcess(), AlternateBase);
}
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,
(AlternateBase != NULL) ?
((PCHAR)AlternateBase + Section->PointerToRawData) : 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
);
}
}
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);
}
//
// Zero out remaining bytes up to the next section
//
RtlZeroMemory(DestVirtualAddress + Section->SizeOfRawData,
(ULONG)(NextVirtualAddress - DestVirtualAddress - SubSectionSize)
);
NextVirtualAddress = DestVirtualAddress;
Section--;
}
//
// Unmap the alternate base if it is there
//
if (AlternateBase != NULL) {
NtUnmapViewOfSection (NtCurrentProcess(), AlternateBase);
}
// 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;
ULONG ImagePages = NATIVE_BYTES_TO_PAGES (NtHeaders->OptionalHeader.SizeOfImage);
EndOfSection = Section->PointerToRawData + Section->SizeOfRawData;
if (NATIVE_BYTES_TO_PAGES (EndOfSection) > ImagePages) {
ExtraPages = NATIVE_BYTES_TO_PAGES (EndOfSection) - ImagePages;
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;
if (SizeOfBlock == 0) {
if (ShowSnaps) {
DbgPrint("Image at %lx contains invalid block size. Stopping fixups\n",
ImageBase);
}
break;
}
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;
}
}
//
// If any of the variables inside the shared section is exported, then
// we need to fix up its RVA to point to the proper location at
// the end of the image.
//
LdrpWx86FixupExportedSharedSection (
DllBase,
NtHeaders
);
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;
}
NTSTATUS
LdrpWx86FixupExportedSharedSection (
IN PVOID ImageBase,
IN PIMAGE_NT_HEADERS NtHeaders
)
/*++
Routine Description:
This function goes through the exported entries from this module,
and relocates (fixup) any address that lie inside any shared
read/write to the end of the image.
Arguments:
ImageBase - Virtual address for image base.
NtHeaders - Address of the image's header.
Return Value:
NTSTATUS.
--*/
{
PIMAGE_EXPORT_DIRECTORY ImageExportDirectory;
ULONG TotalBytes;
ULONG SharedRelocFixup;
ULONG Export;
PULONG ExportEntry;
NTSTATUS NtStatus = STATUS_SUCCESS;
ImageExportDirectory = RtlImageDirectoryEntryToData (
ImageBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_EXPORT,
&TotalBytes);
if ((ImageExportDirectory == NULL) || (TotalBytes == 0)) {
return NtStatus;
}
ExportEntry = (PULONG)((ULONG)ImageBase + ImageExportDirectory->AddressOfFunctions);
for (Export = 0 ; Export < ImageExportDirectory->NumberOfFunctions ; Export++) {
SharedRelocFixup = LdrpWx86RelocatedFixupDiff (
ImageBase,
NtHeaders,
ExportEntry[Export]
);
if (SharedRelocFixup != 0) {
if (ShowSnaps) {
DbgPrint("LdrpWx86FixupExportedSharedSection: Changing export Export[%lx] from %lx to %lx\n",
Export,
ExportEntry[Export],
ExportEntry [Export] + SharedRelocFixup);
}
ExportEntry [Export] += SharedRelocFixup;
}
}
return NtStatus;
}
////////////////////////////////////////////////////
ULONG
LdrpWx86RelocatedFixupDiff(
IN PUCHAR ImageBase,
IN PIMAGE_NT_HEADERS NtHeaders,
IN ULONG Offset
)
{
PIMAGE_SECTION_HEADER SectionHeader;
ULONG i;
ULONG NumberOfSharedDataPages;
ULONG NumberOfNativePagesForImage;
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;
PIMAGE_NT_HEADERS NtHeaders;
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;
}
NtHeaders = RtlImageNtHeader (ImageBase);
while (TotalBytes) {
SizeOfBlock = NextBlock->SizeOfBlock;
if (SizeOfBlock == 0) {
if (ShowSnaps) {
DbgPrint("Image at %lx contains invalid block size. Stopping fixups\n",
ImageBase);
}
break;
}
TotalBytes -= SizeOfBlock;
SizeOfBlock -= sizeof(IMAGE_BASE_RELOCATION);
SizeOfBlock /= sizeof(USHORT);
NextOffset = (PUSHORT) ((PCHAR)NextBlock +
sizeof(IMAGE_BASE_RELOCATION));
NextBlock->VirtualAddress += LdrpWx86RelocatedFixupDiff (
ImageBase,
NtHeaders,
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;
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 :
//
// Absolute - no fixup required.
//
break;
case IMAGE_REL_BASED_SECTION :
//
// Section Relative reloc. Ignore for now.
//
break;
case IMAGE_REL_BASED_REL32 :
//
// Relative intrasection. Ignore for now.
//
break;
default :
//
// Illegal - illegal relocation type.
//
return (PIMAGE_BASE_RELOCATION)NULL;
}
++NextOffset;
}
return (PIMAGE_BASE_RELOCATION)NextOffset;
}
#endif // BUILD_WOW6432