/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

   queryvm.c

Abstract:

    This module contains the routines which implement the
    NtQueryVirtualMemory service.

Author:

    Lou Perazzoli (loup) 21-Aug-1989

Revision History:

--*/

#include "mi.h"

extern POBJECT_TYPE IoFileObjectType;

NTSTATUS
MiGetWorkingSetInfo (
    IN PMEMORY_WORKING_SET_INFORMATION WorkingSetInfo,
    IN ULONG Length,
    IN PEPROCESS Process
    );

MMPTE
MiCaptureSystemPte (
    IN PMMPTE PointerProtoPte,
    IN PEPROCESS Process
    );

#if DBG
PEPROCESS MmWatchProcess;
VOID MmFooBar(VOID);
#endif // DBG

ULONG
MiQueryAddressState (
    IN PVOID Va,
    IN PMMVAD Vad,
    IN PEPROCESS TargetProcess,
    OUT PULONG ReturnedProtect
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,NtQueryVirtualMemory)
#pragma alloc_text(PAGE,MiQueryAddressState)
#pragma alloc_text(PAGELK,MiGetWorkingSetInfo)
#endif


NTSTATUS
NtQueryVirtualMemory (
    IN HANDLE ProcessHandle,
    IN PVOID BaseAddress,
    IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
    OUT PVOID MemoryInformation,
    IN ULONG MemoryInformationLength,
    OUT PULONG ReturnLength OPTIONAL
     )

/*++

Routine Description:

    This function provides the capability to determine the state,
    protection, and type of a region of pages within the virtual address
    space of the subject process.

    The state of the first page within the region is determined  and then
    subsequent entries in the process address map are scanned from the
    base address upward until either the entire range of pages has been
    scanned or until a page with a nonmatching set of attributes is
    encountered. The region attributes, the length of the region of pages
    with matching attributes, and an appropriate status value are
    returned.

    If the entire region of pages does not have a matching set of
    attributes, then the returned length parameter value can be used to
    calculate the address and length of the region of pages that was not
    scanned.

Arguments:


    ProcessHandle - An open handle to a process object.

    BaseAddress - The base address of the region of pages to be
        queried. This value is rounded down to the next host-page-
        address boundary.

    MemoryInformationClass - The memory information class about which
        to retrieve information.

    MemoryInformation - A pointer to a buffer that receives the
        specified information.  The format and content of the buffer
        depend on the specified information class.


        MemoryBasicInformation - Data type is PMEMORY_BASIC_INFORMATION.

            MEMORY_BASIC_INFORMATION Structure


            ULONG RegionSize - The size of the region in bytes
                beginning at the base address in which all pages have
                identical attributes.

            ULONG State - The state of the pages within the region.

                State Values                        State Values

                MEM_COMMIT - The state of the pages within the region
                    is committed.

                MEM_FREE - The state of the pages within the region
                    is free.

                MEM_RESERVE - The state of the pages within the
                    region is reserved.

            ULONG Protect - The protection of the pages within the
                region.


                Protect Values                        Protect Values

                PAGE_NOACCESS - No access to the region of pages is
                    allowed. An attempt to read, write, or execute
                    within the region results in an access violation
                    (i.e., a GP fault).

                PAGE_EXECUTE - Execute access to the region of pages
                    is allowed. An attempt to read or write within
                    the region results in an access violation.

                PAGE_READONLY - Read-only and execute access to the
                    region of pages is allowed. An attempt to write
                    within the region results in an access violation.

                PAGE_READWRITE - Read, write, and execute access to
                    the region of pages is allowed. If write access
                    to the underlying section is allowed, then a
                    single copy of the pages are shared. Otherwise,
                    the pages are shared read-only/copy-on-write.

                PAGE_GUARD - Read, write, and execute access to the
                    region of pages is allowed; however, access to
                    the region causes a "guard region entered"
                    condition to be raised in the subject process.

                PAGE_NOCACHE - Disable the placement of committed
                    pages into the data cache.

            ULONG Type - The type of pages within the region.


                Type Values

                MEM_PRIVATE - The pages within the region are
                    private.

                MEM_MAPPED - The pages within the region are mapped
                    into the view of a section.

                MEM_IMAGE - The pages within the region are mapped
                    into the view of an image section.

    MemoryInformationLength - Specifies the length in bytes  of
        the memory information buffer.

    ReturnLength - An optional pointer which, if specified,
        receives the number of bytes placed in the process
        information buffer.


Return Value:

    Returns the status

    TBS


Environment:

    Kernel mode.

--*/

