--- /dev/null
+/* gzread.c -- zlib functions for reading gzip files
+ * Copyright (C) 2004, 2005, 2010 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "gzguts.h"
+
+/* Local functions */
+local int gz_load OF((gz_statep, unsigned char *, unsigned, unsigned *));
+local int gz_avail OF((gz_statep));
+local int gz_next4 OF((gz_statep, unsigned long *));
+local int gz_head OF((gz_statep));
+local int gz_decomp OF((gz_statep));
+local int gz_make OF((gz_statep));
+local int gz_skip OF((gz_statep, z_off64_t));
+
+/* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from
+ state->fd, and update state->eof, state->err, and state->msg as appropriate.
+ This function needs to loop on read(), since read() is not guaranteed to
+ read the number of bytes requested, depending on the type of descriptor. */
+local int gz_load(state, buf, len, have)
+ gz_statep state;
+ unsigned char *buf;
+ unsigned len;
+ unsigned *have;
+{
+ int ret;
+
+ *have = 0;
+ do {
+ ret = read(state->fd, buf + *have, len - *have);
+ if (ret <= 0)
+ break;
+ *have += ret;
+ } while (*have < len);
+ if (ret < 0) {
+ gz_error(state, Z_ERRNO, zstrerror());
+ return -1;
+ }
+ if (ret == 0)
+ state->eof = 1;
+ return 0;
+}
+
+/* Load up input buffer and set eof flag if last data loaded -- return -1 on
+ error, 0 otherwise. Note that the eof flag is set when the end of the input
+ file is reached, even though there may be unused data in the buffer. Once
+ that data has been used, no more attempts will be made to read the file.
+ gz_avail() assumes that strm->avail_in == 0. */
+local int gz_avail(state)
+ gz_statep state;
+{
+ z_streamp strm = &(state->strm);
+
+ if (state->err != Z_OK)
+ return -1;
+ if (state->eof == 0) {
+ if (gz_load(state, state->in, state->size,
+ (unsigned *)&(strm->avail_in)) == -1)
+ return -1;
+ strm->next_in = state->in;
+ }
+ return 0;
+}
+
+/* Get next byte from input, or -1 if end or error. */
+#define NEXT() ((strm->avail_in == 0 && gz_avail(state) == -1) ? -1 : \
+ (strm->avail_in == 0 ? -1 : \
+ (strm->avail_in--, *(strm->next_in)++)))
+
+/* Get a four-byte little-endian integer and return 0 on success and the value
+ in *ret. Otherwise -1 is returned and *ret is not modified. */
+local int gz_next4(state, ret)
+ gz_statep state;
+ unsigned long *ret;
+{
+ int ch;
+ unsigned long val;
+ z_streamp strm = &(state->strm);
+
+ val = NEXT();
+ val += (unsigned)NEXT() << 8;
+ val += (unsigned long)NEXT() << 16;
+ ch = NEXT();
+ if (ch == -1)
+ return -1;
+ val += (unsigned long)ch << 24;
+ *ret = val;
+ return 0;
+}
+
+/* Look for gzip header, set up for inflate or copy. state->have must be zero.
+ If this is the first time in, allocate required memory. state->how will be
+ left unchanged if there is no more input data available, will be set to COPY
+ if there is no gzip header and direct copying will be performed, or it will
+ be set to GZIP for decompression, and the gzip header will be skipped so
+ that the next available input data is the raw deflate stream. If direct
+ copying, then leftover input data from the input buffer will be copied to
+ the output buffer. In that case, all further file reads will be directly to
+ either the output buffer or a user buffer. If decompressing, the inflate
+ state and the check value will be initialized. gz_head() will return 0 on
+ success or -1 on failure. Failures may include read errors or gzip header
+ errors. */
+local int gz_head(state)
+ gz_statep state;
+{
+ z_streamp strm = &(state->strm);
+ int flags;
+ unsigned len;
+
+ /* allocate read buffers and inflate memory */
+ if (state->size == 0) {
+ /* allocate buffers */
+ state->in = malloc(state->want);
+ state->out = malloc(state->want << 1);
+ if (state->in == NULL || state->out == NULL) {
+ if (state->out != NULL)
+ free(state->out);
+ if (state->in != NULL)
+ free(state->in);
+ gz_error(state, Z_MEM_ERROR, "out of memory");
+ return -1;
+ }
+ state->size = state->want;
+
+ /* allocate inflate memory */
+ state->strm.zalloc = Z_NULL;
+ state->strm.zfree = Z_NULL;
+ state->strm.opaque = Z_NULL;
+ state->strm.avail_in = 0;
+ state->strm.next_in = Z_NULL;
+ if (inflateInit2(&(state->strm), -15) != Z_OK) { /* raw inflate */
+ free(state->out);
+ free(state->in);
+ state->size = 0;
+ gz_error(state, Z_MEM_ERROR, "out of memory");
+ return -1;
+ }
+ }
+
+ /* get some data in the input buffer */
+ if (strm->avail_in == 0) {
+ if (gz_avail(state) == -1)
+ return -1;
+ if (strm->avail_in == 0)
+ return 0;
+ }
+
+ /* look for the gzip magic header bytes 31 and 139 */
+ if (strm->next_in[0] == 31) {
+ strm->avail_in--;
+ strm->next_in++;
+ if (strm->avail_in == 0 && gz_avail(state) == -1)
+ return -1;
+ if (strm->avail_in && strm->next_in[0] == 139) {
+ /* we have a gzip header, woo hoo! */
+ strm->avail_in--;
+ strm->next_in++;
+
+ /* skip rest of header */
+ if (NEXT() != 8) { /* compression method */
+ gz_error(state, Z_DATA_ERROR, "unknown compression method");
+ return -1;
+ }
+ flags = NEXT();
+ if (flags & 0xe0) { /* reserved flag bits */
+ gz_error(state, Z_DATA_ERROR, "unknown header flags set");
+ return -1;
+ }
+ NEXT(); /* modification time */
+ NEXT();
+ NEXT();
+ NEXT();
+ NEXT(); /* extra flags */
+ NEXT(); /* operating system */
+ if (flags & 4) { /* extra field */
+ len = (unsigned)NEXT();
+ len += (unsigned)NEXT() << 8;
+ while (len--)
+ if (NEXT() < 0)
+ break;
+ }
+ if (flags & 8) /* file name */
+ while (NEXT() > 0)
+ ;
+ if (flags & 16) /* comment */
+ while (NEXT() > 0)
+ ;
+ if (flags & 2) { /* header crc */
+ NEXT();
+ NEXT();
+ }
+ /* an unexpected end of file is not checked for here -- it will be
+ noticed on the first request for uncompressed data */
+
+ /* set up for decompression */
+ inflateReset(strm);
+ strm->adler = crc32(0L, Z_NULL, 0);
+ state->how = GZIP;
+ state->direct = 0;
+ return 0;
+ }
+ else {
+ /* not a gzip file -- save first byte (31) and fall to raw i/o */
+ state->out[0] = 31;
+ state->have = 1;
+ }
+ }
+
+ /* doing raw i/o, save start of raw data for seeking, copy any leftover
+ input to output -- this assumes that the output buffer is larger than
+ the input buffer, which also assures space for gzungetc() */
+ state->raw = state->pos;
+ state->next = state->out;
+ if (strm->avail_in) {
+ memcpy(state->next + state->have, strm->next_in, strm->avail_in);
+ state->have += strm->avail_in;
+ strm->avail_in = 0;
+ }
+ state->how = COPY;
+ state->direct = 1;
+ return 0;
+}
+
+/* Decompress from input to the provided next_out and avail_out in the state.
+ If the end of the compressed data is reached, then verify the gzip trailer
+ check value and length (modulo 2^32). state->have and state->next are set
+ to point to the just decompressed data, and the crc is updated. If the
+ trailer is verified, state->how is reset to LOOK to look for the next gzip
+ stream or raw data, once state->have is depleted. Returns 0 on success, -1
+ on failure. Failures may include invalid compressed data or a failed gzip
+ trailer verification. */
+local int gz_decomp(state)
+ gz_statep state;
+{
+ int ret;
+ unsigned had;
+ unsigned long crc, len;
+ z_streamp strm = &(state->strm);
+
+ /* fill output buffer up to end of deflate stream */
+ had = strm->avail_out;
+ do {
+ /* get more input for inflate() */
+ if (strm->avail_in == 0 && gz_avail(state) == -1)
+ return -1;
+ if (strm->avail_in == 0) {
+ gz_error(state, Z_DATA_ERROR, "unexpected end of file");
+ return -1;
+ }
+
+ /* decompress and handle errors */
+ ret = inflate(strm, Z_NO_FLUSH);
+ if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) {
+ gz_error(state, Z_STREAM_ERROR,
+ "internal error: inflate stream corrupt");
+ return -1;
+ }
+ if (ret == Z_MEM_ERROR) {
+ gz_error(state, Z_MEM_ERROR, "out of memory");
+ return -1;
+ }
+ if (ret == Z_DATA_ERROR) { /* deflate stream invalid */
+ gz_error(state, Z_DATA_ERROR,
+ strm->msg == NULL ? "compressed data error" : strm->msg);
+ return -1;
+ }
+ } while (strm->avail_out && ret != Z_STREAM_END);
+
+ /* update available output and crc check value */
+ state->have = had - strm->avail_out;
+ state->next = strm->next_out - state->have;
+ strm->adler = crc32(strm->adler, state->next, state->have);
+
+ /* check gzip trailer if at end of deflate stream */
+ if (ret == Z_STREAM_END) {
+ if (gz_next4(state, &crc) == -1 || gz_next4(state, &len) == -1) {
+ gz_error(state, Z_DATA_ERROR, "unexpected end of file");
+ return -1;
+ }
+ if (crc != strm->adler) {
+ gz_error(state, Z_DATA_ERROR, "incorrect data check");
+ return -1;
+ }
+ if (len != (strm->total_out & 0xffffffffL)) {
+ gz_error(state, Z_DATA_ERROR, "incorrect length check");
+ return -1;
+ }
+ state->how = LOOK; /* ready for next stream, once have is 0 (leave
+ state->direct unchanged to remember how) */
+ }
+
+ /* good decompression */
+ return 0;
+}
+
+/* Make data and put in the output buffer. Assumes that state->have == 0.
+ Data is either copied from the input file or decompressed from the input
+ file depending on state->how. If state->how is LOOK, then a gzip header is
+ looked for (and skipped if found) to determine wither to copy or decompress.
+ Returns -1 on error, otherwise 0. gz_make() will leave state->have as COPY
+ or GZIP unless the end of the input file has been reached and all data has
+ been processed. */
+local int gz_make(state)
+ gz_statep state;
+{
+ z_streamp strm = &(state->strm);
+
+ if (state->how == LOOK) { /* look for gzip header */
+ if (gz_head(state) == -1)
+ return -1;
+ if (state->have) /* got some data from gz_head() */
+ return 0;
+ }
+ if (state->how == COPY) { /* straight copy */
+ if (gz_load(state, state->out, state->size << 1, &(state->have)) == -1)
+ return -1;
+ state->next = state->out;
+ }
+ else if (state->how == GZIP) { /* decompress */
+ strm->avail_out = state->size << 1;
+ strm->next_out = state->out;
+ if (gz_decomp(state) == -1)
+ return -1;
+ }
+ return 0;
+}
+
+/* Skip len uncompressed bytes of output. Return -1 on error, 0 on success. */
+local int gz_skip(state, len)
+ gz_statep state;
+ z_off64_t len;
+{
+ unsigned n;
+
+ /* skip over len bytes or reach end-of-file, whichever comes first */
+ while (len)
+ /* skip over whatever is in output buffer */
+ if (state->have) {
+ n = GT_OFF(state->have) || (z_off64_t)state->have > len ?
+ (unsigned)len : state->have;
+ state->have -= n;
+ state->next += n;
+ state->pos += n;
+ len -= n;
+ }
+
+ /* output buffer empty -- return if we're at the end of the input */
+ else if (state->eof && state->strm.avail_in == 0)
+ break;
+
+ /* need more data to skip -- load up output buffer */
+ else {
+ /* get more output, looking for header if required */
+ if (gz_make(state) == -1)
+ return -1;
+ }
+ return 0;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzread(file, buf, len)
+ gzFile file;
+ voidp buf;
+ unsigned len;
+{
+ unsigned got, n;
+ gz_statep state;
+ z_streamp strm;
+
+ /* get internal structure */
+ if (file == NULL)
+ return -1;
+ state = (gz_statep)file;
+ strm = &(state->strm);
+
+ /* check that we're reading and that there's no error */
+ if (state->mode != GZ_READ || state->err != Z_OK)
+ return -1;
+
+ /* since an int is returned, make sure len fits in one, otherwise return
+ with an error (this avoids the flaw in the interface) */
+ if ((int)len < 0) {
+ gz_error(state, Z_BUF_ERROR, "requested length does not fit in int");
+ return -1;
+ }
+
+ /* if len is zero, avoid unnecessary operations */
+ if (len == 0)
+ return 0;
+
+ /* process a skip request */
+ if (state->seek) {
+ state->seek = 0;
+ if (gz_skip(state, state->skip) == -1)
+ return -1;
+ }
+
+ /* get len bytes to buf, or less than len if at the end */
+ got = 0;
+ do {
+ /* first just try copying data from the output buffer */
+ if (state->have) {
+ n = state->have > len ? len : state->have;
+ memcpy(buf, state->next, n);
+ state->next += n;
+ state->have -= n;
+ }
+
+ /* output buffer empty -- return if we're at the end of the input */
+ else if (state->eof && strm->avail_in == 0)
+ break;
+
+ /* need output data -- for small len or new stream load up our output
+ buffer */
+ else if (state->how == LOOK || len < (state->size << 1)) {
+ /* get more output, looking for header if required */
+ if (gz_make(state) == -1)
+ return -1;
+ continue; /* no progress yet -- go back to memcpy() above */
+ /* the copy above assures that we will leave with space in the
+ output buffer, allowing at least one gzungetc() to succeed */
+ }
+
+ /* large len -- read directly into user buffer */
+ else if (state->how == COPY) { /* read directly */
+ if (gz_load(state, buf, len, &n) == -1)
+ return -1;
+ }
+
+ /* large len -- decompress directly into user buffer */
+ else { /* state->how == GZIP */
+ strm->avail_out = len;
+ strm->next_out = buf;
+ if (gz_decomp(state) == -1)
+ return -1;
+ n = state->have;
+ state->have = 0;
+ }
+
+ /* update progress */
+ len -= n;
+ buf = (char *)buf + n;
+ got += n;
+ state->pos += n;
+ } while (len);
+
+ /* return number of bytes read into user buffer (will fit in int) */
+ return (int)got;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzgetc(file)
+ gzFile file;
+{
+ int ret;
+ unsigned char buf[1];
+ gz_statep state;
+
+ /* get internal structure */
+ if (file == NULL)
+ return -1;
+ state = (gz_statep)file;
+
+ /* check that we're reading and that there's no error */
+ if (state->mode != GZ_READ || state->err != Z_OK)
+ return -1;
+
+ /* try output buffer (no need to check for skip request) */
+ if (state->have) {
+ state->have--;
+ state->pos++;
+ return *(state->next)++;
+ }
+
+ /* nothing there -- try gzread() */
+ ret = gzread(file, buf, 1);
+ return ret < 1 ? -1 : buf[0];
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzungetc(c, file)
+ int c;
+ gzFile file;
+{
+ gz_statep state;
+
+ /* get internal structure */
+ if (file == NULL)
+ return -1;
+ state = (gz_statep)file;
+
+ /* check that we're reading and that there's no error */
+ if (state->mode != GZ_READ || state->err != Z_OK)
+ return -1;
+
+ /* process a skip request */
+ if (state->seek) {
+ state->seek = 0;
+ if (gz_skip(state, state->skip) == -1)
+ return -1;
+ }
+
+ /* can't push EOF */
+ if (c < 0)
+ return -1;
+
+ /* if output buffer empty, put byte at end (allows more pushing) */
+ if (state->have == 0) {
+ state->have = 1;
+ state->next = state->out + (state->size << 1) - 1;
+ state->next[0] = c;
+ state->pos--;
+ return c;
+ }
+
+ /* if no room, give up (must have already done a gzungetc()) */
+ if (state->have == (state->size << 1)) {
+ gz_error(state, Z_BUF_ERROR, "out of room to push characters");
+ return -1;
+ }
+
+ /* slide output data if needed and insert byte before existing data */
+ if (state->next == state->out) {
+ unsigned char *src = state->out + state->have;
+ unsigned char *dest = state->out + (state->size << 1);
+ while (src > state->out)
+ *--dest = *--src;
+ state->next = dest;
+ }
+ state->have++;
+ state->next--;
+ state->next[0] = c;
+ state->pos--;
+ return c;
+}
+
+/* -- see zlib.h -- */
+char * ZEXPORT gzgets(file, buf, len)
+ gzFile file;
+ char *buf;
+ int len;
+{
+ unsigned left, n;
+ char *str;
+ unsigned char *eol;
+ gz_statep state;
+
+ /* check parameters and get internal structure */
+ if (file == NULL || buf == NULL || len < 1)
+ return NULL;
+ state = (gz_statep)file;
+
+ /* check that we're reading and that there's no error */
+ if (state->mode != GZ_READ || state->err != Z_OK)
+ return NULL;
+
+ /* process a skip request */
+ if (state->seek) {
+ state->seek = 0;
+ if (gz_skip(state, state->skip) == -1)
+ return NULL;
+ }
+
+ /* copy output bytes up to new line or len - 1, whichever comes first --
+ append a terminating zero to the string (we don't check for a zero in
+ the contents, let the user worry about that) */
+ str = buf;
+ left = (unsigned)len - 1;
+ if (left) do {
+ /* assure that something is in the output buffer */
+ if (state->have == 0) {
+ if (gz_make(state) == -1)
+ return NULL; /* error */
+ if (state->have == 0) { /* end of file */
+ if (buf == str) /* got bupkus */
+ return NULL;
+ break; /* got something -- return it */
+ }
+ }
+
+ /* look for end-of-line in current output buffer */
+ n = state->have > left ? left : state->have;
+ eol = memchr(state->next, '\n', n);
+ if (eol != NULL)
+ n = (unsigned)(eol - state->next) + 1;
+
+ /* copy through end-of-line, or remainder if not found */
+ memcpy(buf, state->next, n);
+ state->have -= n;
+ state->next += n;
+ state->pos += n;
+ left -= n;
+ buf += n;
+ } while (left && eol == NULL);
+
+ /* found end-of-line or out of space -- terminate string and return it */
+ buf[0] = 0;
+ return str;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzdirect(file)
+ gzFile file;
+{
+ gz_statep state;
+
+ /* get internal structure */
+ if (file == NULL)
+ return 0;
+ state = (gz_statep)file;
+
+ /* check that we're reading */
+ if (state->mode != GZ_READ)
+ return 0;
+
+ /* if the state is not known, but we can find out, then do so (this is
+ mainly for right after a gzopen() or gzdopen()) */
+ if (state->how == LOOK && state->have == 0)
+ (void)gz_head(state);
+
+ /* return 1 if reading direct, 0 if decompressing a gzip stream */
+ return state->direct;
+}
+
+/* -- see zlib.h -- */
+int ZEXPORT gzclose_r(file)
+ gzFile file;
+{
+ int ret;
+ gz_statep state;
+
+ /* get internal structure */
+ if (file == NULL)
+ return Z_STREAM_ERROR;
+ state = (gz_statep)file;
+
+ /* check that we're reading */
+ if (state->mode != GZ_READ)
+ return Z_STREAM_ERROR;
+
+ /* free memory and close file */
+ if (state->size) {
+ inflateEnd(&(state->strm));
+ free(state->out);
+ free(state->in);
+ }
+ gz_error(state, Z_OK, NULL);
+ free(state->path);
+ ret = close(state->fd);
+ free(state);
+ return ret ? Z_ERRNO : Z_OK;
+}
--- /dev/null
+/*
+* PROJECT: ReactOS Kernel
+* LICENSE: BSD - See COPYING.ARM in the top level directory
+* FILE: ntoskrnl/mm/ARM3/sysldr.c
+* PURPOSE: Contains the Kernel Loader (SYSLDR) for loading PE files.
+* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
+* ReactOS Portable Systems Group
+*/
+
+/* INCLUDES *******************************************************************/
+
+#include <ntoskrnl.h>
+#define NDEBUG
+#include <debug.h>
+
+#line 16 "ARM³::LOADER"
+#define MODULE_INVOLVED_IN_ARM3
+#include "../ARM3/miarm.h"
+
+/* GCC's incompetence strikes again */
+__inline
+VOID
+sprintf_nt(IN PCHAR Buffer,
+ IN PCHAR Format,
+ IN ...)
+{
+ va_list ap;
+ va_start(ap, Format);
+ vsprintf(Buffer, Format, ap);
+ va_end(ap);
+}
+
+/* GLOBALS ********************************************************************/
+
+LIST_ENTRY PsLoadedModuleList;
+LIST_ENTRY MmLoadedUserImageList;
+KSPIN_LOCK PsLoadedModuleSpinLock;
+ERESOURCE PsLoadedModuleResource;
+ULONG_PTR PsNtosImageBase;
+KMUTANT MmSystemLoadLock;
+
+PFN_NUMBER MmTotalSystemDriverPages;
+
+PVOID MmUnloadedDrivers;
+PVOID MmLastUnloadedDrivers;
+
+BOOLEAN MmMakeLowMemory;
+BOOLEAN MmEnforceWriteProtection = TRUE;
+
+PMMPTE MiKernelResourceStartPte, MiKernelResourceEndPte;
+ULONG_PTR ExPoolCodeStart, ExPoolCodeEnd, MmPoolCodeStart, MmPoolCodeEnd;
+ULONG_PTR MmPteCodeStart, MmPteCodeEnd;
+
+/* FUNCTIONS ******************************************************************/
+
+PVOID
+NTAPI
+MiCacheImageSymbols(IN PVOID BaseAddress)
+{
+ ULONG DebugSize;
+ PVOID DebugDirectory = NULL;
+ PAGED_CODE();
+
+ /* Make sure it's safe to access the image */
+ _SEH2_TRY
+ {
+ /* Get the debug directory */
+ DebugDirectory = RtlImageDirectoryEntryToData(BaseAddress,
+ TRUE,
+ IMAGE_DIRECTORY_ENTRY_DEBUG,
+ &DebugSize);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* Nothing */
+ }
+ _SEH2_END;
+
+ /* Return the directory */
+ return DebugDirectory;
+}
+
+NTSTATUS
+NTAPI
+MiLoadImageSection(IN OUT PVOID *SectionPtr,
+ OUT PVOID *ImageBase,
+ IN PUNICODE_STRING FileName,
+ IN BOOLEAN SessionLoad,
+ IN PLDR_DATA_TABLE_ENTRY LdrEntry)
+{
+ PROS_SECTION_OBJECT Section = *SectionPtr;
+ NTSTATUS Status;
+ PEPROCESS Process;
+ PVOID Base = NULL;
+ SIZE_T ViewSize = 0;
+ KAPC_STATE ApcState;
+ LARGE_INTEGER SectionOffset = {{0, 0}};
+ BOOLEAN LoadSymbols = FALSE;
+ PFN_NUMBER PteCount;
+ PMMPTE PointerPte, LastPte;
+ PVOID DriverBase;
+ MMPTE TempPte;
+ PAGED_CODE();
+
+ /* Detect session load */
+ if (SessionLoad)
+ {
+ /* Fail */
+ DPRINT1("Session loading not yet supported!\n");
+ while (TRUE);
+ }
+
+ /* Not session load, shouldn't have an entry */
+ ASSERT(LdrEntry == NULL);
+
+ /* Attach to the system process */
+ KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);
+
+ /* Check if we need to load symbols */
+ if (NtGlobalFlag & FLG_ENABLE_KDEBUG_SYMBOL_LOAD)
+ {
+ /* Yes we do */
+ LoadSymbols = TRUE;
+ NtGlobalFlag &= ~FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
+ }
+
+ /* Map the driver */
+ Process = PsGetCurrentProcess();
+ Status = MmMapViewOfSection(Section,
+ Process,
+ &Base,
+ 0,
+ 0,
+ &SectionOffset,
+ &ViewSize,
+ ViewUnmap,
+ 0,
+ PAGE_EXECUTE);
+
+ /* Re-enable the flag */
+ if (LoadSymbols) NtGlobalFlag |= FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
+
+ /* Check if we failed with distinguished status code */
+ if (Status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH)
+ {
+ /* Change it to something more generic */
+ Status = STATUS_INVALID_IMAGE_FORMAT;
+ }
+
+ /* Now check if we failed */
+ if (!NT_SUCCESS(Status))
+ {
+ /* Detach and return */
+ KeUnstackDetachProcess(&ApcState);
+ return Status;
+ }
+
+ /* Reserve system PTEs needed */
+ PteCount = ROUND_TO_PAGES(Section->ImageSection->ImageSize) >> PAGE_SHIFT;
+ PointerPte = MiReserveSystemPtes(PteCount, SystemPteSpace);
+ if (!PointerPte) return STATUS_INSUFFICIENT_RESOURCES;
+
+ /* New driver base */
+ LastPte = PointerPte + PteCount;
+ DriverBase = MiPteToAddress(PointerPte);
+
+ /* The driver is here */
+ *ImageBase = DriverBase;
+
+ /* Loop the new driver PTEs */
+ TempPte = ValidKernelPte;
+ while (PointerPte < LastPte)
+ {
+ /* Allocate a page */
+ TempPte.u.Hard.PageFrameNumber = MiAllocatePfn(PointerPte, MM_EXECUTE);
+
+ /* Write it */
+ MI_WRITE_VALID_PTE(PointerPte, TempPte);
+
+ /* Move on */
+ PointerPte++;
+ }
+
+ /* Copy the image */
+ RtlCopyMemory(DriverBase, Base, PteCount << PAGE_SHIFT);
+
+ /* Now unmap the view */
+ Status = MmUnmapViewOfSection(Process, Base);
+ ASSERT(NT_SUCCESS(Status));
+
+ /* Detach and return status */
+ KeUnstackDetachProcess(&ApcState);
+ return Status;
+}
+
+PVOID
+NTAPI
+MiLocateExportName(IN PVOID DllBase,
+ IN PCHAR ExportName)
+{
+ PULONG NameTable;
+ PUSHORT OrdinalTable;
+ PIMAGE_EXPORT_DIRECTORY ExportDirectory;
+ LONG Low = 0, Mid = 0, High, Ret;
+ USHORT Ordinal;
+ PVOID Function;
+ ULONG ExportSize;
+ PULONG ExportTable;
+ PAGED_CODE();
+
+ /* Get the export directory */
+ ExportDirectory = RtlImageDirectoryEntryToData(DllBase,
+ TRUE,
+ IMAGE_DIRECTORY_ENTRY_EXPORT,
+ &ExportSize);
+ if (!ExportDirectory) return NULL;
+
+ /* Setup name tables */
+ NameTable = (PULONG)((ULONG_PTR)DllBase +
+ ExportDirectory->AddressOfNames);
+ OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
+ ExportDirectory->AddressOfNameOrdinals);
+
+ /* Do a binary search */
+ High = ExportDirectory->NumberOfNames - 1;
+ while (High >= Low)
+ {
+ /* Get new middle value */
+ Mid = (Low + High) >> 1;
+
+ /* Compare name */
+ Ret = strcmp(ExportName, (PCHAR)DllBase + NameTable[Mid]);
+ if (Ret < 0)
+ {
+ /* Update high */
+ High = Mid - 1;
+ }
+ else if (Ret > 0)
+ {
+ /* Update low */
+ Low = Mid + 1;
+ }
+ else
+ {
+ /* We got it */
+ break;
+ }
+ }
+
+ /* Check if we couldn't find it */
+ if (High < Low) return NULL;
+
+ /* Otherwise, this is the ordinal */
+ Ordinal = OrdinalTable[Mid];
+
+ /* Resolve the address and write it */
+ ExportTable = (PULONG)((ULONG_PTR)DllBase +
+ ExportDirectory->AddressOfFunctions);
+ Function = (PVOID)((ULONG_PTR)DllBase + ExportTable[Ordinal]);
+
+ /* Check if the function is actually a forwarder */
+ if (((ULONG_PTR)Function > (ULONG_PTR)ExportDirectory) &&
+ ((ULONG_PTR)Function < ((ULONG_PTR)ExportDirectory + ExportSize)))
+ {
+ /* It is, fail */
+ return NULL;
+ }
+
+ /* We found it */
+ return Function;
+}
+
+NTSTATUS
+NTAPI
+MmCallDllInitialize(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
+ IN PLIST_ENTRY ListHead)
+{
+ UNICODE_STRING ServicesKeyName = RTL_CONSTANT_STRING(
+ L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
+ PMM_DLL_INITIALIZE DllInit;
+ UNICODE_STRING RegPath, ImportName;
+ NTSTATUS Status;
+
+ /* Try to see if the image exports a DllInitialize routine */
+ DllInit = (PMM_DLL_INITIALIZE)MiLocateExportName(LdrEntry->DllBase,
+ "DllInitialize");
+ if (!DllInit) return STATUS_SUCCESS;
+
+ /* Do a temporary copy of BaseDllName called ImportName
+ * because we'll alter the length of the string
+ */
+ ImportName.Length = LdrEntry->BaseDllName.Length;
+ ImportName.MaximumLength = LdrEntry->BaseDllName.MaximumLength;
+ ImportName.Buffer = LdrEntry->BaseDllName.Buffer;
+
+ /* Obtain the path to this dll's service in the registry */
+ RegPath.MaximumLength = ServicesKeyName.Length +
+ ImportName.Length + sizeof(UNICODE_NULL);
+ RegPath.Buffer = ExAllocatePoolWithTag(NonPagedPool,
+ RegPath.MaximumLength,
+ TAG_LDR_WSTR);
+
+ /* Check if this allocation was unsuccessful */
+ if (!RegPath.Buffer) return STATUS_INSUFFICIENT_RESOURCES;
+
+ /* Build and append the service name itself */
+ RegPath.Length = ServicesKeyName.Length;
+ RtlCopyMemory(RegPath.Buffer,
+ ServicesKeyName.Buffer,
+ ServicesKeyName.Length);
+
+ /* Check if there is a dot in the filename */
+ if (wcschr(ImportName.Buffer, L'.'))
+ {
+ /* Remove the extension */
+ ImportName.Length = (wcschr(ImportName.Buffer, L'.') -
+ ImportName.Buffer) * sizeof(WCHAR);
+ }
+
+ /* Append service name (the basename without extension) */
+ RtlAppendUnicodeStringToString(&RegPath, &ImportName);
+
+ /* Now call the DllInit func */
+ DPRINT("Calling DllInit(%wZ)\n", &RegPath);
+ Status = DllInit(&RegPath);
+
+ /* Clean up */
+ ExFreePool(RegPath.Buffer);
+
+ /* Return status value which DllInitialize returned */
+ return Status;
+}
+
+BOOLEAN
+NTAPI
+MiCallDllUnloadAndUnloadDll(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
+{
+ NTSTATUS Status;
+ PMM_DLL_UNLOAD Func;
+ PAGED_CODE();
+
+ /* Get the unload routine */
+ Func = (PMM_DLL_UNLOAD)MiLocateExportName(LdrEntry->DllBase, "DllUnload");
+ if (!Func) return FALSE;
+
+ /* Call it and check for success */
+ Status = Func();
+ if (!NT_SUCCESS(Status)) return FALSE;
+
+ /* Lie about the load count so we can unload the image */
+ ASSERT(LdrEntry->LoadCount == 0);
+ LdrEntry->LoadCount = 1;
+
+ /* Unload it and return true */
+ MmUnloadSystemImage(LdrEntry);
+ return TRUE;
+}
+
+NTSTATUS
+NTAPI
+MiDereferenceImports(IN PLOAD_IMPORTS ImportList)
+{
+ SIZE_T i;
+ LOAD_IMPORTS SingleEntry;
+ PLDR_DATA_TABLE_ENTRY LdrEntry;
+ PVOID CurrentImports;
+ PAGED_CODE();
+
+ /* Check if there's no imports or if we're a boot driver */
+ if ((ImportList == MM_SYSLDR_NO_IMPORTS) ||
+ (ImportList == MM_SYSLDR_BOOT_LOADED) ||
+ (ImportList->Count == 0))
+ {
+ /* Then there's nothing to do */
+ return STATUS_SUCCESS;
+ }
+
+ /* Check for single-entry */
+ if ((ULONG_PTR)ImportList & MM_SYSLDR_SINGLE_ENTRY)
+ {
+ /* Set it up */
+ SingleEntry.Count = 1;
+ SingleEntry.Entry[0] = (PVOID)((ULONG_PTR)ImportList &~ MM_SYSLDR_SINGLE_ENTRY);
+
+ /* Use this as the import list */
+ ImportList = &SingleEntry;
+ }
+
+ /* Loop the import list */
+ for (i = 0; (i < ImportList->Count) && (ImportList->Entry[i]); i++)
+ {
+ /* Get the entry */
+ LdrEntry = ImportList->Entry[i];
+ DPRINT1("%wZ <%wZ>\n", &LdrEntry->FullDllName, &LdrEntry->BaseDllName);
+
+ /* Skip boot loaded images */
+ if (LdrEntry->LoadedImports == MM_SYSLDR_BOOT_LOADED) continue;
+
+ /* Dereference the entry */
+ ASSERT(LdrEntry->LoadCount >= 1);
+ if (!--LdrEntry->LoadCount)
+ {
+ /* Save the import data in case unload fails */
+ CurrentImports = LdrEntry->LoadedImports;
+
+ /* This is the last entry */
+ LdrEntry->LoadedImports = MM_SYSLDR_NO_IMPORTS;
+ if (MiCallDllUnloadAndUnloadDll(LdrEntry))
+ {
+ /* Unloading worked, parse this DLL's imports too */
+ MiDereferenceImports(CurrentImports);
+
+ /* Check if we had valid imports */
+ if ((CurrentImports != MM_SYSLDR_BOOT_LOADED) ||
+ (CurrentImports != MM_SYSLDR_NO_IMPORTS) ||
+ !((ULONG_PTR)LdrEntry->LoadedImports & MM_SYSLDR_SINGLE_ENTRY))
+ {
+ /* Free them */
+ ExFreePool(CurrentImports);
+ }
+ }
+ else
+ {
+ /* Unload failed, restore imports */
+ LdrEntry->LoadedImports = CurrentImports;
+ }
+ }
+ }
+
+ /* Done */
+ return STATUS_SUCCESS;
+}
+
+VOID
+NTAPI
+MiClearImports(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
+{
+ PAGED_CODE();
+
+ /* Check if there's no imports or we're a boot driver or only one entry */
+ if ((LdrEntry->LoadedImports == MM_SYSLDR_BOOT_LOADED) ||
+ (LdrEntry->LoadedImports == MM_SYSLDR_NO_IMPORTS) ||
+ ((ULONG_PTR)LdrEntry->LoadedImports & MM_SYSLDR_SINGLE_ENTRY))
+ {
+ /* Nothing to do */
+ return;
+ }
+
+ /* Otherwise, free the import list */
+ ExFreePool(LdrEntry->LoadedImports);
+ LdrEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED;
+}
+
+PVOID
+NTAPI
+MiFindExportedRoutineByName(IN PVOID DllBase,
+ IN PANSI_STRING ExportName)
+{
+ PULONG NameTable;
+ PUSHORT OrdinalTable;
+ PIMAGE_EXPORT_DIRECTORY ExportDirectory;
+ LONG Low = 0, Mid = 0, High, Ret;
+ USHORT Ordinal;
+ PVOID Function;
+ ULONG ExportSize;
+ PULONG ExportTable;
+ PAGED_CODE();
+
+ /* Get the export directory */
+ ExportDirectory = RtlImageDirectoryEntryToData(DllBase,
+ TRUE,
+ IMAGE_DIRECTORY_ENTRY_EXPORT,
+ &ExportSize);
+ if (!ExportDirectory) return NULL;
+
+ /* Setup name tables */
+ NameTable = (PULONG)((ULONG_PTR)DllBase +
+ ExportDirectory->AddressOfNames);
+ OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
+ ExportDirectory->AddressOfNameOrdinals);
+
+ /* Do a binary search */
+ High = ExportDirectory->NumberOfNames - 1;
+ while (High >= Low)
+ {
+ /* Get new middle value */
+ Mid = (Low + High) >> 1;
+
+ /* Compare name */
+ Ret = strcmp(ExportName->Buffer, (PCHAR)DllBase + NameTable[Mid]);
+ if (Ret < 0)
+ {
+ /* Update high */
+ High = Mid - 1;
+ }
+ else if (Ret > 0)
+ {
+ /* Update low */
+ Low = Mid + 1;
+ }
+ else
+ {
+ /* We got it */
+ break;
+ }
+ }
+
+ /* Check if we couldn't find it */
+ if (High < Low) return NULL;
+
+ /* Otherwise, this is the ordinal */
+ Ordinal = OrdinalTable[Mid];
+
+ /* Validate the ordinal */
+ if (Ordinal >= ExportDirectory->NumberOfFunctions) return NULL;
+
+ /* Resolve the address and write it */
+ ExportTable = (PULONG)((ULONG_PTR)DllBase +
+ ExportDirectory->AddressOfFunctions);
+ Function = (PVOID)((ULONG_PTR)DllBase + ExportTable[Ordinal]);
+
+ /* We found it! */
+ ASSERT(!(Function > (PVOID)ExportDirectory) &&
+ (Function < (PVOID)((ULONG_PTR)ExportDirectory + ExportSize)));
+ return Function;
+}
+
+VOID
+NTAPI
+MiProcessLoaderEntry(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
+ IN BOOLEAN Insert)
+{
+ KIRQL OldIrql;
+
+ /* Acquire module list lock */
+ KeEnterCriticalRegion();
+ ExAcquireResourceExclusiveLite(&PsLoadedModuleResource, TRUE);
+
+ /* Acquire the spinlock too as we will insert or remove the entry */
+ OldIrql = KeAcquireSpinLockRaiseToSynch(&PsLoadedModuleSpinLock);
+
+ /* Insert or remove from the list */
+ Insert ? InsertTailList(&PsLoadedModuleList, &LdrEntry->InLoadOrderLinks) :
+ RemoveEntryList(&LdrEntry->InLoadOrderLinks);
+
+ /* Release locks */
+ KeReleaseSpinLock(&PsLoadedModuleSpinLock, OldIrql);
+ ExReleaseResourceLite(&PsLoadedModuleResource);
+ KeLeaveCriticalRegion();
+}
+
+VOID
+NTAPI
+MiUpdateThunks(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
+ IN PVOID OldBase,
+ IN PVOID NewBase,
+ IN ULONG Size)
+{
+ ULONG_PTR OldBaseTop, Delta;
+ PLDR_DATA_TABLE_ENTRY LdrEntry;
+ PLIST_ENTRY NextEntry;
+ ULONG ImportSize, i;
+ PULONG_PTR ImageThunk;
+ PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
+
+ /* Calculate the top and delta */
+ OldBaseTop = (ULONG_PTR)OldBase + Size - 1;
+ Delta = (ULONG_PTR)NewBase - (ULONG_PTR)OldBase;
+
+ /* Loop the loader block */
+ for (NextEntry = LoaderBlock->LoadOrderListHead.Flink;
+ NextEntry != &LoaderBlock->LoadOrderListHead;
+ NextEntry = NextEntry->Flink)
+ {
+ /* Get the loader entry */
+ LdrEntry = CONTAINING_RECORD(NextEntry,
+ LDR_DATA_TABLE_ENTRY,
+ InLoadOrderLinks);
+#ifdef _WORKING_LINKER_
+ /* Get the IAT */
+ ImageThunk = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
+ TRUE,
+ IMAGE_DIRECTORY_ENTRY_IAT,
+ &ImportSize);
+ if (!ImageThunk) continue;
+
+ /* Make sure we have an IAT */
+ DPRINT("[Mm0]: Updating thunks in: %wZ\n", &LdrEntry->BaseDllName);
+ for (i = 0; i < ImportSize; i++, ImageThunk++)
+ {
+ /* Check if it's within this module */
+ if ((*ImageThunk >= (ULONG_PTR)OldBase) && (*ImageThunk <= OldBaseTop))
+ {
+ /* Relocate it */
+ DPRINT("[Mm0]: Updating IAT at: %p. Old Entry: %p. New Entry: %p.\n",
+ ImageThunk, *ImageThunk, *ImageThunk + Delta);
+ *ImageThunk += Delta;
+ }
+ }
+#else
+ /* Get the import table */
+ i = ImportSize;
+ ImportDescriptor = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
+ TRUE,
+ IMAGE_DIRECTORY_ENTRY_IMPORT,
+ &ImportSize);
+ if (!ImportDescriptor) continue;
+
+ /* Make sure we have an IAT */
+ DPRINT("[Mm0]: Updating thunks in: %wZ\n", &LdrEntry->BaseDllName);
+ while ((ImportDescriptor->Name) &&
+ (ImportDescriptor->OriginalFirstThunk))
+ {
+ /* Get the image thunk */
+ ImageThunk = (PVOID)((ULONG_PTR)LdrEntry->DllBase +
+ ImportDescriptor->FirstThunk);
+ while (*ImageThunk)
+ {
+ /* Check if it's within this module */
+ if ((*ImageThunk >= (ULONG_PTR)OldBase) && (*ImageThunk <= OldBaseTop))
+ {
+ /* Relocate it */
+ DPRINT("[Mm0]: Updating IAT at: %p. Old Entry: %p. New Entry: %p.\n",
+ ImageThunk, *ImageThunk, *ImageThunk + Delta);
+ *ImageThunk += Delta;
+ }
+
+ /* Go to the next thunk */
+ ImageThunk++;
+ }
+
+ /* Go to the next import */
+ ImportDescriptor++;
+ }
+#endif
+ }
+}
+
+NTSTATUS
+NTAPI
+MiSnapThunk(IN PVOID DllBase,
+ IN PVOID ImageBase,
+ IN PIMAGE_THUNK_DATA Name,
+ IN PIMAGE_THUNK_DATA Address,
+ IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
+ IN ULONG ExportSize,
+ IN BOOLEAN SnapForwarder,
+ OUT PCHAR *MissingApi)
+{
+ BOOLEAN IsOrdinal;
+ USHORT Ordinal;
+ PULONG NameTable;
+ PUSHORT OrdinalTable;
+ PIMAGE_IMPORT_BY_NAME NameImport;
+ USHORT Hint;
+ ULONG Low = 0, Mid = 0, High;
+ LONG Ret;
+ NTSTATUS Status;
+ PCHAR MissingForwarder;
+ CHAR NameBuffer[MAXIMUM_FILENAME_LENGTH];
+ PULONG ExportTable;
+ ANSI_STRING DllName;
+ UNICODE_STRING ForwarderName;
+ PLIST_ENTRY NextEntry;
+ PLDR_DATA_TABLE_ENTRY LdrEntry;
+ ULONG ForwardExportSize;
+ PIMAGE_EXPORT_DIRECTORY ForwardExportDirectory;
+ PIMAGE_IMPORT_BY_NAME ForwardName;
+ ULONG ForwardLength;
+ IMAGE_THUNK_DATA ForwardThunk;
+ PAGED_CODE();
+
+ /* Check if this is an ordinal */
+ IsOrdinal = IMAGE_SNAP_BY_ORDINAL(Name->u1.Ordinal);
+ if ((IsOrdinal) && !(SnapForwarder))
+ {
+ /* Get the ordinal number and set it as missing */
+ Ordinal = (USHORT)(IMAGE_ORDINAL(Name->u1.Ordinal) -
+ ExportDirectory->Base);
+ *MissingApi = (PCHAR)(ULONG_PTR)Ordinal;
+ }
+ else
+ {
+ /* Get the VA if we don't have to snap */
+ if (!SnapForwarder) Name->u1.AddressOfData += (ULONG_PTR)ImageBase;
+ NameImport = (PIMAGE_IMPORT_BY_NAME)Name->u1.AddressOfData;
+
+ /* Copy the procedure name */
+ strncpy(*MissingApi,
+ (PCHAR)&NameImport->Name[0],
+ MAXIMUM_FILENAME_LENGTH - 1);
+
+ /* Setup name tables */
+ DPRINT("Import name: %s\n", NameImport->Name);
+ NameTable = (PULONG)((ULONG_PTR)DllBase +
+ ExportDirectory->AddressOfNames);
+ OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
+ ExportDirectory->AddressOfNameOrdinals);
+
+ /* Get the hint and check if it's valid */
+ Hint = NameImport->Hint;
+ if ((Hint < ExportDirectory->NumberOfNames) &&
+ !(strcmp((PCHAR)NameImport->Name, (PCHAR)DllBase + NameTable[Hint])))
+ {
+ /* We have a match, get the ordinal number from here */
+ Ordinal = OrdinalTable[Hint];
+ }
+ else
+ {
+ /* Do a binary search */
+ High = ExportDirectory->NumberOfNames - 1;
+ while (High >= Low)
+ {
+ /* Get new middle value */
+ Mid = (Low + High) >> 1;
+
+ /* Compare name */
+ Ret = strcmp((PCHAR)NameImport->Name, (PCHAR)DllBase + NameTable[Mid]);
+ if (Ret < 0)
+ {
+ /* Update high */
+ High = Mid - 1;
+ }
+ else if (Ret > 0)
+ {
+ /* Update low */
+ Low = Mid + 1;
+ }
+ else
+ {
+ /* We got it */
+ break;
+ }
+ }
+
+ /* Check if we couldn't find it */
+ if (High < Low) return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
+
+ /* Otherwise, this is the ordinal */
+ Ordinal = OrdinalTable[Mid];
+ }
+ }
+
+ /* Check if the ordinal is invalid */
+ if (Ordinal >= ExportDirectory->NumberOfFunctions)
+ {
+ /* Fail */
+ Status = STATUS_DRIVER_ORDINAL_NOT_FOUND;
+ }
+ else
+ {
+ /* In case the forwarder is missing */
+ MissingForwarder = NameBuffer;
+
+ /* Resolve the address and write it */
+ ExportTable = (PULONG)((ULONG_PTR)DllBase +
+ ExportDirectory->AddressOfFunctions);
+ Address->u1.Function = (ULONG_PTR)DllBase + ExportTable[Ordinal];
+
+ /* Assume success from now on */
+ Status = STATUS_SUCCESS;
+
+ /* Check if the function is actually a forwarder */
+ if ((Address->u1.Function > (ULONG_PTR)ExportDirectory) &&
+ (Address->u1.Function < ((ULONG_PTR)ExportDirectory + ExportSize)))
+ {
+ /* Now assume failure in case the forwarder doesn't exist */
+ Status = STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
+
+ /* Build the forwarder name */
+ DllName.Buffer = (PCHAR)Address->u1.Function;
+ DllName.Length = strchr(DllName.Buffer, '.') -
+ DllName.Buffer +
+ sizeof(ANSI_NULL);
+ DllName.MaximumLength = DllName.Length;
+
+ /* Convert it */
+ if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&ForwarderName,
+ &DllName,
+ TRUE)))
+ {
+ /* We failed, just return an error */
+ return Status;
+ }
+
+ /* Loop the module list */
+ NextEntry = PsLoadedModuleList.Flink;
+ while (NextEntry != &PsLoadedModuleList)
+ {
+ /* Get the loader entry */
+ LdrEntry = CONTAINING_RECORD(NextEntry,
+ LDR_DATA_TABLE_ENTRY,
+ InLoadOrderLinks);
+
+ /* Check if it matches */
+ if (RtlPrefixString((PSTRING)&ForwarderName,
+ (PSTRING)&LdrEntry->BaseDllName,
+ TRUE))
+ {
+ /* Get the forwarder export directory */
+ ForwardExportDirectory =
+ RtlImageDirectoryEntryToData(LdrEntry->DllBase,
+ TRUE,
+ IMAGE_DIRECTORY_ENTRY_EXPORT,
+ &ForwardExportSize);
+ if (!ForwardExportDirectory) break;
+
+ /* Allocate a name entry */
+ ForwardLength = strlen(DllName.Buffer + DllName.Length) +
+ sizeof(ANSI_NULL);
+ ForwardName = ExAllocatePoolWithTag(PagedPool,
+ sizeof(*ForwardName) +
+ ForwardLength,
+ TAG_LDR_WSTR);
+ if (!ForwardName) break;
+
+ /* Copy the data */
+ RtlCopyMemory(&ForwardName->Name[0],
+ DllName.Buffer + DllName.Length,
+ ForwardLength);
+ ForwardName->Hint = 0;
+
+ /* Set the new address */
+ ForwardThunk.u1.AddressOfData = (ULONG_PTR)ForwardName;
+
+ /* Snap the forwarder */
+ Status = MiSnapThunk(LdrEntry->DllBase,
+ ImageBase,
+ &ForwardThunk,
+ &ForwardThunk,
+ ForwardExportDirectory,
+ ForwardExportSize,
+ TRUE,
+ &MissingForwarder);
+
+ /* Free the forwarder name and set the thunk */
+ ExFreePoolWithTag(ForwardName, TAG_LDR_WSTR);
+ Address->u1 = ForwardThunk.u1;
+ break;
+ }
+
+ /* Go to the next entry */
+ NextEntry = NextEntry->Flink;
+ }
+
+ /* Free the name */
+ RtlFreeUnicodeString(&ForwarderName);
+ }
+ }
+
+ /* Return status */
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+MmUnloadSystemImage(IN PVOID ImageHandle)
+{
+ PLDR_DATA_TABLE_ENTRY LdrEntry = ImageHandle;
+ PVOID BaseAddress = LdrEntry->DllBase;
+ NTSTATUS Status;
+ STRING TempName;
+ BOOLEAN HadEntry = FALSE;
+
+ /* Acquire the loader lock */
+ KeEnterCriticalRegion();
+ KeWaitForSingleObject(&MmSystemLoadLock,
+ WrVirtualMemory,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ /* Check if this driver was loaded at boot and didn't get imports parsed */
+ if (LdrEntry->LoadedImports == MM_SYSLDR_BOOT_LOADED) goto Done;
+
+ /* We should still be alive */
+ ASSERT(LdrEntry->LoadCount != 0);
+ LdrEntry->LoadCount--;
+
+ /* Check if we're still loaded */
+ if (LdrEntry->LoadCount) goto Done;
+
+ /* We should cleanup... are symbols loaded */
+ if (LdrEntry->Flags & LDRP_DEBUG_SYMBOLS_LOADED)
+ {
+ /* Create the ANSI name */
+ Status = RtlUnicodeStringToAnsiString(&TempName,
+ &LdrEntry->BaseDllName,
+ TRUE);
+ if (NT_SUCCESS(Status))
+ {
+ /* Unload the symbols */
+ DbgUnLoadImageSymbols(&TempName,
+ BaseAddress,
+ (ULONG_PTR)ZwCurrentProcess());
+ RtlFreeAnsiString(&TempName);
+ }
+ }
+
+ /* FIXME: Free the driver */
+ DPRINT1("Leaking driver: %wZ\n", &LdrEntry->BaseDllName);
+ //MmFreeSection(LdrEntry->DllBase);
+
+ /* Check if we're linked in */
+ if (LdrEntry->InLoadOrderLinks.Flink)
+ {
+ /* Remove us */
+ MiProcessLoaderEntry(LdrEntry, FALSE);
+ HadEntry = TRUE;
+ }
+
+ /* Dereference and clear the imports */
+ MiDereferenceImports(LdrEntry->LoadedImports);
+ MiClearImports(LdrEntry);
+
+ /* Check if the entry needs to go away */
+ if (HadEntry)
+ {
+ /* Check if it had a name */
+ if (LdrEntry->FullDllName.Buffer)
+ {
+ /* Free it */
+ ExFreePool(LdrEntry->FullDllName.Buffer);
+ }
+
+ /* Check if we had a section */
+ if (LdrEntry->SectionPointer)
+ {
+ /* Dereference it */
+ ObDereferenceObject(LdrEntry->SectionPointer);
+ }
+
+ /* Free the entry */
+ ExFreePool(LdrEntry);
+ }
+
+ /* Release the system lock and return */
+Done:
+ KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
+ KeLeaveCriticalRegion();
+ return STATUS_SUCCESS;
+}
+
+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)
+{
+ PCHAR MissingApiBuffer = *MissingApi, ImportName;
+ PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor, CurrentImport;
+ ULONG ImportSize, ImportCount = 0, LoadedImportsSize, ExportSize;
+ PLOAD_IMPORTS LoadedImports, NewImports;
+ ULONG GdiLink, NormalLink, i;
+ BOOLEAN ReferenceNeeded, Loaded;
+ ANSI_STRING TempString;
+ UNICODE_STRING NameString, DllName;
+ PLDR_DATA_TABLE_ENTRY LdrEntry = NULL, DllEntry, ImportEntry = NULL;
+ PVOID ImportBase, DllBase;
+ PLIST_ENTRY NextEntry;
+ PIMAGE_EXPORT_DIRECTORY ExportDirectory;
+ NTSTATUS Status;
+ PIMAGE_THUNK_DATA OrigThunk, FirstThunk;
+ PAGED_CODE();
+ DPRINT("%s - ImageBase: %p. ImageFileDirectory: %wZ\n",
+ __FUNCTION__, ImageBase, ImageFileDirectory);
+
+ /* Assume no imports */
+ *LoadImports = MM_SYSLDR_NO_IMPORTS;
+
+ /* Get the import descriptor */
+ ImportDescriptor = RtlImageDirectoryEntryToData(ImageBase,
+ TRUE,
+ IMAGE_DIRECTORY_ENTRY_IMPORT,
+ &ImportSize);
+ if (!ImportDescriptor) return STATUS_SUCCESS;
+
+ /* Loop all imports to count them */
+ for (CurrentImport = ImportDescriptor;
+ (CurrentImport->Name) && (CurrentImport->OriginalFirstThunk);
+ CurrentImport++)
+ {
+ /* One more */
+ ImportCount++;
+ }
+
+ /* Make sure we have non-zero imports */
+ if (ImportCount)
+ {
+ /* Calculate and allocate the list we'll need */
+ LoadedImportsSize = ImportCount * sizeof(PVOID) + sizeof(SIZE_T);
+ LoadedImports = ExAllocatePoolWithTag(PagedPool,
+ LoadedImportsSize,
+ 'TDmM');
+ if (LoadedImports)
+ {
+ /* Zero it */
+ RtlZeroMemory(LoadedImports, LoadedImportsSize);
+ LoadedImports->Count = ImportCount;
+ }
+ }
+ else
+ {
+ /* No table */
+ LoadedImports = NULL;
+ }
+
+ /* Reset the import count and loop descriptors again */
+ ImportCount = GdiLink = NormalLink = 0;
+ while ((ImportDescriptor->Name) && (ImportDescriptor->OriginalFirstThunk))
+ {
+ /* Get the name */
+ ImportName = (PCHAR)((ULONG_PTR)ImageBase + ImportDescriptor->Name);
+
+ /* Check if this is a GDI driver */
+ GdiLink = GdiLink |
+ !(_strnicmp(ImportName, "win32k", sizeof("win32k") - 1));
+
+ /* We can also allow dxapi (for Windows compat, allow IRT and coverage )*/
+ NormalLink = NormalLink |
+ ((_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) &&
+ (_strnicmp(ImportName, "dxapi", sizeof("dxapi") - 1)) &&
+ (_strnicmp(ImportName, "coverage", sizeof("coverage") - 1)) &&
+ (_strnicmp(ImportName, "irt", sizeof("irt") - 1)));
+
+ /* Check if this is a valid GDI driver */
+ if ((GdiLink) && (NormalLink))
+ {
+ /* It's not, it's importing stuff it shouldn't be! */
+ MiDereferenceImports(LoadedImports);
+ if (LoadedImports) ExFreePool(LoadedImports);
+ return STATUS_PROCEDURE_NOT_FOUND;
+ }
+
+ /* Check for user-mode printer or video card drivers, which don't belong */
+ 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)))
+ {
+ /* This is not kernel code */
+ MiDereferenceImports(LoadedImports);
+ if (LoadedImports) ExFreePool(LoadedImports);
+ return STATUS_PROCEDURE_NOT_FOUND;
+ }
+
+ /* Check if this is a "core" import, which doesn't get referenced */
+ if (!(_strnicmp(ImportName, "ntoskrnl", sizeof("ntoskrnl") - 1)) ||
+ !(_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) ||
+ !(_strnicmp(ImportName, "hal", sizeof("hal") - 1)))
+ {
+ /* Don't reference this */
+ ReferenceNeeded = FALSE;
+ }
+ else
+ {
+ /* Reference these modules */
+ ReferenceNeeded = TRUE;
+ }
+
+ /* Now setup a unicode string for the import */
+ RtlInitAnsiString(&TempString, ImportName);
+ Status = RtlAnsiStringToUnicodeString(&NameString, &TempString, TRUE);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Failed */
+ MiDereferenceImports(LoadedImports);
+ if (LoadedImports) ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
+ return Status;
+ }
+
+ /* We don't support name prefixes yet */
+ if (NamePrefix) DPRINT1("Name Prefix not yet supported!\n");
+
+ /* Remember that we haven't loaded the import at this point */
+CheckDllState:
+ Loaded = FALSE;
+ ImportBase = NULL;
+
+ /* Loop the driver list */
+ NextEntry = PsLoadedModuleList.Flink;
+ while (NextEntry != &PsLoadedModuleList)
+ {
+ /* Get the loader entry and compare the name */
+ LdrEntry = CONTAINING_RECORD(NextEntry,
+ LDR_DATA_TABLE_ENTRY,
+ InLoadOrderLinks);
+ if (RtlEqualUnicodeString(&NameString,
+ &LdrEntry->BaseDllName,
+ TRUE))
+ {
+ /* Get the base address */
+ ImportBase = LdrEntry->DllBase;
+
+ /* Check if we haven't loaded yet, and we need references */
+ if (!(Loaded) && (ReferenceNeeded))
+ {
+ /* Make sure we're not already loading */
+ if (!(LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS))
+ {
+ /* Increase the load count */
+ LdrEntry->LoadCount++;
+ }
+ }
+
+ /* Done, break out */
+ break;
+ }
+
+ /* Go to the next entry */
+ NextEntry = NextEntry->Flink;
+ }
+
+ /* Check if we haven't loaded the import yet */
+ if (!ImportBase)
+ {
+ /* Setup the import DLL name */
+ DllName.MaximumLength = NameString.Length +
+ ImageFileDirectory->Length +
+ sizeof(UNICODE_NULL);
+ DllName.Buffer = ExAllocatePoolWithTag(NonPagedPool,
+ DllName.MaximumLength,
+ 'TDmM');
+ if (DllName.Buffer)
+ {
+ /* Setup the base length and copy it */
+ DllName.Length = ImageFileDirectory->Length;
+ RtlCopyMemory(DllName.Buffer,
+ ImageFileDirectory->Buffer,
+ ImageFileDirectory->Length);
+
+ /* Now add the import name and null-terminate it */
+ RtlAppendStringToString((PSTRING)&DllName,
+ (PSTRING)&NameString);
+ DllName.Buffer[(DllName.MaximumLength - 1) / sizeof(WCHAR)] = UNICODE_NULL;
+
+ /* Load the image */
+ Status = MmLoadSystemImage(&DllName,
+ NamePrefix,
+ NULL,
+ FALSE,
+ (PVOID)&DllEntry,
+ &DllBase);
+ if (NT_SUCCESS(Status))
+ {
+ /* We can free the DLL Name */
+ ExFreePool(DllName.Buffer);
+ }
+ else
+ {
+ /* Fill out the information for the error */
+ *MissingDriver = DllName.Buffer;
+ *(PULONG)MissingDriver |= 1;
+ *MissingApi = NULL;
+ }
+ }
+ else
+ {
+ /* We're out of resources */
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ /* Check if we're OK until now */
+ if (NT_SUCCESS(Status))
+ {
+ /* We're now loaded */
+ Loaded = TRUE;
+
+ /* Sanity check */
+ ASSERT(DllBase = DllEntry->DllBase);
+
+ /* Call the initialization routines */
+ Status = MmCallDllInitialize(DllEntry, &PsLoadedModuleList);
+ if (!NT_SUCCESS(Status))
+ {
+ /* We failed, unload the image */
+ MmUnloadSystemImage(DllEntry);
+ while (TRUE);
+ Loaded = FALSE;
+ }
+ }
+
+ /* Check if we failed by here */
+ if (!NT_SUCCESS(Status))
+ {
+ /* Cleanup and return */
+ RtlFreeUnicodeString(&NameString);
+ MiDereferenceImports(LoadedImports);
+ if (LoadedImports) ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
+ return Status;
+ }
+
+ /* Loop again to make sure that everything is OK */
+ goto CheckDllState;
+ }
+
+ /* Check if we're support to reference this import */
+ if ((ReferenceNeeded) && (LoadedImports))
+ {
+ /* Make sure we're not already loading */
+ if (!(LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS))
+ {
+ /* Add the entry */
+ LoadedImports->Entry[ImportCount] = LdrEntry;
+ ImportCount++;
+ }
+ }
+
+ /* Free the import name */
+ RtlFreeUnicodeString(&NameString);
+
+ /* Set the missing driver name and get the export directory */
+ *MissingDriver = LdrEntry->BaseDllName.Buffer;
+ ExportDirectory = RtlImageDirectoryEntryToData(ImportBase,
+ TRUE,
+ IMAGE_DIRECTORY_ENTRY_EXPORT,
+ &ExportSize);
+ if (!ExportDirectory)
+ {
+ /* Cleanup and return */
+ MiDereferenceImports(LoadedImports);
+ if (LoadedImports) ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
+ return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
+ }
+
+ /* Make sure we have an IAT */
+ if (ImportDescriptor->OriginalFirstThunk)
+ {
+ /* Get the first thunks */
+ OrigThunk = (PVOID)((ULONG_PTR)ImageBase +
+ ImportDescriptor->OriginalFirstThunk);
+ FirstThunk = (PVOID)((ULONG_PTR)ImageBase +
+ ImportDescriptor->FirstThunk);
+
+ /* Loop the IAT */
+ while (OrigThunk->u1.AddressOfData)
+ {
+ /* Snap thunk */
+ Status = MiSnapThunk(ImportBase,
+ ImageBase,
+ OrigThunk++,
+ FirstThunk++,
+ ExportDirectory,
+ ExportSize,
+ FALSE,
+ MissingApi);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Cleanup and return */
+ MiDereferenceImports(LoadedImports);
+ if (LoadedImports) ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
+ return Status;
+ }
+
+ /* Reset the buffer */
+ *MissingApi = MissingApiBuffer;
+ }
+ }
+
+ /* Go to the next import */
+ ImportDescriptor++;
+ }
+
+ /* Check if we have an import list */
+ if (LoadedImports)
+ {
+ /* Reset the count again, and loop entries*/
+ ImportCount = 0;
+ for (i = 0; i < LoadedImports->Count; i++)
+ {
+ if (LoadedImports->Entry[i])
+ {
+ /* Got an entry, OR it with 1 in case it's the single entry */
+ ImportEntry = (PVOID)((ULONG_PTR)LoadedImports->Entry[i] |
+ MM_SYSLDR_SINGLE_ENTRY);
+ ImportCount++;
+ }
+ }
+
+ /* Check if we had no imports */
+ if (!ImportCount)
+ {
+ /* Free the list and set it to no imports */
+ ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
+ LoadedImports = MM_SYSLDR_NO_IMPORTS;
+ }
+ else if (ImportCount == 1)
+ {
+ /* Just one entry, we can free the table and only use our entry */
+ ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
+ LoadedImports = (PLOAD_IMPORTS)ImportEntry;
+ }
+ else if (ImportCount != LoadedImports->Count)
+ {
+ /* Allocate a new list */
+ LoadedImportsSize = ImportCount * sizeof(PVOID) + sizeof(SIZE_T);
+ NewImports = ExAllocatePoolWithTag(PagedPool,
+ LoadedImportsSize,
+ 'TDmM');
+ if (NewImports)
+ {
+ /* Set count */
+ NewImports->Count = 0;
+
+ /* Loop all the imports */
+ for (i = 0; i < LoadedImports->Count; i++)
+ {
+ /* Make sure it's valid */
+ if (LoadedImports->Entry[i])
+ {
+ /* Copy it */
+ NewImports->Entry[NewImports->Count] = LoadedImports->Entry[i];
+ NewImports->Count++;
+ }
+ }
+
+ /* Free the old copy */
+ ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
+ LoadedImports = NewImports;
+ }
+ }
+
+ /* Return the list */
+ *LoadImports = LoadedImports;
+ }
+
+ /* Return success */
+ return STATUS_SUCCESS;
+}
+
+VOID
+NTAPI
+MiReloadBootLoadedDrivers(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
+{
+ PLIST_ENTRY NextEntry;
+ ULONG i = 0;
+ PIMAGE_NT_HEADERS NtHeader;
+ PLDR_DATA_TABLE_ENTRY LdrEntry;
+ PIMAGE_FILE_HEADER FileHeader;
+ BOOLEAN ValidRelocs;
+ PIMAGE_DATA_DIRECTORY DataDirectory;
+ PVOID DllBase, NewImageAddress;
+ NTSTATUS Status;
+ PMMPTE PointerPte, StartPte, LastPte;
+ PFN_NUMBER PteCount;
+ PMMPFN Pfn1;
+ MMPTE TempPte, OldPte;
+
+ /* Loop driver list */
+ for (NextEntry = LoaderBlock->LoadOrderListHead.Flink;
+ NextEntry != &LoaderBlock->LoadOrderListHead;
+ NextEntry = NextEntry->Flink)
+ {
+ /* Get the loader entry and NT header */
+ LdrEntry = CONTAINING_RECORD(NextEntry,
+ LDR_DATA_TABLE_ENTRY,
+ InLoadOrderLinks);
+ NtHeader = RtlImageNtHeader(LdrEntry->DllBase);
+
+ /* Debug info */
+ DPRINT("[Mm0]: Driver at: %p ending at: %p for module: %wZ\n",
+ LdrEntry->DllBase,
+ (ULONG_PTR)LdrEntry->DllBase + LdrEntry->SizeOfImage,
+ &LdrEntry->FullDllName);
+
+ /* Skip kernel and HAL */
+ /* ROS HACK: Skip BOOTVID/KDCOM too */
+ i++;
+ if (i <= 4) continue;
+
+ /* Skip non-drivers */
+ if (!NtHeader) continue;
+
+ /* Get the file header and make sure we can relocate */
+ FileHeader = &NtHeader->FileHeader;
+ if (FileHeader->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) continue;
+ if (NtHeader->OptionalHeader.NumberOfRvaAndSizes <
+ IMAGE_DIRECTORY_ENTRY_BASERELOC) continue;
+
+ /* Everything made sense until now, check the relocation section too */
+ DataDirectory = &NtHeader->OptionalHeader.
+ DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
+ if (!DataDirectory->VirtualAddress)
+ {
+ /* We don't really have relocations */
+ ValidRelocs = FALSE;
+ }
+ else
+ {
+ /* Make sure the size is valid */
+ if ((DataDirectory->VirtualAddress + DataDirectory->Size) >
+ LdrEntry->SizeOfImage)
+ {
+ /* They're not, skip */
+ continue;
+ }
+
+ /* We have relocations */
+ ValidRelocs = TRUE;
+ }
+
+ /* Remember the original address */
+ DllBase = LdrEntry->DllBase;
+
+ /* Get the first PTE and the number of PTEs we'll need */
+ PointerPte = StartPte = MiAddressToPte(LdrEntry->DllBase);
+ PteCount = ROUND_TO_PAGES(LdrEntry->SizeOfImage) >> PAGE_SHIFT;
+ LastPte = StartPte + PteCount;
+
+ /* Loop the PTEs */
+ while (PointerPte < LastPte)
+ {
+ /* Mark the page modified in the PFN database */
+ ASSERT(PointerPte->u.Hard.Valid == 1);
+ Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
+ ASSERT(Pfn1->u3.e1.Rom == 0);
+ Pfn1->u3.e1.Modified = TRUE;
+
+ /* Next */
+ PointerPte++;
+ }
+
+ /* Now reserve system PTEs for the image */
+ PointerPte = MiReserveSystemPtes(PteCount, SystemPteSpace);
+ if (!PointerPte)
+ {
+ /* Shouldn't happen */
+ DPRINT1("[Mm0]: Couldn't allocate driver section!\n");
+ while (TRUE);
+ }
+
+ /* This is the new virtual address for the module */
+ LastPte = PointerPte + PteCount;
+ NewImageAddress = MiPteToAddress(PointerPte);
+
+ /* Sanity check */
+ DPRINT("[Mm0]: Copying from: %p to: %p\n", DllBase, NewImageAddress);
+ ASSERT(ExpInitializationPhase == 0);
+
+ /* Loop the new driver PTEs */
+ TempPte = ValidKernelPte;
+ while (PointerPte < LastPte)
+ {
+ /* Copy the old data */
+ OldPte = *StartPte;
+ ASSERT(OldPte.u.Hard.Valid == 1);
+
+ /* Set page number from the loader's memory */
+ TempPte.u.Hard.PageFrameNumber = OldPte.u.Hard.PageFrameNumber;
+
+ /* Write it */
+ MI_WRITE_VALID_PTE(PointerPte, TempPte);
+
+ /* Move on */
+ PointerPte++;
+ StartPte++;
+ }
+
+ /* Update position */
+ PointerPte -= PteCount;
+
+ /* Sanity check */
+ ASSERT(*(PULONG)NewImageAddress == *(PULONG)DllBase);
+
+ /* Set the image base to the address where the loader put it */
+ NtHeader->OptionalHeader.ImageBase = (ULONG_PTR)DllBase;
+
+ /* Check if we had relocations */
+ if (ValidRelocs)
+ {
+ /* Relocate the image */
+ Status = LdrRelocateImageWithBias(NewImageAddress,
+ 0,
+ "SYSLDR",
+ STATUS_SUCCESS,
+ STATUS_CONFLICTING_ADDRESSES,
+ STATUS_INVALID_IMAGE_FORMAT);
+ if (!NT_SUCCESS(Status))
+ {
+ /* This shouldn't happen */
+ DPRINT1("Relocations failed!\n");
+ while (TRUE);
+ }
+ }
+
+ /* Update the loader entry */
+ LdrEntry->DllBase = NewImageAddress;
+
+ /* Update the thunks */
+ DPRINT("[Mm0]: Updating thunks to: %wZ\n", &LdrEntry->BaseDllName);
+ MiUpdateThunks(LoaderBlock,
+ DllBase,
+ NewImageAddress,
+ LdrEntry->SizeOfImage);
+
+ /* Update the loader entry */
+ LdrEntry->Flags |= LDRP_SYSTEM_MAPPED;
+ LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)NewImageAddress +
+ NtHeader->OptionalHeader.AddressOfEntryPoint);
+ LdrEntry->SizeOfImage = PteCount << PAGE_SHIFT;
+
+ /* FIXME: We'll need to fixup the PFN linkage when switching to ARM3 */
+ }
+}
+
+NTSTATUS
+NTAPI
+MiBuildImportsForBootDrivers(VOID)
+{
+ PLIST_ENTRY NextEntry, NextEntry2;
+ PLDR_DATA_TABLE_ENTRY LdrEntry, KernelEntry, HalEntry, LdrEntry2, LastEntry;
+ PLDR_DATA_TABLE_ENTRY* EntryArray;
+ UNICODE_STRING KernelName = RTL_CONSTANT_STRING(L"ntoskrnl.exe");
+ UNICODE_STRING HalName = RTL_CONSTANT_STRING(L"hal.dll");
+ PLOAD_IMPORTS LoadedImports;
+ ULONG LoadedImportsSize, ImportSize;
+ PULONG_PTR ImageThunk;
+ ULONG_PTR DllBase, DllEnd;
+ ULONG Modules = 0, i, j = 0;
+ PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
+
+ /* Initialize variables */
+ KernelEntry = HalEntry = LastEntry = NULL;
+
+ /* Loop the loaded module list... we are early enough that no lock is needed */
+ NextEntry = PsLoadedModuleList.Flink;
+ while (NextEntry != &PsLoadedModuleList)
+ {
+ /* Get the entry */
+ LdrEntry = CONTAINING_RECORD(NextEntry,
+ LDR_DATA_TABLE_ENTRY,
+ InLoadOrderLinks);
+
+ /* Check if it's the kernel or HAL */
+ if (RtlEqualUnicodeString(&KernelName, &LdrEntry->BaseDllName, TRUE))
+ {
+ /* Found it */
+ KernelEntry = LdrEntry;
+ }
+ else if (RtlEqualUnicodeString(&HalName, &LdrEntry->BaseDllName, TRUE))
+ {
+ /* Found it */
+ HalEntry = LdrEntry;
+ }
+
+ /* Check if this is a driver DLL */
+ if (LdrEntry->Flags & LDRP_DRIVER_DEPENDENT_DLL)
+ {
+ /* Check if this is the HAL or kernel */
+ if ((LdrEntry == HalEntry) || (LdrEntry == KernelEntry))
+ {
+ /* Add a reference */
+ LdrEntry->LoadCount = 1;
+ }
+ else
+ {
+ /* No referencing needed */
+ LdrEntry->LoadCount = 0;
+ }
+ }
+ else
+ {
+ /* No referencing needed */
+ LdrEntry->LoadCount = 0;
+ }
+
+ /* Remember this came from the loader */
+ LdrEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED;
+
+ /* Keep looping */
+ NextEntry = NextEntry->Flink;
+ Modules++;
+ }
+
+ /* We must have at least found the kernel and HAL */
+ if (!(HalEntry) || (!KernelEntry)) return STATUS_NOT_FOUND;
+
+ /* Allocate the list */
+ EntryArray = ExAllocatePoolWithTag(PagedPool, Modules * sizeof(PVOID), 'TDmM');
+ if (!EntryArray) return STATUS_INSUFFICIENT_RESOURCES;
+
+ /* Loop the loaded module list again */
+ NextEntry = PsLoadedModuleList.Flink;
+ while (NextEntry != &PsLoadedModuleList)
+ {
+ /* Get the entry */
+ LdrEntry = CONTAINING_RECORD(NextEntry,
+ LDR_DATA_TABLE_ENTRY,
+ InLoadOrderLinks);
+#ifdef _WORKING_LOADER_
+ /* Get its imports */
+ ImageThunk = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
+ TRUE,
+ IMAGE_DIRECTORY_ENTRY_IAT,
+ &ImportSize);
+ if (!ImageThunk)
+#else
+ /* Get its imports */
+ ImportDescriptor = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
+ TRUE,
+ IMAGE_DIRECTORY_ENTRY_IMPORT,
+ &ImportSize);
+ if (!ImportDescriptor)
+#endif
+ {
+ /* None present */
+ LdrEntry->LoadedImports = MM_SYSLDR_NO_IMPORTS;
+ NextEntry = NextEntry->Flink;
+ continue;
+ }
+
+ /* Clear the list and count the number of IAT thunks */
+ RtlZeroMemory(EntryArray, Modules * sizeof(PVOID));
+#ifdef _WORKING_LOADER_
+ ImportSize /= sizeof(ULONG_PTR);
+
+ /* Scan the thunks */
+ for (i = 0, DllBase = 0, DllEnd = 0; i < ImportSize; i++, ImageThunk++)
+#else
+ i = DllBase = DllEnd = 0;
+ while ((ImportDescriptor->Name) &&
+ (ImportDescriptor->OriginalFirstThunk))
+ {
+ /* Get the image thunk */
+ ImageThunk = (PVOID)((ULONG_PTR)LdrEntry->DllBase +
+ ImportDescriptor->FirstThunk);
+ while (*ImageThunk)
+#endif
+ {
+ /* Do we already have an address? */
+ if (DllBase)
+ {
+ /* Is the thunk in the same address? */
+ if ((*ImageThunk >= DllBase) && (*ImageThunk < DllEnd))
+ {
+ /* Skip it, we already have a reference for it */
+ ASSERT(EntryArray[j]);
+ ImageThunk++;
+ continue;
+ }
+ }
+
+ /* Loop the loaded module list to locate this address owner */
+ j = 0;
+ NextEntry2 = PsLoadedModuleList.Flink;
+ while (NextEntry2 != &PsLoadedModuleList)
+ {
+ /* Get the entry */
+ LdrEntry2 = CONTAINING_RECORD(NextEntry2,
+ LDR_DATA_TABLE_ENTRY,
+ InLoadOrderLinks);
+
+ /* Get the address range for this module */
+ DllBase = (ULONG_PTR)LdrEntry2->DllBase;
+ DllEnd = DllBase + LdrEntry2->SizeOfImage;
+
+ /* Check if this IAT entry matches it */
+ if ((*ImageThunk >= DllBase) && (*ImageThunk < DllEnd))
+ {
+ /* Save it */
+ //DPRINT1("Found imported dll: %wZ\n", &LdrEntry2->BaseDllName);
+ EntryArray[j] = LdrEntry2;
+ break;
+ }
+
+ /* Keep searching */
+ NextEntry2 = NextEntry2->Flink;
+ j++;
+ }
+
+ /* Do we have a thunk outside the range? */
+ if ((*ImageThunk < DllBase) || (*ImageThunk >= DllEnd))
+ {
+ /* Could be 0... */
+ if (*ImageThunk)
+ {
+ /* Should not be happening */
+ DPRINT1("Broken IAT entry for %p at %p (%lx)\n",
+ LdrEntry, ImageThunk, *ImageThunk);
+ ASSERT(FALSE);
+ }
+
+ /* Reset if we hit this */
+ DllBase = 0;
+ }
+#ifndef _WORKING_LOADER_
+ ImageThunk++;
+ }
+
+ i++;
+ ImportDescriptor++;
+#endif
+ }
+
+ /* Now scan how many imports we really have */
+ for (i = 0, ImportSize = 0; i < Modules; i++)
+ {
+ /* Skip HAL and kernel */
+ if ((EntryArray[i]) &&
+ (EntryArray[i] != HalEntry) &&
+ (EntryArray[i] != KernelEntry))
+ {
+ /* A valid reference */
+ LastEntry = EntryArray[i];
+ ImportSize++;
+ }
+ }
+
+ /* Do we have any imports after all? */
+ if (!ImportSize)
+ {
+ /* No */
+ LdrEntry->LoadedImports = MM_SYSLDR_NO_IMPORTS;
+ }
+ else if (ImportSize == 1)
+ {
+ /* A single entry import */
+ LdrEntry->LoadedImports = (PVOID)((ULONG_PTR)LastEntry | MM_SYSLDR_SINGLE_ENTRY);
+ LastEntry->LoadCount++;
+ }
+ else
+ {
+ /* We need an import table */
+ LoadedImportsSize = ImportSize * sizeof(PVOID) + sizeof(SIZE_T);
+ LoadedImports = ExAllocatePoolWithTag(PagedPool,
+ LoadedImportsSize,
+ 'TDmM');
+ ASSERT(LoadedImports);
+
+ /* Save the count */
+ LoadedImports->Count = ImportSize;
+
+ /* Now copy all imports */
+ for (i = 0, j = 0; i < Modules; i++)
+ {
+ /* Skip HAL and kernel */
+ if ((EntryArray[i]) &&
+ (EntryArray[i] != HalEntry) &&
+ (EntryArray[i] != KernelEntry))
+ {
+ /* A valid reference */
+ //DPRINT1("Found valid entry: %p\n", EntryArray[i]);
+ LoadedImports->Entry[j] = EntryArray[i];
+ EntryArray[i]->LoadCount++;
+ j++;
+ }
+ }
+
+ /* Should had as many entries as we expected */
+ ASSERT(j == ImportSize);
+ LdrEntry->LoadedImports = LoadedImports;
+ }
+
+ /* Next */
+ NextEntry = NextEntry->Flink;
+ }
+
+ /* Free the initial array */
+ ExFreePool(EntryArray);
+
+ /* FIXME: Might not need to keep the HAL/Kernel imports around */
+
+ /* Kernel and HAL are loaded at boot */
+ KernelEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED;
+ HalEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED;
+
+ /* All worked well */
+ return STATUS_SUCCESS;
+}
+
+VOID
+NTAPI
+MiLocateKernelSections(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
+{
+ ULONG_PTR DllBase;
+ PIMAGE_NT_HEADERS NtHeaders;
+ PIMAGE_SECTION_HEADER SectionHeader;
+ ULONG Sections, Size;
+
+ /* Get the kernel section header */
+ DllBase = (ULONG_PTR)LdrEntry->DllBase;
+ NtHeaders = RtlImageNtHeader((PVOID)DllBase);
+ SectionHeader = IMAGE_FIRST_SECTION(NtHeaders);
+
+ /* Loop all the sections */
+ Sections = NtHeaders->FileHeader.NumberOfSections;
+ while (Sections)
+ {
+ /* Grab the size of the section */
+ Size = max(SectionHeader->SizeOfRawData, SectionHeader->Misc.VirtualSize);
+
+ /* Check for .RSRC section */
+ if (*(PULONG)SectionHeader->Name == 'rsr.')
+ {
+ /* Remember the PTEs so we can modify them later */
+ MiKernelResourceStartPte = MiAddressToPte(DllBase +
+ SectionHeader->VirtualAddress);
+ MiKernelResourceEndPte = MiKernelResourceStartPte +
+ BYTES_TO_PAGES(SectionHeader->VirtualAddress + Size);
+ }
+ else if (*(PULONG)SectionHeader->Name == 'LOOP')
+ {
+ /* POOLCODE vs. POOLMI */
+ if (*(PULONG)&SectionHeader->Name[4] == 'EDOC')
+ {
+ /* Found Ex* Pool code */
+ ExPoolCodeStart = DllBase + SectionHeader->VirtualAddress;
+ ExPoolCodeEnd = ExPoolCodeStart + Size;
+ }
+ else if (*(PUSHORT)&SectionHeader->Name[4] == 'MI')
+ {
+ /* Found Mm* Pool code */
+ MmPoolCodeStart = DllBase + SectionHeader->VirtualAddress;
+ MmPoolCodeEnd = ExPoolCodeStart + Size;
+ }
+ }
+ else if ((*(PULONG)SectionHeader->Name == 'YSIM') &&
+ (*(PULONG)&SectionHeader->Name[4] == 'ETPS'))
+ {
+ /* Found MISYSPTE (Mm System PTE code)*/
+ MmPteCodeStart = DllBase + SectionHeader->VirtualAddress;
+ MmPteCodeEnd = ExPoolCodeStart + Size;
+ }
+
+ /* Keep going */
+ Sections--;
+ SectionHeader++;
+ }
+}
+
+BOOLEAN
+NTAPI
+MiInitializeLoadedModuleList(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
+{
+ PLDR_DATA_TABLE_ENTRY LdrEntry, NewEntry;
+ PLIST_ENTRY ListHead, NextEntry;
+ ULONG EntrySize;
+
+ /* Setup the loaded module list and locks */
+ ExInitializeResourceLite(&PsLoadedModuleResource);
+ KeInitializeSpinLock(&PsLoadedModuleSpinLock);
+ InitializeListHead(&PsLoadedModuleList);
+
+ /* Get loop variables and the kernel entry */
+ ListHead = &LoaderBlock->LoadOrderListHead;
+ NextEntry = ListHead->Flink;
+ LdrEntry = CONTAINING_RECORD(NextEntry,
+ LDR_DATA_TABLE_ENTRY,
+ InLoadOrderLinks);
+ PsNtosImageBase = (ULONG_PTR)LdrEntry->DllBase;
+
+ /* Locate resource section, pool code, and system pte code */
+ MiLocateKernelSections(LdrEntry);
+
+ /* Loop the loader block */
+ while (NextEntry != ListHead)
+ {
+ /* Get the loader entry */
+ LdrEntry = CONTAINING_RECORD(NextEntry,
+ LDR_DATA_TABLE_ENTRY,
+ InLoadOrderLinks);
+
+ /* FIXME: ROS HACK. Make sure this is a driver */
+ if (!RtlImageNtHeader(LdrEntry->DllBase))
+ {
+ /* Skip this entry */
+ NextEntry = NextEntry->Flink;
+ continue;
+ }
+
+ /* Calculate the size we'll need and allocate a copy */
+ EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
+ LdrEntry->BaseDllName.MaximumLength +
+ sizeof(UNICODE_NULL);
+ NewEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_LDR_WSTR);
+ if (!NewEntry) return FALSE;
+
+ /* Copy the entry over */
+ *NewEntry = *LdrEntry;
+
+ /* Allocate the name */
+ NewEntry->FullDllName.Buffer =
+ ExAllocatePoolWithTag(PagedPool,
+ LdrEntry->FullDllName.MaximumLength +
+ sizeof(UNICODE_NULL),
+ TAG_LDR_WSTR);
+ if (!NewEntry->FullDllName.Buffer) return FALSE;
+
+ /* Set the base name */
+ NewEntry->BaseDllName.Buffer = (PVOID)(NewEntry + 1);
+
+ /* Copy the full and base name */
+ RtlCopyMemory(NewEntry->FullDllName.Buffer,
+ LdrEntry->FullDllName.Buffer,
+ LdrEntry->FullDllName.MaximumLength);
+ RtlCopyMemory(NewEntry->BaseDllName.Buffer,
+ LdrEntry->BaseDllName.Buffer,
+ LdrEntry->BaseDllName.MaximumLength);
+
+ /* Null-terminate the base name */
+ NewEntry->BaseDllName.Buffer[NewEntry->BaseDllName.Length /
+ sizeof(WCHAR)] = UNICODE_NULL;
+
+ /* Insert the entry into the list */
+ InsertTailList(&PsLoadedModuleList, &NewEntry->InLoadOrderLinks);
+ NextEntry = NextEntry->Flink;
+ }
+
+ /* Build the import lists for the boot drivers */
+ MiBuildImportsForBootDrivers();
+
+ /* We're done */
+ return TRUE;
+}
+
+LOGICAL
+NTAPI
+MiUseLargeDriverPage(IN ULONG NumberOfPtes,
+ IN OUT PVOID *ImageBaseAddress,
+ IN PUNICODE_STRING BaseImageName,
+ IN BOOLEAN BootDriver)
+{
+ PLIST_ENTRY NextEntry;
+ BOOLEAN DriverFound = FALSE;
+ PMI_LARGE_PAGE_DRIVER_ENTRY LargePageDriverEntry;
+ ASSERT(KeGetCurrentIrql () <= APC_LEVEL);
+ ASSERT(*ImageBaseAddress >= MmSystemRangeStart);
+
+#ifdef _X86_
+ if (!(KeFeatureBits & KF_LARGE_PAGE)) return FALSE;
+ if (!(__readcr4() & CR4_PSE)) return FALSE;
+#endif
+
+ /* Make sure there's enough system PTEs for a large page driver */
+ if (MmTotalFreeSystemPtes[SystemPteSpace] < (16 * (PDE_MAPPED_VA >> PAGE_SHIFT)))
+ {
+ return FALSE;
+ }
+
+ /* This happens if the registry key had a "*" (wildcard) in it */
+ if (MiLargePageAllDrivers == 0)
+ {
+ /* It didn't, so scan the list */
+ NextEntry = MiLargePageDriverList.Flink;
+ while (NextEntry != &MiLargePageDriverList)
+ {
+ /* Check if the driver name matches */
+ LargePageDriverEntry = CONTAINING_RECORD(NextEntry,
+ MI_LARGE_PAGE_DRIVER_ENTRY,
+ Links);
+ if (RtlEqualUnicodeString(BaseImageName,
+ &LargePageDriverEntry->BaseName,
+ TRUE))
+ {
+ /* Enable large pages for this driver */
+ DriverFound = TRUE;
+ break;
+ }
+
+ /* Keep trying */
+ NextEntry = NextEntry->Flink;
+ }
+
+ /* If we didn't find the driver, it doesn't need large pages */
+ if (DriverFound == FALSE) return FALSE;
+ }
+
+ /* Nothing to do yet */
+ DPRINT1("Large pages not supported!\n");
+ return FALSE;
+}
+
+ULONG
+NTAPI
+MiComputeDriverProtection(IN BOOLEAN SessionSpace,
+ IN ULONG SectionProtection)
+{
+ ULONG Protection = MM_ZERO_ACCESS;
+
+ /* Check if the caller gave anything */
+ if (SectionProtection)
+ {
+ /* Always turn on execute access */
+ SectionProtection |= IMAGE_SCN_MEM_EXECUTE;
+
+ /* Check if the registry setting is on or not */
+ if (!MmEnforceWriteProtection)
+ {
+ /* Turn on write access too */
+ SectionProtection |= (IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE);
+ }
+ }
+
+ /* Convert to internal PTE flags */
+ if (SectionProtection & IMAGE_SCN_MEM_EXECUTE) Protection |= MM_EXECUTE;
+ if (SectionProtection & IMAGE_SCN_MEM_READ) Protection |= MM_READONLY;
+
+ /* Check for write access */
+ if (SectionProtection & IMAGE_SCN_MEM_WRITE)
+ {
+ /* Session space is not supported */
+ if (SessionSpace)
+ {
+ DPRINT1("Session drivers not supported\n");
+ ASSERT(SessionSpace == FALSE);
+ }
+ else
+ {
+ /* Convert to internal PTE flag */
+ Protection = (Protection & MM_EXECUTE) ? MM_EXECUTE_READWRITE : MM_READWRITE;
+ }
+ }
+
+ /* If there's no access at all by now, convert to internal no access flag */
+ if (Protection == MM_ZERO_ACCESS) Protection = MM_NOACCESS;
+
+ /* Return the computed PTE protection */
+ return Protection;
+}
+
+VOID
+NTAPI
+MiSetSystemCodeProtection(IN PMMPTE FirstPte,
+ IN PMMPTE LastPte,
+ IN ULONG ProtectionMask)
+{
+ /* I'm afraid to introduce regressions at the moment... */
+ return;
+}
+
+VOID
+NTAPI
+MiWriteProtectSystemImage(IN PVOID ImageBase)
+{
+ PIMAGE_NT_HEADERS NtHeaders;
+ PIMAGE_SECTION_HEADER Section;
+ PFN_NUMBER DriverPages;
+ ULONG CurrentProtection, SectionProtection, CombinedProtection, ProtectionMask;
+ ULONG Sections, Size;
+ ULONG_PTR BaseAddress, CurrentAddress;
+ PMMPTE PointerPte, StartPte, LastPte, CurrentPte, ComboPte = NULL;
+ ULONG CurrentMask, CombinedMask = 0;
+ PAGED_CODE();
+
+ /* No need to write protect physical memory-backed drivers (large pages) */
+ if (MI_IS_PHYSICAL_ADDRESS(ImageBase)) return;
+
+ /* Get the image headers */
+ NtHeaders = RtlImageNtHeader(ImageBase);
+ if (!NtHeaders) return;
+
+ /* Check if this is a session driver or not */
+ if (!MI_IS_SESSION_ADDRESS(ImageBase))
+ {
+ /* Don't touch NT4 drivers */
+ if (NtHeaders->OptionalHeader.MajorOperatingSystemVersion < 5) return;
+ if (NtHeaders->OptionalHeader.MajorImageVersion < 5) return;
+ }
+ else
+ {
+ /* Not supported */
+ DPRINT1("Session drivers not supported\n");
+ ASSERT(FALSE);
+ }
+
+ /* These are the only protection masks we care about */
+ ProtectionMask = IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE;
+
+ /* Calculate the number of pages this driver is occupying */
+ DriverPages = BYTES_TO_PAGES(NtHeaders->OptionalHeader.SizeOfImage);
+
+ /* Get the number of sections and the first section header */
+ Sections = NtHeaders->FileHeader.NumberOfSections;
+ ASSERT(Sections != 0);
+ Section = IMAGE_FIRST_SECTION(NtHeaders);
+
+ /* Loop all the sections */
+ CurrentAddress = (ULONG_PTR)ImageBase;
+ while (Sections)
+ {
+ /* Get the section size */
+ Size = max(Section->SizeOfRawData, Section->Misc.VirtualSize);
+
+ /* Get its virtual address */
+ BaseAddress = (ULONG_PTR)ImageBase + Section->VirtualAddress;
+ if (BaseAddress < CurrentAddress)
+ {
+ /* Windows doesn't like these */
+ DPRINT1("Badly linked image!\n");
+ return;
+ }
+
+ /* Remember the current address */
+ CurrentAddress = BaseAddress + Size - 1;
+
+ /* Next */
+ Sections--;
+ Section++;
+ }
+
+ /* Get the number of sections and the first section header */
+ Sections = NtHeaders->FileHeader.NumberOfSections;
+ ASSERT(Sections != 0);
+ Section = IMAGE_FIRST_SECTION(NtHeaders);
+
+ /* Set the address at the end to initialize the loop */
+ CurrentAddress = (ULONG_PTR)Section + Sections - 1;
+ CurrentProtection = IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ;
+
+ /* Set the PTE points for the image, and loop its sections */
+ StartPte = MiAddressToPte(ImageBase);
+ LastPte = StartPte + DriverPages;
+ while (Sections)
+ {
+ /* Get the section size */
+ Size = max(Section->SizeOfRawData, Section->Misc.VirtualSize);
+
+ /* Get its virtual address and PTE */
+ BaseAddress = (ULONG_PTR)ImageBase + Section->VirtualAddress;
+ PointerPte = MiAddressToPte(BaseAddress);
+
+ /* Check if we were already protecting a run, and found a new run */
+ if ((ComboPte) && (PointerPte > ComboPte))
+ {
+ /* Compute protection */
+ CombinedMask = MiComputeDriverProtection(FALSE, CombinedProtection);
+
+ /* Set it */
+ MiSetSystemCodeProtection(ComboPte, ComboPte, CombinedMask);
+
+ /* Check for overlap */
+ if (ComboPte == StartPte) StartPte++;
+
+ /* One done, reset variables */
+ ComboPte = NULL;
+ CombinedProtection = 0;
+ }
+
+ /* Break out when needed */
+ if (PointerPte >= LastPte) break;
+
+ /* Get the requested protection from the image header */
+ SectionProtection = Section->Characteristics & ProtectionMask;
+ if (SectionProtection == CurrentProtection)
+ {
+ /* Same protection, so merge the request */
+ CurrentAddress = BaseAddress + Size - 1;
+
+ /* Next */
+ Sections--;
+ Section++;
+ continue;
+ }
+
+ /* This is now a new section, so close up the old one */
+ CurrentPte = MiAddressToPte(CurrentAddress);
+
+ /* Check for overlap */
+ if (CurrentPte == PointerPte)
+ {
+ /* Skip the last PTE, since it overlaps with us */
+ CurrentPte--;
+
+ /* And set the PTE we will merge with */
+ ASSERT((ComboPte == NULL) || (ComboPte == PointerPte));
+ ComboPte = PointerPte;
+
+ /* Get the most flexible protection by merging both */
+ CombinedMask |= (SectionProtection | CurrentProtection);
+ }
+
+ /* Loop any PTEs left */
+ if (CurrentPte >= StartPte)
+ {
+ /* Sanity check */
+ ASSERT(StartPte < LastPte);
+
+ /* Make sure we don't overflow past the last PTE in the driver */
+ if (CurrentPte >= LastPte) CurrentPte = LastPte - 1;
+ ASSERT(CurrentPte >= StartPte);
+
+ /* Compute the protection and set it */
+ CurrentMask = MiComputeDriverProtection(FALSE, CurrentProtection);
+ MiSetSystemCodeProtection(StartPte, CurrentPte, CurrentMask);
+ }
+
+ /* Set new state */
+ StartPte = PointerPte;
+ CurrentAddress = BaseAddress + Size - 1;
+ CurrentProtection = SectionProtection;
+
+ /* Next */
+ Sections--;
+ Section++;
+ }
+
+ /* Is there a leftover section to merge? */
+ if (ComboPte)
+ {
+ /* Compute and set the protection */
+ CombinedMask = MiComputeDriverProtection(FALSE, CombinedProtection);
+ MiSetSystemCodeProtection(ComboPte, ComboPte, CombinedMask);
+
+ /* Handle overlap */
+ if (ComboPte == StartPte) StartPte++;
+ }
+
+ /* Finally, handle the last section */
+ CurrentPte = MiPteToAddress(CurrentAddress);
+ if ((StartPte < LastPte) && (CurrentPte >= StartPte))
+ {
+ /* Handle overlap */
+ if (CurrentPte >= LastPte) CurrentPte = LastPte - 1;
+ ASSERT(CurrentPte >= StartPte);
+
+ /* Compute and set the protection */
+ CurrentMask = MiComputeDriverProtection(FALSE, CurrentProtection);
+ MiSetSystemCodeProtection(StartPte, CurrentPte, CurrentMask);
+ }
+}
+
+VOID
+NTAPI
+MiSetPagingOfDriver(IN PMMPTE PointerPte,
+ IN PMMPTE LastPte)
+{
+ PVOID ImageBase;
+ PETHREAD CurrentThread;
+ PFN_NUMBER PageCount = 0, PageFrameIndex;
+ PMMPFN Pfn1;
+ PAGED_CODE();
+
+ /* Get the driver's base address */
+ ImageBase = MiPteToAddress(PointerPte);
+ ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(ImageBase) == FALSE);
+
+ /* If this is a large page, it's stuck in physical memory */
+ if (MI_IS_PHYSICAL_ADDRESS(ImageBase)) return;
+
+ /* We should lock the system working set -- we don't have one yet, so just be consistent */
+ CurrentThread = PsGetCurrentThread();
+ KeEnterGuardedRegion();
+ ASSERT((CurrentThread->OwnsSystemWorkingSetExclusive == 0) &&
+ (CurrentThread->OwnsSystemWorkingSetShared == 0));
+ CurrentThread->OwnsSystemWorkingSetExclusive = 1;
+
+ /* Loop the PTEs */
+ while (PointerPte <= LastPte)
+ {
+ /* Check for valid PTE */
+ if (PointerPte->u.Hard.Valid == 1)
+ {
+ PageFrameIndex = PFN_FROM_PTE(PointerPte);
+ Pfn1 = MiGetPfnEntry(PageFrameIndex);
+ ASSERT(Pfn1->u2.ShareCount == 1);
+
+ /* No working sets in ReactOS yet */
+ PageCount++;
+ }
+
+ ImageBase = (PVOID)((ULONG_PTR)ImageBase + PAGE_SIZE);
+ PointerPte++;
+ }
+
+ /* Release the working set "lock" */
+ ASSERT(KeAreAllApcsDisabled() == TRUE);
+ CurrentThread->OwnsSystemWorkingSetExclusive = 0;
+ KeLeaveGuardedRegion();
+
+ /* Do we have any driver pages? */
+ if (PageCount)
+ {
+ /* Update counters */
+ InterlockedExchangeAdd((PLONG)&MmTotalSystemDriverPages, PageCount);
+ }
+}
+
+VOID
+NTAPI
+MiEnablePagingOfDriver(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
+{
+ ULONG_PTR ImageBase;
+ PIMAGE_NT_HEADERS NtHeaders;
+ ULONG Sections, Alignment, Size;
+ PIMAGE_SECTION_HEADER Section;
+ PMMPTE PointerPte = NULL, LastPte = NULL;
+ if (MmDisablePagingExecutive) return;
+
+ /* Get the driver base address and its NT header */
+ ImageBase = (ULONG_PTR)LdrEntry->DllBase;
+ NtHeaders = RtlImageNtHeader((PVOID)ImageBase);
+ if (!NtHeaders) return;
+
+ /* Get the sections and their alignment */
+ Sections = NtHeaders->FileHeader.NumberOfSections;
+ Alignment = NtHeaders->OptionalHeader.SectionAlignment - 1;
+
+ /* Loop each section */
+ Section = IMAGE_FIRST_SECTION(NtHeaders);
+ while (Sections)
+ {
+ /* Find PAGE or .edata */
+ if ((*(PULONG)Section->Name == 'EGAP') ||
+ (*(PULONG)Section->Name == 'ade.'))
+ {
+ /* Had we already done some work? */
+ if (!PointerPte)
+ {
+ /* Nope, setup the first PTE address */
+ PointerPte = MiAddressToPte(ROUND_TO_PAGES(ImageBase +
+ Section->
+ VirtualAddress));
+ }
+
+ /* Compute the size */
+ Size = max(Section->SizeOfRawData, Section->Misc.VirtualSize);
+
+ /* Find the last PTE that maps this section */
+ LastPte = MiAddressToPte(ImageBase +
+ Section->VirtualAddress +
+ Alignment +
+ Size -
+ PAGE_SIZE);
+ }
+ else
+ {
+ /* Had we found a section before? */
+ if (PointerPte)
+ {
+ /* Mark it as pageable */
+ MiSetPagingOfDriver(PointerPte, LastPte);
+ PointerPte = NULL;
+ }
+ }
+
+ /* Keep searching */
+ Sections--;
+ Section++;
+ }
+
+ /* Handle the straggler */
+ if (PointerPte) MiSetPagingOfDriver(PointerPte, LastPte);
+}
+
+BOOLEAN
+NTAPI
+MmVerifyImageIsOkForMpUse(IN PVOID BaseAddress)
+{
+ PIMAGE_NT_HEADERS NtHeader;
+ PAGED_CODE();
+
+ /* Get NT Headers */
+ NtHeader = RtlImageNtHeader(BaseAddress);
+ if (NtHeader)
+ {
+ /* Check if this image is only safe for UP while we have 2+ CPUs */
+ if ((KeNumberProcessors > 1) &&
+ (NtHeader->FileHeader.Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY))
+ {
+ /* Fail */
+ return FALSE;
+ }
+ }
+
+ /* Otherwise, it's safe */
+ return TRUE;
+}
+
+NTSTATUS
+NTAPI
+MmCheckSystemImage(IN HANDLE ImageHandle,
+ IN BOOLEAN PurgeSection)
+{
+ NTSTATUS Status;
+ HANDLE SectionHandle;
+ PVOID ViewBase = NULL;
+ SIZE_T ViewSize = 0;
+ IO_STATUS_BLOCK IoStatusBlock;
+ FILE_STANDARD_INFORMATION FileStandardInfo;
+ KAPC_STATE ApcState;
+ PIMAGE_NT_HEADERS NtHeaders;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ PAGED_CODE();
+
+ /* Setup the object attributes */
+ InitializeObjectAttributes(&ObjectAttributes,
+ NULL,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ NULL,
+ NULL);
+
+ /* Create a section for the DLL */
+ Status = ZwCreateSection(&SectionHandle,
+ SECTION_MAP_EXECUTE,
+ &ObjectAttributes,
+ NULL,
+ PAGE_EXECUTE,
+ SEC_IMAGE,
+ ImageHandle);
+ if (!NT_SUCCESS(Status)) return Status;
+
+ /* Make sure we're in the system process */
+ KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);
+
+ /* Map it */
+ Status = ZwMapViewOfSection(SectionHandle,
+ NtCurrentProcess(),
+ &ViewBase,
+ 0,
+ 0,
+ NULL,
+ &ViewSize,
+ ViewShare,
+ 0,
+ PAGE_EXECUTE);
+ if (!NT_SUCCESS(Status))
+ {
+ /* We failed, close the handle and return */
+ KeUnstackDetachProcess(&ApcState);
+ ZwClose(SectionHandle);
+ return Status;
+ }
+
+ /* Now query image information */
+ Status = ZwQueryInformationFile(ImageHandle,
+ &IoStatusBlock,
+ &FileStandardInfo,
+ sizeof(FileStandardInfo),
+ FileStandardInformation);
+ if (NT_SUCCESS(Status))
+ {
+ /* First, verify the checksum */
+ if (!LdrVerifyMappedImageMatchesChecksum(ViewBase,
+ ViewSize,
+ FileStandardInfo.
+ EndOfFile.LowPart))
+ {
+ /* Set checksum failure */
+ Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
+ goto Fail;
+ }
+
+ /* Make sure it's a real image */
+ NtHeaders = RtlImageNtHeader(ViewBase);
+ if (!NtHeaders)
+ {
+ /* Set checksum failure */
+ Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
+ goto Fail;
+ }
+
+ /* Make sure it's for the correct architecture */
+ if ((NtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_NATIVE) ||
+ (NtHeaders->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC))
+ {
+ /* Set protection failure */
+ Status = STATUS_INVALID_IMAGE_PROTECT;
+ goto Fail;
+ }
+
+ /* Check that it's a valid SMP image if we have more then one CPU */
+ if (!MmVerifyImageIsOkForMpUse(ViewBase))
+ {
+ /* Otherwise it's not the right image */
+ Status = STATUS_IMAGE_MP_UP_MISMATCH;
+ }
+ }
+
+ /* Unmap the section, close the handle, and return status */
+Fail:
+ ZwUnmapViewOfSection(NtCurrentProcess(), ViewBase);
+ KeUnstackDetachProcess(&ApcState);
+ ZwClose(SectionHandle);
+ 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;
+ IO_STATUS_BLOCK IoStatusBlock;
+ PIMAGE_NT_HEADERS NtHeader;
+ UNICODE_STRING BaseName, BaseDirectory, PrefixName, UnicodeTemp;
+ PLDR_DATA_TABLE_ENTRY LdrEntry = NULL;
+ ULONG EntrySize, DriverSize;
+ PLOAD_IMPORTS LoadedImports = MM_SYSLDR_NO_IMPORTS;
+ PCHAR MissingApiName, Buffer;
+ PWCHAR MissingDriverName;
+ HANDLE SectionHandle;
+ ACCESS_MASK DesiredAccess;
+ PVOID Section = NULL;
+ BOOLEAN LockOwned = FALSE;
+ PLIST_ENTRY NextEntry;
+ IMAGE_INFO ImageInfo;
+ STRING AnsiTemp;
+ 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;
+ }
+
+ /* Allocate a buffer we'll use for names */
+ Buffer = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH, 'nLmM');
+ 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;
+
+ /* Check for loader snap debugging */
+ if (NtGlobalFlag & FLG_SHOW_LDR_SNAPS)
+ {
+ /* Print out standard string */
+ DPRINT1("MM:SYSLDR Loading %wZ (%wZ) %s\n",
+ &PrefixName, &BaseName, Flags ? "in session space" : "");
+ }
+
+ /* 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 | OBJ_KERNEL_HANDLE,
+ NULL,
+ NULL);
+
+ /* Open the image */
+ Status = ZwOpenFile(&FileHandle,
+ FILE_EXECUTE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_READ | FILE_SHARE_DELETE,
+ 0);
+ 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_PROTECT))
+ {
+ /* Fail loading */
+ 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;
+ }
+
+ /* Load the image */
+ Status = MiLoadImageSection(&Section,
+ &ModuleLoadBase,
+ FileName,
+ FALSE,
+ NULL);
+ ASSERT(Status != STATUS_ALREADY_COMMITTED);
+
+ /* Get the size of the driver */
+ DriverSize = ((PROS_SECTION_OBJECT)Section)->ImageSection->ImageSize;
+
+ /* Make sure we're not being loaded into session space */
+ if (!Flags)
+ {
+ /* Check for success */
+ if (NT_SUCCESS(Status))
+ {
+ /* Support large pages for drivers */
+ MiUseLargeDriverPage(DriverSize / PAGE_SIZE,
+ &ModuleLoadBase,
+ &BaseName,
+ TRUE);
+ }
+
+ /* Dereference the section */
+ ObDereferenceObject(Section);
+ Section = NULL;
+ }
+
+ /* Check for failure of the load earlier */
+ if (!NT_SUCCESS(Status)) goto Quickie;
+
+ /* Relocate the driver */
+ Status = LdrRelocateImageWithBias(ModuleLoadBase,
+ 0,
+ "SYSLDR",
+ STATUS_SUCCESS,
+ STATUS_CONFLICTING_ADDRESSES,
+ STATUS_INVALID_IMAGE_FORMAT);
+ if (!NT_SUCCESS(Status)) goto Quickie;
+
+
+ /* Get the NT Header */
+ NtHeader = RtlImageNtHeader(ModuleLoadBase);
+
+ /* 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 |= LDRP_ENTRY_NATIVE;
+ }
+
+ /* Setup the rest of the entry */
+ LdrEntry->DllBase = ModuleLoadBase;
+ LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)ModuleLoadBase +
+ NtHeader->OptionalHeader.AddressOfEntryPoint);
+ LdrEntry->SizeOfImage = DriverSize;
+ LdrEntry->CheckSum = NtHeader->OptionalHeader.CheckSum;
+ LdrEntry->SectionPointer = Section;
+
+ /* 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 / sizeof(WCHAR)] = 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 / sizeof(WCHAR)] = UNICODE_NULL;
+ }
+
+ /* Add the entry */
+ MiProcessLoaderEntry(LdrEntry, TRUE);
+
+ /* Resolve imports */
+ MissingApiName = Buffer;
+ Status = MiResolveImageReferences(ModuleLoadBase,
+ &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 */
+ ExFreePoolWithTag(LdrEntry, TAG_MODULE_OBJECT);
+ LdrEntry = NULL;
+ goto Quickie;
+ }
+
+ /* Update the loader entry */
+ LdrEntry->Flags |= (LDRP_SYSTEM_MAPPED |
+ LDRP_ENTRY_PROCESSED |
+ LDRP_MM_LOADED);
+ LdrEntry->Flags &= ~LDRP_LOAD_IN_PROGRESS;
+ LdrEntry->LoadedImports = LoadedImports;
+
+ /* FIXME: Call driver verifier's loader function */
+
+ /* Write-protect the system image */
+ MiWriteProtectSystemImage(LdrEntry->DllBase);
+
+ /* Check if notifications are enabled */
+ if (PsImageNotifyEnabled)
+ {
+ /* Fill out the notification data */
+ ImageInfo.Properties = 0;
+ ImageInfo.ImageAddressingMode = IMAGE_ADDRESSING_MODE_32BIT;
+ ImageInfo.SystemModeImage = TRUE;
+ ImageInfo.ImageSize = LdrEntry->SizeOfImage;
+ ImageInfo.ImageBase = LdrEntry->DllBase;
+ ImageInfo.ImageSectionNumber = ImageInfo.ImageSelector = 0;
+
+ /* Send the notification */
+ PspRunLoadImageNotifyRoutines(FileName, NULL, &ImageInfo);
+ }
+
+#if defined(KDBG) || defined(_WINKD_)
+ /* MiCacheImageSymbols doesn't detect rossym */
+ if (TRUE)
+#else
+ /* Check if there's symbols */
+ if (MiCacheImageSymbols(LdrEntry->DllBase))
+#endif
+ {
+ /* Check if the system root is present */
+ if ((PrefixName.Length > (11 * sizeof(WCHAR))) &&
+ !(_wcsnicmp(PrefixName.Buffer, L"\\SystemRoot", 11)))
+ {
+ /* Add the system root */
+ UnicodeTemp = PrefixName;
+ UnicodeTemp.Buffer += 11;
+ UnicodeTemp.Length -= (11 * sizeof(WCHAR));
+ sprintf_nt(Buffer,
+ "%ws%wZ",
+ &SharedUserData->NtSystemRoot[2],
+ &UnicodeTemp);
+ }
+ else
+ {
+ /* Build the name */
+ sprintf_nt(Buffer, "%wZ", &BaseName);
+ }
+
+ /* Setup the ansi string */
+ RtlInitString(&AnsiTemp, Buffer);
+
+ /* Notify the debugger */
+ DbgLoadImageSymbols(&AnsiTemp,
+ LdrEntry->DllBase,
+ (ULONG_PTR)ZwCurrentProcess());
+ LdrEntry->Flags |= LDRP_DEBUG_SYMBOLS_LOADED;
+ }
+
+ /* Page the driver */
+ ASSERT(Section == NULL);
+ MiEnablePagingOfDriver(LdrEntry);
+
+ /* Return pointers */
+ *ModuleObject = LdrEntry;
+ *ImageBaseAddress = LdrEntry->DllBase;
+
+Quickie:
+ /* Check if we have the lock acquired */
+ if (LockOwned)
+ {
+ /* Release the lock */
+ KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
+ KeLeaveCriticalRegion();
+ LockOwned = FALSE;
+ }
+
+ /* If we have a file handle, close it */
+ if (FileHandle) ZwClose(FileHandle);
+
+ /* Check if we had a prefix */
+ if (NamePrefix) ExFreePool(PrefixName.Buffer);
+
+ /* Free the name buffer and return status */
+ ExFreePoolWithTag(Buffer, TAG_LDR_WSTR);
+ return Status;
+}
+
+PLDR_DATA_TABLE_ENTRY
+NTAPI
+MiLookupDataTableEntry(IN PVOID Address)
+{
+ PLDR_DATA_TABLE_ENTRY LdrEntry, FoundEntry = NULL;
+ PLIST_ENTRY NextEntry;
+ PAGED_CODE();
+
+ /* Loop entries */
+ NextEntry = PsLoadedModuleList.Flink;
+ do
+ {
+ /* Get the loader entry */
+ LdrEntry = CONTAINING_RECORD(NextEntry,
+ LDR_DATA_TABLE_ENTRY,
+ InLoadOrderLinks);
+
+ /* Check if the address matches */
+ if ((Address >= LdrEntry->DllBase) &&
+ (Address < (PVOID)((ULONG_PTR)LdrEntry->DllBase +
+ LdrEntry->SizeOfImage)))
+ {
+ /* Found a match */
+ FoundEntry = LdrEntry;
+ break;
+ }
+
+ /* Move on */
+ NextEntry = NextEntry->Flink;
+ } while(NextEntry != &PsLoadedModuleList);
+
+ /* Return the entry */
+ return FoundEntry;
+}
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+/*
+ * @implemented
+ */
+PVOID
+NTAPI
+MmPageEntireDriver(IN PVOID AddressWithinSection)
+{
+ PMMPTE StartPte, EndPte;
+ PLDR_DATA_TABLE_ENTRY LdrEntry;
+ PAGED_CODE();
+
+ /* Get the loader entry */
+ LdrEntry = MiLookupDataTableEntry(AddressWithinSection);
+ if (!LdrEntry) return NULL;
+
+ /* Check if paging of kernel mode is disabled or if the driver is mapped as an image */
+ if ((MmDisablePagingExecutive) || (LdrEntry->SectionPointer))
+ {
+ /* Don't do anything, just return the base address */
+ return LdrEntry->DllBase;
+ }
+
+ /* Wait for active DPCs to finish before we page out the driver */
+ KeFlushQueuedDpcs();
+
+ /* Get the PTE range for the whole driver image */
+ StartPte = MiAddressToPte((ULONG_PTR)LdrEntry->DllBase);
+ EndPte = MiAddressToPte((ULONG_PTR)LdrEntry->DllBase + LdrEntry->SizeOfImage);
+
+ /* Enable paging for the PTE range */
+ ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(AddressWithinSection) == FALSE);
+ MiSetPagingOfDriver(StartPte, EndPte);
+
+ /* Return the base address */
+ return LdrEntry->DllBase;
+}
+
+/*
+ * @unimplemented
+ */
+VOID
+NTAPI
+MmResetDriverPaging(IN PVOID AddressWithinSection)
+{
+ UNIMPLEMENTED;
+}
+
+/*
+ * @implemented
+ */
+PVOID
+NTAPI
+MmGetSystemRoutineAddress(IN PUNICODE_STRING SystemRoutineName)
+{
+ PVOID ProcAddress = NULL;
+ ANSI_STRING AnsiRoutineName;
+ NTSTATUS Status;
+ PLIST_ENTRY NextEntry;
+ PLDR_DATA_TABLE_ENTRY LdrEntry;
+ BOOLEAN Found = FALSE;
+ UNICODE_STRING KernelName = RTL_CONSTANT_STRING(L"ntoskrnl.exe");
+ UNICODE_STRING HalName = RTL_CONSTANT_STRING(L"hal.dll");
+ ULONG Modules = 0;
+
+ /* Convert routine to ansi name */
+ Status = RtlUnicodeStringToAnsiString(&AnsiRoutineName,
+ SystemRoutineName,
+ TRUE);
+ if (!NT_SUCCESS(Status)) return NULL;
+
+ /* Lock the list */
+ KeEnterCriticalRegion();
+ ExAcquireResourceSharedLite(&PsLoadedModuleResource, TRUE);
+
+ /* Loop the loaded module list */
+ NextEntry = PsLoadedModuleList.Flink;
+ while (NextEntry != &PsLoadedModuleList)
+ {
+ /* Get the entry */
+ LdrEntry = CONTAINING_RECORD(NextEntry,
+ LDR_DATA_TABLE_ENTRY,
+ InLoadOrderLinks);
+
+ /* Check if it's the kernel or HAL */
+ if (RtlEqualUnicodeString(&KernelName, &LdrEntry->BaseDllName, TRUE))
+ {
+ /* Found it */
+ Found = TRUE;
+ Modules++;
+ }
+ else if (RtlEqualUnicodeString(&HalName, &LdrEntry->BaseDllName, TRUE))
+ {
+ /* Found it */
+ Found = TRUE;
+ Modules++;
+ }
+
+ /* Check if we found a valid binary */
+ if (Found)
+ {
+ /* Find the procedure name */
+ ProcAddress = MiFindExportedRoutineByName(LdrEntry->DllBase,
+ &AnsiRoutineName);
+
+ /* Break out if we found it or if we already tried both modules */
+ if (ProcAddress) break;
+ if (Modules == 2) break;
+ }
+
+ /* Keep looping */
+ NextEntry = NextEntry->Flink;
+ }
+
+ /* Release the lock */
+ ExReleaseResourceLite(&PsLoadedModuleResource);
+ KeLeaveCriticalRegion();
+
+ /* Free the string and return */
+ RtlFreeAnsiString(&AnsiRoutineName);
+ return ProcAddress;
+}
+