提取共享内存的大小
Extracting shared memory's size
我正在尝试了解我应该如何提取 MapViewOfFile 的 return 缓冲区大小。我使用以下命令分配共享内存
hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, dwDataSize, strSharedMemoryName.c_str());
使用以下代码片段填充内容:
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
能够做到这一点。
我尝试执行以下代码片段:
MEMORY_BASIC_INFORMATION info;
SIZE_T szBufferSize = ::VirtualQueryEx(::GetCurrentProcess(), pData, &info, sizeof(info));
但是,如果我没记错的话,它会给我单个页面的大小,我该如何利用它来给我整个缓冲区的大小?
谢谢!
据我所知,无法检索现有文件映射或文件映射视图的大小。您应该自己跟踪此信息。
MSDN states that VirtualQueryEx is capable of doing such thing.
不,VirtualQueryEx 只能确定为视图保留的页数。这意味着结果总是四舍五入到页面大小。此外,没有明确保证 MapViewOfFile 将仅保留映射文件所需的最少页面数。例如,它可能会选择将其四舍五入到分配粒度。
这实际上是可能的,但微软出于某种原因没有记录它。 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_QUERY
和 ZwOpenSection
下一步 - 为什么要调用 DuplicateHandle
?!?这在任务上下文调用中是绝对没有意义的。我们通过 OpenFileMappingW
或 ZwOpenSection
.
获得了部分句柄
最后 GetModuleHandle
+ GetProcAddress
ZwQuerySection
是什么?例如,我们如何在没有 GetModuleHandle
+ GetProcAddress
的情况下调用 OpenFileMappingW
? GetProcAddress
怎么调用?以同样的方式,我们可以通过 ntdll.lib 调用 ZwQuerySection
- 只需 link 和 ntdll.dll存在于每个 wdk lib 文件夹中。所以最终代码必须是:
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);
}
我正在尝试了解我应该如何提取 MapViewOfFile 的 return 缓冲区大小。我使用以下命令分配共享内存
hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, dwDataSize, strSharedMemoryName.c_str());
使用以下代码片段填充内容:
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
能够做到这一点。
我尝试执行以下代码片段:
MEMORY_BASIC_INFORMATION info;
SIZE_T szBufferSize = ::VirtualQueryEx(::GetCurrentProcess(), pData, &info, sizeof(info));
但是,如果我没记错的话,它会给我单个页面的大小,我该如何利用它来给我整个缓冲区的大小?
谢谢!
据我所知,无法检索现有文件映射或文件映射视图的大小。您应该自己跟踪此信息。
MSDN states that VirtualQueryEx is capable of doing such thing.
不,VirtualQueryEx 只能确定为视图保留的页数。这意味着结果总是四舍五入到页面大小。此外,没有明确保证 MapViewOfFile 将仅保留映射文件所需的最少页面数。例如,它可能会选择将其四舍五入到分配粒度。
这实际上是可能的,但微软出于某种原因没有记录它。 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_QUERY
和 ZwOpenSection
下一步 - 为什么要调用 DuplicateHandle
?!?这在任务上下文调用中是绝对没有意义的。我们通过 OpenFileMappingW
或 ZwOpenSection
.
最后 GetModuleHandle
+ GetProcAddress
ZwQuerySection
是什么?例如,我们如何在没有 GetModuleHandle
+ GetProcAddress
的情况下调用 OpenFileMappingW
? GetProcAddress
怎么调用?以同样的方式,我们可以通过 ntdll.lib 调用 ZwQuerySection
- 只需 link 和 ntdll.dll存在于每个 wdk lib 文件夹中。所以最终代码必须是:
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);
}