{
    KPROCESSOR_MODE PreviousMode;
    PEPROCESS TargetProcess;
    NTSTATUS Status;
    PMMVAD Vad;
    BOOLEAN PteIsZero = FALSE;
    PVOID Va;
    BOOLEAN Found = FALSE;
    ULONG TheRegionSize;
    ULONG NewProtect;
    ULONG NewState;
    PVOID FilePointer;

    MEMORY_BASIC_INFORMATION Info;

    //
    // The only supported option is MEMORY_BASIC_INFORMATION, make
    // sure the user's buffer is large enough for this.
    //

    //
    // Check argument validity.
    //
    switch (MemoryInformationClass) {
        case MemoryBasicInformation:
                if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION)) {
                    return STATUS_INFO_LENGTH_MISMATCH;
                }
                break;

        case MemoryWorkingSetInformation:
                break;

        case MemoryMappedFilenameInformation:
                FilePointer = NULL;
                break;
        default:
            return STATUS_INVALID_INFO_CLASS;
    }

    PreviousMode = KeGetPreviousMode();

    if (PreviousMode != KernelMode) {

        //
        // Check arguments.
        //

        try {

            ProbeForWrite(MemoryInformation,
                          MemoryInformationLength,
                          sizeof(ULONG));

            if (ARGUMENT_PRESENT(ReturnLength)) {
                ProbeForWriteUlong(ReturnLength);
            }

        } except (EXCEPTION_EXECUTE_HANDLER) {

            //
            // If an exception occurs during the probe or capture
            // of the initial values, then handle the exception and
            // return the exception code as the status value.
            //

            return GetExceptionCode();
        }
    }
    if (BaseAddress > MM_HIGHEST_USER_ADDRESS) {
        return STATUS_INVALID_PARAMETER;
    }

    if (BaseAddress >= MM_HIGHEST_VAD_ADDRESS) {

        //
        // Indicate a reserved area from this point on.
        //

        if ( MemoryInformationClass == MemoryBasicInformation ) {

            try {
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->AllocationBase =
                                      (PVOID)((ULONG)MM_HIGHEST_VAD_ADDRESS + 1);
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->AllocationProtect =
                                                                      PAGE_READONLY;
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->BaseAddress =
                                                       PAGE_ALIGN(BaseAddress);
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->RegionSize =
                                    ((ULONG)MM_HIGHEST_USER_ADDRESS + 1) -
                                                (ULONG)PAGE_ALIGN(BaseAddress);
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->State = MEM_RESERVE;
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->Protect = PAGE_NOACCESS;
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->Type = MEM_PRIVATE;

                if (ARGUMENT_PRESENT(ReturnLength)) {
                    *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
                }

#if defined(MM_SHARED_USER_DATA_VA)
                if (PAGE_ALIGN(BaseAddress) == (PVOID)MM_SHARED_USER_DATA_VA) {

                    //
                    // This is the page that is double mapped between
                    // user mode and kernel mode.
                    //

                    ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->Protect =
                                                                 PAGE_READONLY;
                    ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->RegionSize =
                                                                 PAGE_SIZE;
                    ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->State =
                                                                 MEM_COMMIT;
                }
#endif

            } except (EXCEPTION_EXECUTE_HANDLER) {

                //
                // Just return success.
                //
            }

            return STATUS_SUCCESS;
        } else {
            return STATUS_INVALID_ADDRESS;
        }
    }

    if ( ProcessHandle == NtCurrentProcess() ) {
        TargetProcess = PsGetCurrentProcess();
    } else {
        Status = ObReferenceObjectByHandle ( ProcessHandle,
                                             PROCESS_QUERY_INFORMATION,
                                             PsProcessType,
                                             PreviousMode,
                                             (PVOID *)&TargetProcess,
                                             NULL );

        if (!NT_SUCCESS(Status)) {
            return Status;
        }
    }

    if (MemoryInformationClass == MemoryWorkingSetInformation) {

        MmLockPagableSectionByHandle(ExPageLockHandle);

        Status = MiGetWorkingSetInfo (MemoryInformation,
                                      MemoryInformationLength,
                                      TargetProcess);
        MmUnlockPagableImageSection(ExPageLockHandle);

        if ( ProcessHandle != NtCurrentProcess() ) {
            ObDereferenceObject (TargetProcess);
        }
        try {

            if (ARGUMENT_PRESENT(ReturnLength)) {
                *ReturnLength = ((((PMEMORY_WORKING_SET_INFORMATION)
                                    MemoryInformation)->NumberOfEntries - 1) *
                                        sizeof(ULONG)) +
                                        sizeof(MEMORY_WORKING_SET_INFORMATION);
            }

        } except (EXCEPTION_EXECUTE_HANDLER) {
        }

    return STATUS_SUCCESS;
    }

    //
    // If the specified process is not the current process, attach
    // to the specified process.
    //

    KeAttachProcess (&TargetProcess->Pcb);

    //
    // Get working set mutex and block APCs.
    //

    LOCK_WS_AND_ADDRESS_SPACE (TargetProcess);

    //
    // Make sure the address space was not deleted, if so, return an error.
    //

    if (TargetProcess->AddressSpaceDeleted != 0) {
        UNLOCK_WS (TargetProcess);
        UNLOCK_ADDRESS_SPACE (TargetProcess);
        KeDetachProcess();
        if ( ProcessHandle != NtCurrentProcess() ) {
            ObDereferenceObject (TargetProcess);
        }
        return STATUS_PROCESS_IS_TERMINATING;
    }

    //
    // Locate the VAD that contiains the base address or the VAD
    // which follows the base address.
    //

    Vad = TargetProcess->VadRoot;

    for (;;) {

        if (Vad == (PMMVAD)NULL) {
            break;
        }

        if ((BaseAddress >= Vad->StartingVa) &&
            (BaseAddress <= Vad->EndingVa)) {
            Found = TRUE;
            break;
        }

        if (BaseAddress < Vad->StartingVa) {
            if (Vad->LeftChild == (PMMVAD)NULL) {
                break;
            }
            Vad = Vad->LeftChild;

        } else {
            if (BaseAddress < Vad->EndingVa) {
                break;
            }
            if (Vad->RightChild == (PMMVAD)NULL) {
                break;
            }
            Vad = Vad->RightChild;
        }
    }

    if (!Found) {

        //
        // There is no virtual address allocated at the base
        // address.  Return the size of the hole starting at
        // the base address.
        //

        if (Vad == NULL) {
            TheRegionSize = ((ULONG)MM_HIGHEST_VAD_ADDRESS + 1) -
                                                (ULONG)PAGE_ALIGN(BaseAddress);
        } else {
            if (Vad->StartingVa < BaseAddress) {

                //
                // We are looking at the Vad which occupies the range
                // just before the desired range.  Get the next Vad.
                //

                Vad = MiGetNextVad (Vad);
                if (Vad == NULL) {
                    TheRegionSize = ((ULONG)MM_HIGHEST_VAD_ADDRESS + 1) -
                                                (ULONG)PAGE_ALIGN(BaseAddress);
                } else {
                    TheRegionSize = (ULONG)Vad->StartingVa -
                                                (ULONG)PAGE_ALIGN(BaseAddress);
                }
            } else {
                TheRegionSize = (ULONG)Vad->StartingVa -
                                                (ULONG)PAGE_ALIGN(BaseAddress);
            }
        }

        UNLOCK_WS (TargetProcess);
        UNLOCK_ADDRESS_SPACE (TargetProcess);
        KeDetachProcess();

        if ( ProcessHandle != NtCurrentProcess() ) {
            ObDereferenceObject (TargetProcess);
        }

        //
        // Establish an exception handler and write the information and
        // returned length.
        //

        if ( MemoryInformationClass == MemoryBasicInformation ) {
            try {

                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->AllocationBase =
                                                                            NULL;
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->AllocationProtect =
                                                                            0;
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->BaseAddress =
                                                            PAGE_ALIGN(BaseAddress);
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->RegionSize =
                                                                    TheRegionSize;
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->State = MEM_FREE;
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->Protect = PAGE_NOACCESS;
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->Type = 0;

                if (ARGUMENT_PRESENT(ReturnLength)) {
                    *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
                }

            } except (EXCEPTION_EXECUTE_HANDLER) {

                //
                // Just return success.
                //
            }

            return STATUS_SUCCESS;
        }
        return STATUS_INVALID_ADDRESS;
    }

    //
    // Found a vad.
    //

    Va = PAGE_ALIGN(BaseAddress);
    Info.BaseAddress = Va;

    //
    // There is a page mapped at the base address.
    //

    if (Vad->u.VadFlags.PrivateMemory) {
        Info.Type = MEM_PRIVATE;
    } else if (Vad->u.VadFlags.ImageMap == 0) {
        Info.Type = MEM_MAPPED;

        if ( MemoryInformationClass == MemoryMappedFilenameInformation ) {
            if (Vad->ControlArea) {
                FilePointer = Vad->ControlArea->FilePointer;
            }
            if ( !FilePointer ) {
                FilePointer = (PVOID)1;
            } else {
                ObReferenceObject(FilePointer);
            }
        }

    } else {
        Info.Type = MEM_IMAGE;
    }

    Info.State = MiQueryAddressState (Va, Vad, TargetProcess, &Info.Protect);

    Va = (PVOID)((PCHAR)Va + PAGE_SIZE);

    while (Va <= Vad->EndingVa) {

        NewState = MiQueryAddressState (Va,
                                        Vad,
                                        TargetProcess,
                                        &NewProtect);

        if ((NewState != Info.State) || (NewProtect != Info.Protect)) {

            //
            // The state for this address does not match, calculate
            // size and return.
            //

            break;
        }
        Va = (PVOID)((ULONG)Va + PAGE_SIZE);
    } // end while

    Info.RegionSize = ((ULONG)Va - (ULONG)Info.BaseAddress);
    Info.AllocationBase = Vad->StartingVa;
    Info.AllocationProtect = MI_CONVERT_FROM_PTE_PROTECTION (
                                             Vad->u.VadFlags.Protection);

    //
    // A range has been found, release the mutexes, deattach from the
    // target process and return the information.
    //

    UNLOCK_WS (TargetProcess);
    UNLOCK_ADDRESS_SPACE (TargetProcess);
    KeDetachProcess();

    if ( ProcessHandle != NtCurrentProcess() ) {
        ObDereferenceObject (TargetProcess);
    }

