- Implement enable/disable of protection for PE sections in ntdll

- Finally get rid of duplicated relocation code in ntdll by using the relocate function from RTL and enable/disable protection
- Some improvements added to RTL's relocation function, it's split up into two functions now

svn path=/trunk/; revision=24020
This commit is contained in:
Aleksey Bragin 2006-09-10 13:33:30 +00:00
parent 65df8c2bad
commit b52f7b069b
2 changed files with 177 additions and 169 deletions

View file

@ -1198,126 +1198,89 @@ LdrGetExportByName(PVOID BaseAddress,
return (PVOID)NULL;
}
/**********************************************************************
* NAME LOCAL
* LdrPerformRelocations
*
* DESCRIPTION
* Relocate a DLL's memory image.
*
* ARGUMENTS
*
* RETURN VALUE
*
* REVISIONS
*
* NOTE
*
*/
static NTSTATUS
LdrPerformRelocations(PIMAGE_NT_HEADERS NTHeaders,
PVOID ImageBase)
/*++
* @name LdrpSetProtection
*
* Local routine, used to set or reset pages protection.
* Used during PE relocation process.
*
* @param BaseAddress
* Base address of the image.
*
* @param Protection
* FALSE to disable protection, TRUE to enable it
*
* @return Usual NTSTATUS.
*
* @remarks None.
*
*--*/
NTSTATUS
LdrpSetProtection(IN PVOID BaseAddress,
IN BOOLEAN Protection)
{
PIMAGE_DATA_DIRECTORY RelocationDDir;
PIMAGE_BASE_RELOCATION RelocationDir, RelocationEnd;
ULONG Count, ProtectSize, OldProtect, OldProtect2;
PVOID Page, ProtectPage, ProtectPage2;
PUSHORT TypeOffset;
ULONG_PTR Delta;
NTSTATUS Status;
PIMAGE_NT_HEADERS NtHeaders;
PIMAGE_SECTION_HEADER SectionHeader;
ULONG i;
PVOID Address;
SIZE_T Size;
NTSTATUS Status;
ULONG NewAccess, OldAccess;
if (NTHeaders->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED)
NtHeaders = RtlImageNtHeader(BaseAddress);
/* Ensure image is valid */
if (NtHeaders == NULL)
return STATUS_INVALID_IMAGE_FORMAT;
/* Get the first section header */
SectionHeader = IMAGE_FIRST_SECTION(NtHeaders);
/* Loop through every section */
for (i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++, SectionHeader++)
{
return STATUS_UNSUCCESSFUL;
}
/* If section has no data - just skip to the next section */
if (SectionHeader->SizeOfRawData == 0)
continue;
RelocationDDir =
&NTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
if (RelocationDDir->VirtualAddress == 0 || RelocationDDir->Size == 0)
{
return STATUS_SUCCESS;
}
ProtectSize = PAGE_SIZE;
Delta = (ULONG_PTR)ImageBase - NTHeaders->OptionalHeader.ImageBase;
RelocationDir = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)ImageBase +
RelocationDDir->VirtualAddress);
RelocationEnd = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)ImageBase +
RelocationDDir->VirtualAddress + RelocationDDir->Size);
while (RelocationDir < RelocationEnd &&
RelocationDir->SizeOfBlock > 0)
{
Count = (RelocationDir->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) /
sizeof(USHORT);
Page = (PVOID)((ULONG_PTR)ImageBase + (ULONG_PTR)RelocationDir->VirtualAddress);
TypeOffset = (PUSHORT)(RelocationDir + 1);
/* Unprotect the page(s) we're about to relocate. */
ProtectPage = Page;
Status = NtProtectVirtualMemory(NtCurrentProcess(),
&ProtectPage,
&ProtectSize,
PAGE_READWRITE,
&OldProtect);
if (!NT_SUCCESS(Status))
/* We need to act only if section isn't RW */
if (!(SectionHeader->Characteristics & IMAGE_SCN_MEM_WRITE))
{
DPRINT1("Failed to unprotect relocation target.\n");
return Status;
}
if (RelocationDir->VirtualAddress + PAGE_SIZE <
NTHeaders->OptionalHeader.SizeOfImage)
{
ProtectPage2 = (PVOID)((ULONG_PTR)ProtectPage + PAGE_SIZE);
Status = NtProtectVirtualMemory(NtCurrentProcess(),
&ProtectPage2,
&ProtectSize,
PAGE_READWRITE,
&OldProtect2);
if (!NT_SUCCESS(Status))
if (Protection)
{
DPRINT1("Failed to unprotect relocation target (2).\n");
NtProtectVirtualMemory(NtCurrentProcess(),
&ProtectPage,
&ProtectSize,
OldProtect,
&OldProtect);
return Status;
/* Check if the section was also executable, then we should
give it PAGE_EXECUTE, otherwise just PAGE_READONLY */
NewAccess = (SectionHeader->Characteristics & IMAGE_SCN_MEM_EXECUTE) ? PAGE_EXECUTE : PAGE_READONLY;
/* Also check caching */
NewAccess |= (SectionHeader->Characteristics & IMAGE_SCN_MEM_NOT_CACHED) ? PAGE_NOCACHE : 0;
}
else
{
/* Set access to RW */
NewAccess = PAGE_READWRITE;
}
}
else
{
ProtectPage2 = NULL;
}
RelocationDir = LdrProcessRelocationBlock((ULONG_PTR)Page,
Count,
TypeOffset,
Delta);
if (RelocationDir == NULL)
return STATUS_UNSUCCESSFUL;
/* Get the address and size of the section, and set protectin accordingly */
Address = (PVOID)((ULONG_PTR)BaseAddress + SectionHeader->VirtualAddress);
Size = SectionHeader->SizeOfRawData;
/* Restore old page protection. */
NtProtectVirtualMemory(NtCurrentProcess(),
&ProtectPage,
&ProtectSize,
OldProtect,
&OldProtect);
Status = NtProtectVirtualMemory(NtCurrentProcess(),
&Address,
&Size,
NewAccess,
&OldAccess);
if (ProtectPage2 != NULL)
{
NtProtectVirtualMemory(NtCurrentProcess(),
&ProtectPage2,
&ProtectSize,
OldProtect2,
&OldProtect2);
if (!NT_SUCCESS(Status))
return Status;
}
}
return STATUS_SUCCESS;
/* Instruction cache should be flushed if we enabled protection */
if (Protection)
NtFlushInstructionCache(NtCurrentProcess(), NULL, 0);
return STATUS_SUCCESS;
}
static NTSTATUS
@ -1898,12 +1861,32 @@ PEPFUNC LdrPEStartup (PVOID ImageBase,
if (ImageBase != (PVOID) NTHeaders->OptionalHeader.ImageBase)
{
DPRINT("LDR: Performing relocations\n");
Status = LdrPerformRelocations(NTHeaders, ImageBase);
/* Remove protection */
Status = LdrpSetProtection(ImageBase, FALSE);
if (!NT_SUCCESS(Status))
{
DPRINT1("LdrPerformRelocations() failed\n");
DPRINT1("LdrpSetProtection() failed with Status=0x%08X\n", Status);
return NULL;
}
/* Relocate */
Status = LdrRelocateImageWithBias(ImageBase, 0, "", STATUS_SUCCESS,
STATUS_CONFLICTING_ADDRESSES, STATUS_INVALID_IMAGE_FORMAT);
if (!NT_SUCCESS(Status))
{
DPRINT1("LdrRelocateImageWithBias() failed with Status=0x%08X\n", Status);
return NULL;
}
/* Set protection back */
Status = LdrpSetProtection(ImageBase, TRUE);
if (!NT_SUCCESS(Status))
{
DPRINT1("LdrpSetProtection() failed with Status=0x%08X\n", Status);
return NULL;
}
}
if (Module != NULL)
@ -2078,10 +2061,13 @@ LdrpLoadModule(IN PWSTR SearchPath OPTIONAL,
{
DPRINT1("Relocating (%lx -> %p) %wZ\n",
NtHeaders->OptionalHeader.ImageBase, ImageBase, &FullDosName);
Status = LdrPerformRelocations(NtHeaders, ImageBase);
Status = LdrRelocateImageWithBias(ImageBase, 0, "", STATUS_SUCCESS,
STATUS_CONFLICTING_ADDRESSES, STATUS_INVALID_IMAGE_FORMAT);
if (!NT_SUCCESS(Status))
{
DPRINT1("LdrPerformRelocations() failed\n");
DPRINT1("LdrRelocateImageWithBias() failed\n");
NtUnmapViewOfSection (NtCurrentProcess (), ImageBase);
NtClose (SectionHandle);
RtlFreeUnicodeString(&FullDosName);

View file

@ -3,8 +3,10 @@
* FILE: lib/rtl/image.c
* PURPOSE: Image handling functions
* Relocate functions were previously located in
* ntoskrnl/ldr/loader.c
* PROGRAMMER: Eric Kohl + original authors from loader.c file
* ntoskrnl/ldr/loader.c and
* dll/ntdll/ldr/utils.c files
* PROGRAMMER: Eric Kohl + original authors from loader.c and utils.c file
* Aleksey Bragin
*/
/* INCLUDES *****************************************************************/
@ -149,6 +151,70 @@ RtlImageRvaToVa (
(ULONG_PTR)Section->VirtualAddress);
}
PIMAGE_BASE_RELOCATION
LdrProcessRelocationBlockLongLong(
IN ULONG_PTR Address,
IN ULONG Count,
IN PUSHORT TypeOffset,
IN LONGLONG Delta
)
{
SHORT Offset;
USHORT Type;
USHORT i;
PUSHORT ShortPtr;
PULONG LongPtr;
for (i = 0; i < Count; i++)
{
Offset = *TypeOffset & 0xFFF;
Type = *TypeOffset >> 12;
ShortPtr = (PUSHORT)(RVA(Address, Offset));
/*
* Don't relocate within the relocation section itself.
* GCC/LD generates sometimes relocation records for the relocation section.
* This is a bug in GCC/LD.
* Fix for it disabled, since it was only in ntoskrnl and not in ntdll
*/
/*
if ((ULONG_PTR)ShortPtr < (ULONG_PTR)RelocationDir ||
(ULONG_PTR)ShortPtr >= (ULONG_PTR)RelocationEnd)
{*/
switch (Type)
{
/* case IMAGE_REL_BASED_SECTION : */
/* case IMAGE_REL_BASED_REL32 : */
case IMAGE_REL_BASED_ABSOLUTE:
break;
case IMAGE_REL_BASED_HIGH:
*ShortPtr += HIWORD(Delta);
break;
case IMAGE_REL_BASED_LOW:
*ShortPtr += LOWORD(Delta);
break;
case IMAGE_REL_BASED_HIGHLOW:
LongPtr = (PULONG)RVA(Address, Offset);
*LongPtr += Delta;
break;
case IMAGE_REL_BASED_HIGHADJ:
case IMAGE_REL_BASED_MIPS_JMPADDR:
default:
DPRINT1("Unknown/unsupported fixup type %hu.\n", Type);
DPRINT1("Address %x, Current %d, Count %d, *TypeOffset %x\n", Address, i, Count, *TypeOffset);
return (PIMAGE_BASE_RELOCATION)NULL;
}
TypeOffset++;
}
return (PIMAGE_BASE_RELOCATION)TypeOffset;
}
ULONG
NTAPI
LdrRelocateImageWithBias(
@ -163,14 +229,10 @@ LdrRelocateImageWithBias(
PIMAGE_NT_HEADERS NtHeaders;
PIMAGE_DATA_DIRECTORY RelocationDDir;
PIMAGE_BASE_RELOCATION RelocationDir, RelocationEnd;
ULONG Count, i;
PVOID Address;//, MaxAddress;
ULONG Count;
ULONG_PTR Address;
PUSHORT TypeOffset;
ULONG_PTR Delta;
SHORT Offset;
USHORT Type;
PUSHORT ShortPtr;
PULONG LongPtr;
LONGLONG Delta;
NtHeaders = RtlImageNtHeader(BaseAddress);
@ -192,64 +254,24 @@ LdrRelocateImageWithBias(
Delta = (ULONG_PTR)BaseAddress - NtHeaders->OptionalHeader.ImageBase + AdditionalBias;
RelocationDir = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)BaseAddress + RelocationDDir->VirtualAddress);
RelocationEnd = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)RelocationDir + RelocationDDir->Size);
//MaxAddress = RVA(BaseAddress, DriverSize);
while (RelocationDir < RelocationEnd &&
RelocationDir->SizeOfBlock > 0)
{
Count = (RelocationDir->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT);
Address = RVA(BaseAddress, RelocationDir->VirtualAddress);
Address = (ULONG_PTR)RVA(BaseAddress, RelocationDir->VirtualAddress);
TypeOffset = (PUSHORT)(RelocationDir + 1);
for (i = 0; i < Count; i++)
RelocationDir = LdrProcessRelocationBlockLongLong(Address,
Count,
TypeOffset,
Delta);
if (RelocationDir == NULL)
{
Offset = *TypeOffset & 0xFFF;
Type = *TypeOffset >> 12;
ShortPtr = (PUSHORT)(RVA(Address, Offset));
/* Don't relocate after the end of the loaded driver */
/*if ((PVOID)ShortPtr >= MaxAddress)
{
break;
}*/
/*
* Don't relocate within the relocation section itself.
* GCC/LD generates sometimes relocation records for the relocation section.
* This is a bug in GCC/LD.
*/
if ((ULONG_PTR)ShortPtr < (ULONG_PTR)RelocationDir ||
(ULONG_PTR)ShortPtr >= (ULONG_PTR)RelocationEnd)
{
switch (Type)
{
case IMAGE_REL_BASED_ABSOLUTE:
break;
case IMAGE_REL_BASED_HIGH:
*ShortPtr += HIWORD(Delta);
break;
case IMAGE_REL_BASED_LOW:
*ShortPtr += LOWORD(Delta);
break;
case IMAGE_REL_BASED_HIGHLOW:
LongPtr = (PULONG)ShortPtr;
*LongPtr += Delta;
break;
case IMAGE_REL_BASED_HIGHADJ:
case IMAGE_REL_BASED_MIPS_JMPADDR:
default:
DPRINT1("Unknown/unsupported fixup type %hu.\n", Type);
DPRINT1("Address %x, Current %d, Count %d, *TypeOffset %x\n", Address, i, Count, *TypeOffset);
return Invalid;
}
}
TypeOffset++;
DPRINT1("Error during call to LdrProcessRelocationBlockLongLong()!\n");
return Invalid;
}
RelocationDir = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)RelocationDir + RelocationDir->SizeOfBlock);
}
return Success;