【问题标题】:Extracting shared memory's size提取共享内存的大小
【发布时间】:2016-01-18 17:17:59
【问题描述】:

我试图了解我应该如何准确地提取 MapViewOfFile 的返回缓冲区大小。我使用以下命令分配共享内存

hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, dwDataSize, strSharedMemoryName.c_str());

使用以下代码 sn-p 填充它:

pBuffer = DynamicAPI::MapViewOfFile(hFileMapping, FILE_MAP_WRITE, 0, 0, dwDataSize);
if (nullptr == pBuffer || GetLastError() != 0)
{
    LOG_ERROR(L"Failed to MapViewOfFile: " << GetLastError());
    break;
}
// Copy buffer to the shared memory
::CopyMemory(pBuffer, pData, dwDataSize);

然后,在其他地方,尝试重新打开该共享内存并读取整个缓冲区:

HANDLE hSharedMemory = OpenFileMapping(FILE_MAP_READ, FALSE, m_strSharedName.c_str());
    if (nullptr == hSharedMemory)
    {
        return false;
    }

    LPVOID pData = nullptr;
    if (nullptr == (pData = MapViewOfFile(hSharedMemory, FILE_MAP_READ, 0, 0, 0)))
    {
        LOG_ERROR(L"Failed to MapViewOfFile");
        return false;
    }

我的下一行将是

std::string strData = pData; // use std::string::assign

但是,我不知道 pData 有多大,一种选择是在缓冲区中发送整体大小,但是 MSDN 声明 VirtualQueryEx 能够做这样的事情。

我尝试执行以下代码sn-p:

MEMORY_BASIC_INFORMATION info;
SIZE_T szBufferSize = ::VirtualQueryEx(::GetCurrentProcess(), pData, &info, sizeof(info));

但是,如果我没记错的话,这给了我单个页面的大小,我如何利用它来给我整个缓冲区的大小?

谢谢!

【问题讨论】:

  • But this, gives me the size of a single page if I'm not mistaken 取决于“this”是什么意思。 MEMORY_BASIC_INFORMATION::RegionSize 应该给你 - 等待它 - 区域的大小。如果您查看的是VirtualQueryEx 的返回值,那么基本上就是sizeof(MEMORY_BASIC_INFORMATION)(如文档中所述)。

标签: c++ windows pointers shared-memory


【解决方案1】:

据我所知,没有办法检索现有文件映射或文件映射视图的大小。您应该自己跟踪这些信息。

MSDN 声明 VirtualQueryEx 能够做这样的事情。

不,VirtualQueryEx 只能确定为视图保留的页数。这意味着结果总是四舍五入到一个页面大小。此外,没有明确保证 MapViewOfFile 将仅保留映射文件所需的最小页面数。例如,它可能会选择将其向上舍入到分配粒度。

