如何判断虚拟内存页是否已锁定

How to tell if a virtual memory page has been locked?

本文关键字:是否 锁定 虚拟内存 何判断 判断      更新时间:2023-10-16

比如说,如果在某个时候我的进程中的虚拟内存范围被锁定为这样:

//Memory was reserved & committed as such
void* pMem = ::VirtualAlloc(NULL, 4096, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
//...
//And then
::VirtualLock(pMem, 4096);

因此,在我的进程中在虚拟内存中具有页面的任意地址,我可以判断它是否locked吗?

通过使用

Win32 API这是不可能的。 但如果使用带有未记录MEMORY_INFORMATION_CLASS值的ZwQueryVirtualMemory - 这是可能的。 有关数据结构定义 - 请参阅 ntmmapi.h

我们需要将MemoryWorkingSetExInformationMEMORY_WORKING_SET_EX_BLOCK一起使用并使用ULONG_PTR Locked : 1;成员

演示测试:

NTSTATUS IsPageLocked(PVOID BaseAddress, BOOLEAN& Locked)
{
    MEMORY_WORKING_SET_EX_INFORMATION mwsei = { BaseAddress };
    NTSTATUS status = ZwQueryVirtualMemory(NtCurrentProcess(), 0, MemoryWorkingSetExInformation, &mwsei, sizeof(mwsei), 0);
    if (0 <= status)
    {
        if (mwsei.VirtualAttributes.Valid)
        {
            Locked = mwsei.VirtualAttributes.Locked;
        }
        else
        {
            status = STATUS_INVALID_ADDRESS;
        }
    }
    return status;
}
NTSTATUS IsPageLockedEx(PVOID BaseAddress, BOOLEAN& Locked)
{
    NTSTATUS status = IsPageLocked(BaseAddress, Locked);
    if (0 > status)
    {
        DbgPrint("IsPageLocked - error %xn", status);
    }
    else
    {
        DbgPrint("IsPageLocked = %xn", Locked);
    }
    return status;
}
void CheckVA()
{
    if (PVOID pv = VirtualAlloc(NULL, PAGE_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE))
    {
        BOOLEAN Locked;
        NTSTATUS status = IsPageLockedEx(pv, Locked);
        if (status == STATUS_INVALID_ADDRESS)
        {
            // at this point  physical memory for pv not yet committed. reference memory for commit physical page
            if (*(PBYTE)pv) __nop();
            status = IsPageLockedEx(pv, Locked);
        }
        if (VirtualLock(pv, PAGE_SIZE))
        {
            IsPageLockedEx(pv, Locked);
            if (VirtualUnlock(pv, PAGE_SIZE))
            {
                IsPageLockedEx(pv, Locked);
            }
            else
            {
                __debugbreak();
            }
        }
        VirtualFree(pv, 0, MEM_RELEASE);
    }
}

DbgPrint 输出

IsPageLocked - error c0000141
IsPageLocked = 0
IsPageLocked = 1
IsPageLocked = 0

对于将来看到这一点的任何人,XP和更高版本现在都包含这样做的API:QueryWorkingSet和QueryWorkingSetEx。

在PSAPI_WORKING_SET_EX_BLOCK结构中查找Locked位,类似于依赖于ZwQueryVirtualMemory的答案。