Improve RtlImageNtHeaderEx:
- Fix signed/unsigned mismatch when comparing NT header offset
- Simplify overflow checks
- Add missing overflow-into-systemspace check
CR-77 / CORE-8091 #resolve

svn path=/trunk/; revision=67140
This commit is contained in:
Timo Kreuzer
2015-04-10 19:21:22 +00:00
parent 2e5a937eee
commit e617f69eee
8 changed files with 69 additions and 42 deletions

View File

@@ -20,6 +20,8 @@
#include <freeldr.h>
PVOID MmHighestUserAddress = (PVOID)MI_HIGHEST_USER_ADDRESS;
#if DBG
VOID FASTCALL
CHECK_PAGED_CODE_RTL(char *file, int line)

View File

@@ -16,6 +16,7 @@
SIZE_T RtlpAllocDeallocQueryBufferSize = PAGE_SIZE;
PTEB LdrpTopLevelDllBeingLoadedTeb = NULL;
PVOID MmHighestUserAddress = (PVOID)MI_HIGHEST_USER_ADDRESS;
/* FUNCTIONS ***************************************************************/

View File

@@ -37,6 +37,11 @@ extern "C" {
#define MM_ALLOCATION_GRANULARITY_SHIFT 16L
#define MM_PAGE_FRAME_NUMBER_SIZE 52
//
// User space range limit
//
#define MI_HIGHEST_USER_ADDRESS (PVOID)0x000007FFFFFEFFFFULL
//
// Address of the shared user page
//

View File

@@ -36,6 +36,11 @@ extern "C" {
#define MM_ALLOCATION_GRANULARITY_SHIFT 16L
#define MM_PAGE_FRAME_NUMBER_SIZE 20
//
// User space range limit
//
#define MI_HIGHEST_USER_ADDRESS (PVOID)0x7FFEFFFF
//
// Address of the shared user page
//

View File

@@ -137,32 +137,42 @@ LdrVerifyMappedImageMatchesChecksum(
*/
NTSTATUS
NTAPI
RtlImageNtHeaderEx(IN ULONG Flags,
IN PVOID Base,
IN ULONG64 Size,
OUT PIMAGE_NT_HEADERS *OutHeaders)
RtlImageNtHeaderEx(
_In_ ULONG Flags,
_In_ PVOID Base,
_In_ ULONG64 Size,
_Out_ PIMAGE_NT_HEADERS *OutHeaders)
{
PIMAGE_NT_HEADERS NtHeaders;
PIMAGE_DOS_HEADER DosHeader;
BOOLEAN WantsRangeCheck;
ULONG NtHeaderOffset;
/* You must want NT Headers, no? */
if (!OutHeaders) return STATUS_INVALID_PARAMETER;
if (OutHeaders == NULL)
{
DPRINT1("OutHeaders is NULL\n");
return STATUS_INVALID_PARAMETER;
}
/* Assume failure */
*OutHeaders = NULL;
/* Validate Flags */
if (Flags &~ RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK)
if (Flags & ~RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK)
{
DPRINT1("Invalid flag combination... check for new API flags?\n");
DPRINT1("Invalid flags: 0x%lx\n", Flags);
return STATUS_INVALID_PARAMETER;
}
/* Validate base */
if (!(Base) || (Base == (PVOID)-1)) return STATUS_INVALID_PARAMETER;
if ((Base == NULL) || (Base == (PVOID)-1))
{
DPRINT1("Invalid base address: %p\n", Base);
return STATUS_INVALID_PARAMETER;
}
/* Check if the caller wants validation */
/* Check if the caller wants range checks */
WantsRangeCheck = !(Flags & RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK);
if (WantsRangeCheck)
{
@@ -179,56 +189,56 @@ RtlImageNtHeaderEx(IN ULONG Flags,
if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE)
{
/* Not a valid COFF */
DPRINT1("Not an MZ file\n");
DPRINT1("Invalid image DOS signature!\n");
return STATUS_INVALID_IMAGE_FORMAT;
}
/* Get the offset to the NT headers (and copy from LONG to ULONG) */
NtHeaderOffset = DosHeader->e_lfanew;
/* The offset must not be larger than 256MB, as a hard-coded check.
In Windows this check is only done in user mode, not in kernel mode,
but it shouldn't harm to have it anyway. Note that without this check,
other overflow checks would become necessary! */
if (NtHeaderOffset >= (256 * 1024 * 1024))
{
/* Fail */
DPRINT1("NT headers offset is larger than 256MB!\n");
return STATUS_INVALID_IMAGE_FORMAT;
}
/* Check if the caller wants validation */
if (WantsRangeCheck)
{
/* The offset should fit in the passsed-in size */
if (DosHeader->e_lfanew >= Size)
/* Make sure the file header fits into the size */
if ((NtHeaderOffset +
RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS, FileHeader)) >= Size)
{
/* Fail */
DPRINT1("e_lfanew is larger than PE file\n");
return STATUS_INVALID_IMAGE_FORMAT;
}
/* It shouldn't be past 4GB either */
if (DosHeader->e_lfanew >=
(MAXULONG - sizeof(IMAGE_DOS_SIGNATURE) - sizeof(IMAGE_FILE_HEADER)))
{
/* Fail */
DPRINT1("e_lfanew is larger than 4GB\n");
return STATUS_INVALID_IMAGE_FORMAT;
}
/* And the whole file shouldn't overflow past 4GB */
if ((DosHeader->e_lfanew +
sizeof(IMAGE_DOS_SIGNATURE) - sizeof(IMAGE_FILE_HEADER)) >= Size)
{
/* Fail */
DPRINT1("PE is larger than 4GB\n");
DPRINT1("NT headers beyond image size!\n");
return STATUS_INVALID_IMAGE_FORMAT;
}
}
/* The offset also can't be larger than 256MB, as a hard-coded check */
if (DosHeader->e_lfanew >= (256 * 1024 * 1024))
/* Now get a pointer to the NT Headers */
NtHeaders = (PIMAGE_NT_HEADERS)((ULONG_PTR)Base + NtHeaderOffset);
/* Check if the mapping is in user space */
if (Base <= MmHighestUserAddress)
{
/* Fail */
DPRINT1("PE offset is larger than 256MB\n");
return STATUS_INVALID_IMAGE_FORMAT;
/* Make sure we don't overflow into kernel space */
if ((PVOID)(NtHeaders + 1) > MmHighestUserAddress)
{
DPRINT1("Image overflows from user space into kernel space!\n");
return STATUS_INVALID_IMAGE_FORMAT;
}
}
/* Now it's safe to get the NT Headers */
NtHeaders = (PIMAGE_NT_HEADERS)((ULONG_PTR)Base + DosHeader->e_lfanew);
/* Verify the PE Signature */
if (NtHeaders->Signature != IMAGE_NT_SIGNATURE)
{
/* Fail */
DPRINT1("PE signature missing\n");
DPRINT1("Invalid image NT signature!\n");
return STATUS_INVALID_IMAGE_FORMAT;
}
@@ -246,6 +256,10 @@ RtlImageNtHeader(IN PVOID Base)
{
PIMAGE_NT_HEADERS NtHeader;
ULONG c = 1;
ULONG s = FIELD_OFFSET(IMAGE_OPTIONAL_HEADER32, DataDirectory[c]);
(void)s;
/* Call the new API */
RtlImageNtHeaderEx(RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK,
Base,

View File

@@ -34,6 +34,8 @@ extern VOID FASTCALL CHECK_PAGED_CODE_RTL(char *file, int line);
#define RVA(m, b) ((PVOID)((ULONG_PTR)(b) + (ULONG_PTR)(m)))
extern PVOID MmHighestUserAddress;
NTSTATUS
NTAPI
RtlpSafeCopyMemory(

View File

@@ -5,7 +5,6 @@
/* Memory layout base addresses */
#define MI_LOWEST_VAD_ADDRESS (PVOID)0x000000007FF00000ULL
#define MI_HIGHEST_USER_ADDRESS (PVOID)0x000007FFFFFEFFFFULL
#define MI_USER_PROBE_ADDRESS (PVOID)0x000007FFFFFF0000ULL
#define MI_DEFAULT_SYSTEM_RANGE_START (PVOID)0xFFFF080000000000ULL
#define MI_REAL_SYSTEM_RANGE_START 0xFFFF800000000000ULL

View File

@@ -29,7 +29,6 @@
#define MI_SYSTEM_VIEW_SIZE (32 * _1MB)
#define MI_HIGHEST_USER_ADDRESS (PVOID)0x7FFEFFFF
#define MI_USER_PROBE_ADDRESS (PVOID)0x7FFF0000
#define MI_DEFAULT_SYSTEM_RANGE_START (PVOID)0x80000000
#define MI_SYSTEM_CACHE_WS_START (PVOID)0xC0C00000