mirror of
https://github.com/reactos/reactos.git
synced 2024-08-16 16:39:08 +00:00
746 lines
24 KiB
C
746 lines
24 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS kernel
|
|
* FILE: ntoskrnl/mm/pe.c
|
|
* PURPOSE: Loader for PE executables
|
|
*
|
|
* PROGRAMMERS: KJK::Hyperion <hackbunny@reactos.com>
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
|
|
//#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
#include <reactos/exeformat.h>
|
|
|
|
static ULONG SectionCharacteristicsToProtect[16] =
|
|
{
|
|
PAGE_NOACCESS, /* 0 = NONE */
|
|
PAGE_NOACCESS, /* 1 = SHARED */
|
|
PAGE_EXECUTE, /* 2 = EXECUTABLE */
|
|
PAGE_EXECUTE, /* 3 = EXECUTABLE, SHARED */
|
|
PAGE_READONLY, /* 4 = READABLE */
|
|
PAGE_READONLY, /* 5 = READABLE, SHARED */
|
|
PAGE_EXECUTE_READ, /* 6 = READABLE, EXECUTABLE */
|
|
PAGE_EXECUTE_READ, /* 7 = READABLE, EXECUTABLE, SHARED */
|
|
/*
|
|
* FIXME? do we really need the WriteCopy field in segments? can't we use
|
|
* PAGE_WRITECOPY here?
|
|
*/
|
|
PAGE_READWRITE, /* 8 = WRITABLE */
|
|
PAGE_READWRITE, /* 9 = WRITABLE, SHARED */
|
|
PAGE_EXECUTE_READWRITE, /* 10 = WRITABLE, EXECUTABLE */
|
|
PAGE_EXECUTE_READWRITE, /* 11 = WRITABLE, EXECUTABLE, SHARED */
|
|
PAGE_READWRITE, /* 12 = WRITABLE, READABLE */
|
|
PAGE_READWRITE, /* 13 = WRITABLE, READABLE, SHARED */
|
|
PAGE_EXECUTE_READWRITE, /* 14 = WRITABLE, READABLE, EXECUTABLE */
|
|
PAGE_EXECUTE_READWRITE, /* 15 = WRITABLE, READABLE, EXECUTABLE, SHARED */
|
|
};
|
|
|
|
/* TODO: Intsafe should be made into a library, as it's generally useful */
|
|
static __inline BOOLEAN Intsafe_CanAddULongPtr(IN ULONG_PTR Addend1, IN ULONG_PTR Addend2)
|
|
{
|
|
return Addend1 <= (MAXULONG_PTR - Addend2);
|
|
}
|
|
|
|
static __inline BOOLEAN Intsafe_CanAddLong64(IN LONG64 Addend1, IN LONG64 Addend2)
|
|
{
|
|
return Addend1 <= (MAXLONGLONG - Addend2);
|
|
}
|
|
|
|
static __inline BOOLEAN Intsafe_CanAddULong32(IN ULONG Addend1, IN ULONG Addend2)
|
|
{
|
|
return Addend1 <= (MAXULONG - Addend2);
|
|
}
|
|
|
|
static __inline BOOLEAN Intsafe_AddULong32(OUT PULONG Result, IN ULONG Addend1, IN ULONG Addend2)
|
|
{
|
|
if(!Intsafe_CanAddULong32(Addend1, Addend2))
|
|
return FALSE;
|
|
|
|
*Result = Addend1 + Addend2;
|
|
return TRUE;
|
|
}
|
|
|
|
static __inline BOOLEAN Intsafe_CanMulULong32(IN ULONG Factor1, IN ULONG Factor2)
|
|
{
|
|
return Factor1 <= (MAXULONG / Factor2);
|
|
}
|
|
|
|
static __inline BOOLEAN Intsafe_CanOffsetPointer(IN CONST VOID * Pointer, IN SIZE_T Offset)
|
|
{
|
|
/* FIXME: (PVOID)MAXULONG_PTR isn't necessarily a valid address */
|
|
return Intsafe_CanAddULongPtr((ULONG_PTR)Pointer, Offset);
|
|
}
|
|
|
|
/* TODO: these are standard DDK/PSDK macros */
|
|
#ifndef RTL_FIELD_SIZE
|
|
#define RTL_FIELD_SIZE(TYPE_, FIELD_) (sizeof(((TYPE_ *)0)->FIELD_))
|
|
#endif
|
|
|
|
#ifndef RTL_SIZEOF_THROUGH_FIELD
|
|
#define RTL_SIZEOF_THROUGH_FIELD(TYPE_, FIELD_) \
|
|
(FIELD_OFFSET(TYPE_, FIELD_) + RTL_FIELD_SIZE(TYPE_, FIELD_))
|
|
#endif
|
|
|
|
#ifndef RTL_CONTAINS_FIELD
|
|
#define RTL_CONTAINS_FIELD(P_, SIZE_, FIELD_) \
|
|
((ULONG_PTR)(P_) + (ULONG_PTR)(SIZE_) > (ULONG_PTR)&((P_)->FIELD_) + sizeof((P_)->FIELD_))
|
|
#endif
|
|
|
|
static __inline BOOLEAN IsPowerOf2(IN ULONG Number)
|
|
{
|
|
if(Number == 0)
|
|
return FALSE;
|
|
return (Number & (Number - 1)) == 0;
|
|
}
|
|
|
|
static __inline ULONG ModPow2(IN ULONG Address, IN ULONG Alignment)
|
|
{
|
|
ASSERT(IsPowerOf2(Alignment));
|
|
return Address & (Alignment - 1);
|
|
}
|
|
|
|
static __inline BOOLEAN IsAligned(IN ULONG Address, IN ULONG Alignment)
|
|
{
|
|
return ModPow2(Address, Alignment) == 0;
|
|
}
|
|
|
|
static __inline BOOLEAN AlignUp(OUT PULONG AlignedAddress, IN ULONG Address, IN ULONG Alignment)
|
|
{
|
|
ULONG nExcess = ModPow2(Address, Alignment);
|
|
|
|
if(nExcess == 0)
|
|
{
|
|
*AlignedAddress = Address;
|
|
return nExcess == 0;
|
|
}
|
|
else
|
|
return Intsafe_AddULong32(AlignedAddress, Address, Alignment - nExcess);
|
|
}
|
|
|
|
#define PEFMT_FIELDS_EQUAL(TYPE1_, TYPE2_, FIELD_) \
|
|
( \
|
|
(FIELD_OFFSET(TYPE1_, FIELD_) == FIELD_OFFSET(TYPE2_, FIELD_)) && \
|
|
(RTL_FIELD_SIZE(TYPE1_, FIELD_) == RTL_FIELD_SIZE(TYPE2_, FIELD_)) \
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// FIXME: All this whitespace is "padding" so the C_ASSERTs aren't on the same lines as asserts in other headers.
|
|
// This is necessary because of the way we define C_ASSERT in a gcc compatible way.
|
|
// This can be removed once we upgrade to gcc 4.3.x or later (which implements __COUNTER__).
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// PeFmtCreateSection depends on the following:
|
|
//
|
|
C_ASSERT(EXEFMT_LOAD_HEADER_SIZE >= sizeof(IMAGE_DOS_HEADER));
|
|
C_ASSERT(sizeof(IMAGE_NT_HEADERS32) <= sizeof(IMAGE_NT_HEADERS64));
|
|
|
|
C_ASSERT(TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == TYPE_ALIGNMENT(IMAGE_NT_HEADERS64));
|
|
C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader) == RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS64, FileHeader));
|
|
C_ASSERT(FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader) == FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader));
|
|
|
|
C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, Magic));
|
|
C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SectionAlignment));
|
|
C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, FileAlignment));
|
|
C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, Subsystem));
|
|
C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, MinorSubsystemVersion));
|
|
C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, MajorSubsystemVersion));
|
|
C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, AddressOfEntryPoint));
|
|
C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SizeOfCode));
|
|
C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SizeOfHeaders));
|
|
|
|
/*
|
|
References:
|
|
[1] Microsoft Corporation, "Microsoft Portable Executable and Common Object
|
|
File Format Specification", revision 6.0 (February 1999)
|
|
*/
|
|
NTSTATUS NTAPI PeFmtCreateSection(IN CONST VOID * FileHeader,
|
|
IN SIZE_T FileHeaderSize,
|
|
IN PVOID File,
|
|
OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
|
|
OUT PULONG Flags,
|
|
IN PEXEFMT_CB_READ_FILE ReadFileCb,
|
|
IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb)
|
|
{
|
|
NTSTATUS nStatus;
|
|
ULONG cbFileHeaderOffsetSize = 0;
|
|
ULONG cbSectionHeadersOffset = 0;
|
|
ULONG cbSectionHeadersSize;
|
|
ULONG cbSectionHeadersOffsetSize = 0;
|
|
ULONG cbOptHeaderSize;
|
|
ULONG cbHeadersSize = 0;
|
|
ULONG nSectionAlignment;
|
|
ULONG nFileAlignment;
|
|
const IMAGE_DOS_HEADER * pidhDosHeader;
|
|
const IMAGE_NT_HEADERS32 * pinhNtHeader;
|
|
const IMAGE_OPTIONAL_HEADER32 * piohOptHeader;
|
|
const IMAGE_SECTION_HEADER * pishSectionHeaders;
|
|
PMM_SECTION_SEGMENT pssSegments;
|
|
LARGE_INTEGER lnOffset;
|
|
PVOID pBuffer;
|
|
ULONG nPrevVirtualEndOfSegment = 0;
|
|
ULONG nFileSizeOfHeaders = 0;
|
|
ULONG i;
|
|
|
|
ASSERT(FileHeader);
|
|
ASSERT(FileHeaderSize > 0);
|
|
ASSERT(File);
|
|
ASSERT(ImageSectionObject);
|
|
ASSERT(ReadFileCb);
|
|
ASSERT(AllocateSegmentsCb);
|
|
|
|
ASSERT(Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize));
|
|
|
|
ASSERT(((UINT_PTR)FileHeader % TYPE_ALIGNMENT(IMAGE_DOS_HEADER)) == 0);
|
|
|
|
#define DIE(ARGS_) { DPRINT ARGS_; goto l_Return; }
|
|
|
|
pBuffer = NULL;
|
|
pidhDosHeader = FileHeader;
|
|
|
|
/* DOS HEADER */
|
|
nStatus = STATUS_ROS_EXEFMT_UNKNOWN_FORMAT;
|
|
|
|
/* image too small to be an MZ executable */
|
|
if(FileHeaderSize < sizeof(IMAGE_DOS_HEADER))
|
|
DIE(("Too small to be an MZ executable, size is %lu\n", FileHeaderSize));
|
|
|
|
/* no MZ signature */
|
|
if(pidhDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
|
|
DIE(("No MZ signature found, e_magic is %hX\n", pidhDosHeader->e_magic));
|
|
|
|
/* not a Windows executable */
|
|
if(pidhDosHeader->e_lfanew <= 0)
|
|
DIE(("Not a Windows executable, e_lfanew is %d\n", pidhDosHeader->e_lfanew));
|
|
|
|
/* NT HEADER */
|
|
nStatus = STATUS_INVALID_IMAGE_FORMAT;
|
|
|
|
if(!Intsafe_AddULong32(&cbFileHeaderOffsetSize, pidhDosHeader->e_lfanew, RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader)))
|
|
DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
|
|
|
|
if(FileHeaderSize < cbFileHeaderOffsetSize)
|
|
pinhNtHeader = NULL;
|
|
else
|
|
{
|
|
/*
|
|
* we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
|
|
* and FileHeaderSize >= cbFileHeaderOffsetSize, so this holds true too
|
|
*/
|
|
ASSERT(Intsafe_CanOffsetPointer(FileHeader, pidhDosHeader->e_lfanew));
|
|
pinhNtHeader = (PVOID)((UINT_PTR)FileHeader + pidhDosHeader->e_lfanew);
|
|
}
|
|
|
|
/*
|
|
* the buffer doesn't contain the NT file header, or the alignment is wrong: we
|
|
* need to read the header from the file
|
|
*/
|
|
if(FileHeaderSize < cbFileHeaderOffsetSize ||
|
|
(UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
|
|
{
|
|
ULONG cbNtHeaderSize;
|
|
ULONG cbReadSize;
|
|
PVOID pData;
|
|
|
|
l_ReadHeaderFromFile:
|
|
cbNtHeaderSize = 0;
|
|
lnOffset.QuadPart = pidhDosHeader->e_lfanew;
|
|
|
|
/* read the header from the file */
|
|
nStatus = ReadFileCb(File, &lnOffset, sizeof(IMAGE_NT_HEADERS64), &pData, &pBuffer, &cbReadSize);
|
|
|
|
if(!NT_SUCCESS(nStatus))
|
|
DIE(("ReadFile failed, status %08X\n", nStatus));
|
|
|
|
ASSERT(pData);
|
|
ASSERT(pBuffer);
|
|
ASSERT(cbReadSize > 0);
|
|
|
|
nStatus = STATUS_INVALID_IMAGE_FORMAT;
|
|
|
|
/* the buffer doesn't contain the file header */
|
|
if(cbReadSize < RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader))
|
|
DIE(("The file doesn't contain the PE file header\n"));
|
|
|
|
pinhNtHeader = pData;
|
|
|
|
/* object still not aligned: copy it to the beginning of the buffer */
|
|
if((UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
|
|
{
|
|
ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == 0);
|
|
RtlMoveMemory(pBuffer, pData, cbReadSize);
|
|
pinhNtHeader = pBuffer;
|
|
}
|
|
|
|
/* invalid NT header */
|
|
nStatus = STATUS_INVALID_IMAGE_PROTECT;
|
|
|
|
if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
|
|
DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
|
|
|
|
nStatus = STATUS_INVALID_IMAGE_FORMAT;
|
|
|
|
if(!Intsafe_AddULong32(&cbNtHeaderSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
|
|
DIE(("The full NT header is too large\n"));
|
|
|
|
/* the buffer doesn't contain the whole NT header */
|
|
if(cbReadSize < cbNtHeaderSize)
|
|
DIE(("The file doesn't contain the full NT header\n"));
|
|
}
|
|
else
|
|
{
|
|
ULONG cbOptHeaderOffsetSize = 0;
|
|
|
|
nStatus = STATUS_INVALID_IMAGE_FORMAT;
|
|
|
|
/* don't trust an invalid NT header */
|
|
if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
|
|
DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
|
|
|
|
if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
|
|
DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
|
|
|
|
if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, cbOptHeaderOffsetSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
|
|
DIE(("The NT header is too large, SizeOfOptionalHeader is %X\n", pinhNtHeader->FileHeader.SizeOfOptionalHeader));
|
|
|
|
/* the buffer doesn't contain the whole NT header: read it from the file */
|
|
if(cbOptHeaderOffsetSize > FileHeaderSize)
|
|
goto l_ReadHeaderFromFile;
|
|
}
|
|
|
|
/* read information from the NT header */
|
|
piohOptHeader = &pinhNtHeader->OptionalHeader;
|
|
cbOptHeaderSize = pinhNtHeader->FileHeader.SizeOfOptionalHeader;
|
|
|
|
nStatus = STATUS_INVALID_IMAGE_FORMAT;
|
|
|
|
if(!RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Magic))
|
|
DIE(("The optional header doesn't contain the Magic field, SizeOfOptionalHeader is %X\n", cbOptHeaderSize));
|
|
|
|
/* ASSUME: RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject)); */
|
|
|
|
switch(piohOptHeader->Magic)
|
|
{
|
|
case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
|
|
case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
|
|
break;
|
|
|
|
default:
|
|
DIE(("Unrecognized optional header, Magic is %X\n", piohOptHeader->Magic));
|
|
}
|
|
|
|
if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SectionAlignment) &&
|
|
RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, FileAlignment))
|
|
{
|
|
/* See [1], section 3.4.2 */
|
|
if(piohOptHeader->SectionAlignment < PAGE_SIZE)
|
|
{
|
|
if(piohOptHeader->FileAlignment != piohOptHeader->SectionAlignment)
|
|
DIE(("Sections aren't page-aligned and the file alignment isn't the same\n"));
|
|
}
|
|
else if(piohOptHeader->SectionAlignment < piohOptHeader->FileAlignment)
|
|
DIE(("The section alignment is smaller than the file alignment\n"));
|
|
|
|
nSectionAlignment = piohOptHeader->SectionAlignment;
|
|
nFileAlignment = piohOptHeader->FileAlignment;
|
|
|
|
if(!IsPowerOf2(nSectionAlignment) || !IsPowerOf2(nFileAlignment))
|
|
DIE(("The section alignment (%u) and file alignment (%u) aren't both powers of 2\n", nSectionAlignment, nFileAlignment));
|
|
}
|
|
else
|
|
{
|
|
nSectionAlignment = PAGE_SIZE;
|
|
nFileAlignment = PAGE_SIZE;
|
|
}
|
|
|
|
ASSERT(IsPowerOf2(nSectionAlignment));
|
|
ASSERT(IsPowerOf2(nFileAlignment));
|
|
|
|
switch(piohOptHeader->Magic)
|
|
{
|
|
/* PE32 */
|
|
case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
|
|
{
|
|
if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, ImageBase))
|
|
ImageSectionObject->ImageBase = piohOptHeader->ImageBase;
|
|
|
|
if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfImage))
|
|
ImageSectionObject->ImageSize = piohOptHeader->SizeOfImage;
|
|
|
|
if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackReserve))
|
|
ImageSectionObject->StackReserve = piohOptHeader->SizeOfStackReserve;
|
|
|
|
if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackCommit))
|
|
ImageSectionObject->StackCommit = piohOptHeader->SizeOfStackCommit;
|
|
|
|
break;
|
|
}
|
|
|
|
/* PE32+ */
|
|
case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
|
|
{
|
|
const IMAGE_OPTIONAL_HEADER64 * pioh64OptHeader;
|
|
|
|
pioh64OptHeader = (const IMAGE_OPTIONAL_HEADER64 *)piohOptHeader;
|
|
|
|
if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, ImageBase))
|
|
{
|
|
if(pioh64OptHeader->ImageBase > MAXULONG_PTR)
|
|
DIE(("ImageBase exceeds the address space\n"));
|
|
|
|
ImageSectionObject->ImageBase = (ULONG_PTR)pioh64OptHeader->ImageBase;
|
|
}
|
|
|
|
if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfImage))
|
|
{
|
|
if(pioh64OptHeader->SizeOfImage > MAXULONG_PTR)
|
|
DIE(("SizeOfImage exceeds the address space\n"));
|
|
|
|
ImageSectionObject->ImageSize = pioh64OptHeader->SizeOfImage;
|
|
}
|
|
|
|
if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackReserve))
|
|
{
|
|
if(pioh64OptHeader->SizeOfStackReserve > MAXULONG_PTR)
|
|
DIE(("SizeOfStackReserve exceeds the address space\n"));
|
|
|
|
ImageSectionObject->StackReserve = pioh64OptHeader->SizeOfStackReserve;
|
|
}
|
|
|
|
if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackCommit))
|
|
{
|
|
if(pioh64OptHeader->SizeOfStackCommit > MAXULONG_PTR)
|
|
DIE(("SizeOfStackCommit exceeds the address space\n"));
|
|
|
|
ImageSectionObject->StackCommit = pioh64OptHeader->SizeOfStackCommit;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* [1], section 3.4.2 */
|
|
if((ULONG_PTR)ImageSectionObject->ImageBase % 0x10000)
|
|
DIE(("ImageBase is not aligned on a 64KB boundary"));
|
|
|
|
if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Subsystem))
|
|
{
|
|
ImageSectionObject->Subsystem = piohOptHeader->Subsystem;
|
|
|
|
if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
|
|
RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MajorSubsystemVersion))
|
|
{
|
|
ImageSectionObject->MinorSubsystemVersion = piohOptHeader->MinorSubsystemVersion;
|
|
ImageSectionObject->MajorSubsystemVersion = piohOptHeader->MajorSubsystemVersion;
|
|
}
|
|
}
|
|
|
|
if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
|
|
{
|
|
ImageSectionObject->EntryPoint = piohOptHeader->ImageBase +
|
|
piohOptHeader->AddressOfEntryPoint;
|
|
}
|
|
|
|
if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfCode))
|
|
ImageSectionObject->Executable = piohOptHeader->SizeOfCode != 0;
|
|
else
|
|
ImageSectionObject->Executable = TRUE;
|
|
|
|
ImageSectionObject->ImageCharacteristics = pinhNtHeader->FileHeader.Characteristics;
|
|
ImageSectionObject->Machine = pinhNtHeader->FileHeader.Machine;
|
|
|
|
/* SECTION HEADERS */
|
|
nStatus = STATUS_INVALID_IMAGE_FORMAT;
|
|
|
|
/* see [1], section 3.3 */
|
|
if(pinhNtHeader->FileHeader.NumberOfSections > 96)
|
|
DIE(("Too many sections, NumberOfSections is %u\n", pinhNtHeader->FileHeader.NumberOfSections));
|
|
|
|
/*
|
|
* the additional segment is for the file's headers. They need to be present for
|
|
* the benefit of the dynamic loader (to locate exports, defaults for thread
|
|
* parameters, resources, etc.)
|
|
*/
|
|
ImageSectionObject->NrSegments = pinhNtHeader->FileHeader.NumberOfSections + 1;
|
|
|
|
/* file offset for the section headers */
|
|
if(!Intsafe_AddULong32(&cbSectionHeadersOffset, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
|
|
DIE(("Offset overflow\n"));
|
|
|
|
if(!Intsafe_AddULong32(&cbSectionHeadersOffset, cbSectionHeadersOffset, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
|
|
DIE(("Offset overflow\n"));
|
|
|
|
/* size of the section headers */
|
|
ASSERT(Intsafe_CanMulULong32(pinhNtHeader->FileHeader.NumberOfSections, sizeof(IMAGE_SECTION_HEADER)));
|
|
cbSectionHeadersSize = pinhNtHeader->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
|
|
|
|
if(!Intsafe_AddULong32(&cbSectionHeadersOffsetSize, cbSectionHeadersOffset, cbSectionHeadersSize))
|
|
DIE(("Section headers too large\n"));
|
|
|
|
/* size of the executable's headers */
|
|
if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfHeaders))
|
|
{
|
|
// if(!IsAligned(piohOptHeader->SizeOfHeaders, nFileAlignment))
|
|
// DIE(("SizeOfHeaders is not aligned\n"));
|
|
|
|
if(cbSectionHeadersSize > piohOptHeader->SizeOfHeaders)
|
|
DIE(("The section headers overflow SizeOfHeaders\n"));
|
|
|
|
cbHeadersSize = piohOptHeader->SizeOfHeaders;
|
|
}
|
|
else if(!AlignUp(&cbHeadersSize, cbSectionHeadersOffsetSize, nFileAlignment))
|
|
DIE(("Overflow aligning the size of headers\n"));
|
|
|
|
if(pBuffer)
|
|
{
|
|
ExFreePool(pBuffer);
|
|
pBuffer = NULL;
|
|
}
|
|
/* WARNING: pinhNtHeader IS NO LONGER USABLE */
|
|
/* WARNING: piohOptHeader IS NO LONGER USABLE */
|
|
/* WARNING: pioh64OptHeader IS NO LONGER USABLE */
|
|
|
|
if(FileHeaderSize < cbSectionHeadersOffsetSize)
|
|
pishSectionHeaders = NULL;
|
|
else
|
|
{
|
|
/*
|
|
* we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
|
|
* and FileHeaderSize >= cbSectionHeadersOffsetSize, so this holds true too
|
|
*/
|
|
ASSERT(Intsafe_CanOffsetPointer(FileHeader, cbSectionHeadersOffset));
|
|
pishSectionHeaders = (PVOID)((UINT_PTR)FileHeader + cbSectionHeadersOffset);
|
|
}
|
|
|
|
/*
|
|
* the buffer doesn't contain the section headers, or the alignment is wrong:
|
|
* read the headers from the file
|
|
*/
|
|
if(FileHeaderSize < cbSectionHeadersOffsetSize ||
|
|
(UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
|
|
{
|
|
PVOID pData;
|
|
ULONG cbReadSize;
|
|
|
|
lnOffset.QuadPart = cbSectionHeadersOffset;
|
|
|
|
/* read the header from the file */
|
|
nStatus = ReadFileCb(File, &lnOffset, cbSectionHeadersSize, &pData, &pBuffer, &cbReadSize);
|
|
|
|
if(!NT_SUCCESS(nStatus))
|
|
DIE(("ReadFile failed with status %08X\n", nStatus));
|
|
|
|
ASSERT(pData);
|
|
ASSERT(pBuffer);
|
|
ASSERT(cbReadSize > 0);
|
|
|
|
nStatus = STATUS_INVALID_IMAGE_FORMAT;
|
|
|
|
/* the buffer doesn't contain all the section headers */
|
|
if(cbReadSize < cbSectionHeadersSize)
|
|
DIE(("The file doesn't contain all of the section headers\n"));
|
|
|
|
pishSectionHeaders = pData;
|
|
|
|
/* object still not aligned: copy it to the beginning of the buffer */
|
|
if((UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
|
|
{
|
|
ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) == 0);
|
|
RtlMoveMemory(pBuffer, pData, cbReadSize);
|
|
pishSectionHeaders = pBuffer;
|
|
}
|
|
}
|
|
|
|
/* SEGMENTS */
|
|
/* allocate the segments */
|
|
nStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
ImageSectionObject->Segments = AllocateSegmentsCb(ImageSectionObject->NrSegments);
|
|
|
|
if(ImageSectionObject->Segments == NULL)
|
|
DIE(("AllocateSegments failed\n"));
|
|
|
|
/* initialize the headers segment */
|
|
pssSegments = ImageSectionObject->Segments;
|
|
|
|
// ASSERT(IsAligned(cbHeadersSize, nFileAlignment));
|
|
|
|
if(!AlignUp(&nFileSizeOfHeaders, cbHeadersSize, nFileAlignment))
|
|
DIE(("Cannot align the size of the section headers\n"));
|
|
|
|
if(!AlignUp(&nPrevVirtualEndOfSegment, cbHeadersSize, nSectionAlignment))
|
|
DIE(("Cannot align the size of the section headers\n"));
|
|
|
|
pssSegments[0].FileOffset = 0;
|
|
pssSegments[0].Protection = PAGE_READONLY;
|
|
pssSegments[0].Length = nPrevVirtualEndOfSegment;
|
|
pssSegments[0].RawLength = nFileSizeOfHeaders;
|
|
pssSegments[0].VirtualAddress = 0;
|
|
pssSegments[0].Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA;
|
|
pssSegments[0].WriteCopy = TRUE;
|
|
|
|
/* skip the headers segment */
|
|
++ pssSegments;
|
|
|
|
nStatus = STATUS_INVALID_IMAGE_FORMAT;
|
|
|
|
/* convert the executable sections into segments. See also [1], section 4 */
|
|
for(i = 0; i < ImageSectionObject->NrSegments - 1; ++ i)
|
|
{
|
|
ULONG nCharacteristics;
|
|
|
|
/* validate the alignment */
|
|
if(!IsAligned(pishSectionHeaders[i].VirtualAddress, nSectionAlignment))
|
|
DIE(("VirtualAddress[%u] is not aligned\n", i));
|
|
|
|
/* sections must be contiguous, ordered by base address and non-overlapping */
|
|
if(pishSectionHeaders[i].VirtualAddress != nPrevVirtualEndOfSegment)
|
|
DIE(("Memory gap between section %u and the previous\n", i));
|
|
|
|
/* ignore explicit BSS sections */
|
|
if(pishSectionHeaders[i].SizeOfRawData != 0)
|
|
{
|
|
/* validate the alignment */
|
|
#if 0
|
|
/* Yes, this should be a multiple of FileAlignment, but there's
|
|
* stuff out there that isn't. We can cope with that
|
|
*/
|
|
if(!IsAligned(pishSectionHeaders[i].SizeOfRawData, nFileAlignment))
|
|
DIE(("SizeOfRawData[%u] is not aligned\n", i));
|
|
#endif
|
|
|
|
// if(!IsAligned(pishSectionHeaders[i].PointerToRawData, nFileAlignment))
|
|
// DIE(("PointerToRawData[%u] is not aligned\n", i));
|
|
|
|
/* conversion */
|
|
pssSegments[i].FileOffset = pishSectionHeaders[i].PointerToRawData;
|
|
pssSegments[i].RawLength = pishSectionHeaders[i].SizeOfRawData;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(pssSegments[i].FileOffset == 0);
|
|
ASSERT(pssSegments[i].RawLength == 0);
|
|
}
|
|
|
|
ASSERT(Intsafe_CanAddLong64(pssSegments[i].FileOffset, pssSegments[i].RawLength));
|
|
|
|
nCharacteristics = pishSectionHeaders[i].Characteristics;
|
|
|
|
/* no explicit protection */
|
|
if((nCharacteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) == 0)
|
|
{
|
|
if(nCharacteristics & IMAGE_SCN_CNT_CODE)
|
|
nCharacteristics |= IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ;
|
|
|
|
if(nCharacteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
|
|
nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
|
|
|
|
if(nCharacteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
|
|
nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
|
|
}
|
|
|
|
/* see table above */
|
|
pssSegments[i].Protection = SectionCharacteristicsToProtect[nCharacteristics >> 28];
|
|
pssSegments[i].WriteCopy = !(nCharacteristics & IMAGE_SCN_MEM_SHARED);
|
|
|
|
if(pishSectionHeaders[i].Misc.VirtualSize == 0 || pishSectionHeaders[i].Misc.VirtualSize < pishSectionHeaders[i].SizeOfRawData)
|
|
pssSegments[i].Length = pishSectionHeaders[i].SizeOfRawData;
|
|
else
|
|
pssSegments[i].Length = pishSectionHeaders[i].Misc.VirtualSize;
|
|
|
|
if(!AlignUp(&pssSegments[i].Length, pssSegments[i].Length, nSectionAlignment))
|
|
DIE(("Cannot align the virtual size of section %u\n", i));
|
|
|
|
ASSERT(IsAligned(pssSegments[i].Length, nSectionAlignment));
|
|
|
|
if(pssSegments[i].Length == 0)
|
|
DIE(("Virtual size of section %u is null\n", i));
|
|
|
|
pssSegments[i].VirtualAddress = pishSectionHeaders[i].VirtualAddress;
|
|
pssSegments[i].Characteristics = pishSectionHeaders[i].Characteristics;
|
|
|
|
/* ensure the memory image is no larger than 4GB */
|
|
if(!Intsafe_AddULong32(&nPrevVirtualEndOfSegment, pssSegments[i].VirtualAddress, pssSegments[i].Length))
|
|
DIE(("The image is larger than 4GB\n"));
|
|
}
|
|
|
|
/* spare our caller some work in validating the segments */
|
|
*Flags = EXEFMT_LOAD_ASSUME_SEGMENTS_SORTED | EXEFMT_LOAD_ASSUME_SEGMENTS_NO_OVERLAP;
|
|
|
|
if(nSectionAlignment >= PAGE_SIZE)
|
|
*Flags |= EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED;
|
|
|
|
/* Success */
|
|
nStatus = STATUS_ROS_EXEFMT_LOADED_FORMAT | EXEFMT_LOADED_PE32;
|
|
|
|
l_Return:
|
|
if(pBuffer)
|
|
ExFreePool(pBuffer);
|
|
|
|
return nStatus;
|
|
}
|
|
|
|
/* EOF */
|