#if DBG
    if (MmDebug & MM_DBG_SHOW_NT_CALLS) {
        if ( !MmWatchProcess ) {
            DbgPrint("queryvm base %lx allocbase %lx protect %lx size %lx\n",
                Info.BaseAddress, Info.AllocationBase, Info.AllocationProtect,
                Info.RegionSize);
            DbgPrint("    state %lx  protect %lx  type %lx\n",
                Info.State, Info.Protect, Info.Type);
        }
    }
#endif //DBG

    if ( MemoryInformationClass == MemoryBasicInformation ) {
        try {

            *(PMEMORY_BASIC_INFORMATION)MemoryInformation = Info;

            if (ARGUMENT_PRESENT(ReturnLength)) {
                *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
            }

        } except (EXCEPTION_EXECUTE_HANDLER) {
        }
        return STATUS_SUCCESS;
    }

    //
    // Try to return the name of the file that is mapped.
    //

    if ( !FilePointer ) {
        return STATUS_INVALID_ADDRESS;
    } else if ( FilePointer == (PVOID)1 ) {
        return STATUS_FILE_INVALID;
    }

    //
    // We have a referenced pointer to the file. Call ObQueryNameString
    // and get the file name
    //

    Status = ObQueryNameString(
                FilePointer,
                MemoryInformation,
                MemoryInformationLength,
                ReturnLength
                );
    ObDereferenceObject(FilePointer);
    return Status;
}


