2534 lines
70 KiB
C
2534 lines
70 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
sysload.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the code to load DLLs into the system
|
||
portion of the address space and calls the DLL at it's
|
||
initialization entry point.
|
||
|
||
Author:
|
||
|
||
Lou Perazzoli 21-May-1991
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "mi.h"
|
||
|
||
extern ULONG MmPagedPoolCommit;
|
||
|
||
KMUTANT MmSystemLoadLock;
|
||
|
||
ULONG MmTotalSystemDriverPages;
|
||
|
||
ULONG MmDriverCommit;
|
||
|
||
//
|
||
// ****** temporary ******
|
||
//
|
||
// Define reference to external spin lock.
|
||
//
|
||
// ****** temporary ******
|
||
//
|
||
|
||
extern KSPIN_LOCK PsLoadedModuleSpinLock;
|
||
|
||
#if DBG
|
||
ULONG MiPagesConsumed;
|
||
#endif
|
||
|
||
ULONG
|
||
CacheImageSymbols(
|
||
IN PVOID ImageBase
|
||
);
|
||
|
||
NTSTATUS
|
||
MiResolveImageReferences(
|
||
PVOID ImageBase,
|
||
IN PUNICODE_STRING ImageFileDirectory,
|
||
OUT PCHAR *MissingProcedureName,
|
||
OUT PWSTR *MissingDriverName
|
||
);
|
||
|
||
NTSTATUS
|
||
MiSnapThunk(
|
||
IN PVOID DllBase,
|
||
IN PVOID ImageBase,
|
||
IN OUT PIMAGE_THUNK_DATA Thunk,
|
||
IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
|
||
IN ULONG ExportSize,
|
||
IN BOOLEAN SnapForwarder,
|
||
OUT PCHAR *MissingProcedureName
|
||
);
|
||
|
||
NTSTATUS
|
||
MiLoadImageSection (
|
||
IN PSECTION SectionPointer,
|
||
OUT PVOID *ImageBase
|
||
);
|
||
|
||
VOID
|
||
MiEnablePagingOfDriver (
|
||
IN PVOID ImageHandle
|
||
);
|
||
|
||
VOID
|
||
MiSetPagingOfDriver (
|
||
IN PMMPTE PointerPte,
|
||
IN PMMPTE LastPte
|
||
);
|
||
|
||
PVOID
|
||
MiLookupImageSectionByName (
|
||
IN PVOID Base,
|
||
IN BOOLEAN MappedAsImage,
|
||
IN PCHAR SectionName,
|
||
OUT PULONG SectionSize
|
||
);
|
||
|
||
NTSTATUS
|
||
MiUnloadSystemImageByForce (
|
||
IN ULONG NumberOfPtes,
|
||
IN PVOID ImageBase
|
||
);
|
||
|
||
|
||
NTSTATUS
|
||
MmCheckSystemImage(
|
||
IN HANDLE ImageFileHandle
|
||
);
|
||
|
||
LONG
|
||
MiMapCacheExceptionFilter (
|
||
OUT PNTSTATUS Status,
|
||
IN PEXCEPTION_POINTERS ExceptionPointer
|
||
);
|
||
|
||
VOID
|
||
MiSetImageProtectWrite (
|
||
IN PSEGMENT Segment
|
||
);
|
||
|
||
ULONG
|
||
MiSetProtectionOnTransitionPte (
|
||
IN PMMPTE PointerPte,
|
||
IN ULONG ProtectionMask
|
||
);
|
||
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE,MmCheckSystemImage)
|
||
#pragma alloc_text(PAGE,MmLoadSystemImage)
|
||
#pragma alloc_text(PAGE,MiResolveImageReferences)
|
||
#pragma alloc_text(PAGE,MiSnapThunk)
|
||
#pragma alloc_text(PAGE,MiUnloadSystemImageByForce)
|
||
#pragma alloc_text(PAGE,MiEnablePagingOfDriver)
|
||
#pragma alloc_text(PAGE,MmPageEntireDriver)
|
||
#pragma alloc_text(PAGE,MiSetImageProtectWrite)
|
||
|
||
#if !defined(NT_UP)
|
||
#pragma alloc_text(PAGE,MmVerifyImageIsOkForMpUse)
|
||
#endif // NT_UP
|
||
|
||
#pragma alloc_text(PAGELK,MiLoadImageSection)
|
||
#pragma alloc_text(PAGELK,MmFreeDriverInitialization)
|
||
#pragma alloc_text(PAGELK,MmUnloadSystemImage)
|
||
#pragma alloc_text(PAGELK,MiSetPagingOfDriver)
|
||
#pragma alloc_text(PAGELK,MmResetDriverPaging)
|
||
#endif
|
||
|
||
|
||
|
||
NTSTATUS
|
||
MmLoadSystemImage (
|
||
IN PUNICODE_STRING ImageFileName,
|
||
OUT PVOID *ImageHandle,
|
||
OUT PVOID *ImageBaseAddress
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine reads the image pages from the specified section into
|
||
the system and returns the address of the DLL's header.
|
||
|
||
At successful completion, the Section is referenced so it remains
|
||
until the system image is unloaded.
|
||
|
||
Arguments:
|
||
|
||
ImageName - Supplies the unicode name of the image to load.
|
||
|
||
ImageFileName - Supplies the full path name (including the image name)
|
||
of the image to load.
|
||
|
||
Section - Returns a pointer to the referenced section object of the
|
||
image that was loaded.
|
||
|
||
ImageBaseAddress - Returns the image base within the system.
|
||
|
||
Return Value:
|
||
|
||
Status of the load operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLDR_DATA_TABLE_ENTRY DataTableEntry;
|
||
NTSTATUS Status;
|
||
PSECTION SectionPointer;
|
||
PIMAGE_NT_HEADERS NtHeaders;
|
||
UNICODE_STRING BaseName;
|
||
UNICODE_STRING BaseDirectory;
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
HANDLE FileHandle = (HANDLE)0;
|
||
HANDLE SectionHandle;
|
||
IO_STATUS_BLOCK IoStatus;
|
||
CHAR NameBuffer[ MAXIMUM_FILENAME_LENGTH ];
|
||
PLIST_ENTRY NextEntry;
|
||
ULONG NumberOfPtes;
|
||
PCHAR MissingProcedureName;
|
||
PWSTR MissingDriverName;
|
||
|
||
PAGED_CODE();
|
||
|
||
KeWaitForSingleObject (&MmSystemLoadLock,
|
||
WrVirtualMemory,
|
||
KernelMode,
|
||
FALSE,
|
||
(PLARGE_INTEGER)NULL);
|
||
|
||
#if DBG
|
||
if ( NtGlobalFlag & FLG_SHOW_LDR_SNAPS ) {
|
||
DbgPrint( "MM:SYSLDR Loading %wZ\n", ImageFileName );
|
||
}
|
||
#endif
|
||
MissingProcedureName = NULL;
|
||
MissingDriverName = NULL;
|
||
|
||
//
|
||
// Attempt to open the driver image itself. If this fails, then the
|
||
// driver image cannot be located, so nothing else matters.
|
||
//
|
||
|
||
InitializeObjectAttributes( &ObjectAttributes,
|
||
ImageFileName,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
NULL );
|
||
|
||
Status = ZwOpenFile( &FileHandle,
|
||
FILE_EXECUTE,
|
||
&ObjectAttributes,
|
||
&IoStatus,
|
||
FILE_SHARE_READ | FILE_SHARE_DELETE,
|
||
0 );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
//
|
||
// Don't raise hard error status for file not found.
|
||
//
|
||
|
||
goto return2;
|
||
}
|
||
|
||
Status = MmCheckSystemImage(FileHandle);
|
||
if ( Status == STATUS_IMAGE_CHECKSUM_MISMATCH || Status == STATUS_IMAGE_MP_UP_MISMATCH) {
|
||
goto return1;
|
||
}
|
||
|
||
if (ImageFileName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR) {
|
||
PWCHAR p;
|
||
ULONG l;
|
||
|
||
p = &ImageFileName->Buffer[ImageFileName->Length>>1];
|
||
while (*(p-1) != OBJ_NAME_PATH_SEPARATOR) {
|
||
p--;
|
||
}
|
||
l = &ImageFileName->Buffer[ImageFileName->Length>>1] - p;
|
||
l *= sizeof(WCHAR);
|
||
BaseName.Length = (USHORT)l;
|
||
BaseName.Buffer = p;
|
||
} else {
|
||
BaseName.Length = ImageFileName->Length;
|
||
BaseName.Buffer = ImageFileName->Buffer;
|
||
}
|
||
|
||
BaseName.MaximumLength = BaseName.Length;
|
||
BaseDirectory = *ImageFileName;
|
||
BaseDirectory.Length -= BaseName.Length;
|
||
BaseDirectory.MaximumLength = BaseDirectory.Length;
|
||
|
||
//
|
||
// Check to see if this name already exists in the loader database.
|
||
//
|
||
|
||
NextEntry = PsLoadedModuleList.Flink;
|
||
while (NextEntry != &PsLoadedModuleList) {
|
||
DataTableEntry = CONTAINING_RECORD(NextEntry,
|
||
LDR_DATA_TABLE_ENTRY,
|
||
InLoadOrderLinks);
|
||
if (RtlEqualString((PSTRING)ImageFileName,
|
||
(PSTRING)&DataTableEntry->FullDllName,
|
||
TRUE)) {
|
||
|
||
*ImageHandle = DataTableEntry;
|
||
*ImageBaseAddress = DataTableEntry->DllBase;
|
||
DataTableEntry->LoadCount = +1;
|
||
Status = STATUS_IMAGE_ALREADY_LOADED;
|
||
goto return2;
|
||
}
|
||
|
||
NextEntry = NextEntry->Flink;
|
||
}
|
||
|
||
//
|
||
// Now attempt to create an image section for the file. If this fails,
|
||
// then the driver file is not an image.
|
||
//
|
||
|
||
Status = ZwCreateSection (&SectionHandle,
|
||
SECTION_ALL_ACCESS,
|
||
(POBJECT_ATTRIBUTES) NULL,
|
||
(PLARGE_INTEGER) NULL,
|
||
PAGE_EXECUTE,
|
||
SEC_IMAGE,
|
||
FileHandle );
|
||
if (!NT_SUCCESS( Status )) {
|
||
goto return1;
|
||
}
|
||
|
||
//
|
||
// Now reference the section handle.
|
||
//
|
||
|
||
Status = ObReferenceObjectByHandle (SectionHandle,
|
||
SECTION_MAP_EXECUTE,
|
||
MmSectionObjectType,
|
||
KernelMode,
|
||
(PVOID *) &SectionPointer,
|
||
(POBJECT_HANDLE_INFORMATION) NULL );
|
||
|
||
ZwClose (SectionHandle);
|
||
if (!NT_SUCCESS (Status)) {
|
||
goto return1;
|
||
}
|
||
|
||
if ((SectionPointer->Segment->BasedAddress == (PVOID)MmSystemSpaceViewStart) &&
|
||
(SectionPointer->Segment->ControlArea->NumberOfMappedViews == 0)) {
|
||
NumberOfPtes = 0;
|
||
Status = MmMapViewInSystemSpace (SectionPointer,
|
||
ImageBaseAddress,
|
||
&NumberOfPtes);
|
||
if ((NT_SUCCESS( Status ) &&
|
||
(*ImageBaseAddress == SectionPointer->Segment->BasedAddress))) {
|
||
SectionPointer->Segment->ControlArea->u.Flags.ImageMappedInSystemSpace = 1;
|
||
NumberOfPtes = (NumberOfPtes + 1) >> PAGE_SHIFT;
|
||
MiSetImageProtectWrite (SectionPointer->Segment);
|
||
goto BindImage;
|
||
}
|
||
}
|
||
MmLockPagableSectionByHandle (ExPageLockHandle);
|
||
Status = MiLoadImageSection (SectionPointer, ImageBaseAddress);
|
||
|
||
MmUnlockPagableImageSection(ExPageLockHandle);
|
||
NumberOfPtes = SectionPointer->Segment->TotalNumberOfPtes;
|
||
ObDereferenceObject (SectionPointer);
|
||
SectionPointer = (PVOID)0xFFFFFFFF;
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
goto return1;
|
||
}
|
||
|
||
//
|
||
// Apply the fixups to the section and resolve its image references.
|
||
//
|
||
|
||
try {
|
||
Status = (NTSTATUS)LdrRelocateImage(*ImageBaseAddress,
|
||
"SYSLDR",
|
||
(ULONG)STATUS_SUCCESS,
|
||
(ULONG)STATUS_CONFLICTING_ADDRESSES,
|
||
(ULONG)STATUS_INVALID_IMAGE_FORMAT
|
||
);
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
Status = GetExceptionCode();
|
||
KdPrint(("MM:sysload - LdrRelocateImage failed status %lx\n",
|
||
Status));
|
||
}
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
|
||
//
|
||
// Unload the system image and dereference the section.
|
||
//
|
||
|
||
MiUnloadSystemImageByForce (NumberOfPtes, *ImageBaseAddress);
|
||
goto return1;
|
||
}
|
||
|
||
BindImage:
|
||
|
||
try {
|
||
MissingProcedureName = NameBuffer;
|
||
Status = MiResolveImageReferences(*ImageBaseAddress,
|
||
&BaseDirectory,
|
||
&MissingProcedureName,
|
||
&MissingDriverName
|
||
);
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
Status = GetExceptionCode();
|
||
KdPrint(("MM:sysload - ResolveImageReferences failed status %lx\n",
|
||
Status));
|
||
}
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
MiUnloadSystemImageByForce (NumberOfPtes, *ImageBaseAddress);
|
||
goto return1;
|
||
}
|
||
|
||
#if DBG
|
||
if (NtGlobalFlag & FLG_SHOW_LDR_SNAPS) {
|
||
KdPrint (("MM:loaded driver - consumed %ld. pages\n",MiPagesConsumed));
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Allocate a data table entry for structured exception handling.
|
||
//
|
||
|
||
DataTableEntry = ExAllocatePoolWithTag (NonPagedPool,
|
||
sizeof(LDR_DATA_TABLE_ENTRY) +
|
||
BaseName.Length + sizeof(UNICODE_NULL),
|
||
'dLmM');
|
||
if (DataTableEntry == NULL) {
|
||
MiUnloadSystemImageByForce (NumberOfPtes, *ImageBaseAddress);
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto return1;
|
||
}
|
||
|
||
//
|
||
// Initialize the address of the DLL image file header and the entry
|
||
// point address.
|
||
//
|
||
|
||
NtHeaders = RtlImageNtHeader(*ImageBaseAddress);
|
||
|
||
DataTableEntry->DllBase = *ImageBaseAddress;
|
||
DataTableEntry->EntryPoint =
|
||
(PVOID)((ULONG)*ImageBaseAddress + NtHeaders->OptionalHeader.AddressOfEntryPoint);
|
||
DataTableEntry->SizeOfImage = NumberOfPtes << PAGE_SHIFT;
|
||
DataTableEntry->CheckSum = NtHeaders->OptionalHeader.CheckSum;
|
||
DataTableEntry->SectionPointer = (PVOID)SectionPointer;
|
||
|
||
//
|
||
// Store the DLL name.
|
||
//
|
||
|
||
DataTableEntry->BaseDllName.Buffer = (PWSTR)(DataTableEntry + 1);
|
||
DataTableEntry->BaseDllName.Length = BaseName.Length;
|
||
DataTableEntry->BaseDllName.MaximumLength = BaseName.Length;
|
||
RtlMoveMemory (DataTableEntry->BaseDllName.Buffer,
|
||
BaseName.Buffer,
|
||
BaseName.Length );
|
||
DataTableEntry->BaseDllName.Buffer[BaseName.Length/sizeof(WCHAR)] = UNICODE_NULL;
|
||
|
||
DataTableEntry->FullDllName.Buffer = ExAllocatePoolWithTag (PagedPool,
|
||
ImageFileName->Length + sizeof(UNICODE_NULL),
|
||
'TDmM');
|
||
if (DataTableEntry->FullDllName.Buffer == NULL) {
|
||
|
||
//
|
||
// Pool could not be allocated, just set the length to 0.
|
||
//
|
||
|
||
DataTableEntry->FullDllName.Length = 0;
|
||
DataTableEntry->FullDllName.MaximumLength = 0;
|
||
} else {
|
||
DataTableEntry->FullDllName.Length = ImageFileName->Length;
|
||
DataTableEntry->FullDllName.MaximumLength = ImageFileName->Length;
|
||
RtlMoveMemory (DataTableEntry->FullDllName.Buffer,
|
||
ImageFileName->Buffer,
|
||
ImageFileName->Length);
|
||
DataTableEntry->FullDllName.Buffer[ImageFileName->Length/sizeof(WCHAR)] = UNICODE_NULL;
|
||
}
|
||
|
||
//
|
||
// Initialize the flags, load count, and insert the data table entry
|
||
// in the loaded module list.
|
||
//
|
||
|
||
DataTableEntry->Flags = LDRP_ENTRY_PROCESSED;
|
||
DataTableEntry->LoadCount = 1;
|
||
|
||
if (CacheImageSymbols (*ImageBaseAddress)) {
|
||
|
||
//
|
||
// TEMP TEMP TEMP rip out when debugger converted
|
||
//
|
||
|
||
ANSI_STRING AnsiName;
|
||
UNICODE_STRING UnicodeName;
|
||
|
||
//
|
||
// \SystemRoot is 11 characters in length
|
||
//
|
||
if (ImageFileName->Length > (11 * sizeof( WCHAR )) &&
|
||
!_wcsnicmp( ImageFileName->Buffer, L"\\SystemRoot", 11 )
|
||
) {
|
||
UnicodeName = *ImageFileName;
|
||
UnicodeName.Buffer += 11;
|
||
UnicodeName.Length -= (11 * sizeof( WCHAR ));
|
||
sprintf( NameBuffer, "%ws%wZ", &SharedUserData->NtSystemRoot[2], &UnicodeName );
|
||
} else {
|
||
sprintf( NameBuffer, "%wZ", &BaseName );
|
||
}
|
||
RtlInitString( &AnsiName, NameBuffer );
|
||
DbgLoadImageSymbols( &AnsiName,
|
||
*ImageBaseAddress,
|
||
(ULONG)-1
|
||
);
|
||
DataTableEntry->Flags |= LDRP_DEBUG_SYMBOLS_LOADED;
|
||
}
|
||
|
||
//
|
||
// Acquire the loaded module list resource and insert this entry
|
||
// into the list.
|
||
//
|
||
|
||
KeEnterCriticalRegion();
|
||
ExAcquireResourceExclusive (&PsLoadedModuleResource, TRUE);
|
||
|
||
ExInterlockedInsertTailList(&PsLoadedModuleList,
|
||
&DataTableEntry->InLoadOrderLinks,
|
||
&PsLoadedModuleSpinLock);
|
||
|
||
ExReleaseResource (&PsLoadedModuleResource);
|
||
KeLeaveCriticalRegion();
|
||
|
||
//
|
||
// Flush the instruction cache on all systems in the configuration.
|
||
//
|
||
|
||
KeSweepIcache (TRUE);
|
||
*ImageHandle = DataTableEntry;
|
||
Status = STATUS_SUCCESS;
|
||
|
||
if (SectionPointer == (PVOID)0xFFFFFFFF) {
|
||
MiEnablePagingOfDriver (DataTableEntry);
|
||
}
|
||
|
||
return1:
|
||
|
||
if (FileHandle) {
|
||
ZwClose (FileHandle);
|
||
}
|
||
if (!NT_SUCCESS(Status)) {
|
||
ULONG ErrorParameters[ 3 ];
|
||
ULONG NumberOfParameters;
|
||
ULONG UnicodeStringParameterMask;
|
||
ULONG ErrorResponse;
|
||
ANSI_STRING AnsiString;
|
||
UNICODE_STRING ProcedureName;
|
||
UNICODE_STRING DriverName;
|
||
|
||
//
|
||
// Hard error time. A driver could not be loaded.
|
||
//
|
||
|
||
KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
|
||
ErrorParameters[ 0 ] = (ULONG)ImageFileName;
|
||
NumberOfParameters = 1;
|
||
UnicodeStringParameterMask = 1;
|
||
|
||
RtlInitUnicodeString( &ProcedureName, NULL );
|
||
if (Status == STATUS_DRIVER_ORDINAL_NOT_FOUND ||
|
||
Status == STATUS_DRIVER_ENTRYPOINT_NOT_FOUND ||
|
||
Status == STATUS_PROCEDURE_NOT_FOUND
|
||
) {
|
||
NumberOfParameters = 3;
|
||
UnicodeStringParameterMask = 0x5;
|
||
RtlInitUnicodeString( &DriverName, MissingDriverName );
|
||
ErrorParameters[ 2 ] = (ULONG)&DriverName;
|
||
if ((ULONG)MissingProcedureName & 0xFFFF0000) {
|
||
//
|
||
// If not an ordinal, pass as unicode string
|
||
//
|
||
|
||
RtlInitAnsiString( &AnsiString, MissingProcedureName );
|
||
RtlAnsiStringToUnicodeString( &ProcedureName, &AnsiString, TRUE );
|
||
ErrorParameters[ 1 ] = (ULONG)&ProcedureName;
|
||
UnicodeStringParameterMask |= 0x2;
|
||
} else {
|
||
//
|
||
// Just pass ordinal values as is.
|
||
//
|
||
|
||
ErrorParameters[ 1 ] = (ULONG)MissingProcedureName;
|
||
}
|
||
} else {
|
||
NumberOfParameters = 2;
|
||
ErrorParameters[ 1 ] = (ULONG)Status;
|
||
Status = STATUS_DRIVER_UNABLE_TO_LOAD;
|
||
}
|
||
|
||
ZwRaiseHardError (Status,
|
||
NumberOfParameters,
|
||
UnicodeStringParameterMask,
|
||
ErrorParameters,
|
||
OptionOk,
|
||
&ErrorResponse);
|
||
|
||
if (ProcedureName.Buffer != NULL) {
|
||
RtlFreeUnicodeString( &ProcedureName );
|
||
}
|
||
return Status;
|
||
}
|
||
|
||
return2:
|
||
|
||
KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
MiLoadImageSection (
|
||
IN PSECTION SectionPointer,
|
||
OUT PVOID *ImageBaseAddress
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine loads the specified image into the kernel part of the
|
||
address space.
|
||
|
||
Arguments:
|
||
|
||
Section - Supplies the section object for the image.
|
||
|
||
ImageBaseAddress - Returns the address that the image header is at.
|
||
|
||
Return Value:
|
||
|
||
Status of the operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG PagesRequired = 0;
|
||
PMMPTE ProtoPte;
|
||
PMMPTE FirstPte;
|
||
PMMPTE LastPte;
|
||
PMMPTE PointerPte;
|
||
PEPROCESS Process;
|
||
ULONG NumberOfPtes;
|
||
MMPTE PteContents;
|
||
MMPTE TempPte;
|
||
PMMPFN Pfn1;
|
||
ULONG PageFrameIndex;
|
||
KIRQL OldIrql;
|
||
PVOID UserVa;
|
||
PVOID SystemVa;
|
||
NTSTATUS Status;
|
||
NTSTATUS ExceptionStatus;
|
||
PVOID Base;
|
||
ULONG ViewSize;
|
||
LARGE_INTEGER SectionOffset;
|
||
BOOLEAN LoadSymbols;
|
||
|
||
//
|
||
// Calculate the number of pages required to load this image.
|
||
//
|
||
|
||
ProtoPte = SectionPointer->Segment->PrototypePte;
|
||
NumberOfPtes = SectionPointer->Segment->TotalNumberOfPtes;
|
||
|
||
while (NumberOfPtes != 0) {
|
||
PteContents = *ProtoPte;
|
||
|
||
if ((PteContents.u.Hard.Valid == 1) ||
|
||
(PteContents.u.Soft.Protection != MM_NOACCESS)) {
|
||
PagesRequired += 1;
|
||
}
|
||
NumberOfPtes -= 1;
|
||
ProtoPte += 1;
|
||
}
|
||
|
||
//
|
||
// See if ample pages exist to load this image.
|
||
//
|
||
|
||
#if DBG
|
||
MiPagesConsumed = PagesRequired;
|
||
#endif
|
||
|
||
LOCK_PFN (OldIrql);
|
||
|
||
if (MmResidentAvailablePages <= (LONG)PagesRequired) {
|
||
UNLOCK_PFN (OldIrql);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
MmResidentAvailablePages -= PagesRequired;
|
||
UNLOCK_PFN (OldIrql);
|
||
|
||
//
|
||
// Reserve the necessary system address space.
|
||
//
|
||
|
||
FirstPte = MiReserveSystemPtes (SectionPointer->Segment->TotalNumberOfPtes,
|
||
SystemPteSpace,
|
||
0,
|
||
0,
|
||
FALSE );
|
||
|
||
if (FirstPte == NULL) {
|
||
LOCK_PFN (OldIrql);
|
||
MmResidentAvailablePages += PagesRequired;
|
||
UNLOCK_PFN (OldIrql);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Map a view into the user portion of the address space.
|
||
//
|
||
|
||
Process = PsGetCurrentProcess();
|
||
|
||
ZERO_LARGE (SectionOffset);
|
||
Base = NULL;
|
||
ViewSize = 0;
|
||
if ( NtGlobalFlag & FLG_ENABLE_KDEBUG_SYMBOL_LOAD ) {
|
||
LoadSymbols = TRUE;
|
||
NtGlobalFlag &= ~FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
|
||
} else {
|
||
LoadSymbols = FALSE;
|
||
}
|
||
Status = MmMapViewOfSection ( SectionPointer,
|
||
Process,
|
||
&Base,
|
||
0,
|
||
0,
|
||
&SectionOffset,
|
||
&ViewSize,
|
||
ViewUnmap,
|
||
0,
|
||
PAGE_EXECUTE);
|
||
|
||
if ( LoadSymbols ) {
|
||
NtGlobalFlag |= FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
|
||
}
|
||
if (Status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH) {
|
||
Status = STATUS_INVALID_IMAGE_FORMAT;
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
LOCK_PFN (OldIrql);
|
||
MmResidentAvailablePages += PagesRequired;
|
||
UNLOCK_PFN (OldIrql);
|
||
MiReleaseSystemPtes (FirstPte,
|
||
SectionPointer->Segment->TotalNumberOfPtes,
|
||
SystemPteSpace);
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Allocate a physical page(s) and copy the image data.
|
||
//
|
||
|
||
ProtoPte = SectionPointer->Segment->PrototypePte;
|
||
NumberOfPtes = SectionPointer->Segment->TotalNumberOfPtes;
|
||
PointerPte = FirstPte;
|
||
SystemVa = MiGetVirtualAddressMappedByPte (PointerPte);
|
||
*ImageBaseAddress = SystemVa;
|
||
UserVa = Base;
|
||
TempPte = ValidKernelPte;
|
||
|
||
while (NumberOfPtes != 0) {
|
||
PteContents = *ProtoPte;
|
||
if ((PteContents.u.Hard.Valid == 1) ||
|
||
(PteContents.u.Soft.Protection != MM_NOACCESS)) {
|
||
|
||
LOCK_PFN (OldIrql);
|
||
MiEnsureAvailablePageOrWait (NULL, NULL);
|
||
PageFrameIndex = MiRemoveAnyPage(
|
||
MI_GET_PAGE_COLOR_FROM_PTE (PointerPte));
|
||
PointerPte->u.Long = MM_KERNEL_DEMAND_ZERO_PTE;
|
||
MiInitializePfn (PageFrameIndex, PointerPte, 1);
|
||
UNLOCK_PFN (OldIrql);
|
||
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
|
||
*PointerPte = TempPte;
|
||
LastPte = PointerPte;
|
||
#if DBG
|
||
|
||
{
|
||
PMMPFN Pfn;
|
||
Pfn = MI_PFN_ELEMENT (PageFrameIndex);
|
||
ASSERT (Pfn->u1.WsIndex == 0);
|
||
}
|
||
#endif //DBG
|
||
|
||
try {
|
||
|
||
RtlMoveMemory (SystemVa, UserVa, PAGE_SIZE);
|
||
|
||
} except (MiMapCacheExceptionFilter (&ExceptionStatus,
|
||
GetExceptionInformation())) {
|
||
|
||
//
|
||
// An exception occurred, unmap the view and
|
||
// return the error to the caller.
|
||
//
|
||
|
||
ProtoPte = FirstPte;
|
||
LOCK_PFN (OldIrql);
|
||
while (ProtoPte <= PointerPte) {
|
||
if (ProtoPte->u.Hard.Valid == 1) {
|
||
|
||
//
|
||
// Delete the page.
|
||
//
|
||
|
||
PageFrameIndex = ProtoPte->u.Hard.PageFrameNumber;
|
||
|
||
//
|
||
// Set the pointer to PTE as empty so the page
|
||
// is deleted when the reference count goes to zero.
|
||
//
|
||
|
||
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
|
||
MiDecrementShareAndValidCount (Pfn1->PteFrame);
|
||
MI_SET_PFN_DELETED (Pfn1);
|
||
MiDecrementShareCountOnly (PageFrameIndex);
|
||
|
||
*ProtoPte = ZeroPte;
|
||
}
|
||
ProtoPte += 1;
|
||
}
|
||
|
||
MmResidentAvailablePages += PagesRequired;
|
||
UNLOCK_PFN (OldIrql);
|
||
MiReleaseSystemPtes (FirstPte,
|
||
SectionPointer->Segment->TotalNumberOfPtes,
|
||
SystemPteSpace);
|
||
Status = MmUnmapViewOfSection (Process, Base);
|
||
ASSERT (NT_SUCCESS (Status));
|
||
|
||
return ExceptionStatus;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// PTE is no access.
|
||
//
|
||
|
||
*PointerPte = ZeroKernelPte;
|
||
}
|
||
|
||
NumberOfPtes -= 1;
|
||
ProtoPte += 1;
|
||
PointerPte += 1;
|
||
SystemVa = (PVOID)((ULONG)SystemVa + PAGE_SIZE);
|
||
UserVa = (PVOID)((ULONG)UserVa + PAGE_SIZE);
|
||
}
|
||
|
||
Status = MmUnmapViewOfSection (Process, Base);
|
||
ASSERT (NT_SUCCESS (Status));
|
||
|
||
//
|
||
// Indicate that this section has been loaded into the system.
|
||
//
|
||
|
||
SectionPointer->Segment->SystemImageBase = *ImageBaseAddress;
|
||
|
||
//
|
||
// Charge commitment for the number of pages that were used by
|
||
// the driver.
|
||
//
|
||
|
||
MiChargeCommitmentCantExpand (PagesRequired, TRUE);
|
||
MmDriverCommit += PagesRequired;
|
||
return Status;
|
||
}
|
||
|
||
VOID
|
||
MmFreeDriverInitialization (
|
||
IN PVOID ImageHandle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine removes the pages that relocate and debug information from
|
||
the address space of the driver.
|
||
|
||
NOTE: This routine looks at the last sections defined in the image
|
||
header and if that section is marked as DISCARDABLE in the
|
||
characteristics, it is removed from the image. This means
|
||
that all discardable sections at the end of the driver are
|
||
deleted.
|
||
|
||
Arguments:
|
||
|
||
SectionObject - Supplies the section object for the image.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLDR_DATA_TABLE_ENTRY DataTableEntry;
|
||
KIRQL OldIrql;
|
||
PMMPTE PointerPte;
|
||
PMMPTE LastPte;
|
||
ULONG NumberOfPtes;
|
||
PVOID Base;
|
||
ULONG i;
|
||
PIMAGE_NT_HEADERS NtHeaders;
|
||
PIMAGE_SECTION_HEADER NtSection;
|
||
PIMAGE_SECTION_HEADER FoundSection;
|
||
ULONG PagesDeleted;
|
||
ULONG ResidentPages;
|
||
|
||
|
||
MmLockPagableSectionByHandle(ExPageLockHandle);
|
||
DataTableEntry = (PLDR_DATA_TABLE_ENTRY)ImageHandle;
|
||
Base = DataTableEntry->DllBase;
|
||
|
||
NumberOfPtes = DataTableEntry->SizeOfImage >> PAGE_SHIFT;
|
||
LastPte = MiGetPteAddress (Base) + NumberOfPtes;
|
||
|
||
NtHeaders = (PIMAGE_NT_HEADERS)RtlImageNtHeader(Base);
|
||
|
||
NtSection = (PIMAGE_SECTION_HEADER)((ULONG)NtHeaders +
|
||
sizeof(ULONG) +
|
||
sizeof(IMAGE_FILE_HEADER) +
|
||
NtHeaders->FileHeader.SizeOfOptionalHeader
|
||
);
|
||
|
||
NtSection += NtHeaders->FileHeader.NumberOfSections;
|
||
|
||
FoundSection = NULL;
|
||
for (i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++) {
|
||
NtSection -= 1;
|
||
if ((NtSection->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0) {
|
||
FoundSection = NtSection;
|
||
} else {
|
||
|
||
//
|
||
// There was a non discardable section between the this
|
||
// section and the last non discardable section, don't
|
||
// discard this section and don't look any more.
|
||
//
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (FoundSection != NULL) {
|
||
|
||
PointerPte = MiGetPteAddress (ROUND_TO_PAGES (
|
||
(ULONG)Base + FoundSection->VirtualAddress));
|
||
NumberOfPtes = LastPte - PointerPte;
|
||
|
||
PagesDeleted = MiDeleteSystemPagableVm (PointerPte,
|
||
NumberOfPtes,
|
||
ZeroKernelPte.u.Long,
|
||
&ResidentPages);
|
||
|
||
MmResidentAvailablePages += PagesDeleted;
|
||
MiReturnCommitment (PagesDeleted);
|
||
MmDriverCommit -= PagesDeleted;
|
||
#if DBG
|
||
MiPagesConsumed -= PagesDeleted;
|
||
#endif
|
||
}
|
||
|
||
MmUnlockPagableImageSection(ExPageLockHandle);
|
||
return;
|
||
}
|
||
VOID
|
||
MiEnablePagingOfDriver (
|
||
IN PVOID ImageHandle
|
||
)
|
||
|
||
{
|
||
PLDR_DATA_TABLE_ENTRY DataTableEntry;
|
||
PMMPTE LastPte;
|
||
PMMPTE PointerPte;
|
||
PVOID Base;
|
||
ULONG i;
|
||
PIMAGE_NT_HEADERS NtHeaders;
|
||
PIMAGE_SECTION_HEADER FoundSection;
|
||
|
||
//
|
||
// Don't page kernel mode code if customer does not want it paged.
|
||
//
|
||
|
||
if (MmDisablePagingExecutive) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// If the driver has pagable code, make it paged.
|
||
//
|
||
|
||
DataTableEntry = (PLDR_DATA_TABLE_ENTRY)ImageHandle;
|
||
Base = DataTableEntry->DllBase;
|
||
|
||
NtHeaders = (PIMAGE_NT_HEADERS)RtlImageNtHeader(Base);
|
||
|
||
FoundSection = (PIMAGE_SECTION_HEADER)((ULONG)NtHeaders +
|
||
sizeof(ULONG) +
|
||
sizeof(IMAGE_FILE_HEADER) +
|
||
NtHeaders->FileHeader.SizeOfOptionalHeader
|
||
);
|
||
|
||
i = NtHeaders->FileHeader.NumberOfSections;
|
||
PointerPte = NULL;
|
||
|
||
while (i > 0) {
|
||
#if DBG
|
||
if ((*(PULONG)FoundSection->Name == 'tini') ||
|
||
(*(PULONG)FoundSection->Name == 'egap')) {
|
||
DbgPrint("driver %wZ has lower case sections (init or pagexxx)\n",
|
||
&DataTableEntry->FullDllName);
|
||
}
|
||
#endif //DBG
|
||
|
||
//
|
||
// Mark as pagable any section which starts with the
|
||
// first 4 characters PAGE or .eda (for the .edata section).
|
||
//
|
||
|
||
if ((*(PULONG)FoundSection->Name == 'EGAP') ||
|
||
(*(PULONG)FoundSection->Name == 'ade.')) {
|
||
|
||
//
|
||
// This section is pagable, save away the start and end.
|
||
//
|
||
|
||
if (PointerPte == NULL) {
|
||
|
||
//
|
||
// Previous section was NOT pagable, get the start address.
|
||
//
|
||
|
||
PointerPte = MiGetPteAddress (ROUND_TO_PAGES (
|
||
(ULONG)Base + FoundSection->VirtualAddress));
|
||
}
|
||
LastPte = MiGetPteAddress ((ULONG)Base +
|
||
FoundSection->VirtualAddress +
|
||
(NtHeaders->OptionalHeader.SectionAlignment - 1) +
|
||
(FoundSection->SizeOfRawData - PAGE_SIZE));
|
||
|
||
} else {
|
||
|
||
//
|
||
// This section is not pagable, if the previous section was
|
||
// pagable, enable it.
|
||
//
|
||
|
||
if (PointerPte != NULL) {
|
||
MiSetPagingOfDriver (PointerPte, LastPte);
|
||
PointerPte = NULL;
|
||
}
|
||
}
|
||
i -= 1;
|
||
FoundSection += 1;
|
||
}
|
||
if (PointerPte != NULL) {
|
||
MiSetPagingOfDriver (PointerPte, LastPte);
|
||
}
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
MiSetPagingOfDriver (
|
||
IN PMMPTE PointerPte,
|
||
IN PMMPTE LastPte
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine marks the specified range of PTEs as pagable.
|
||
|
||
Arguments:
|
||
|
||
PointerPte - Supplies the starting PTE.
|
||
|
||
LastPte - Supplies the ending PTE.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PVOID Base;
|
||
ULONG PageFrameIndex;
|
||
PMMPFN Pfn;
|
||
MMPTE TempPte;
|
||
MMPTE PreviousPte;
|
||
KIRQL OldIrql1;
|
||
KIRQL OldIrql;
|
||
|
||
PAGED_CODE ();
|
||
|
||
if (MI_IS_PHYSICAL_ADDRESS(MiGetVirtualAddressMappedByPte(PointerPte))) {
|
||
|
||
//
|
||
// No need to lock physical addresses.
|
||
//
|
||
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Lock this routine into memory.
|
||
//
|
||
|
||
MmLockPagableSectionByHandle(ExPageLockHandle);
|
||
|
||
LOCK_SYSTEM_WS (OldIrql1);
|
||
LOCK_PFN (OldIrql);
|
||
|
||
Base = MiGetVirtualAddressMappedByPte (PointerPte);
|
||
|
||
while (PointerPte <= LastPte) {
|
||
|
||
//
|
||
// Check to make sure this PTE has not already been
|
||
// made pagable (or deleted). It is pagable if it
|
||
// is not valid, or if the PFN database wsindex element
|
||
// is non zero.
|
||
//
|
||
|
||
if (PointerPte->u.Hard.Valid == 1) {
|
||
PageFrameIndex = PointerPte->u.Hard.PageFrameNumber;
|
||
Pfn = MI_PFN_ELEMENT (PageFrameIndex);
|
||
ASSERT (Pfn->u2.ShareCount == 1);
|
||
|
||
//
|
||
// Original PTE may need to be set for drivers loaded
|
||
// via osldr.
|
||
//
|
||
|
||
if (Pfn->OriginalPte.u.Long == 0) {
|
||
Pfn->OriginalPte.u.Long = MM_KERNEL_DEMAND_ZERO_PTE;
|
||
}
|
||
|
||
if (Pfn->u1.WsIndex == 0) {
|
||
|
||
TempPte = *PointerPte;
|
||
|
||
MI_MAKE_VALID_PTE_TRANSITION (TempPte,
|
||
Pfn->OriginalPte.u.Soft.Protection);
|
||
|
||
PreviousPte.u.Flush = KeFlushSingleTb (Base,
|
||
TRUE,
|
||
TRUE,
|
||
(PHARDWARE_PTE)PointerPte,
|
||
TempPte.u.Flush);
|
||
|
||
MI_CAPTURE_DIRTY_BIT_TO_PFN (&PreviousPte, Pfn);
|
||
|
||
//
|
||
// Flush the translation buffer and decrement the number of valid
|
||
// PTEs within the containing page table page. Note that for a
|
||
// private page, the page table page is still needed because the
|
||
// page is in transiton.
|
||
//
|
||
|
||
MiDecrementShareCount (PageFrameIndex);
|
||
MmResidentAvailablePages += 1;
|
||
MmTotalSystemDriverPages += 1;
|
||
}
|
||
}
|
||
Base = (PVOID)((PCHAR)Base + PAGE_SIZE);
|
||
PointerPte += 1;
|
||
}
|
||
|
||
UNLOCK_PFN (OldIrql);
|
||
UNLOCK_SYSTEM_WS (OldIrql1);
|
||
MmUnlockPagableImageSection(ExPageLockHandle);
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
MiLockPagesOfDriver (
|
||
IN PMMPTE PointerPte,
|
||
IN PMMPTE LastPte
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine marks the specified range of PTEs as NONpagable.
|
||
|
||
Arguments:
|
||
|
||
PointerPte - Supplies the starting PTE.
|
||
|
||
LastPte - Supplies the ending PTE.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PVOID Base;
|
||
ULONG PageFrameIndex;
|
||
PMMPFN Pfn;
|
||
MMPTE TempPte;
|
||
KIRQL OldIrql;
|
||
KIRQL OldIrql1;
|
||
|
||
PAGED_CODE ();
|
||
|
||
//
|
||
// Lock this routine in memory.
|
||
//
|
||
|
||
MmLockPagableSectionByHandle(ExPageLockHandle);
|
||
|
||
LOCK_SYSTEM_WS (OldIrql1);
|
||
LOCK_PFN (OldIrql);
|
||
|
||
Base = MiGetVirtualAddressMappedByPte (PointerPte);
|
||
|
||
while (PointerPte <= LastPte) {
|
||
|
||
//
|
||
// Check to make sure this PTE has not already been
|
||
// made pagable (or deleted). It is pagable if it
|
||
// is not valid, or if the PFN database wsindex element
|
||
// is non zero.
|
||
//
|
||
|
||
if (PointerPte->u.Hard.Valid == 1) {
|
||
PageFrameIndex = PointerPte->u.Hard.PageFrameNumber;
|
||
Pfn = MI_PFN_ELEMENT (PageFrameIndex);
|
||
ASSERT (Pfn->u2.ShareCount == 1);
|
||
|
||
if (Pfn->u1.WsIndex == 0) {
|
||
|
||
TempPte = *PointerPte;
|
||
|
||
MI_MAKE_VALID_PTE_TRANSITION (TempPte,
|
||
Pfn->OriginalPte.u.Soft.Protection);
|
||
|
||
KeFlushSingleTb (Base,
|
||
TRUE,
|
||
TRUE,
|
||
(PHARDWARE_PTE)PointerPte,
|
||
TempPte.u.Flush);
|
||
|
||
//
|
||
// Flush the translation buffer and decrement the number of valid
|
||
// PTEs within the containing page table page. Note that for a
|
||
// private page, the page table page is still needed because the
|
||
// page is in transiton.
|
||
//
|
||
|
||
MiDecrementShareCount (PageFrameIndex);
|
||
Base = (PVOID)((PCHAR)Base + PAGE_SIZE);
|
||
PointerPte += 1;
|
||
MmResidentAvailablePages += 1;
|
||
MmTotalSystemDriverPages++;
|
||
}
|
||
}
|
||
}
|
||
|
||
UNLOCK_PFN (OldIrql);
|
||
UNLOCK_SYSTEM_WS (OldIrql1);
|
||
MmUnlockPagableImageSection(ExPageLockHandle);
|
||
return;
|
||
}
|
||
|
||
|
||
PVOID
|
||
MmPageEntireDriver (
|
||
IN PVOID AddressWithinSection
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine allows a driver to page out all of its code and
|
||
data regardless of the attributes of the various image sections.
|
||
|
||
Note, this routine can be called multiple times with no
|
||
intervening calls to MmResetDriverPaging.
|
||
|
||
Arguments:
|
||
|
||
AddressWithinSection - Supplies an address within the driver, e.g.
|
||
DriverEntry.
|
||
|
||
Return Value:
|
||
|
||
Base address of driver.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, APC_LEVEL or below.
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
PLDR_DATA_TABLE_ENTRY DataTableEntry;
|
||
PMMPTE FirstPte;
|
||
PMMPTE LastPte;
|
||
PVOID BaseAddress;
|
||
|
||
//
|
||
// Don't page kernel mode code if disabled via registry.
|
||
//
|
||
|
||
DataTableEntry = MiLookupDataTableEntry (AddressWithinSection, FALSE);
|
||
if ((DataTableEntry->SectionPointer != (PVOID)0xffffffff) ||
|
||
(MmDisablePagingExecutive)) {
|
||
|
||
//
|
||
// Driver is mapped as image, always pagable.
|
||
//
|
||
|
||
return DataTableEntry->DllBase;
|
||
}
|
||
BaseAddress = DataTableEntry->DllBase;
|
||
FirstPte = MiGetPteAddress (DataTableEntry->DllBase);
|
||
LastPte = (FirstPte - 1) + (DataTableEntry->SizeOfImage >> PAGE_SHIFT);
|
||
MiSetPagingOfDriver (FirstPte, LastPte);
|
||
|
||
return BaseAddress;
|
||
}
|
||
|
||
|
||
VOID
|
||
MmResetDriverPaging (
|
||
IN PVOID AddressWithinSection
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routines resets the driver paging to what the image specified.
|
||
Hence image sections such as the IAT, .text, .data will be locked
|
||
down in memory.
|
||
|
||
Note, there is no requirement that MmPageEntireDriver was called.
|
||
|
||
Arguments:
|
||
|
||
AddressWithinSection - Supplies an address within the driver, e.g.
|
||
DriverEntry.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Kernel mode, APC_LEVEL or below.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLDR_DATA_TABLE_ENTRY DataTableEntry;
|
||
PMMPTE LastPte;
|
||
PMMPTE PointerPte;
|
||
PVOID Base;
|
||
ULONG i;
|
||
PIMAGE_NT_HEADERS NtHeaders;
|
||
PIMAGE_SECTION_HEADER FoundSection;
|
||
KIRQL OldIrql;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Don't page kernel mode code if disabled via registry.
|
||
//
|
||
|
||
if (MmDisablePagingExecutive) {
|
||
return;
|
||
}
|
||
|
||
if (MI_IS_PHYSICAL_ADDRESS(AddressWithinSection)) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// If the driver has pagable code, make it paged.
|
||
//
|
||
|
||
DataTableEntry = MiLookupDataTableEntry (AddressWithinSection, FALSE);
|
||
|
||
if (DataTableEntry->SectionPointer != (PVOID)0xFFFFFFFF) {
|
||
|
||
//
|
||
// Driver is mapped by image hence already paged.
|
||
//
|
||
|
||
return;
|
||
}
|
||
|
||
Base = DataTableEntry->DllBase;
|
||
|
||
NtHeaders = (PIMAGE_NT_HEADERS)RtlImageNtHeader(Base);
|
||
|
||
FoundSection = (PIMAGE_SECTION_HEADER)((ULONG)NtHeaders +
|
||
sizeof(ULONG) +
|
||
sizeof(IMAGE_FILE_HEADER) +
|
||
NtHeaders->FileHeader.SizeOfOptionalHeader
|
||
);
|
||
|
||
i = NtHeaders->FileHeader.NumberOfSections;
|
||
PointerPte = NULL;
|
||
|
||
while (i > 0) {
|
||
#if DBG
|
||
if ((*(PULONG)FoundSection->Name == 'tini') ||
|
||
(*(PULONG)FoundSection->Name == 'egap')) {
|
||
DbgPrint("driver %wZ has lower case sections (init or pagexxx)\n",
|
||
&DataTableEntry->FullDllName);
|
||
}
|
||
#endif //DBG
|
||
|
||
//
|
||
// Don't lock down code for sections marked as discardable or
|
||
// sections marked with the first 4 characters PAGE or .eda
|
||
// (for the .edata section) or INIT.
|
||
//
|
||
|
||
if (((FoundSection->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0) ||
|
||
(*(PULONG)FoundSection->Name == 'EGAP') ||
|
||
(*(PULONG)FoundSection->Name == 'ade.') ||
|
||
(*(PULONG)FoundSection->Name == 'TINI')) {
|
||
|
||
NOTHING;
|
||
|
||
} else {
|
||
|
||
//
|
||
// This section is nonpagable.
|
||
//
|
||
|
||
PointerPte = MiGetPteAddress (
|
||
(ULONG)Base + FoundSection->VirtualAddress);
|
||
LastPte = MiGetPteAddress ((ULONG)Base +
|
||
FoundSection->VirtualAddress +
|
||
(FoundSection->SizeOfRawData - 1));
|
||
ASSERT (PointerPte <= LastPte);
|
||
MmLockPagableSectionByHandle(ExPageLockHandle);
|
||
LOCK_PFN (OldIrql);
|
||
MiLockCode (PointerPte, LastPte, MM_LOCK_BY_NONPAGE);
|
||
UNLOCK_PFN (OldIrql);
|
||
MmUnlockPagableImageSection(ExPageLockHandle);
|
||
}
|
||
i -= 1;
|
||
FoundSection += 1;
|
||
}
|
||
return;
|
||
}
|
||
|
||
NTSTATUS
|
||
MiUnloadSystemImageByForce (
|
||
IN ULONG NumberOfPtes,
|
||
IN PVOID ImageBase
|
||
)
|
||
|
||
{
|
||
LDR_DATA_TABLE_ENTRY DataTableEntry;
|
||
|
||
RtlZeroMemory (&DataTableEntry, sizeof(LDR_DATA_TABLE_ENTRY));
|
||
|
||
DataTableEntry.DllBase = ImageBase;
|
||
DataTableEntry.SizeOfImage = NumberOfPtes << PAGE_SHIFT;
|
||
|
||
return MmUnloadSystemImage ((PVOID)&DataTableEntry);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
MmUnloadSystemImage (
|
||
IN PVOID ImageHandle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine unloads a previously loaded system image and returns
|
||
the allocated resources.
|
||
|
||
Arguments:
|
||
|
||
Section - Supplies a pointer to the section object of the image to unload.
|
||
|
||
Return Value:
|
||
|
||
TBS
|
||
|
||
--*/
|
||
|
||
{
|
||
PLDR_DATA_TABLE_ENTRY DataTableEntry;
|
||
PMMPTE FirstPte;
|
||
ULONG PagesRequired;
|
||
ULONG ResidentPages;
|
||
PMMPTE PointerPte;
|
||
ULONG NumberOfPtes;
|
||
KIRQL OldIrql;
|
||
PVOID BasedAddress;
|
||
|
||
KeWaitForSingleObject (&MmSystemLoadLock,
|
||
WrVirtualMemory,
|
||
KernelMode,
|
||
FALSE,
|
||
(PLARGE_INTEGER)NULL);
|
||
|
||
MmLockPagableSectionByHandle(ExPageLockHandle);
|
||
|
||
DataTableEntry = (PLDR_DATA_TABLE_ENTRY)ImageHandle;
|
||
BasedAddress = DataTableEntry->DllBase;
|
||
|
||
//
|
||
// Unload symbols from debugger.
|
||
//
|
||
|
||
if (DataTableEntry->Flags & LDRP_DEBUG_SYMBOLS_LOADED) {
|
||
|
||
//
|
||
// TEMP TEMP TEMP rip out when debugger converted
|
||
//
|
||
|
||
ANSI_STRING AnsiName;
|
||
NTSTATUS Status;
|
||
|
||
Status = RtlUnicodeStringToAnsiString( &AnsiName,
|
||
&DataTableEntry->BaseDllName,
|
||
TRUE );
|
||
|
||
if (NT_SUCCESS( Status)) {
|
||
DbgUnLoadImageSymbols( &AnsiName,
|
||
BasedAddress,
|
||
(ULONG)-1);
|
||
RtlFreeAnsiString( &AnsiName );
|
||
}
|
||
}
|
||
|
||
FirstPte = MiGetPteAddress (BasedAddress);
|
||
PointerPte = FirstPte;
|
||
NumberOfPtes = DataTableEntry->SizeOfImage >> PAGE_SHIFT;
|
||
|
||
PagesRequired = MiDeleteSystemPagableVm (PointerPte,
|
||
NumberOfPtes,
|
||
ZeroKernelPte.u.Long,
|
||
&ResidentPages);
|
||
|
||
LOCK_PFN (OldIrql);
|
||
MmResidentAvailablePages += ResidentPages;
|
||
UNLOCK_PFN (OldIrql);
|
||
MiReleaseSystemPtes (FirstPte,
|
||
NumberOfPtes,
|
||
SystemPteSpace);
|
||
MiReturnCommitment (PagesRequired);
|
||
MmDriverCommit -= PagesRequired;
|
||
|
||
//
|
||
// Search the loaded module list for the data table entry that describes
|
||
// the DLL that was just unloaded. It is possible an entry is not in the
|
||
// list if a failure occured at a point in loading the DLL just before
|
||
// the data table entry was generated.
|
||
//
|
||
|
||
if (DataTableEntry->InLoadOrderLinks.Flink != NULL) {
|
||
KeEnterCriticalRegion();
|
||
ExAcquireResourceExclusive (&PsLoadedModuleResource, TRUE);
|
||
|
||
ExAcquireSpinLock (&PsLoadedModuleSpinLock, &OldIrql);
|
||
|
||
RemoveEntryList(&DataTableEntry->InLoadOrderLinks);
|
||
ExReleaseSpinLock (&PsLoadedModuleSpinLock, OldIrql);
|
||
if (DataTableEntry->FullDllName.Buffer != NULL) {
|
||
ExFreePool (DataTableEntry->FullDllName.Buffer);
|
||
}
|
||
ExFreePool((PVOID)DataTableEntry);
|
||
|
||
ExReleaseResource (&PsLoadedModuleResource);
|
||
KeLeaveCriticalRegion();
|
||
}
|
||
MmUnlockPagableImageSection(ExPageLockHandle);
|
||
|
||
KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
MiResolveImageReferences (
|
||
PVOID ImageBase,
|
||
IN PUNICODE_STRING ImageFileDirectory,
|
||
OUT PCHAR *MissingProcedureName,
|
||
OUT PWSTR *MissingDriverName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine resolves the references from the newly loaded driver
|
||
to the kernel, hal and other drivers.
|
||
|
||
Arguments:
|
||
|
||
ImageBase - Supplies the address of which the image header resides.
|
||
|
||
ImageFileDirectory - Supplies the directory to load referenced DLLs.
|
||
|
||
Return Value:
|
||
|
||
TBS
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PVOID ImportBase;
|
||
ULONG ImportSize;
|
||
PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
|
||
NTSTATUS st;
|
||
ULONG ExportSize;
|
||
PIMAGE_EXPORT_DIRECTORY ExportDirectory;
|
||
PIMAGE_THUNK_DATA Thunk;
|
||
PSZ ImportName;
|
||
PLIST_ENTRY NextEntry;
|
||
PLDR_DATA_TABLE_ENTRY DataTableEntry;
|
||
ANSI_STRING AnsiString;
|
||
UNICODE_STRING ImportDescriptorName_U;
|
||
UNICODE_STRING DllToLoad;
|
||
PVOID Section;
|
||
PVOID BaseAddress;
|
||
ULONG LinkWin32k = 0;
|
||
ULONG LinkNonWin32k = 0;
|
||
|
||
PAGED_CODE();
|
||
|
||
ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)RtlImageDirectoryEntryToData(
|
||
ImageBase,
|
||
TRUE,
|
||
IMAGE_DIRECTORY_ENTRY_IMPORT,
|
||
&ImportSize);
|
||
|
||
if (ImportDescriptor) {
|
||
|
||
//
|
||
// We do not support bound images in the kernel
|
||
//
|
||
|
||
if (ImportDescriptor->TimeDateStamp == (ULONG)-1) {
|
||
#if DBG
|
||
KeBugCheckEx (BOUND_IMAGE_UNSUPPORTED,
|
||
(ULONG)ImportDescriptor,
|
||
(ULONG)ImageBase,
|
||
(ULONG)ImageFileDirectory,
|
||
(ULONG)ImportSize);
|
||
#else
|
||
return (STATUS_PROCEDURE_NOT_FOUND);
|
||
#endif
|
||
}
|
||
|
||
while (ImportDescriptor->Name && ImportDescriptor->FirstThunk) {
|
||
|
||
ImportName = (PSZ)((ULONG)ImageBase + ImportDescriptor->Name);
|
||
|
||
//
|
||
// A driver can link with win32k.sys if and only if it is a GDI
|
||
// driver.
|
||
// Also display drivers can only link to win32k.sys (and lego ...).
|
||
//
|
||
// So if we get a driver that links to win32k.sys and has more
|
||
// than one set of imports, we will fail to load it.
|
||
//
|
||
|
||
LinkWin32k = LinkWin32k |
|
||
(!_strnicmp(ImportName, "win32k", sizeof("win32k") - 1));
|
||
|
||
//
|
||
// We don't want to count coverage, win32k and irt (lego) since
|
||
// display drivers CAN link against these.
|
||
//
|
||
|
||
LinkNonWin32k = LinkNonWin32k |
|
||
((_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) &&
|
||
(_strnicmp(ImportName, "coverage", sizeof("coverage") - 1)) &&
|
||
(_strnicmp(ImportName, "irt", sizeof("irt") - 1)));
|
||
|
||
|
||
if (LinkNonWin32k && LinkWin32k) {
|
||
return (STATUS_PROCEDURE_NOT_FOUND);
|
||
}
|
||
|
||
if ((!_strnicmp(ImportName, "ntdll", sizeof("ntdll") - 1)) ||
|
||
(!_strnicmp(ImportName, "winsrv", sizeof("winsrv") - 1)) ||
|
||
(!_strnicmp(ImportName, "advapi32", sizeof("advapi32") - 1)) ||
|
||
(!_strnicmp(ImportName, "kernel32", sizeof("kernel32") - 1)) ||
|
||
(!_strnicmp(ImportName, "user32", sizeof("user32") - 1)) ||
|
||
(!_strnicmp(ImportName, "gdi32", sizeof("gdi32") - 1)) ) {
|
||
|
||
return (STATUS_PROCEDURE_NOT_FOUND);
|
||
}
|
||
|
||
ReCheck:
|
||
RtlInitAnsiString(&AnsiString, ImportName);
|
||
st = RtlAnsiStringToUnicodeString(&ImportDescriptorName_U,
|
||
&AnsiString,
|
||
TRUE);
|
||
if (!NT_SUCCESS(st)) {
|
||
return st;
|
||
}
|
||
|
||
NextEntry = PsLoadedModuleList.Flink;
|
||
ImportBase = NULL;
|
||
while (NextEntry != &PsLoadedModuleList) {
|
||
DataTableEntry = CONTAINING_RECORD(NextEntry,
|
||
LDR_DATA_TABLE_ENTRY,
|
||
InLoadOrderLinks);
|
||
if (RtlEqualString((PSTRING)&ImportDescriptorName_U,
|
||
(PSTRING)&DataTableEntry->BaseDllName,
|
||
TRUE
|
||
)) {
|
||
ImportBase = DataTableEntry->DllBase;
|
||
break;
|
||
}
|
||
NextEntry = NextEntry->Flink;
|
||
}
|
||
|
||
if (!ImportBase) {
|
||
|
||
//
|
||
// The DLL name was not located, attempt to load this dll.
|
||
//
|
||
|
||
DllToLoad.MaximumLength = ImportDescriptorName_U.Length +
|
||
ImageFileDirectory->Length +
|
||
(USHORT)sizeof(WCHAR);
|
||
|
||
DllToLoad.Buffer = ExAllocatePoolWithTag (NonPagedPool,
|
||
DllToLoad.MaximumLength,
|
||
'TDmM');
|
||
|
||
if (DllToLoad.Buffer == NULL) {
|
||
RtlFreeUnicodeString( &ImportDescriptorName_U );
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
DllToLoad.Length = ImageFileDirectory->Length;
|
||
RtlMoveMemory (DllToLoad.Buffer,
|
||
ImageFileDirectory->Buffer,
|
||
ImageFileDirectory->Length);
|
||
|
||
RtlAppendStringToString ((PSTRING)&DllToLoad,
|
||
(PSTRING)&ImportDescriptorName_U);
|
||
|
||
st = MmLoadSystemImage (&DllToLoad,
|
||
&Section,
|
||
&BaseAddress);
|
||
|
||
ExFreePool (DllToLoad.Buffer);
|
||
if (!NT_SUCCESS(st)) {
|
||
RtlFreeUnicodeString( &ImportDescriptorName_U );
|
||
return st;
|
||
}
|
||
goto ReCheck;
|
||
}
|
||
|
||
RtlFreeUnicodeString( &ImportDescriptorName_U );
|
||
*MissingDriverName = DataTableEntry->BaseDllName.Buffer;
|
||
|
||
ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(
|
||
ImportBase,
|
||
TRUE,
|
||
IMAGE_DIRECTORY_ENTRY_EXPORT,
|
||
&ExportSize
|
||
);
|
||
|
||
if (!ExportDirectory) {
|
||
return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
|
||
}
|
||
|
||
//
|
||
// Walk through the IAT and snap all the thunks.
|
||
//
|
||
|
||
if ( (Thunk = ImportDescriptor->FirstThunk) ) {
|
||
Thunk = (PIMAGE_THUNK_DATA)((ULONG)ImageBase + (ULONG)Thunk);
|
||
while (Thunk->u1.AddressOfData) {
|
||
st = MiSnapThunk(ImportBase,
|
||
ImageBase,
|
||
Thunk++,
|
||
ExportDirectory,
|
||
ExportSize,
|
||
FALSE,
|
||
MissingProcedureName
|
||
);
|
||
if (!NT_SUCCESS(st) ) {
|
||
return st;
|
||
}
|
||
}
|
||
}
|
||
|
||
ImportDescriptor++;
|
||
}
|
||
}
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
MiSnapThunk(
|
||
IN PVOID DllBase,
|
||
IN PVOID ImageBase,
|
||
IN OUT PIMAGE_THUNK_DATA Thunk,
|
||
IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
|
||
IN ULONG ExportSize,
|
||
IN BOOLEAN SnapForwarder,
|
||
OUT PCHAR *MissingProcedureName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function snaps a thunk using the specified Export Section data.
|
||
If the section data does not support the thunk, then the thunk is
|
||
partially snapped (Dll field is still non-null, but snap address is
|
||
set).
|
||
|
||
Arguments:
|
||
|
||
DllBase - Base if DLL being snapped to
|
||
|
||
ImageBase - Base of image that contains the thunks to snap.
|
||
|
||
Thunk - On input, supplies the thunk to snap. When successfully
|
||
snapped, the function field is set to point to the address in
|
||
the DLL, and the DLL field is set to NULL.
|
||
|
||
ExportDirectory - Supplies the Export Section data from a DLL.
|
||
|
||
SnapForwarder - determine if the snap is for a forwarder, and therefore
|
||
Address of Data is already setup.
|
||
|
||
Return Value:
|
||
|
||
|
||
STATUS_SUCCESS or STATUS_DRIVER_ENTRYPOINT_NOT_FOUND or
|
||
STATUS_DRIVER_ORDINAL_NOT_FOUND
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
BOOLEAN Ordinal;
|
||
USHORT OrdinalNumber;
|
||
PULONG NameTableBase;
|
||
PUSHORT NameOrdinalTableBase;
|
||
PULONG Addr;
|
||
USHORT HintIndex;
|
||
ULONG High;
|
||
ULONG Low;
|
||
ULONG Middle;
|
||
LONG Result;
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Determine if snap is by name, or by ordinal
|
||
//
|
||
|
||
Ordinal = (BOOLEAN)IMAGE_SNAP_BY_ORDINAL(Thunk->u1.Ordinal);
|
||
|
||
if (Ordinal && !SnapForwarder) {
|
||
|
||
OrdinalNumber = (USHORT)(IMAGE_ORDINAL(Thunk->u1.Ordinal) -
|
||
ExportDirectory->Base);
|
||
|
||
*MissingProcedureName = (PCHAR)(ULONG)OrdinalNumber;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Change AddressOfData from an RVA to a VA.
|
||
//
|
||
|
||
if (!SnapForwarder) {
|
||
Thunk->u1.AddressOfData = (PIMAGE_IMPORT_BY_NAME)((ULONG)ImageBase +
|
||
(ULONG)Thunk->u1.AddressOfData);
|
||
}
|
||
|
||
strncpy( *MissingProcedureName,
|
||
&Thunk->u1.AddressOfData->Name[0],
|
||
MAXIMUM_FILENAME_LENGTH - 1
|
||
);
|
||
|
||
//
|
||
// Lookup Name in NameTable
|
||
//
|
||
|
||
NameTableBase = (PULONG)((ULONG)DllBase + (ULONG)ExportDirectory->AddressOfNames);
|
||
NameOrdinalTableBase = (PUSHORT)((ULONG)DllBase + (ULONG)ExportDirectory->AddressOfNameOrdinals);
|
||
|
||
//
|
||
// Before dropping into binary search, see if
|
||
// the hint index results in a successful
|
||
// match. If the hint index is zero, then
|
||
// drop into binary search.
|
||
//
|
||
|
||
HintIndex = Thunk->u1.AddressOfData->Hint;
|
||
if ((ULONG)HintIndex < ExportDirectory->NumberOfNames &&
|
||
!strcmp((PSZ)Thunk->u1.AddressOfData->Name,
|
||
(PSZ)((ULONG)DllBase + NameTableBase[HintIndex]))) {
|
||
OrdinalNumber = NameOrdinalTableBase[HintIndex];
|
||
|
||
} else {
|
||
|
||
//
|
||
// Lookup the import name in the name table using a binary search.
|
||
//
|
||
|
||
Low = 0;
|
||
High = ExportDirectory->NumberOfNames - 1;
|
||
|
||
while (High >= Low) {
|
||
|
||
//
|
||
// Compute the next probe index and compare the import name
|
||
// with the export name entry.
|
||
//
|
||
|
||
Middle = (Low + High) >> 1;
|
||
Result = strcmp(&Thunk->u1.AddressOfData->Name[0],
|
||
(PCHAR)((ULONG)DllBase + NameTableBase[Middle]));
|
||
|
||
if (Result < 0) {
|
||
High = Middle - 1;
|
||
|
||
} else if (Result > 0) {
|
||
Low = Middle + 1;
|
||
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If the high index is less than the low index, then a matching
|
||
// table entry was not found. Otherwise, get the ordinal number
|
||
// from the ordinal table.
|
||
//
|
||
|
||
if (High < Low) {
|
||
return (STATUS_DRIVER_ENTRYPOINT_NOT_FOUND);
|
||
} else {
|
||
OrdinalNumber = NameOrdinalTableBase[Middle];
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// If OrdinalNumber is not within the Export Address Table,
|
||
// then DLL does not implement function. Snap to LDRP_BAD_DLL.
|
||
//
|
||
|
||
if ((ULONG)OrdinalNumber >= ExportDirectory->NumberOfFunctions) {
|
||
Status = STATUS_DRIVER_ORDINAL_NOT_FOUND;
|
||
|
||
} else {
|
||
|
||
Addr = (PULONG)((ULONG)DllBase + (ULONG)ExportDirectory->AddressOfFunctions);
|
||
Thunk->u1.Function = (PULONG)((ULONG)DllBase + Addr[OrdinalNumber]);
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
if ( ((ULONG)Thunk->u1.Function > (ULONG)ExportDirectory) &&
|
||
((ULONG)Thunk->u1.Function < ((ULONG)ExportDirectory + ExportSize)) ) {
|
||
|
||
UNICODE_STRING UnicodeString;
|
||
ANSI_STRING ForwardDllName;
|
||
|
||
PLIST_ENTRY NextEntry;
|
||
PLDR_DATA_TABLE_ENTRY DataTableEntry;
|
||
ULONG ExportSize;
|
||
PIMAGE_EXPORT_DIRECTORY ExportDirectory;
|
||
|
||
Status = STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
|
||
|
||
//
|
||
// Include the dot in the length so we can do prefix later on.
|
||
//
|
||
|
||
ForwardDllName.Buffer = (PCHAR)Thunk->u1.Function;
|
||
ForwardDllName.Length = strchr(ForwardDllName.Buffer, '.') -
|
||
ForwardDllName.Buffer + 1;
|
||
ForwardDllName.MaximumLength = ForwardDllName.Length;
|
||
|
||
if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&UnicodeString,
|
||
&ForwardDllName,
|
||
TRUE))) {
|
||
|
||
NextEntry = PsLoadedModuleList.Flink;
|
||
|
||
while (NextEntry != &PsLoadedModuleList) {
|
||
|
||
DataTableEntry = CONTAINING_RECORD(NextEntry,
|
||
LDR_DATA_TABLE_ENTRY,
|
||
InLoadOrderLinks);
|
||
|
||
//
|
||
// We have to do a case INSENSITIVE comparison for
|
||
// forwarder because the linker just took what is in the
|
||
// def file, as opposed to looking in the exporting
|
||
// image for the name.
|
||
// we alos use the prefix function to ignore the .exe or
|
||
// .sys or .dll at the end.
|
||
//
|
||
|
||
if (RtlPrefixString((PSTRING)&UnicodeString,
|
||
(PSTRING)&DataTableEntry->BaseDllName,
|
||
TRUE)) {
|
||
|
||
ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)
|
||
RtlImageDirectoryEntryToData(DataTableEntry->DllBase,
|
||
TRUE,
|
||
IMAGE_DIRECTORY_ENTRY_EXPORT,
|
||
&ExportSize);
|
||
|
||
if (ExportDirectory) {
|
||
|
||
IMAGE_THUNK_DATA thunkData;
|
||
PIMAGE_IMPORT_BY_NAME addressOfData;
|
||
ULONG length;
|
||
|
||
// one extra byte for NULL,
|
||
|
||
length = strlen(ForwardDllName.Buffer +
|
||
ForwardDllName.Length) + 1;
|
||
|
||
addressOfData = (PIMAGE_IMPORT_BY_NAME)
|
||
ExAllocatePoolWithTag (PagedPool,
|
||
length +
|
||
sizeof(IMAGE_IMPORT_BY_NAME),
|
||
' mM');
|
||
|
||
if (addressOfData) {
|
||
|
||
RtlCopyMemory(&(addressOfData->Name[0]),
|
||
ForwardDllName.Buffer +
|
||
ForwardDllName.Length,
|
||
length);
|
||
|
||
addressOfData->Hint = 0;
|
||
|
||
thunkData.u1.AddressOfData = addressOfData;
|
||
|
||
Status = MiSnapThunk(DataTableEntry->DllBase,
|
||
ImageBase,
|
||
&thunkData,
|
||
ExportDirectory,
|
||
ExportSize,
|
||
TRUE,
|
||
MissingProcedureName
|
||
);
|
||
|
||
ExFreePool(addressOfData);
|
||
|
||
Thunk->u1 = thunkData.u1;
|
||
}
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
NextEntry = NextEntry->Flink;
|
||
}
|
||
|
||
RtlFreeUnicodeString(&UnicodeString);
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
return Status;
|
||
}
|
||
#if 0
|
||
PVOID
|
||
MiLookupImageSectionByName (
|
||
IN PVOID Base,
|
||
IN BOOLEAN MappedAsImage,
|
||
IN PCHAR SectionName,
|
||
OUT PULONG SectionSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function locates a Directory Entry within the image header
|
||
and returns either the virtual address or seek address of the
|
||
data the Directory describes.
|
||
|
||
Arguments:
|
||
|
||
Base - Supplies the base of the image or data file.
|
||
|
||
MappedAsImage - FALSE if the file is mapped as a data file.
|
||
- TRUE if the file is mapped as an image.
|
||
|
||
SectionName - Supplies the name of the section to lookup.
|
||
|
||
SectionSize - Return the size of the section.
|
||
|
||
Return Value:
|
||
|
||
NULL - The file does not contain data for the specified section.
|
||
|
||
NON-NULL - Returns the address where the section is mapped in memory.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i, j, Match;
|
||
PIMAGE_NT_HEADERS NtHeaders;
|
||
PIMAGE_SECTION_HEADER NtSection;
|
||
|
||
NtHeaders = RtlImageNtHeader(Base);
|
||
NtSection = IMAGE_FIRST_SECTION( NtHeaders );
|
||
for (i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++) {
|
||
Match = TRUE;
|
||
for (j = 0; j < IMAGE_SIZEOF_SHORT_NAME; j++) {
|
||
if (SectionName[j] != NtSection->Name[j]) {
|
||
Match = FALSE;
|
||
break;
|
||
}
|
||
if (SectionName[j] == '\0') {
|
||
break;
|
||
}
|
||
}
|
||
if (Match) {
|
||
break;
|
||
}
|
||
NtSection += 1;
|
||
}
|
||
if (Match) {
|
||
*SectionSize = NtSection->SizeOfRawData;
|
||
if (MappedAsImage) {
|
||
return( (PVOID)((ULONG)Base + NtSection->VirtualAddress));
|
||
} else {
|
||
return( (PVOID)((ULONG)Base + NtSection->PointerToRawData));
|
||
}
|
||
}
|
||
return( NULL );
|
||
}
|
||
#endif //0
|
||
|
||
NTSTATUS
|
||
MmCheckSystemImage(
|
||
IN HANDLE ImageFileHandle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function ensures the checksum for a system image is correct.
|
||
data the Directory describes.
|
||
|
||
Arguments:
|
||
|
||
ImageFileHandle - Supplies the file handle of the image.
|
||
Return Value:
|
||
|
||
Status value.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NTSTATUS Status;
|
||
HANDLE Section;
|
||
PVOID ViewBase;
|
||
ULONG ViewSize;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
FILE_STANDARD_INFORMATION StandardInfo;
|
||
|
||
PAGED_CODE();
|
||
|
||
Status = NtCreateSection(
|
||
&Section,
|
||
SECTION_MAP_EXECUTE,
|
||
NULL,
|
||
NULL,
|
||
PAGE_EXECUTE,
|
||
SEC_COMMIT,
|
||
ImageFileHandle
|
||
);
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
return Status;
|
||
}
|
||
|
||
ViewBase = NULL;
|
||
ViewSize = 0;
|
||
|
||
Status = NtMapViewOfSection(
|
||
Section,
|
||
NtCurrentProcess(),
|
||
(PVOID *)&ViewBase,
|
||
0L,
|
||
0L,
|
||
NULL,
|
||
&ViewSize,
|
||
ViewShare,
|
||
0L,
|
||
PAGE_EXECUTE
|
||
);
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
NtClose(Section);
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// now the image is mapped as a data file... Calculate it's size and then
|
||
// check it's checksum
|
||
//
|
||
|
||
Status = NtQueryInformationFile(
|
||
ImageFileHandle,
|
||
&IoStatusBlock,
|
||
&StandardInfo,
|
||
sizeof(StandardInfo),
|
||
FileStandardInformation
|
||
);
|
||
|
||
if ( NT_SUCCESS(Status) ) {
|
||
|
||
try {
|
||
if (!LdrVerifyMappedImageMatchesChecksum(ViewBase,StandardInfo.EndOfFile.LowPart)) {
|
||
Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
|
||
}
|
||
#if !defined(NT_UP)
|
||
if ( !MmVerifyImageIsOkForMpUse(ViewBase) ) {
|
||
Status = STATUS_IMAGE_MP_UP_MISMATCH;
|
||
}
|
||
#endif // NT_UP
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
|
||
}
|
||
}
|
||
|
||
NtUnmapViewOfSection(NtCurrentProcess(),ViewBase);
|
||
NtClose(Section);
|
||
return Status;
|
||
}
|
||
|
||
#if !defined(NT_UP)
|
||
BOOLEAN
|
||
MmVerifyImageIsOkForMpUse(
|
||
IN PVOID BaseAddress
|
||
)
|
||
{
|
||
PIMAGE_NT_HEADERS NtHeaders;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// If the file is an image file, then subtract the two checksum words
|
||
// in the optional header from the computed checksum before adding
|
||
// the file length, and set the value of the header checksum.
|
||
//
|
||
|
||
NtHeaders = RtlImageNtHeader(BaseAddress);
|
||
if (NtHeaders != NULL) {
|
||
if ( KeNumberProcessors > 1 &&
|
||
(NtHeaders->FileHeader.Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY) ) {
|
||
return FALSE;
|
||
}
|
||
}
|
||
return TRUE;
|
||
}
|
||
#endif // NT_UP
|
||
|
||
|
||
ULONG
|
||
MiDeleteSystemPagableVm (
|
||
IN PMMPTE PointerPte,
|
||
IN ULONG NumberOfPtes,
|
||
IN ULONG NewPteValue,
|
||
OUT PULONG ResidentPages
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function deletes pageable system address space (paged pool
|
||
or driver pagable sections).
|
||
|
||
Arguments:
|
||
|
||
PointerPte - Supplies the start of the PTE range to delete.
|
||
|
||
NumberOfPtes - Supplies the number of PTEs in the range.
|
||
|
||
NewPteValue - Supplies the new value for the PTE.
|
||
|
||
ResidentPages - Returns the number of resident pages freed.
|
||
|
||
Return Value:
|
||
|
||
Returns the number of pages actually freed.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG PageFrameIndex;
|
||
MMPTE PteContents;
|
||
PMMPFN Pfn1;
|
||
ULONG ValidPages = 0;
|
||
ULONG PagesRequired = 0;
|
||
MMPTE NewContents;
|
||
ULONG WsIndex;
|
||
KIRQL OldIrql;
|
||
MMPTE_FLUSH_LIST PteFlushList;
|
||
|
||
ASSERT (KeGetCurrentIrql() <= APC_LEVEL);
|
||
|
||
PteFlushList.Count = 0;
|
||
NewContents.u.Long = NewPteValue;
|
||
while (NumberOfPtes != 0) {
|
||
PteContents = *PointerPte;
|
||
|
||
if (PteContents.u.Long != ZeroKernelPte.u.Long) {
|
||
|
||
if (PteContents.u.Hard.Valid == 1) {
|
||
|
||
LOCK_SYSTEM_WS (OldIrql)
|
||
|
||
PteContents = *(volatile MMPTE *)PointerPte;
|
||
if (PteContents.u.Hard.Valid == 0) {
|
||
UNLOCK_SYSTEM_WS (OldIrql);
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Delete the page.
|
||
//
|
||
|
||
PageFrameIndex = PteContents.u.Hard.PageFrameNumber;
|
||
|
||
//
|
||
// Set the pointer to PTE as empty so the page
|
||
// is deleted when the reference count goes to zero.
|
||
//
|
||
|
||
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
|
||
|
||
//
|
||
// Check to see if this is a pagable page in which
|
||
// case it needs to be removed from the working set list.
|
||
//
|
||
|
||
WsIndex = Pfn1->u1.WsIndex;
|
||
if (WsIndex == 0) {
|
||
ValidPages += 1;
|
||
} else {
|
||
MiRemoveWsle (WsIndex,
|
||
MmSystemCacheWorkingSetList );
|
||
MiReleaseWsle (WsIndex, &MmSystemCacheWs);
|
||
}
|
||
UNLOCK_SYSTEM_WS (OldIrql);
|
||
|
||
LOCK_PFN (OldIrql);
|
||
#if DBG
|
||
if ((Pfn1->u3.e2.ReferenceCount > 1) &&
|
||
(Pfn1->u3.e1.WriteInProgress == 0)) {
|
||
DbgPrint ("MM:SYSLOAD - deleting pool locked for I/O %lx\n",
|
||
PageFrameIndex);
|
||
ASSERT (Pfn1->u3.e2.ReferenceCount == 1);
|
||
}
|
||
#endif //DBG
|
||
MiDecrementShareAndValidCount (Pfn1->PteFrame);
|
||
MI_SET_PFN_DELETED (Pfn1);
|
||
MiDecrementShareCountOnly (PageFrameIndex);
|
||
*PointerPte = NewContents;
|
||
UNLOCK_PFN (OldIrql);
|
||
|
||
//
|
||
// Flush the TB for this page.
|
||
//
|
||
|
||
if (PteFlushList.Count != MM_MAXIMUM_FLUSH_COUNT) {
|
||
PteFlushList.FlushPte[PteFlushList.Count] = PointerPte;
|
||
PteFlushList.FlushVa[PteFlushList.Count] =
|
||
MiGetVirtualAddressMappedByPte (PointerPte);
|
||
PteFlushList.Count += 1;
|
||
}
|
||
|
||
} else if (PteContents.u.Soft.Transition == 1) {
|
||
|
||
LOCK_PFN (OldIrql);
|
||
|
||
PteContents = *(volatile MMPTE *)PointerPte;
|
||
|
||
if (PteContents.u.Soft.Transition == 0) {
|
||
UNLOCK_PFN (OldIrql);
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Transition, release page.
|
||
//
|
||
|
||
PageFrameIndex = PteContents.u.Trans.PageFrameNumber;
|
||
|
||
//
|
||
// Set the pointer to PTE as empty so the page
|
||
// is deleted when the reference count goes to zero.
|
||
//
|
||
|
||
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
|
||
|
||
MI_SET_PFN_DELETED (Pfn1);
|
||
|
||
MiDecrementShareCount (Pfn1->PteFrame);
|
||
|
||
//
|
||
// Check the reference count for the page, if the reference
|
||
// count is zero, move the page to the free list, if the
|
||
// reference count is not zero, ignore this page. When the
|
||
// refernce count goes to zero, it will be placed on the
|
||
// free list.
|
||
//
|
||
|
||
if (Pfn1->u3.e2.ReferenceCount == 0) {
|
||
MiUnlinkPageFromList (Pfn1);
|
||
MiReleasePageFileSpace (Pfn1->OriginalPte);
|
||
MiInsertPageInList (MmPageLocationList[FreePageList],
|
||
PageFrameIndex);
|
||
}
|
||
#if DBG
|
||
if ((Pfn1->u3.e2.ReferenceCount > 1) &&
|
||
(Pfn1->u3.e1.WriteInProgress == 0)) {
|
||
DbgPrint ("MM:SYSLOAD - deleting pool locked for I/O %lx\n",
|
||
PageFrameIndex);
|
||
DbgBreakPoint();
|
||
}
|
||
#endif //DBG
|
||
|
||
*PointerPte = NewContents;
|
||
UNLOCK_PFN (OldIrql);
|
||
} else {
|
||
|
||
//
|
||
// Demand zero, release page file space.
|
||
//
|
||
if (PteContents.u.Soft.PageFileHigh != 0) {
|
||
LOCK_PFN (OldIrql);
|
||
MiReleasePageFileSpace (PteContents);
|
||
UNLOCK_PFN (OldIrql);
|
||
}
|
||
|
||
*PointerPte = NewContents;
|
||
}
|
||
|
||
PagesRequired += 1;
|
||
}
|
||
|
||
NumberOfPtes -= 1;
|
||
PointerPte += 1;
|
||
}
|
||
LOCK_PFN (OldIrql);
|
||
MiFlushPteList (&PteFlushList, TRUE, NewContents);
|
||
UNLOCK_PFN (OldIrql);
|
||
|
||
*ResidentPages = ValidPages;
|
||
return PagesRequired;
|
||
}
|
||
|
||
VOID
|
||
MiSetImageProtectWrite (
|
||
IN PSEGMENT Segment
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function sets the protection of all prototype PTEs to writable.
|
||
|
||
Arguments:
|
||
|
||
Segment - a pointer to the segment to protect.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PMMPTE PointerPte;
|
||
PMMPTE LastPte;
|
||
MMPTE PteContents;
|
||
|
||
PointerPte = Segment->PrototypePte;
|
||
LastPte = PointerPte + Segment->NonExtendedPtes;
|
||
|
||
do {
|
||
PteContents = *PointerPte;
|
||
ASSERT (PteContents.u.Hard.Valid == 0);
|
||
if (PteContents.u.Long != ZeroPte.u.Long) {
|
||
if ((PteContents.u.Soft.Prototype == 0) &&
|
||
(PteContents.u.Soft.Transition == 1)) {
|
||
if (MiSetProtectionOnTransitionPte (PointerPte,
|
||
MM_EXECUTE_READWRITE)) {
|
||
continue;
|
||
}
|
||
} else {
|
||
PointerPte->u.Soft.Protection = MM_EXECUTE_READWRITE;
|
||
}
|
||
}
|
||
PointerPte += 1;
|
||
} while (PointerPte < LastPte );
|
||
return;
|
||
}
|