|
|
/*++
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) 18-Sep-2001 SamerA Change page protection of image-pages to be compatible with what the section header says (x86 compatibility). 24-Oct-2001 SamerA Correct calculation of variables offsets inside the relocated shared section. --*/
#include "ldrp.h"
#include "ntos.h"
#define PAGE_SIZE_X86 (0x1000)
#if defined(BUILD_WOW6432)
//
// This table is indexed by the last 3 bits of the SectionHeader->Characteristics to get the page protection
// from the section protection value. The result value is to be passed to NtProtectVirtualMemory ()
//
ULONG SectionToPageProtection [8] = { PAGE_NOACCESS, PAGE_EXECUTE, PAGE_READONLY, PAGE_EXECUTE_READ, PAGE_READWRITE, PAGE_EXECUTE_READWRITE, PAGE_READWRITE, PAGE_EXECUTE_READWRITE };
//
// 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 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 PIMAGE_NT_HEADERS NtHeaders ) { PIMAGE_SECTION_HEADER SectionHeader; ULONG SrcRawData; ULONG SrcEndRawData; 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);
if (NtHeaders->FileHeader.NumberOfSections == 0) { return FALSE; }
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))) { SrcSize = MI_ROUND_TO_SIZE(SectionHeader[Section].Misc.VirtualSize, PAGE_SIZE_X86); }
if (SrcRawData > (MAXULONG - SrcSize)) { break; }
SrcEndRawData = SrcRawData + SrcSize;
//
// This section needs to be moved down
//
for (SectionCheck = 0 ; SectionCheck < NtHeaders->FileHeader.NumberOfSections ; SectionCheck++) {
if (Section == SectionCheck) { continue; }
SrcSize = SectionHeader[SectionCheck].SizeOfRawData; if ((SectionHeader[SectionCheck].Misc.VirtualSize > 0) && (SrcSize > MI_ROUND_TO_SIZE(SectionHeader[SectionCheck].Misc.VirtualSize, PAGE_SIZE_X86))) { SrcSize = MI_ROUND_TO_SIZE(SectionHeader[SectionCheck].Misc.VirtualSize, PAGE_SIZE_X86); }
#if WOW6432_DBG
DbgPrint("SrcRawData=%lx, SrcEndRawData=%lx, SectionFirstVA=%lx, SectionEndVA=%lx\n", SrcRawData, SrcEndRawData, SectionHeader[SectionCheck].VirtualAddress, SectionHeader[SectionCheck].VirtualAddress + SrcSize); #endif
if (SectionHeader[SectionCheck].VirtualAddress > (MAXULONG - SrcSize)) { 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 PCUNICODE_STRING ImageName OPTIONAL, 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. SUCCESS - Means the image has been found to have overlapped sections, and an alternate section has been mapped in. FAILURE - The image mapping failed or the image doesn't have overlapped sections.
--*/
{ 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 (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 LdrpWx86ProtectImagePages ( IN PVOID Base, IN BOOLEAN Reset )
/*++
Routine Description:
This function loops through the image sections, and reset the page protection to what is specified inside the section header.
Arguments:
Base - Base of image. Reset - Indicate whether to reset to original section protection.
Return Value:
SUCCESS or reason NtProtectVirtualMemory failed.
--*/
{ NTSTATUS NtStatus = STATUS_SUCCESS; ULONG SectionIndex; ULONG RegionSize; ULONG SectionCharacteristics; ULONG PageProtection; ULONG OldProtection; PIMAGE_NT_HEADERS NtHeaders; PIMAGE_SECTION_HEADER Section; PVOID VirtualAddress; NtHeaders = RtlImageNtHeader (Base);
if (NtHeaders == NULL) { if (ShowSnaps) { DbgPrint ("LdrpWx86ProtectImagePages - Failed to read NtImageHeaders. Base=%p\n", Base); } return STATUS_INTERNAL_ERROR; }
NtStatus = Wx86SetRelocatedSharedProtection (Base, Reset);
if (!NT_SUCCESS (NtStatus)) { if (ShowSnaps) { DbgPrint("LdrpWx86ProtectImagePages - Wx86SetRelocatedSharedProtection(%p, TRUE) failed - NtStatus\n", Base, NtStatus); }
return NtStatus; }
Section = (PIMAGE_SECTION_HEADER) ((ULONG_PTR)NtHeaders + sizeof (ULONG) + sizeof (IMAGE_FILE_HEADER) + NtHeaders->FileHeader.SizeOfOptionalHeader);
//
// Loop through available sections, making sure not to touch sharable pages, as they are relocated to the
// the end of the image.
//
for (SectionIndex = 0 ; SectionIndex < NtHeaders->FileHeader.NumberOfSections ; SectionIndex++, Section++) {
RegionSize = Section->SizeOfRawData;
if (Section->Misc.VirtualSize != 0) { RegionSize = Section->Misc.VirtualSize; }
RegionSize = MI_ROUND_TO_SIZE (RegionSize, PAGE_SIZE_X86);
if (RegionSize != 0) { SectionCharacteristics = Section->Characteristics; if ((SectionCharacteristics & IMAGE_SCN_MEM_SHARED) == 0) { if (Reset) { PageProtection = SectionToPageProtection [SectionCharacteristics >> 29]; } else { PageProtection = PAGE_READWRITE; }
VirtualAddress = (PVOID)((ULONG_PTR)Base + Section->VirtualAddress); NtStatus = NtProtectVirtualMemory (NtCurrentProcess (), &VirtualAddress, &RegionSize, PageProtection, &OldProtection);
#if WOW6432_DBG
DbgPrint("Protecting %lx - VA=%lx, Prot=%lx, SectionProt=%lx, Size = %lx, ST=%lx\n", Base, ((ULONG_PTR)Base + Section->VirtualAddress), PageProtection, SectionCharacteristics, RegionSize, NtStatus); #endif
if (!NT_SUCCESS (NtStatus)) { if (ShowSnaps) { DbgPrint ("LdrpWx86ProtectImagePages - Failed to protect section %lx at %lx with protection %lx\n", SectionIndex, ((ULONG_PTR)Base + Section->VirtualAddress), PageProtection); } return NtStatus; } } } }
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);
if (NtHeaders == NULL) { return STATUS_INVALID_PARAMETER; }
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)) { if (ShowSnaps) { DbgPrint("Wx86SetRelocatedSharedProtection - Failed to protect memory: Va=%p, RegionSize=%lx, NewProtect=%lx - %lx\n", VirtualAddress, RegionSize, NewProtect, st); } return st; } } } }
if (Reset) { st = NtFlushInstructionCache( NtCurrentProcess(), Base, NumberOfNativePagesForImage << NATIVE_PAGE_SHIFT );
if (!NT_SUCCESS(st)) { return st; } }
return STATUS_SUCCESS; }
VOID LdrpWx86RelocateImageDirectoryEntries ( IN PVOID Base, IN PIMAGE_NT_HEADERS32 NtHeaders )
{ ULONG Rva; ULONG DirectoryEntry;
DirectoryEntry = 0; while (DirectoryEntry < NtHeaders->OptionalHeader.NumberOfRvaAndSizes) {
//
// [askhalid] - don't relocate resource entries. Somecode check that against imagezise
// if you must relocate resource entry then must change image size that might cause more appcompat.
//
if ((NtHeaders->OptionalHeader.DataDirectory [DirectoryEntry].VirtualAddress != 0) && (NtHeaders->OptionalHeader.DataDirectory [DirectoryEntry].Size > 0)&& ( DirectoryEntry != IMAGE_DIRECTORY_ENTRY_RESOURCE) ) {
Rva = NtHeaders->OptionalHeader.DataDirectory [DirectoryEntry].VirtualAddress;
NtHeaders->OptionalHeader.DataDirectory [DirectoryEntry].VirtualAddress += LdrpWx86RelocatedFixupDiff (Base, NtHeaders, NtHeaders->OptionalHeader.DataDirectory [DirectoryEntry].VirtualAddress ); if (ShowSnaps) { if (Rva != NtHeaders->OptionalHeader.DataDirectory [DirectoryEntry].VirtualAddress) { DbgPrint("LdrpWx86RelocateImageDirectoryEntry (%p) - Relocating DirEntry %lx from %lx to %lx\n", Base, DirectoryEntry, Rva, NtHeaders->OptionalHeader.DataDirectory [DirectoryEntry].VirtualAddress); } } } DirectoryEntry++; } return; }
NTSTATUS LdrpWx86FormatVirtualImage ( IN PCUNICODE_STRING DosImagePathName OPTIONAL, IN PIMAGE_NT_HEADERS32 NtHeaders, IN PVOID DllBase ) { PIMAGE_SECTION_HEADER SectionTable, Section, LastSection, FirstSection; PUCHAR NextVirtualAddress, SrcVirtualAddress, DestVirtualAddress; PUCHAR ImageBase= DllBase; LONG Size; ULONG NumberOfSharedDataPages; ULONG NumberOfNativePagesForImage; ULONG NumberOfExtraPagesForImage; ULONG_PTR PreferredImageBase; BOOLEAN ImageHasRelocatedSharedSection = FALSE; ULONG bFirst; ULONG SubSectionSize; PVOID AlternateBase; NTSTATUS st; AlternateBase = NULL; st = LdrpWx86CheckVirtualSectionOverlap (DosImagePathName, NtHeaders, &AlternateBase); #if WOW6432_DBG
if (NT_SUCCESS (st) && (AlternateBase != NULL)) { DbgPrint("Mapping in %wZ with AlternateBase = %p\n", DosImagePathName, AlternateBase); } #endif
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 + MI_ROUND_TO_SIZE(NtHeaders->OptionalHeader.SizeOfImage, PAGE_SIZE_X86);
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) && (AlternateBase == NULL)) { //
// 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) ? ((PUCHAR)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 + MI_ROUND_TO_SIZE(NtHeaders->OptionalHeader.SizeOfImage, PAGE_SIZE_X86);
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 (ShowSnaps) { DbgPrint("Unsupported IMAGE_SCN_MEM_SHARED %x\n", Section->Characteristics ); } }
//
// 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 all other addresses.
if (!ImageHasRelocatedSharedSection) { goto LdrwWx86FormatVirtualImageDone; }
st = FixupBlockList(DllBase); if (!NT_SUCCESS(st)) { Wx86SetRelocatedSharedProtection(DllBase, TRUE); return st; }
LdrpWx86RelocateImageDirectoryEntries (DllBase, NtHeaders);
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;
bFirst = 1; for (Section = SectionTable; Section <= LastSection; Section++) {
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, 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); } }
//
// Protect the shared pages
//
st = LdrpWx86ProtectImagePages (DllBase, TRUE);
if (!NT_SUCCESS(st)) { return st; }
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;
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); if (! NtHeaders) { if (ShowSnaps) { DbgPrint("LdrpWx86FixupBlockList: failure " "to locate image header for image at %x.\n", ImageBase); } return STATUS_INVALID_IMAGE_FORMAT; }
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);
if (! NtHeaders) { return FALSE; }
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 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
|