ULONG
MiQueryAddressState (
    IN PVOID Va,
    IN PMMVAD Vad,
    IN PEPROCESS TargetProcess,
    OUT PULONG ReturnedProtect
    )

/*++

Routine Description:


Arguments:

Return Value:

    Returns the state (MEM_COMMIT, MEM_RESERVE, MEM_PRIVATE).

Environment:

    Kernel mode.  Working set lock and address creation lock held.

--*/

{
    PMMPTE PointerPte;
    PMMPTE PointerPde;
    MMPTE CapturedProtoPte;
    PMMPTE ProtoPte;
    ULONG PteIsZero;
    ULONG State;
    ULONG Protect;

#ifdef LARGE_PAGES
    if (Vad->u.VadFlags.LargePages) {
        *ReturnedProtect = MI_CONVERT_FROM_PTE_PROTECTION (
                                             Vad->u.VadFlags.Protection);
        return MEM_COMMIT;
    }
#endif //LARGE_PAGES

    PointerPde = MiGetPdeAddress (Va);
    PointerPte = MiGetPteAddress (Va);

    ASSERT ((Vad->StartingVa <= Va) && (Vad->EndingVa >= Va));

    PteIsZero = TRUE;

    if (MiDoesPdeExistAndMakeValid(PointerPde, TargetProcess, FALSE)) {

        //
        // A PTE exists at this address, see if it is zero.
        //

        if (PointerPte->u.Long != 0) {

            PteIsZero = FALSE;

            //
            // There is a non-zero PTE at this address, use
            // it to build the information block.
            //

            if (MiIsPteDecommittedPage (PointerPte)) {
                Protect = 0;
                State = MEM_RESERVE;
            } else {

                State = MEM_COMMIT;
                if (Vad->u.VadFlags.PhysicalMapping == 1) {

                    //
                    // Physical mapping, there is no corresponding
                    // PFN element to get the page protection from.
                    //

                    Protect = MI_CONVERT_FROM_PTE_PROTECTION (
                                             Vad->u.VadFlags.Protection);
                } else {
                    Protect = MiGetPageProtection (PointerPte,
                                                   TargetProcess);

                    if ((PointerPte->u.Soft.Valid == 0) &&
                        (PointerPte->u.Soft.Prototype == 1) &&
                        (Vad->u.VadFlags.PrivateMemory == 0) &&
                        (Vad->ControlArea != (PCONTROL_AREA)NULL)) {

                        //
                        // Make sure protoPTE is committed.
                        //

                        ProtoPte = MiGetProtoPteAddress(Vad,Va);

                        CapturedProtoPte = MiCaptureSystemPte (ProtoPte,
                                                               TargetProcess);
                        if (CapturedProtoPte.u.Long == 0) {
                            State = MEM_RESERVE;
                            Protect = 0;
                        }
                    }
                }
            }
        }
    }

    if (PteIsZero) {

        //
        // There is no PDE at this address, the template from
        // the VAD supplies the information unless the VAD is
        // for an image file.  For image files the individual
        // protection is on the prototype PTE.
        //

        //
        // Get the default protection information.
        //

        State = MEM_RESERVE;
        Protect = 0;

        if (Vad->u.VadFlags.PhysicalMapping == 1) {

            //
            // Must be banked memory, just return reserved.
            //

            NOTHING;

        } else if ((Vad->u.VadFlags.PrivateMemory == 0) &&
            (Vad->ControlArea != (PCONTROL_AREA)NULL)) {

            //
            // This VAD refers to a section.  Even though the PTE is
            // zero, the actual page may be committed in the section.
            //

            ProtoPte = MiGetProtoPteAddress(Vad,Va);

            CapturedProtoPte = MiCaptureSystemPte (ProtoPte,
                                                   TargetProcess);

            if (CapturedProtoPte.u.Long != 0) {
                State = MEM_COMMIT;

                if (Vad->u.VadFlags.ImageMap == 0) {
                    Protect = MI_CONVERT_FROM_PTE_PROTECTION (
                                              Vad->u.VadFlags.Protection);
                } else {

                    //
                    // This is an image file, the protection is in the
                    // prototype PTE.
                    //

                    Protect = MiGetPageProtection (&CapturedProtoPte,
                                                        TargetProcess);
                }
            }

        } else {

            //
            // Get the protection from the corresponding VAD.
            //

            if (Vad->u.VadFlags.MemCommit) {
                State = MEM_COMMIT;
                Protect = MI_CONVERT_FROM_PTE_PROTECTION (
                                            Vad->u.VadFlags.Protection);
            }
        }
    }

    *ReturnedProtect = Protect;
    return State;
}



