|
|
/*++
Copyright (c) Microsoft Corporation
Module Name:
rtlimagentheader.c
Abstract:
The module contains RtlImageNtHeader and RtlImageNtHeaderEx.
Author:
Jay Krell (JayKrell) February 2002
Environment:
user mode kernel mode boot loader statically linked to imagehlp.dll (actually RtlpImageNtHeader) statically linked to unicows.lib (actually RtlpImageNtHeader)
Revision History:
--*/
#include "ntrtlp.h"
#if DBG
int RtlImageNtHeaderEx_ExceptionFilter( BOOLEAN RangeCheck, ULONG ExceptionCode ) { ASSERT(!RangeCheck || ExceptionCode == STATUS_IN_PAGE_ERROR); return EXCEPTION_EXECUTE_HANDLER; } #else
#define RtlImageNtHeaderEx_ExceptionFilter(RangeCheck, ExceptionCode) EXCEPTION_EXECUTE_HANDLER
#endif
NTSTATUS NTAPI RtlImageNtHeaderEx( ULONG Flags, PVOID Base, ULONG64 Size, OUT PIMAGE_NT_HEADERS * OutHeaders )
/*++
Routine Description:
This function returns the address of the NT Header.
This function is a bit complicated. It is this way because RtlImageNtHeader that it replaces was hard to understand, and this function retains compatibility with RtlImageNtHeader.
RtlImageNtHeader was #ifed such as to act different in each of the three boot loader, kernel, usermode flavors.
boot loader -- no exception handling usermode -- limit msdos header to 256meg, catch any exception accessing the msdos-header or the pe header kernel -- don't cross user/kernel boundary, don't catch the exceptions, no 256meg limit
Arguments:
Flags - RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK -- don't be so picky about the image, for compatibility with RtlImageNtHeader Base - Supplies the base of the image. Size - The size of the view, usually larger than the size of the file on disk. This is available from NtMapViewOfSection but not from MapViewOfFile. OutHeaders -
Return Value:
STATUS_SUCCESS -- everything ok STATUS_INVALID_IMAGE_FORMAT -- bad filesize or signature value STATUS_INVALID_PARAMETER -- bad parameters
--*/
{ PIMAGE_NT_HEADERS NtHeaders = 0; ULONG e_lfanew = 0; BOOLEAN RangeCheck = 0; NTSTATUS Status = 0; const ULONG ValidFlags = RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK;
if (OutHeaders != NULL) { *OutHeaders = NULL; } if (OutHeaders == NULL) { Status = STATUS_INVALID_PARAMETER; goto Exit; } if ((Flags & ~ValidFlags) != 0) { Status = STATUS_INVALID_PARAMETER; goto Exit; } if (Base == NULL || Base == (PVOID)(LONG_PTR)-1) { Status = STATUS_INVALID_PARAMETER; goto Exit; }
RangeCheck = ((Flags & RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK) == 0); if (RangeCheck) { if (Size < sizeof(IMAGE_DOS_HEADER)) { Status = STATUS_INVALID_IMAGE_FORMAT; goto Exit; } } #if !defined (BLDR_KERNEL_RUNTIME) && !defined(NTOS_KERNEL_RUNTIME)
//
// In usermode, catch any exceptions taken while accessing e_magic, e_lfanew or Signature;
// This should only be needed in the no_range_check case now.
//
__try { #define EXIT __leave
#else
//
// Exception handling is not available in the boot loader, and exceptions
// were not historically caught here in kernel mode. Drivers are considered
// trusted, so we can't get an exception here due to a bad file, but we
// could take an inpage error.
//
#define EXIT goto Exit
#endif
if (((PIMAGE_DOS_HEADER)Base)->e_magic != IMAGE_DOS_SIGNATURE) { Status = STATUS_INVALID_IMAGE_FORMAT; EXIT; } e_lfanew = ((PIMAGE_DOS_HEADER)Base)->e_lfanew; if (RangeCheck) { if (e_lfanew >= Size #define SIZEOF_PE_SIGNATURE 4
|| e_lfanew >= (MAXULONG - SIZEOF_PE_SIGNATURE - sizeof(IMAGE_FILE_HEADER)) || (e_lfanew + SIZEOF_PE_SIGNATURE + sizeof(IMAGE_FILE_HEADER)) >= Size ) { Status = STATUS_INVALID_IMAGE_FORMAT; EXIT; } } #if !defined (BLDR_KERNEL_RUNTIME) && !defined(NTOS_KERNEL_RUNTIME)
//
// In usermode, limit msdos header to 256meg.
//
if (e_lfanew >= RTLP_IMAGE_MAX_DOS_HEADER) { Status = STATUS_INVALID_IMAGE_FORMAT; EXIT; } #endif
NtHeaders = (PIMAGE_NT_HEADERS)((PCHAR)Base + e_lfanew); #if defined(NTOS_KERNEL_RUNTIME)
//
// In kernelmode, do not cross from usermode address to kernelmode address.
//
if (Base < MM_HIGHEST_USER_ADDRESS) { if ((PVOID)NtHeaders >= MM_HIGHEST_USER_ADDRESS) { Status = STATUS_INVALID_IMAGE_FORMAT; EXIT; } //
// Note that this check is slightly overeager since IMAGE_NT_HEADERS has
// a builtin array of data_directories that may be larger than the image
// actually has. A better check would be to add FileHeader.SizeOfOptionalHeader,
// after ensuring that the FileHeader does not cross the u/k boundary.
//
if ((PVOID)((PCHAR)NtHeaders + sizeof (IMAGE_NT_HEADERS)) >= MM_HIGHEST_USER_ADDRESS) { Status = STATUS_INVALID_IMAGE_FORMAT; EXIT; } } #endif
if (NtHeaders->Signature != IMAGE_NT_SIGNATURE) { Status = STATUS_INVALID_IMAGE_FORMAT; EXIT; } Status = STATUS_SUCCESS; #if !defined (BLDR_KERNEL_RUNTIME) && !defined(NTOS_KERNEL_RUNTIME)
} __except(RtlImageNtHeaderEx_ExceptionFilter(RangeCheck, GetExceptionCode())) { //
// In usermode, catch any exceptions taken while accessing e_magic, e_lfanew or Signature;
// This should only be needed in the no_range_check case now, the exception filter
// asserts to that affect.
//
// Propagating STATUS_IN_PAGE_ERROR if that's what the exception
// was may be better, but it may be incompatible.
//
// Letting STATUS_IN_PAGE_ERROR go as an exception may also be better.
//
Status = STATUS_INVALID_IMAGE_FORMAT; } #endif
Exit: if (NT_SUCCESS(Status)) { *OutHeaders = NtHeaders; } return Status; } #undef EXIT
PIMAGE_NT_HEADERS NTAPI RtlImageNtHeader( PVOID Base ) { PIMAGE_NT_HEADERS NtHeaders = NULL; (VOID)RtlImageNtHeaderEx(RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK, Base, 0, &NtHeaders); return NtHeaders; }
|