【讨论】:

    【解决方案2】:

    这实际上是可能的,但微软出于某种原因没有记录它。 NtQuerySection API 自 Windows NT 的早期版本就存在,并且在 Windows 10 中仍然存在。

    以下是您的操作方式(显然,您需要自行承担依赖未记录内核 API 的风险):

    HANDLE hFileMapping = ::OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, strSharedMemName);
    _ASSERT(hFileMapping);
    
    ULONGLONG uicbSharedMemSize = 0;
    
    //Get the handle returned by the CreateFileMapping function
    //Assuming the same process here...
    HANDLE hDupH;
    if((::DuplicateHandle(
        ::GetCurrentProcess(), hFileMapping, 
        ::GetCurrentProcess(), &hDupH,
        DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS, FALSE, 0)))
    {
        hFileMapping = hDupH;
    
        enum SECTION_INFORMATION_CLASS{
            SectionBasicInformation,
            SectionImageInformation
        };
    
        typedef struct _SECTION_BASIC_INFORMATION {
          ULONG                   Unknown;
          ULONG                   SectionAttributes;
          LARGE_INTEGER           SectionSize;
        } SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION;
    
        static NTSTATUS
        (NTAPI
        *pfnNtQuerySection)(
          IN HANDLE               SectionHandle,
          IN SECTION_INFORMATION_CLASS InformationClass,
          OUT PVOID               InformationBuffer,
          IN ULONG                InformationBufferSize,
          OUT PULONG              ResultLength OPTIONAL ) = NULL;
    
        if(!pfnNtQuerySection)
            (FARPROC&)pfnNtQuerySection = ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), "NtQuerySection");
    
        if(pfnNtQuerySection)
        {
            SECTION_BASIC_INFORMATION sbi = {0};
            ULONG ucbRead = 0;
            NTSTATUS stat = pfnNtQuerySection(hFileMapping, SectionBasicInformation, &sbi, sizeof(sbi), &ucbRead);
            if(stat >= 0)
            {
                //The size returned will be rounded up to the page size (i.e. 4K in most cases)
                uicbSharedMemSize = sbi.SectionSize.QuadPart;
            }
        }
    }
    

    首先,SECTION_BASIC_INFORMATION 的正确定义是:

    typedef struct _SECTION_BASIC_INFORMATION
    {
        PVOID BaseAddress;
        ULONG AllocationAttributes;
        LARGE_INTEGER MaximumSize;
    } SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION;
    

    这么旧的定义(DWORD 第一个成员 - 错误,不适用于 64 位代码)。

    在第二次调用 NtQuerySection 时,部分句柄必须具有 SECTION_QUERY 访问权限,否则将是 STATUS_ACCESS_DENIED - 所以行:

    OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, strSharedMemName);
    

    错了。必须是

    OpenFileMapping(SECTION_QUERY|FILE_MAP_READ|FILE_MAP_WRITE, FALSE, strSharedMemName);
    

    或任何访问权限,包括SECTION_QUERY

    注意,如果使用OpenFileMapping 并将dwDesiredAccess 设置为SECTION_QUERY,则OpenFileMapping 由于未知原因将其更改为SECTION_MAP_READ - 是此api 中的下一行代码。 if (dwDesiredAccess == SECTION_QUERY) dwDesiredAccess = SECTION_MAP_READ; 所以需要添加一些对SECTION_QUERY 的访问权限。当然,我们可以将SECTION_QUERYZwOpenSection 完全匹配使用

    下一个 - 什么叫DuplicateHandle ?!?这在任务上下文调用中是绝对没有意义的。我们通过OpenFileMappingWZwOpenSection 处理了部分。

    最后是什么GetModuleHandle + GetProcAddressZwQuerySection ?例如,如果没有GetModuleHandle + GetProcAddress,我们如何调用OpenFileMappingWGetProcAddress 怎么打电话?以同样的方式,我们可以调用ZwQuerySection - 只需通过每个 wdk lib 文件夹中存在的 ntdll.libntdll.dll 链接。所以最终的代码必须是:

    if (HANDLE hMap = OpenFileMappingW(SECTION_QUERY|SECTION_MAP_READ, FALSE, name))
    {
        SECTION_BASIC_INFORMATION sbi;
        if (0 <= ZwQuerySection(hMap, SectionBasicInformation, &sbi, sizeof(sbi), 0))
        {
            DbgPrint("section size = %I64x\n", sbi.Size.QuadPart);
        }
        CloseHandle(hMap);
    }
    

    HANDLE hMap;
    if (0 <= ZwOpenSection(&hMap, SECTION_QUERY, &oa))
    {
        SECTION_BASIC_INFORMATION sbi;
        if (0 <= ZwQuerySection(hMap, SectionBasicInformation, &sbi, sizeof(sbi), 0))
        {
            DbgPrint("section size = %I64x\n", sbi.Size.QuadPart);
        }
        CloseHandle(hMap);
    }
    

    【讨论】:

    • 你叫什么DuplicateHandle
    • 您对SECTION_BASIC_INFORMATION 的定义也是错误的。必须是processhacker.sourceforge.net/doc/ntmmapi_8h_source.html#l00178。结果你在 64 位上得到了错误的结果
    • @RbMm:好的,你是对的,SECTION_BASIC_INFORMATION 对于 x64 是错误的。我也错过了SECTION_QUERY,因此求助于DuplicateHandle 使其工作。至于静态链接到ntdll.dll,这也可能是代码的较短版本。
    • DuplicateHandle 只有在另一个句柄(我们用DUPLICATE_SAME_ACCESS 复制的句柄)已经有SECTION_QUERY 时才能“帮助”,否则没有任何变化。真的只需要打开具有正确访问权限的部分 (SECTION_QUERY)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-09
    • 1970-01-01
    • 2022-12-05
    • 1970-01-01
    • 2021-08-24
    • 1970-01-01
    相关资源
    最近更新 更多