igal k
igal k

Reputation: 1914

Extracting shared memory's size

I'm trying to understand how exactly i should extract MapViewOfFile's return buffer size. I used the following command to allocate a shared memory

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

Filled it with stuff using the following code snippet:

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);

And then, somewhere else, tried to reopen that shared memory and read the overall buffer:

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;
    }

my next line was going to be

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

However, i have no idea how big pData is, one option is to send the overall size in the buffer, however MSDN states that VirtualQueryEx is capable of doing such thing.

I tried to execute the following code snippet:

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, how do i utilize this to give me the size of the overall buffer?

thanks!

Upvotes: 2

Views: 3684

Answers (2)

c00000fd
c00000fd

Reputation: 22327

It is actually possible, but Microsoft for some reason did not document it. The NtQuerySection API existed since early versions of Windows NT and still does in Windows 10.

So here's how you do it (obviously at your own risk of relying on an undocumented kernel 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;
        }
    }
}

first of all correct definition for SECTION_BASIC_INFORMATION is :

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

so old definition (with DWORD first member - was wrong and will be not work for 64bit code).

at second for call NtQuerySection the section handle must have SECTION_QUERY access, otherwise will be STATUS_ACCESS_DENIED - so line:

OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, strSharedMemName);

is wrong. must be

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

or any access, including SECTION_QUERY

note, that if use OpenFileMapping and set dwDesiredAccess exactly to SECTION_QUERY the OpenFileMapping by unknown reason change it to SECTION_MAP_READ - was next line of code inside this api. if (dwDesiredAccess == SECTION_QUERY) dwDesiredAccess = SECTION_MAP_READ; so need add some access to SECTION_QUERY. of course we can use SECTION_QUERY exactly with ZwOpenSection

next - for what call DuplicateHandle ?!? this is absolute senseless in task context call. we got handle to section via OpenFileMappingW or ZwOpenSection.

and finally for what GetModuleHandle + GetProcAddress for ZwQuerySection ? how we interesting call OpenFileMappingW without GetModuleHandle + GetProcAddress for example ? how GetProcAddress call ? in same way we can call ZwQuerySection - simply link with ntdll.dll via ntdll.lib which exist in every wdk lib folder. so final code must be:

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);
}

or

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);
}

Upvotes: 1

Harry Johnston
Harry Johnston

Reputation: 36348

As far as I know, there is no way to retrieve the size of an existing file mapping or file mapping view. You are expected to track this information yourself.

MSDN states that VirtualQueryEx is capable of doing such thing.

No, all VirtualQueryEx can determine is the number of pages reserved for the view. That means that the result is always rounded up to a page size. Also, there is no explicit guarantee that MapViewOfFile will reserve only the minimum number of pages necessary to map the file. For example, it might choose to round it up to the allocation granularity.

Upvotes: 7

Related Questions