NTSTATUS
MiGetWorkingSetInfo (
    IN PMEMORY_WORKING_SET_INFORMATION WorkingSetInfo,
    IN ULONG Length,
    IN PEPROCESS Process
    )

{
    PMDL Mdl;
    PMEMORY_WORKING_SET_INFORMATION Info;
    PMEMORY_WORKING_SET_BLOCK Entry;
    PMEMORY_WORKING_SET_BLOCK LastEntry;
    PMMWSLE Wsle;
    PMMWSLE LastWsle;
    ULONG WsSize;
    PMMPTE PointerPte;
    PMMPFN Pfn1;
    NTSTATUS status;

    //
    // Allocate an MDL to map the request.
    //

    Mdl = ExAllocatePoolWithTag (NonPagedPool,
                                 sizeof(MDL) + sizeof(ULONG) +
                                     BYTES_TO_PAGES (Length) * sizeof(ULONG),
                                 '  mM');

    if (Mdl == NULL) {
        return(STATUS_INSUFFICIENT_RESOURCES);
    }

    //
    // Initialize MDL for request.
    //

    MmInitializeMdl(Mdl, WorkingSetInfo, Length);

    try {
        MmProbeAndLockPages (Mdl, KeGetPreviousMode(), IoWriteAccess);
    } except (EXCEPTION_EXECUTE_HANDLER) {
        ExFreePool (Mdl);
        return GetExceptionCode();
    }

    Info = MmGetSystemAddressForMdl (Mdl);

    if (PsGetCurrentProcess() != Process) {
        KeAttachProcess (&Process->Pcb);
    }

    LOCK_WS (Process);

    status = STATUS_SUCCESS;

    if (Process->AddressSpaceDeleted != 0) {
        status = STATUS_PROCESS_IS_TERMINATING;
    }

    WsSize = Process->Vm.WorkingSetSize;
    Info->NumberOfEntries = WsSize;

    if ((WsSize * sizeof(ULONG)) >= Length) {
        status = STATUS_INFO_LENGTH_MISMATCH;
    }

    if (status != STATUS_SUCCESS) {
        UNLOCK_WS (Process);
        KeDetachProcess ();
        MmUnlockPages (Mdl);
        ExFreePool (Mdl);
        return status;
    }

    Wsle = MmWsle;
    LastWsle = &MmWsle[MmWorkingSetList->LastEntry];
    Entry = &Info->WorkingSetInfo[0];
    LastEntry = (PMEMORY_WORKING_SET_BLOCK)(
                            (PCHAR)Info + (Length & (~(sizeof(ULONG) - 1))));

    do {
        if (Wsle->u1.e1.Valid == 1) {
            Entry->VirtualPage = Wsle->u1.e1.VirtualPageNumber;
            PointerPte = MiGetPteAddress (Wsle->u1.VirtualAddress);
            ASSERT (PointerPte->u.Hard.Valid == 1);
            Pfn1 = MI_PFN_ELEMENT (PointerPte->u.Hard.PageFrameNumber);

            Entry->Shared = Pfn1->u3.e1.PrototypePte;
            if (Pfn1->u3.e1.PrototypePte == 0) {
                Entry->Protection = Pfn1->OriginalPte.u.Soft.Protection;
            } else {
                if (Wsle->u1.e1.SameProtectAsProto == 1) {
                    Entry->Protection = Pfn1->OriginalPte.u.Soft.Protection;
                } else {
                    Entry->Protection = Wsle->u1.e1.Protection;
                }
            }
            Entry += 1;
        }
        Wsle += 1;
    }while ((Entry < LastEntry) && (Wsle <= LastWsle));

    UNLOCK_WS (Process);
    KeDetachProcess ();
    MmUnlockPages (Mdl);
    ExFreePool (Mdl);
    return STATUS_SUCCESS;
}