mirror of
https://github.com/reactos/reactos.git
synced 2024-08-02 01:21:00 +00:00
- Fix MmCreateImageSection to use previous mode instead of hardcoding UserMode.
- Implement MiProcessLoaderEntry for adding/removing entries on the PsLoadedModuleList. - Move MmLoadSystemImage from loader.c to sysldr.c - Update MmLoadSystemImage: - Use MmSystemLoadLock. - Support returning the entry for an already-loaded image, instead of loading it twice. - Use Section APIs to map the image... we're still doing a dirty ZwReadFile hack, but at least now we can depend on the PE code to validate the image (so removed the hardcoded validation). - Add more generic cleanup got so we can just jump to it. - Add more stub code and detection code for upcoming features. svn path=/trunk/; revision=25883
This commit is contained in:
parent
736a4de31f
commit
3111dda705
|
@ -20,15 +20,6 @@
|
||||||
|
|
||||||
/* FUNCTIONS *****************************************************************/
|
/* FUNCTIONS *****************************************************************/
|
||||||
|
|
||||||
NTSTATUS
|
|
||||||
NTAPI
|
|
||||||
MiResolveImageReferences(IN PVOID ImageBase,
|
|
||||||
IN PUNICODE_STRING ImageFileDirectory,
|
|
||||||
IN PUNICODE_STRING NamePrefix OPTIONAL,
|
|
||||||
OUT PCHAR *MissingApi,
|
|
||||||
OUT PWCHAR *MissingDriver,
|
|
||||||
OUT PLOAD_IMPORTS *LoadImports);
|
|
||||||
|
|
||||||
static LONG
|
static LONG
|
||||||
LdrpCompareModuleNames (
|
LdrpCompareModuleNames (
|
||||||
IN PUNICODE_STRING String1,
|
IN PUNICODE_STRING String1,
|
||||||
|
@ -168,372 +159,4 @@ LdrUnloadModule ( PLDR_DATA_TABLE_ENTRY ModuleObject )
|
||||||
return(STATUS_SUCCESS);
|
return(STATUS_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Used by NtLoadDriver/IoMgr
|
|
||||||
//
|
|
||||||
NTSTATUS
|
|
||||||
NTAPI
|
|
||||||
MmLoadSystemImage(IN PUNICODE_STRING FileName,
|
|
||||||
IN PUNICODE_STRING NamePrefix OPTIONAL,
|
|
||||||
IN PUNICODE_STRING LoadedName OPTIONAL,
|
|
||||||
IN ULONG Flags,
|
|
||||||
OUT PVOID *ModuleObject,
|
|
||||||
OUT PVOID *ImageBaseAddress)
|
|
||||||
{
|
|
||||||
PVOID ModuleLoadBase;
|
|
||||||
NTSTATUS Status;
|
|
||||||
HANDLE FileHandle;
|
|
||||||
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
||||||
PLDR_DATA_TABLE_ENTRY Module;
|
|
||||||
FILE_STANDARD_INFORMATION FileStdInfo;
|
|
||||||
IO_STATUS_BLOCK IoStatusBlock;
|
|
||||||
unsigned int DriverSize, Idx;
|
|
||||||
ULONG CurrentSize;
|
|
||||||
PVOID DriverBase;
|
|
||||||
PIMAGE_DOS_HEADER PEDosHeader;
|
|
||||||
PIMAGE_NT_HEADERS PENtHeaders;
|
|
||||||
PIMAGE_SECTION_HEADER PESectionHeaders;
|
|
||||||
KIRQL Irql;
|
|
||||||
PIMAGE_NT_HEADERS NtHeader;
|
|
||||||
UNICODE_STRING BaseName, BaseDirectory, PrefixName;
|
|
||||||
PLDR_DATA_TABLE_ENTRY LdrEntry;
|
|
||||||
ULONG EntrySize;
|
|
||||||
PLOAD_IMPORTS LoadedImports = (PVOID)-2;
|
|
||||||
PCHAR MissingApiName, Buffer;
|
|
||||||
PWCHAR MissingDriverName;
|
|
||||||
PAGED_CODE();
|
|
||||||
|
|
||||||
/* Detect session-load */
|
|
||||||
if (Flags)
|
|
||||||
{
|
|
||||||
/* Sanity checks */
|
|
||||||
ASSERT(NamePrefix == NULL);
|
|
||||||
ASSERT(LoadedName == NULL);
|
|
||||||
|
|
||||||
/* Make sure the process is in session too */
|
|
||||||
if (!PsGetCurrentProcess()->ProcessInSession) return STATUS_NO_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ModuleObject) *ModuleObject = NULL;
|
|
||||||
if (ImageBaseAddress) *ImageBaseAddress = NULL;
|
|
||||||
|
|
||||||
/* Allocate a buffer we'll use for names */
|
|
||||||
Buffer = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH, TAG_LDR_WSTR);
|
|
||||||
if (!Buffer)
|
|
||||||
{
|
|
||||||
/* Fail */
|
|
||||||
return STATUS_INSUFFICIENT_RESOURCES;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for a separator */
|
|
||||||
if (FileName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
|
|
||||||
{
|
|
||||||
PWCHAR p;
|
|
||||||
ULONG BaseLength;
|
|
||||||
|
|
||||||
/* Loop the path until we get to the base name */
|
|
||||||
p = &FileName->Buffer[FileName->Length / sizeof(WCHAR)];
|
|
||||||
while (*(p - 1) != OBJ_NAME_PATH_SEPARATOR) p--;
|
|
||||||
|
|
||||||
/* Get the length */
|
|
||||||
BaseLength = (ULONG)(&FileName->Buffer[FileName->Length / sizeof(WCHAR)] - p);
|
|
||||||
BaseLength *= sizeof(WCHAR);
|
|
||||||
|
|
||||||
/* Setup the string */
|
|
||||||
BaseName.Length = (USHORT)BaseLength;
|
|
||||||
BaseName.Buffer = p;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Otherwise, we already have a base name */
|
|
||||||
BaseName.Length = FileName->Length;
|
|
||||||
BaseName.Buffer = FileName->Buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Setup the maximum length */
|
|
||||||
BaseName.MaximumLength = BaseName.Length;
|
|
||||||
|
|
||||||
/* Now compute the base directory */
|
|
||||||
BaseDirectory = *FileName;
|
|
||||||
BaseDirectory.Length -= BaseName.Length;
|
|
||||||
BaseDirectory.MaximumLength = BaseDirectory.Length;
|
|
||||||
|
|
||||||
/* And the prefix, which for now is just the name itself */
|
|
||||||
PrefixName = *FileName;
|
|
||||||
|
|
||||||
/* Check if we have a prefix */
|
|
||||||
if (NamePrefix) DPRINT1("Prefixed images are not yet supported!\n");
|
|
||||||
|
|
||||||
/* Check if we already have a name, use it instead */
|
|
||||||
if (LoadedName) BaseName = *LoadedName;
|
|
||||||
|
|
||||||
/* Open the Module */
|
|
||||||
InitializeObjectAttributes(&ObjectAttributes,
|
|
||||||
FileName,
|
|
||||||
OBJ_CASE_INSENSITIVE,
|
|
||||||
NULL,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
Status = ZwOpenFile(&FileHandle,
|
|
||||||
GENERIC_READ,
|
|
||||||
&ObjectAttributes,
|
|
||||||
&IoStatusBlock,
|
|
||||||
FILE_SHARE_READ,
|
|
||||||
FILE_SYNCHRONOUS_IO_NONALERT);
|
|
||||||
|
|
||||||
if (!NT_SUCCESS(Status))
|
|
||||||
{
|
|
||||||
DPRINT("Could not open module file: %wZ (Status 0x%08lx)\n", FileName, Status);
|
|
||||||
return(Status);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Get the size of the file */
|
|
||||||
Status = ZwQueryInformationFile(FileHandle,
|
|
||||||
&IoStatusBlock,
|
|
||||||
&FileStdInfo,
|
|
||||||
sizeof(FileStdInfo),
|
|
||||||
FileStandardInformation);
|
|
||||||
if (!NT_SUCCESS(Status))
|
|
||||||
{
|
|
||||||
DPRINT("Could not get file size\n");
|
|
||||||
NtClose(FileHandle);
|
|
||||||
return(Status);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Allocate nonpageable memory for driver */
|
|
||||||
ModuleLoadBase = ExAllocatePoolWithTag(NonPagedPool,
|
|
||||||
FileStdInfo.EndOfFile.u.LowPart,
|
|
||||||
TAG_DRIVER_MEM);
|
|
||||||
if (ModuleLoadBase == NULL)
|
|
||||||
{
|
|
||||||
DPRINT("Could not allocate memory for module");
|
|
||||||
NtClose(FileHandle);
|
|
||||||
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Load driver into memory chunk */
|
|
||||||
Status = ZwReadFile(FileHandle,
|
|
||||||
0, 0, 0,
|
|
||||||
&IoStatusBlock,
|
|
||||||
ModuleLoadBase,
|
|
||||||
FileStdInfo.EndOfFile.u.LowPart,
|
|
||||||
0, 0);
|
|
||||||
if (!NT_SUCCESS(Status))
|
|
||||||
{
|
|
||||||
DPRINT("Could not read module file into memory");
|
|
||||||
ExFreePool(ModuleLoadBase);
|
|
||||||
NtClose(FileHandle);
|
|
||||||
return(Status);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ZwClose(FileHandle);
|
|
||||||
|
|
||||||
DPRINT("Processing PE Module at module base:%08lx\n", ModuleLoadBase);
|
|
||||||
|
|
||||||
/* Get header pointers */
|
|
||||||
PEDosHeader = (PIMAGE_DOS_HEADER) ModuleLoadBase;
|
|
||||||
PENtHeaders = RtlImageNtHeader(ModuleLoadBase);
|
|
||||||
NtHeader = PENtHeaders;
|
|
||||||
PESectionHeaders = IMAGE_FIRST_SECTION(PENtHeaders);
|
|
||||||
|
|
||||||
|
|
||||||
/* Check file magic numbers */
|
|
||||||
if (PEDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
|
|
||||||
{
|
|
||||||
DPRINT("Incorrect MZ magic: %04x\n", PEDosHeader->e_magic);
|
|
||||||
return STATUS_UNSUCCESSFUL;
|
|
||||||
}
|
|
||||||
if (PEDosHeader->e_lfanew == 0)
|
|
||||||
{
|
|
||||||
DPRINT("Invalid lfanew offset: %08x\n", PEDosHeader->e_lfanew);
|
|
||||||
return STATUS_UNSUCCESSFUL;
|
|
||||||
}
|
|
||||||
if (PENtHeaders->Signature != IMAGE_NT_SIGNATURE)
|
|
||||||
{
|
|
||||||
DPRINT("Incorrect PE magic: %08x\n", PENtHeaders->Signature);
|
|
||||||
return STATUS_UNSUCCESSFUL;
|
|
||||||
}
|
|
||||||
if (PENtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_I386)
|
|
||||||
{
|
|
||||||
DPRINT("Incorrect Architechture: %04x\n", PENtHeaders->FileHeader.Machine);
|
|
||||||
return STATUS_UNSUCCESSFUL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* FIXME: if image is fixed-address load, then fail */
|
|
||||||
|
|
||||||
/* FIXME: check/verify OS version number */
|
|
||||||
|
|
||||||
DPRINT("OptionalHdrMagic:%04x LinkVersion:%d.%d\n",
|
|
||||||
PENtHeaders->OptionalHeader.Magic,
|
|
||||||
PENtHeaders->OptionalHeader.MajorLinkerVersion,
|
|
||||||
PENtHeaders->OptionalHeader.MinorLinkerVersion);
|
|
||||||
DPRINT("Entry Point:%08lx\n", PENtHeaders->OptionalHeader.AddressOfEntryPoint);
|
|
||||||
|
|
||||||
/* Determine the size of the module */
|
|
||||||
DriverSize = 0;
|
|
||||||
for (Idx = 0; Idx < PENtHeaders->FileHeader.NumberOfSections; Idx++)
|
|
||||||
{
|
|
||||||
if (!(PESectionHeaders[Idx].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
|
|
||||||
{
|
|
||||||
CurrentSize = PESectionHeaders[Idx].VirtualAddress + PESectionHeaders[Idx].Misc.VirtualSize;
|
|
||||||
DriverSize = max(DriverSize, CurrentSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DriverSize = ROUND_UP(DriverSize, PENtHeaders->OptionalHeader.SectionAlignment);
|
|
||||||
DPRINT("DriverSize %x, SizeOfImage %x\n",DriverSize, PENtHeaders->OptionalHeader.SizeOfImage);
|
|
||||||
|
|
||||||
/* Allocate a virtual section for the module */
|
|
||||||
DriverBase = NULL;
|
|
||||||
DriverBase = MmAllocateSection(DriverSize, DriverBase);
|
|
||||||
if (DriverBase == 0)
|
|
||||||
{
|
|
||||||
DPRINT("Failed to allocate a virtual section for driver\n");
|
|
||||||
return STATUS_UNSUCCESSFUL;
|
|
||||||
}
|
|
||||||
DPRINT("DriverBase for %wZ: %x\n", FileName, DriverBase);
|
|
||||||
|
|
||||||
/* Copy headers over */
|
|
||||||
memcpy(DriverBase, ModuleLoadBase, PENtHeaders->OptionalHeader.SizeOfHeaders);
|
|
||||||
|
|
||||||
/* Copy image sections into virtual section */
|
|
||||||
for (Idx = 0; Idx < PENtHeaders->FileHeader.NumberOfSections; Idx++)
|
|
||||||
{
|
|
||||||
CurrentSize = PESectionHeaders[Idx].VirtualAddress + PESectionHeaders[Idx].Misc.VirtualSize;
|
|
||||||
/* Copy current section into current offset of virtual section */
|
|
||||||
if (CurrentSize <= DriverSize &&
|
|
||||||
PESectionHeaders[Idx].SizeOfRawData)
|
|
||||||
{
|
|
||||||
DPRINT("PESectionHeaders[Idx].VirtualAddress + DriverBase %x\n",
|
|
||||||
PESectionHeaders[Idx].VirtualAddress + (ULONG_PTR)DriverBase);
|
|
||||||
memcpy((PVOID)((ULONG_PTR)DriverBase + PESectionHeaders[Idx].VirtualAddress),
|
|
||||||
(PVOID)((ULONG_PTR)ModuleLoadBase + PESectionHeaders[Idx].PointerToRawData),
|
|
||||||
PESectionHeaders[Idx].Misc.VirtualSize > PESectionHeaders[Idx].SizeOfRawData
|
|
||||||
? PESectionHeaders[Idx].SizeOfRawData : PESectionHeaders[Idx].Misc.VirtualSize );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Relocate the driver */
|
|
||||||
Status = LdrRelocateImageWithBias(DriverBase,
|
|
||||||
0,
|
|
||||||
"SYSLDR",
|
|
||||||
STATUS_SUCCESS,
|
|
||||||
STATUS_CONFLICTING_ADDRESSES,
|
|
||||||
STATUS_INVALID_IMAGE_FORMAT);
|
|
||||||
if (!NT_SUCCESS(Status))
|
|
||||||
{
|
|
||||||
/* Fail */
|
|
||||||
return Status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calculate the size we'll need for the entry and allocate it */
|
|
||||||
EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
|
|
||||||
BaseName.Length +
|
|
||||||
sizeof(UNICODE_NULL);
|
|
||||||
|
|
||||||
/* Allocate the entry */
|
|
||||||
LdrEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_MODULE_OBJECT);
|
|
||||||
if (!LdrEntry)
|
|
||||||
{
|
|
||||||
/* Fail */
|
|
||||||
return STATUS_INSUFFICIENT_RESOURCES;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Setup the entry */
|
|
||||||
LdrEntry->Flags = LDRP_LOAD_IN_PROGRESS;
|
|
||||||
LdrEntry->LoadCount = 1;
|
|
||||||
LdrEntry->LoadedImports = LoadedImports;
|
|
||||||
LdrEntry->PatchInformation = NULL;
|
|
||||||
|
|
||||||
/* Check the version */
|
|
||||||
if ((NtHeader->OptionalHeader.MajorOperatingSystemVersion >= 5) &&
|
|
||||||
(NtHeader->OptionalHeader.MajorImageVersion >= 5))
|
|
||||||
{
|
|
||||||
/* Mark this image as a native image */
|
|
||||||
LdrEntry->Flags |= 0x80000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Setup the rest of the entry */
|
|
||||||
LdrEntry->DllBase = DriverBase;
|
|
||||||
LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)DriverBase +
|
|
||||||
NtHeader->OptionalHeader.AddressOfEntryPoint);
|
|
||||||
LdrEntry->SizeOfImage = DriverSize;
|
|
||||||
LdrEntry->CheckSum = NtHeader->OptionalHeader.CheckSum;
|
|
||||||
LdrEntry->SectionPointer = LdrEntry;
|
|
||||||
|
|
||||||
/* Now write the DLL name */
|
|
||||||
LdrEntry->BaseDllName.Buffer = (PVOID)(LdrEntry + 1);
|
|
||||||
LdrEntry->BaseDllName.Length = BaseName.Length;
|
|
||||||
LdrEntry->BaseDllName.MaximumLength = BaseName.Length;
|
|
||||||
|
|
||||||
/* Copy and null-terminate it */
|
|
||||||
RtlCopyMemory(LdrEntry->BaseDllName.Buffer,
|
|
||||||
BaseName.Buffer,
|
|
||||||
BaseName.Length);
|
|
||||||
LdrEntry->BaseDllName.Buffer[BaseName.Length / 2] = UNICODE_NULL;
|
|
||||||
|
|
||||||
/* Now allocate the full name */
|
|
||||||
LdrEntry->FullDllName.Buffer = ExAllocatePoolWithTag(PagedPool,
|
|
||||||
PrefixName.Length +
|
|
||||||
sizeof(UNICODE_NULL),
|
|
||||||
TAG_LDR_WSTR);
|
|
||||||
if (!LdrEntry->FullDllName.Buffer)
|
|
||||||
{
|
|
||||||
/* Don't fail, just set it to zero */
|
|
||||||
LdrEntry->FullDllName.Length = 0;
|
|
||||||
LdrEntry->FullDllName.MaximumLength = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Set it up */
|
|
||||||
LdrEntry->FullDllName.Length = PrefixName.Length;
|
|
||||||
LdrEntry->FullDllName.MaximumLength = PrefixName.Length;
|
|
||||||
|
|
||||||
/* Copy and null-terminate */
|
|
||||||
RtlCopyMemory(LdrEntry->FullDllName.Buffer,
|
|
||||||
PrefixName.Buffer,
|
|
||||||
PrefixName.Length);
|
|
||||||
LdrEntry->FullDllName.Buffer[PrefixName.Length / 2] = UNICODE_NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Insert the entry */
|
|
||||||
KeAcquireSpinLock(&PsLoadedModuleSpinLock, &Irql);
|
|
||||||
InsertTailList(&PsLoadedModuleList, &LdrEntry->InLoadOrderLinks);
|
|
||||||
KeReleaseSpinLock(&PsLoadedModuleSpinLock, Irql);
|
|
||||||
|
|
||||||
/* Resolve imports */
|
|
||||||
MissingApiName = Buffer;
|
|
||||||
Status = MiResolveImageReferences(DriverBase,
|
|
||||||
&BaseDirectory,
|
|
||||||
NULL,
|
|
||||||
&MissingApiName,
|
|
||||||
&MissingDriverName,
|
|
||||||
&LoadedImports);
|
|
||||||
if (!NT_SUCCESS(Status))
|
|
||||||
{
|
|
||||||
/* Fail */
|
|
||||||
ExFreePool(LdrEntry->FullDllName.Buffer);
|
|
||||||
ExFreePool(LdrEntry);
|
|
||||||
return Status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return */
|
|
||||||
Module = LdrEntry;
|
|
||||||
|
|
||||||
/* Cleanup */
|
|
||||||
ExFreePool(ModuleLoadBase);
|
|
||||||
|
|
||||||
if (ModuleObject) *ModuleObject = Module;
|
|
||||||
if (ImageBaseAddress) *ImageBaseAddress = Module->DllBase;
|
|
||||||
|
|
||||||
/* Hook for KDB on loading a driver. */
|
|
||||||
KDB_LOADDRIVER_HOOK(FileName, Module);
|
|
||||||
|
|
||||||
return(STATUS_SUCCESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* EOF */
|
/* EOF */
|
||||||
|
|
|
@ -3292,7 +3292,7 @@ MmCreateImageSection(PROS_SECTION_OBJECT *SectionObject,
|
||||||
Status = ObReferenceObjectByHandle(FileHandle,
|
Status = ObReferenceObjectByHandle(FileHandle,
|
||||||
FileAccess,
|
FileAccess,
|
||||||
IoFileObjectType,
|
IoFileObjectType,
|
||||||
UserMode,
|
ExGetPreviousMode(),
|
||||||
(PVOID*)(PVOID)&FileObject,
|
(PVOID*)(PVOID)&FileObject,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,24 @@ KMUTANT MmSystemLoadLock;
|
||||||
|
|
||||||
/* FUNCTIONS *****************************************************************/
|
/* FUNCTIONS *****************************************************************/
|
||||||
|
|
||||||
|
VOID
|
||||||
|
NTAPI
|
||||||
|
MiProcessLoaderEntry(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
|
||||||
|
IN BOOLEAN Insert)
|
||||||
|
{
|
||||||
|
KIRQL OldIrql;
|
||||||
|
|
||||||
|
/* Acquire the lock */
|
||||||
|
KeAcquireSpinLock(&PsLoadedModuleSpinLock, &OldIrql);
|
||||||
|
|
||||||
|
/* Insert or remove from the list */
|
||||||
|
Insert ? InsertTailList(&PsLoadedModuleList, &LdrEntry->InLoadOrderLinks) :
|
||||||
|
RemoveEntryList(&LdrEntry->InLoadOrderLinks);
|
||||||
|
|
||||||
|
/* Release the lock */
|
||||||
|
KeReleaseSpinLock(&PsLoadedModuleSpinLock, OldIrql);
|
||||||
|
}
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
NTAPI
|
NTAPI
|
||||||
MiUpdateThunks(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
|
MiUpdateThunks(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
|
||||||
|
@ -1019,3 +1037,469 @@ MmCheckSystemImage(IN HANDLE ImageHandle,
|
||||||
ZwClose(SectionHandle);
|
ZwClose(SectionHandle);
|
||||||
return Status;
|
return Status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NTSTATUS
|
||||||
|
NTAPI
|
||||||
|
MmLoadSystemImage(IN PUNICODE_STRING FileName,
|
||||||
|
IN PUNICODE_STRING NamePrefix OPTIONAL,
|
||||||
|
IN PUNICODE_STRING LoadedName OPTIONAL,
|
||||||
|
IN ULONG Flags,
|
||||||
|
OUT PVOID *ModuleObject,
|
||||||
|
OUT PVOID *ImageBaseAddress)
|
||||||
|
{
|
||||||
|
PVOID ModuleLoadBase = NULL;
|
||||||
|
NTSTATUS Status;
|
||||||
|
HANDLE FileHandle = NULL;
|
||||||
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
||||||
|
FILE_STANDARD_INFORMATION FileStdInfo;
|
||||||
|
IO_STATUS_BLOCK IoStatusBlock;
|
||||||
|
ULONG DriverSize = 0, Size, i;
|
||||||
|
PVOID DriverBase;
|
||||||
|
PIMAGE_SECTION_HEADER SectionHeaders;
|
||||||
|
PIMAGE_NT_HEADERS NtHeader;
|
||||||
|
UNICODE_STRING BaseName, BaseDirectory, PrefixName;
|
||||||
|
PLDR_DATA_TABLE_ENTRY LdrEntry = NULL;
|
||||||
|
ULONG EntrySize;
|
||||||
|
PLOAD_IMPORTS LoadedImports = (PVOID)-2;
|
||||||
|
PCHAR MissingApiName, Buffer;
|
||||||
|
PWCHAR MissingDriverName;
|
||||||
|
HANDLE SectionHandle;
|
||||||
|
ACCESS_MASK DesiredAccess;
|
||||||
|
PVOID Section = NULL;
|
||||||
|
BOOLEAN LockOwned = FALSE;
|
||||||
|
PLIST_ENTRY NextEntry;
|
||||||
|
PAGED_CODE();
|
||||||
|
|
||||||
|
/* Detect session-load */
|
||||||
|
if (Flags)
|
||||||
|
{
|
||||||
|
/* Sanity checks */
|
||||||
|
ASSERT(NamePrefix == NULL);
|
||||||
|
ASSERT(LoadedName == NULL);
|
||||||
|
|
||||||
|
/* Make sure the process is in session too */
|
||||||
|
if (!PsGetCurrentProcess()->ProcessInSession) return STATUS_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ModuleObject) *ModuleObject = NULL;
|
||||||
|
if (ImageBaseAddress) *ImageBaseAddress = NULL;
|
||||||
|
|
||||||
|
/* Allocate a buffer we'll use for names */
|
||||||
|
Buffer = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH, TAG_LDR_WSTR);
|
||||||
|
if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES;
|
||||||
|
|
||||||
|
/* Check for a separator */
|
||||||
|
if (FileName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
|
||||||
|
{
|
||||||
|
PWCHAR p;
|
||||||
|
ULONG BaseLength;
|
||||||
|
|
||||||
|
/* Loop the path until we get to the base name */
|
||||||
|
p = &FileName->Buffer[FileName->Length / sizeof(WCHAR)];
|
||||||
|
while (*(p - 1) != OBJ_NAME_PATH_SEPARATOR) p--;
|
||||||
|
|
||||||
|
/* Get the length */
|
||||||
|
BaseLength = (ULONG)(&FileName->Buffer[FileName->Length / sizeof(WCHAR)] - p);
|
||||||
|
BaseLength *= sizeof(WCHAR);
|
||||||
|
|
||||||
|
/* Setup the string */
|
||||||
|
BaseName.Length = (USHORT)BaseLength;
|
||||||
|
BaseName.Buffer = p;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Otherwise, we already have a base name */
|
||||||
|
BaseName.Length = FileName->Length;
|
||||||
|
BaseName.Buffer = FileName->Buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup the maximum length */
|
||||||
|
BaseName.MaximumLength = BaseName.Length;
|
||||||
|
|
||||||
|
/* Now compute the base directory */
|
||||||
|
BaseDirectory = *FileName;
|
||||||
|
BaseDirectory.Length -= BaseName.Length;
|
||||||
|
BaseDirectory.MaximumLength = BaseDirectory.Length;
|
||||||
|
|
||||||
|
/* And the prefix, which for now is just the name itself */
|
||||||
|
PrefixName = *FileName;
|
||||||
|
|
||||||
|
/* Check if we have a prefix */
|
||||||
|
if (NamePrefix) DPRINT1("Prefixed images are not yet supported!\n");
|
||||||
|
|
||||||
|
/* Check if we already have a name, use it instead */
|
||||||
|
if (LoadedName) BaseName = *LoadedName;
|
||||||
|
|
||||||
|
/* Acquire the load lock */
|
||||||
|
LoaderScan:
|
||||||
|
ASSERT(LockOwned == FALSE);
|
||||||
|
LockOwned = TRUE;
|
||||||
|
KeEnterCriticalRegion();
|
||||||
|
KeWaitForSingleObject(&MmSystemLoadLock,
|
||||||
|
WrVirtualMemory,
|
||||||
|
KernelMode,
|
||||||
|
FALSE,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/* Scan the module list */
|
||||||
|
NextEntry = PsLoadedModuleList.Flink;
|
||||||
|
while (NextEntry != &PsLoadedModuleList)
|
||||||
|
{
|
||||||
|
/* Get the entry and compare the names */
|
||||||
|
LdrEntry = CONTAINING_RECORD(NextEntry,
|
||||||
|
LDR_DATA_TABLE_ENTRY,
|
||||||
|
InLoadOrderLinks);
|
||||||
|
if (RtlEqualUnicodeString(&PrefixName, &LdrEntry->FullDllName, TRUE))
|
||||||
|
{
|
||||||
|
/* Found it, break out */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keep scanning */
|
||||||
|
NextEntry = NextEntry->Flink;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we found the image */
|
||||||
|
if (NextEntry != &PsLoadedModuleList)
|
||||||
|
{
|
||||||
|
/* Check if we had already mapped a section */
|
||||||
|
if (Section)
|
||||||
|
{
|
||||||
|
/* Dereference and clear */
|
||||||
|
ObDereferenceObject(Section);
|
||||||
|
Section = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if this was supposed to be a session load */
|
||||||
|
if (!Flags)
|
||||||
|
{
|
||||||
|
/* It wasn't, so just return the data */
|
||||||
|
*ModuleObject = LdrEntry;
|
||||||
|
*ImageBaseAddress = LdrEntry->DllBase;
|
||||||
|
Status = STATUS_IMAGE_ALREADY_LOADED;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* We don't support session loading yet */
|
||||||
|
DPRINT1("Unsupported Session-Load!\n");
|
||||||
|
while (TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do cleanup */
|
||||||
|
goto Quickie;
|
||||||
|
}
|
||||||
|
else if (!Section)
|
||||||
|
{
|
||||||
|
/* It wasn't loaded, and we didn't have a previous attempt */
|
||||||
|
KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
|
||||||
|
KeLeaveCriticalRegion();
|
||||||
|
LockOwned = FALSE;
|
||||||
|
|
||||||
|
/* Check if KD is enabled */
|
||||||
|
if ((KdDebuggerEnabled) && !(KdDebuggerNotPresent))
|
||||||
|
{
|
||||||
|
/* FIXME: Attempt to get image from KD */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We don't have a valid entry */
|
||||||
|
LdrEntry = NULL;
|
||||||
|
|
||||||
|
/* Setup image attributes */
|
||||||
|
InitializeObjectAttributes(&ObjectAttributes,
|
||||||
|
FileName,
|
||||||
|
OBJ_CASE_INSENSITIVE,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/* Open the image */
|
||||||
|
Status = ZwOpenFile(&FileHandle,
|
||||||
|
FILE_EXECUTE,
|
||||||
|
&ObjectAttributes,
|
||||||
|
&IoStatusBlock,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_DELETE,
|
||||||
|
FILE_SYNCHRONOUS_IO_NONALERT);
|
||||||
|
if (!NT_SUCCESS(Status)) goto Quickie;
|
||||||
|
|
||||||
|
/* Validate it */
|
||||||
|
Status = MmCheckSystemImage(FileHandle, FALSE);
|
||||||
|
if ((Status == STATUS_IMAGE_CHECKSUM_MISMATCH) ||
|
||||||
|
(Status == STATUS_IMAGE_MP_UP_MISMATCH) ||
|
||||||
|
(Status == STATUS_INVALID_IMAGE_FORMAT))
|
||||||
|
{
|
||||||
|
/* Fail loading */
|
||||||
|
goto Quickie;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get image size */
|
||||||
|
Status = ZwQueryInformationFile(FileHandle,
|
||||||
|
&IoStatusBlock,
|
||||||
|
&FileStdInfo,
|
||||||
|
sizeof(FileStdInfo),
|
||||||
|
FileStandardInformation);
|
||||||
|
if (!NT_SUCCESS(Status)) goto Quickie;
|
||||||
|
|
||||||
|
/* Allocate memory for image */
|
||||||
|
ModuleLoadBase = ExAllocatePoolWithTag(NonPagedPool,
|
||||||
|
FileStdInfo.EndOfFile.u.LowPart,
|
||||||
|
TAG_DRIVER_MEM);
|
||||||
|
|
||||||
|
/* Read the image */
|
||||||
|
Status = ZwReadFile(FileHandle,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
&IoStatusBlock,
|
||||||
|
ModuleLoadBase,
|
||||||
|
FileStdInfo.EndOfFile.u.LowPart,
|
||||||
|
0,
|
||||||
|
0);
|
||||||
|
if (!NT_SUCCESS(Status)) goto Quickie;
|
||||||
|
|
||||||
|
/* Check if this is a session-load */
|
||||||
|
if (Flags)
|
||||||
|
{
|
||||||
|
/* Then we only need read and execute */
|
||||||
|
DesiredAccess = SECTION_MAP_READ | SECTION_MAP_EXECUTE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Otherwise, we can allow write access */
|
||||||
|
DesiredAccess = SECTION_ALL_ACCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize the attributes for the section */
|
||||||
|
InitializeObjectAttributes(&ObjectAttributes,
|
||||||
|
NULL,
|
||||||
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/* Create the section */
|
||||||
|
Status = ZwCreateSection(&SectionHandle,
|
||||||
|
DesiredAccess,
|
||||||
|
&ObjectAttributes,
|
||||||
|
NULL,
|
||||||
|
PAGE_EXECUTE,
|
||||||
|
SEC_IMAGE,
|
||||||
|
FileHandle);
|
||||||
|
if (!NT_SUCCESS(Status)) goto Quickie;
|
||||||
|
|
||||||
|
/* Now get the section pointer */
|
||||||
|
Status = ObReferenceObjectByHandle(SectionHandle,
|
||||||
|
SECTION_MAP_EXECUTE,
|
||||||
|
MmSectionObjectType,
|
||||||
|
KernelMode,
|
||||||
|
&Section,
|
||||||
|
NULL);
|
||||||
|
ZwClose(SectionHandle);
|
||||||
|
if (!NT_SUCCESS(Status)) goto Quickie;
|
||||||
|
|
||||||
|
/* Check if this was supposed to be a session-load */
|
||||||
|
if (Flags)
|
||||||
|
{
|
||||||
|
/* We don't support session loading yet */
|
||||||
|
DPRINT1("Unsupported Session-Load!\n");
|
||||||
|
while (TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check the loader list again, we should end up in the path below */
|
||||||
|
goto LoaderScan;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* We don't have a valid entry */
|
||||||
|
LdrEntry = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We should have a valid module base */
|
||||||
|
ASSERT(ModuleLoadBase != NULL);
|
||||||
|
|
||||||
|
/* Get header pointers */
|
||||||
|
NtHeader = RtlImageNtHeader(ModuleLoadBase);
|
||||||
|
|
||||||
|
/* Get header pointers */
|
||||||
|
SectionHeaders = IMAGE_FIRST_SECTION(NtHeader);
|
||||||
|
|
||||||
|
/* Determine the size of the module */
|
||||||
|
for (i = 0; i < NtHeader->FileHeader.NumberOfSections; i++)
|
||||||
|
{
|
||||||
|
/* Skip this section if we're not supposed to load it */
|
||||||
|
if (!(SectionHeaders[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
|
||||||
|
{
|
||||||
|
/* Add the size of this section into the total size */
|
||||||
|
Size = SectionHeaders[i].VirtualAddress +
|
||||||
|
SectionHeaders[i].Misc.VirtualSize;
|
||||||
|
DriverSize = max(DriverSize, Size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Round up the driver size to section alignment */
|
||||||
|
DriverSize = ROUND_UP(DriverSize, NtHeader->OptionalHeader.SectionAlignment);
|
||||||
|
|
||||||
|
/* Allocate a virtual section for the module */
|
||||||
|
DriverBase = MmAllocateSection(DriverSize, NULL);
|
||||||
|
|
||||||
|
/* Copy headers over */
|
||||||
|
RtlCopyMemory(DriverBase,
|
||||||
|
ModuleLoadBase,
|
||||||
|
NtHeader->OptionalHeader.SizeOfHeaders);
|
||||||
|
|
||||||
|
/* Copy image sections into virtual section */
|
||||||
|
for (i = 0; i < NtHeader->FileHeader.NumberOfSections; i++)
|
||||||
|
{
|
||||||
|
/* Get the size of this section and check if it's valid and on-disk */
|
||||||
|
Size = SectionHeaders[i].VirtualAddress +
|
||||||
|
SectionHeaders[i].Misc.VirtualSize;
|
||||||
|
if ((Size <= DriverSize) && (SectionHeaders[i].SizeOfRawData))
|
||||||
|
{
|
||||||
|
/* Copy the data from the disk to the image */
|
||||||
|
RtlCopyMemory((PVOID)((ULONG_PTR)DriverBase +
|
||||||
|
SectionHeaders[i].VirtualAddress),
|
||||||
|
(PVOID)((ULONG_PTR)ModuleLoadBase +
|
||||||
|
SectionHeaders[i].PointerToRawData),
|
||||||
|
SectionHeaders[i].Misc.VirtualSize >
|
||||||
|
SectionHeaders[i].SizeOfRawData ?
|
||||||
|
SectionHeaders[i].SizeOfRawData :
|
||||||
|
SectionHeaders[i].Misc.VirtualSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Relocate the driver */
|
||||||
|
Status = LdrRelocateImageWithBias(DriverBase,
|
||||||
|
0,
|
||||||
|
"SYSLDR",
|
||||||
|
STATUS_SUCCESS,
|
||||||
|
STATUS_CONFLICTING_ADDRESSES,
|
||||||
|
STATUS_INVALID_IMAGE_FORMAT);
|
||||||
|
if (!NT_SUCCESS(Status)) goto Quickie;
|
||||||
|
|
||||||
|
/* Calculate the size we'll need for the entry and allocate it */
|
||||||
|
EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
|
||||||
|
BaseName.Length +
|
||||||
|
sizeof(UNICODE_NULL);
|
||||||
|
|
||||||
|
/* Allocate the entry */
|
||||||
|
LdrEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_MODULE_OBJECT);
|
||||||
|
if (!LdrEntry)
|
||||||
|
{
|
||||||
|
/* Fail */
|
||||||
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||||||
|
goto Quickie;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup the entry */
|
||||||
|
LdrEntry->Flags = LDRP_LOAD_IN_PROGRESS;
|
||||||
|
LdrEntry->LoadCount = 1;
|
||||||
|
LdrEntry->LoadedImports = LoadedImports;
|
||||||
|
LdrEntry->PatchInformation = NULL;
|
||||||
|
|
||||||
|
/* Check the version */
|
||||||
|
if ((NtHeader->OptionalHeader.MajorOperatingSystemVersion >= 5) &&
|
||||||
|
(NtHeader->OptionalHeader.MajorImageVersion >= 5))
|
||||||
|
{
|
||||||
|
/* Mark this image as a native image */
|
||||||
|
LdrEntry->Flags |= 0x80000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup the rest of the entry */
|
||||||
|
LdrEntry->DllBase = DriverBase;
|
||||||
|
LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)DriverBase +
|
||||||
|
NtHeader->OptionalHeader.AddressOfEntryPoint);
|
||||||
|
LdrEntry->SizeOfImage = DriverSize;
|
||||||
|
LdrEntry->CheckSum = NtHeader->OptionalHeader.CheckSum;
|
||||||
|
LdrEntry->SectionPointer = LdrEntry;
|
||||||
|
|
||||||
|
/* Now write the DLL name */
|
||||||
|
LdrEntry->BaseDllName.Buffer = (PVOID)(LdrEntry + 1);
|
||||||
|
LdrEntry->BaseDllName.Length = BaseName.Length;
|
||||||
|
LdrEntry->BaseDllName.MaximumLength = BaseName.Length;
|
||||||
|
|
||||||
|
/* Copy and null-terminate it */
|
||||||
|
RtlCopyMemory(LdrEntry->BaseDllName.Buffer,
|
||||||
|
BaseName.Buffer,
|
||||||
|
BaseName.Length);
|
||||||
|
LdrEntry->BaseDllName.Buffer[BaseName.Length / 2] = UNICODE_NULL;
|
||||||
|
|
||||||
|
/* Now allocate the full name */
|
||||||
|
LdrEntry->FullDllName.Buffer = ExAllocatePoolWithTag(PagedPool,
|
||||||
|
PrefixName.Length +
|
||||||
|
sizeof(UNICODE_NULL),
|
||||||
|
TAG_LDR_WSTR);
|
||||||
|
if (!LdrEntry->FullDllName.Buffer)
|
||||||
|
{
|
||||||
|
/* Don't fail, just set it to zero */
|
||||||
|
LdrEntry->FullDllName.Length = 0;
|
||||||
|
LdrEntry->FullDllName.MaximumLength = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Set it up */
|
||||||
|
LdrEntry->FullDllName.Length = PrefixName.Length;
|
||||||
|
LdrEntry->FullDllName.MaximumLength = PrefixName.Length;
|
||||||
|
|
||||||
|
/* Copy and null-terminate */
|
||||||
|
RtlCopyMemory(LdrEntry->FullDllName.Buffer,
|
||||||
|
PrefixName.Buffer,
|
||||||
|
PrefixName.Length);
|
||||||
|
LdrEntry->FullDllName.Buffer[PrefixName.Length / 2] = UNICODE_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add the entry */
|
||||||
|
MiProcessLoaderEntry(LdrEntry, TRUE);
|
||||||
|
|
||||||
|
/* Resolve imports */
|
||||||
|
MissingApiName = Buffer;
|
||||||
|
Status = MiResolveImageReferences(DriverBase,
|
||||||
|
&BaseDirectory,
|
||||||
|
NULL,
|
||||||
|
&MissingApiName,
|
||||||
|
&MissingDriverName,
|
||||||
|
&LoadedImports);
|
||||||
|
if (!NT_SUCCESS(Status))
|
||||||
|
{
|
||||||
|
/* Fail */
|
||||||
|
MiProcessLoaderEntry(LdrEntry, FALSE);
|
||||||
|
|
||||||
|
/* Check if we need to free the name */
|
||||||
|
if (LdrEntry->FullDllName.Buffer)
|
||||||
|
{
|
||||||
|
/* Free it */
|
||||||
|
ExFreePool(LdrEntry->FullDllName.Buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free the entry itself */
|
||||||
|
ExFreePool(LdrEntry);
|
||||||
|
LdrEntry = NULL;
|
||||||
|
goto Quickie;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ModuleObject) *ModuleObject = LdrEntry;
|
||||||
|
if (ImageBaseAddress) *ImageBaseAddress = LdrEntry->DllBase;
|
||||||
|
|
||||||
|
/* Hook for KDB on loading a driver. */
|
||||||
|
KDB_LOADDRIVER_HOOK(FileName, LdrEntry);
|
||||||
|
|
||||||
|
Quickie:
|
||||||
|
/* If we have a file handle, close it */
|
||||||
|
if (FileHandle) ZwClose(FileHandle);
|
||||||
|
|
||||||
|
/* Cleanup */
|
||||||
|
if (ModuleLoadBase) ExFreePool(ModuleLoadBase);
|
||||||
|
|
||||||
|
/* Check if we have the lock acquired */
|
||||||
|
if (LockOwned)
|
||||||
|
{
|
||||||
|
/* Release the lock */
|
||||||
|
KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
|
||||||
|
KeLeaveCriticalRegion();
|
||||||
|
LockOwned = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we had a prefix */
|
||||||
|
if (NamePrefix) ExFreePool(PrefixName.Buffer);
|
||||||
|
|
||||||
|
/* Free the name buffer and return status */
|
||||||
|
ExFreePool(Buffer);
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue