2025-05-13 19:45:22 +03:00

545 lines
17 KiB
C

/*
* Process Hacker .NET Tools -
* .NET Process IPC definitions
*
* Copyright (C) 2015-2016 dmex
*
* This file is part of Process Hacker.
*
* Process Hacker is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Process Hacker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
*/
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the current folder for more information.
//-----------------------------------------------------------------------------
// IPCHeader.h
//
// Define the LegacyPrivate header format for IPC memory mapped files.
//-----------------------------------------------------------------------------
//
// dmex: This header has been highly modified.
// Original: https://github.com/dotnet/coreclr/blob/master/src/ipcman/ipcheader.h
#ifndef _IPC_HEADER_H_
#define _IPC_HEADER_H_
#include "perfcounterdefs.h"
#include "ipcenums.h"
// Current version of the IPC Block
const USHORT VER_IPC_BLOCK = 4;
// Legacy version of the IPC Block
const USHORT VER_LEGACYPRIVATE_IPC_BLOCK = 2;
const USHORT VER_LEGACYPUBLIC_IPC_BLOCK = 3;
//-----------------------------------------------------------------------------
// Entry in the IPC Directory. Ensure binary compatibility across versions
// if we add (or remove) entries. If we remove an block, the entry should
// be EMPTY_ENTRY_OFFSET
//-----------------------------------------------------------------------------
const ULONG EMPTY_ENTRY_OFFSET = 0xFFFFFFFF;
const ULONG EMPTY_ENTRY_SIZE = 0;
typedef struct IPCEntry
{
ULONG Offset; // offset of the IPC Block from the end of the Full IPC Header
ULONG Size; // size (in bytes) of the block
} IPCEntry;
// Newer versions of the CLR use Flags field
const USHORT IPC_FLAG_USES_FLAGS = 0x1;
const USHORT IPC_FLAG_INITIALIZED = 0x2;
const USHORT IPC_FLAG_X86 = 0x4;
// In hindsight, we should have made the offsets be absolute, but we made them
// relative to the end of the FullIPCHeader.
// The problem is that as future versions added new Entries to the directory,
// the header size grew.
// Thus we make IPCEntry::Offset is relative to IPC_ENTRY_OFFSET_BASE, which
// corresponds to sizeof(PrivateIPCHeader) for an v1.0 /v1.1 build.
const ULONG IPC_ENTRY_OFFSET_BASE_X86 = 0x14;
const ULONG IPC_ENTRY_OFFSET_BASE_X64 = 0x0;
/******************************************************************************
* The CLR opens memory mapped files to expose perfcounter values and other
* information to other processes. Historically there have been three memory
* mapped files: the BlockTable, the LegacyPrivateBlock, and the
* LegacyPublicBlock.
*
* BlockTable - The block table was designed to work with multiple runtimes
* running side by side in the same process (SxS in-proc). We have defined
* semantics using interlocked operations that allow a runtime to allocate
* a block from the block table in a thread-safe manner.
*
* LegacyPrivateBlock - The legacy private block was used by older versions
* of the runtime to expose various information to the debugger. The
* legacy private block is not compatible with in-proc SxS, and thus it
* must be removed in the near future. Currently it is being used to expose
* information about AppDomains to the debugger. We will need to keep the
* code that knows how to read the legacy private block as long as we
* continue to support .NET 3.5 SP1.
*
* LegacyPublicBlock - The legacy public block was used by older versions
* of the runtime to expose perfcounter values. The legacy public block is
* not compatible with in-proc SxS, and thus it has been removed. We will
* need to keep the code that knows how to read the legacy public block as
* long as we continue to support .NET 3.5 SP1.
******************************************************************************/
/**************************************** BLOCK TABLE ****************************************/
#define IPC_BLOCK_TABLE_SIZE 65536
#define IPC_BLOCK_SIZE 2048
#define IPC_NUM_BLOCKS_IN_TABLE 32
C_ASSERT(IPC_BLOCK_TABLE_SIZE == IPC_NUM_BLOCKS_IN_TABLE * IPC_BLOCK_SIZE);
typedef struct _IPCHeader
{
// Chunk header
volatile LONG Counter; // *** Volatile<LONG> *** value of 0 is special; means that this block has never been touched before by a writer
ULONG RuntimeId; // value of 0 is special; means that chunk is currently free (runtime ids are always greater than 0)
ULONG Reserved1;
ULONG Reserved2;
// Standard header
USHORT Version; // version of the IPC Block
USHORT Flags; // flags field
ULONG blockSize; // Size of the entire shared memory block
USHORT BuildYear; // stamp for year built
USHORT BuildNumber; // stamp for Month/Day built
ULONG NumEntries; // Number of entries in the table
// Directory
IPCEntry EntryTable[eIPC_MAX]; // entry describing each client's block
} IPCHeader;
#define SXSPUBLIC_IPC_SIZE_NO_PADDING (sizeof(IPCHeader) + sizeof(PerfCounterIPCControlBlock))
#define SXSPUBLIC_WOW64_IPC_SIZE_NO_PADDING (sizeof(IPCHeader) + sizeof(PerfCounterIPCControlBlock_Wow64))
#define SXSPUBLIC_IPC_PAD_SIZE (IPC_BLOCK_SIZE - SXSPUBLIC_IPC_SIZE_NO_PADDING)
#define SXSPUBLIC_WOW64_IPC_PAD_SIZE (IPC_BLOCK_SIZE - SXSPUBLIC_WOW64_IPC_SIZE_NO_PADDING)
#ifndef _WIN64
#include <pshpack4.h>
#endif
typedef struct _IPCControlBlock
{
// Header
IPCHeader Header;
// Client blocks
PerfCounterIPCControlBlock PerfIpcBlock;
// Padding
BYTE Padding[SXSPUBLIC_IPC_PAD_SIZE];
} IPCControlBlock;
#ifndef _WIN64
#include <poppack.h>
#endif
#include <pshpack4.h>
typedef struct _IPCControlBlock_Wow64
{
// Header
IPCHeader Header;
// Client blocks
PerfCounterIPCControlBlock_Wow64 PerfIpcBlock;
// Padding
BYTE Padding[SXSPUBLIC_WOW64_IPC_PAD_SIZE];
} IPCControlBlock_Wow64;
#include <poppack.h>
C_ASSERT(sizeof(IPCControlBlock) == IPC_BLOCK_SIZE);
C_ASSERT(sizeof(IPCControlBlock_Wow64) == IPC_BLOCK_SIZE);
#ifndef _WIN64
#include <pshpack4.h>
#endif
typedef struct IPCControlBlockTable
{
IPCControlBlock Blocks[IPC_NUM_BLOCKS_IN_TABLE];
} IPCControlBlockTable;
#ifndef _WIN64
#include <poppack.h>
#endif
#include <pshpack4.h>
typedef struct IPCControlBlockTable_Wow64
{
IPCControlBlock_Wow64 Blocks[IPC_NUM_BLOCKS_IN_TABLE];
} IPCControlBlockTable_Wow64;
#include <poppack.h>
C_ASSERT(sizeof(IPCControlBlockTable) == IPC_BLOCK_TABLE_SIZE);
C_ASSERT(sizeof(IPCControlBlockTable_Wow64) == IPC_BLOCK_TABLE_SIZE);
#ifndef _WIN64
#include <pshpack4.h>
#endif
typedef struct LegacyPrivateIPCHeader
{
USHORT Version; // version of the IPC Block
USHORT Flags; // flags field
ULONG BlockSize; // Size of the entire shared memory block
HINSTANCE hInstance; // Instance of module that created this header
USHORT BuildYear; // stamp for year built
USHORT BuildNumber; // stamp for Month/Day built
ULONG NumEntries; // Number of entries in the table
} LegacyPrivateIPCHeader;
#ifndef _WIN64
#include <poppack.h>
#endif
#include <pshpack4.h>
typedef struct LegacyPrivateIPCHeader_Wow64
{
USHORT Version; // version of the IPC Block
USHORT Flags; // flags field
ULONG BlockSize; // Size of the entire shared memory block
ULONG hInstance; // Instance of module that created this header
USHORT BuildYear; // stamp for year built
USHORT BuildNumber; // stamp for Month/Day built
ULONG NumEntries; // Number of entries in the table
} LegacyPrivateIPCHeader_Wow64;
#include <poppack.h>
#ifndef _WIN64
#include <pshpack4.h>
#endif
typedef struct FullIPCHeaderLegacyPrivate
{
// Header
LegacyPrivateIPCHeader Header;
// Directory
IPCEntry EntryTable[eLegacyPrivateIPC_MAX]; // entry describing each client's block
} FullIPCHeaderLegacyPrivate;
#ifndef _WIN64
#include <poppack.h>
#endif
#include <pshpack4.h>
typedef struct FullIPCHeaderLegacyPrivate_Wow64
{
// Header
LegacyPrivateIPCHeader_Wow64 Header;
// Directory
IPCEntry EntryTable[eLegacyPrivateIPC_MAX]; // entry describing each client's block
} FullIPCHeaderLegacyPrivate_Wow64;
#include <poppack.h>
#ifndef _WIN64
#include <pshpack4.h>
#endif
typedef struct FullIPCHeaderLegacyPublic
{
// Header
LegacyPrivateIPCHeader Header;
// Directory
IPCEntry EntryTable[eLegacyPublicIPC_MAX]; // entry describing each client's block
} FullIPCHeaderLegacyPublic;
#ifndef _WIN64
#include <poppack.h>
#endif
#include <pshpack4.h>
typedef struct FullIPCHeaderLegacyPublic_Wow64
{
// Header
LegacyPrivateIPCHeader_Wow64 Header;
// Directory
IPCEntry EntryTable[eLegacyPublicIPC_MAX]; // entry describing each client's block
} FullIPCHeaderLegacyPublic_Wow64;
#include <poppack.h>
//-----------------------------------------------------------------------------
// LegacyPrivate (per process) IPC Block for COM+ apps
//-----------------------------------------------------------------------------
#ifndef _WIN64
#include <pshpack4.h>
#endif
typedef struct _LegacyPrivateIPCControlBlock
{
FullIPCHeaderLegacyPrivate FullIPCHeader;
// Client blocks
PerfCounterIPCControlBlock PerfIpcBlock; // no longer used but kept for compat
AppDomainEnumerationIPCBlock AppDomainBlock;
WCHAR InstancePath[MAX_PATH];
} LegacyPrivateIPCControlBlock;
#ifndef _WIN64
#include <poppack.h>
#endif
#include <pshpack4.h>
typedef struct _LegacyPrivateIPCControlBlock_Wow64
{
FullIPCHeaderLegacyPrivate_Wow64 FullIPCHeader;
// Client blocks
PerfCounterIPCControlBlock_Wow64 PerfIpcBlock; // no longer used but kept for compat
AppDomainEnumerationIPCBlock_Wow64 AppDomainBlock;
WCHAR InstancePath[MAX_PATH];
} LegacyPrivateIPCControlBlock_Wow64;
#include <poppack.h>
//-----------------------------------------------------------------------------
// LegacyPublic (per process) IPC Block for CLR apps
//-----------------------------------------------------------------------------
#ifndef _WIN64
#include <pshpack4.h>
#endif
typedef struct LegacyPublicIPCControlBlock
{
FullIPCHeaderLegacyPublic FullIPCHeaderLegacyPublic;
// Client blocks
PerfCounterIPCControlBlock PerfIpcBlock;
} LegacyPublicIPCControlBlock;
#ifndef _WIN64
#include <poppack.h>
#endif
#include <pshpack4.h>
typedef struct _LegacyPublicIPCControlBlock_Wow64
{
FullIPCHeaderLegacyPublic_Wow64 FullIPCHeaderLegacyPublic;
// Client blocks
PerfCounterIPCControlBlock_Wow64 PerfIpcBlock;
} LegacyPublicIPCControlBlock_Wow64;
#include <poppack.h>
//class IPCHeaderLockHolder
//{
// LONG Counter;
// BOOL HaveLock;
// IPCHeader & Header;
//
// public:
//
// IPCHeaderLockHolder(IPCHeader & header) : HaveLock(FALSE), Header(header) {}
//
// BOOL TryGetLock()
// {
// _ASSERTE(!HaveLock);
// LONG oldCounter = Header.Counter;
// if ((oldCounter & 1) != 0)
// return FALSE;
// Counter = oldCounter + 1;
// if (InterlockedCompareExchange((LONG *)(&(Header.Counter)), Counter, oldCounter) != oldCounter)
// return FALSE;
// HaveLock = TRUE;
//
// return TRUE;
// }
//
// BOOL TryGetLock(ULONG numRetries)
// {
// ULONG dwSwitchCount = 0;
//
// for (;;)
// {
// if (TryGetLock())
// return TRUE;
//
// if (numRetries == 0)
// return FALSE;
//
// --numRetries;
// __SwitchToThread(0, ++dwSwitchCount);
// }
// }
//
// void FreeLock()
// {
// _ASSERTE(HaveLock);
// _ASSERTE(Header.Counter == Counter);
// ++Counter;
// Counter = (Counter == 0) ? 2 : Counter;
// Header.Counter = Counter;
// HaveLock = FALSE;
// }
//
// ~IPCHeaderLockHolder()
// {
// if (HaveLock)
// FreeLock();
// }
//};
//
//class IPCHeaderReadHelper
//{
// IPCHeader CachedHeader;
// IPCHeader * pUnreliableHeader;
// BOOL IsOpen;
//
// public:
//
// IPCHeaderReadHelper() : pUnreliableHeader(NULL), IsOpen(FALSE) {}
//
// BOOL TryOpenHeader(IPCHeader * header)
// {
// _ASSERTE(!IsOpen);
//
// pUnreliableHeader = header;
//
// // Read the counter and the runtime ID from the header
// CachedHeader.Counter = pUnreliableHeader->Counter;
// if ((CachedHeader.Counter & 1) != 0)
// return FALSE;
// CachedHeader.RuntimeId = pUnreliableHeader->RuntimeId;
//
// // If runtime ID is 0, then this block is not allocated by
// // a runtime, and thus there is no further work to do
// if (CachedHeader.RuntimeId == 0)
// {
// IsOpen = TRUE;
// return TRUE;
// }
//
// // Read the rest of the values from the header
// CachedHeader.Reserved1 = pUnreliableHeader->Reserved1;
// CachedHeader.Reserved2 = pUnreliableHeader->Reserved2;
// CachedHeader.Version = pUnreliableHeader->Version;
// CachedHeader.Flags = pUnreliableHeader->Flags;
// CachedHeader.blockSize = pUnreliableHeader->blockSize;
// CachedHeader.BuildYear = pUnreliableHeader->BuildYear;
// CachedHeader.BuildNumber = pUnreliableHeader->BuildNumber;
// CachedHeader.numEntries = pUnreliableHeader->numEntries;
//
// // Verify that the header did not change during the read
// LONG counter = pUnreliableHeader->Counter;
// if (CachedHeader.Counter != counter)
// return FALSE;
//
// // Since we know we got a clean read of numEntries, we
// // should be able to assert this with confidence
// if (CachedHeader.numEntries == 0)
// {
// _ASSERTE(!"numEntries from IPCBlock is zero");
// return FALSE;
// }
// else if (CachedHeader.numEntries > eIPC_MAX)
// {
// _ASSERTE(!"numEntries from IPCBlock is too big");
// return FALSE;
// }
//
// if (CachedHeader.blockSize == 0)
// {
// _ASSERTE(!"blockSize from IPCBlock is zero");
// return FALSE;
// }
// else if (CachedHeader.blockSize > IPC_BLOCK_SIZE)
// {
// _ASSERTE(!"blockSize from IPCBlock is too big");
// return FALSE;
// }
//
// // Copy the table
// for (ULONG i = 0; i < CachedHeader.numEntries; ++i)
// {
// CachedHeader.EntryTable[i].Offset = pUnreliableHeader->EntryTable[i].Offset;
// CachedHeader.EntryTable[i].Size = pUnreliableHeader->EntryTable[i].Size;
// if (i == eIPC_PerfCounters)
// {
// if(!((SIZE_T)CachedHeader.EntryTable[i].Offset < IPC_BLOCK_SIZE) && ((SIZE_T)CachedHeader.EntryTable[i].Offset + CachedHeader.EntryTable[i].Size <= IPC_BLOCK_SIZE))
// {
// _ASSERTE(!"PerfCounter section offset + size is too large");
// return FALSE;
// }
// }
// }
//
// // If eIPC_MAX > numEntries, then mark the left over
// // slots in EntryTable as "empty".
// for (ULONG i = CachedHeader.numEntries; i < eIPC_MAX; ++i)
// {
// CachedHeader.EntryTable[i].Offset = EMPTY_ENTRY_OFFSET;
// CachedHeader.EntryTable[i].Size = EMPTY_ENTRY_SIZE;
// }
//
// // Verify again that the header did not change during the read
// counter = pUnreliableHeader->Counter;
// if (CachedHeader.Counter != counter)
// return FALSE;
//
// IsOpen = TRUE;
// return TRUE;
// }
//
//
// BOOL HeaderHasChanged()
// {
// _ASSERTE(IsOpen);
// LONG counter = pUnreliableHeader->Counter;
// return (CachedHeader.Counter != counter) ? TRUE : FALSE;
// }
//
// BOOL IsSentinal()
// {
// _ASSERTE(IsOpen);
// return (CachedHeader.Counter == 0);
// }
//
//
// BOOL UseWow64Structs()
// {
// _ASSERTE(IsOpen);
//#if !defined(_TARGET_X86_)
// return ((CachedHeader.Flags & IPC_FLAG_X86) != 0) ? TRUE : FALSE;
//#else
// return FALSE;
//#endif
// }
//
// void * GetUnreliableSection(EIPCClient eClient)
// {
// if (!IsOpen)
// {
// _ASSERTE(!"IPCHeaderReadHelper is not open");
// return NULL;
// }
//
// if (eClient < 0 || eClient >= eIPC_MAX)
// {
// _ASSERTE(!"eClient is out of bounds");
// return NULL;
// }
//
// if (CachedHeader.EntryTable[eClient].Offset == EMPTY_ENTRY_OFFSET)
// {
// _ASSERTE(!"Section is empty");
// return NULL;
// }
//
// return (BYTE*)pUnreliableHeader + (SIZE_T)CachedHeader.EntryTable[eClient].Offset;
// }
//};
#endif // _IPC_HEADER_H_