545 lines
17 KiB
C
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_
|