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

1044 lines
30 KiB
C

/*
* Process Hacker .NET Tools -
* IPC support functions
*
* 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/>.
*/
#include "dn.h"
#include "clr/dbgappdomain.h"
#include "clr/ipcheader.h"
#include "clr/ipcshared.h"
typedef PVOID (NTAPI* _RtlCreateBoundaryDescriptor)(
_In_ PUNICODE_STRING Name,
_In_ ULONG Flags
);
typedef NTSTATUS (NTAPI* _RtlAddSIDToBoundaryDescriptor)(
_Inout_ PVOID* BoundaryDescriptor,
_In_ PSID RequiredSid
);
typedef VOID (NTAPI* _RtlDeleteBoundaryDescriptor)(
_In_ PVOID BoundaryDescriptor
);
typedef NTSTATUS (WINAPI* _NtOpenPrivateNamespace)(
_Out_ PHANDLE NamespaceHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_ PVOID BoundaryDescriptor
);
static _NtOpenPrivateNamespace NtOpenPrivateNamespace_I = NULL;
static _RtlCreateBoundaryDescriptor RtlCreateBoundaryDescriptor_I = NULL;
static _RtlDeleteBoundaryDescriptor RtlDeleteBoundaryDescriptor_I = NULL;
static _RtlAddSIDToBoundaryDescriptor RtlAddSIDToBoundaryDescriptor_I = NULL;
PPH_STRING GeneratePrivateName(_In_ HANDLE ProcessId)
{
return PhaFormatString(L"\\BaseNamedObjects\\" CorLegacyPrivateIPCBlock, HandleToUlong(ProcessId));
}
PPH_STRING GeneratePrivateNameV4(_In_ HANDLE ProcessId)
{
return PhaFormatString(L"\\BaseNamedObjects\\" CorLegacyPrivateIPCBlockTempV4, HandleToUlong(ProcessId));
}
PPH_STRING GenerateLegacyPublicName(_In_ HANDLE ProcessId)
{
return PhaFormatString(L"\\BaseNamedObjects\\" CorLegacyPublicIPCBlock, HandleToUlong(ProcessId));
}
PPH_STRING GenerateSxSPublicNameV4(_In_ HANDLE ProcessId)
{
return PhaFormatString(L"\\BaseNamedObjects\\" CorSxSPublicIPCBlock, HandleToUlong(ProcessId));
}
PPH_STRING GenerateBoundaryDescriptorName(_In_ HANDLE ProcessId)
{
return PhaFormatString(CorSxSBoundaryDescriptor, HandleToUlong(ProcessId));
}
PVOID GetLegacyBlockTableEntry(
_In_ BOOLEAN Wow64,
_In_ PVOID IpcBlockAddress,
_In_ ULONG EntryId
)
{
if (Wow64)
{
LegacyPrivateIPCControlBlock_Wow64* IpcBlock = IpcBlockAddress;
// skip over directory (variable size)
ULONG offsetBase = IPC_ENTRY_OFFSET_BASE_X86 + IpcBlock->FullIPCHeader.Header.NumEntries * sizeof(IPCEntry);
// Directory has offset in bytes of block
ULONG offsetEntry = IpcBlock->FullIPCHeader.EntryTable[EntryId].Offset;
return ((PBYTE)IpcBlock) + offsetBase + offsetEntry;
}
else
{
LegacyPrivateIPCControlBlock* IpcBlock = IpcBlockAddress;
// skip over directory (variable size)
ULONG offsetBase = IPC_ENTRY_OFFSET_BASE_X64 + IpcBlock->FullIPCHeader.Header.NumEntries * sizeof(IPCEntry);
// Directory has offset in bytes of block
ULONG offsetEntry = IpcBlock->FullIPCHeader.EntryTable[EntryId].Offset;
return ((PBYTE)IpcBlock) + offsetBase + offsetEntry;
}
}
PVOID GetPerfIpcBlock_V2(
_In_ BOOLEAN Wow64,
_In_ PVOID BlockTableAddress
)
{
if (Wow64)
{
LegacyPublicIPCControlBlock_Wow64* ipcLegacyBlockTable_Wow64 = BlockTableAddress;
if (ipcLegacyBlockTable_Wow64->FullIPCHeaderLegacyPublic.Header.Version > VER_LEGACYPUBLIC_IPC_BLOCK)
{
return NULL;
}
return &ipcLegacyBlockTable_Wow64->PerfIpcBlock;
}
else
{
LegacyPublicIPCControlBlock* ipcLegacyBlockTable = BlockTableAddress;
if (ipcLegacyBlockTable->FullIPCHeaderLegacyPublic.Header.Version > VER_IPC_BLOCK)
{
return NULL;
}
return &ipcLegacyBlockTable->PerfIpcBlock;
}
}
PVOID GetPerfIpcBlock_V4(
_In_ BOOLEAN Wow64,
_In_ PVOID BlockTableAddress
)
{
if (Wow64)
{
IPCControlBlockTable_Wow64* ipcBlockTable_Wow64 = BlockTableAddress;
if (ipcBlockTable_Wow64->Blocks->Header.Version > VER_IPC_BLOCK)
{
return NULL;
}
return &ipcBlockTable_Wow64->Blocks->PerfIpcBlock;
}
else
{
IPCControlBlockTable_Wow64* ipcBlockTable = BlockTableAddress;
if (ipcBlockTable->Blocks->Header.Version > VER_IPC_BLOCK)
{
return NULL;
}
return &ipcBlockTable->Blocks->PerfIpcBlock;
}
}
PPH_LIST EnumAppDomainIpcBlock(
_In_ HANDLE ProcessHandle,
_In_ AppDomainEnumerationIPCBlock* AppDomainIpcBlock
)
{
LARGE_INTEGER timeout;
SIZE_T appDomainInfoBlockLength;
HANDLE legacyPrivateBlockMutexHandle = NULL;
AppDomainEnumerationIPCBlock tempBlock;
AppDomainInfo* appDomainInfoBlock = NULL;
PPH_LIST appDomainsList = PhCreateList(1);
__try
{
// If the mutex isn't filled in, the CLR is either starting up or shutting down
if (!AppDomainIpcBlock->Mutex)
{
__leave;
}
// Dup the valid mutex handle into this process.
if (!NT_SUCCESS(NtDuplicateObject(
ProcessHandle,
AppDomainIpcBlock->Mutex,
NtCurrentProcess(),
&legacyPrivateBlockMutexHandle,
GENERIC_ALL,
0,
DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES
)))
{
__leave;
}
// Acquire the mutex, only waiting two seconds.
// We can't actually gaurantee that the target put a mutex object in here.
if (NtWaitForSingleObject(
legacyPrivateBlockMutexHandle,
FALSE,
PhTimeoutFromMilliseconds(&timeout, 2000)
) == STATUS_WAIT_0)
{
// Make sure the mutex handle is still valid. If its not, then we lost a shutdown race.
if (!AppDomainIpcBlock->Mutex)
{
__leave;
}
}
else
{
// Again, landing here is most probably a shutdown race.
__leave;
}
// Beware: If the target pid is not properly honoring the mutex, the data in the IPC block may still shift underneath us.
// If we get here, then hMutex is held by this process.
// Make a copy of the IPC block so that we can gaurantee that it's not changing on us.
memcpy(&tempBlock, AppDomainIpcBlock, sizeof(AppDomainEnumerationIPCBlock));
// It's possible the process will not have any appdomains.
if ((tempBlock.ListOfAppDomains == NULL) != (tempBlock.SizeInBytes == 0))
{
__leave;
}
// All the data in the IPC block is signed integers. They should never be negative,
// so check that now.
if ((tempBlock.TotalSlots < 0) ||
(tempBlock.NumOfUsedSlots < 0) ||
(tempBlock.LastFreedSlot < 0) ||
(tempBlock.SizeInBytes < 0) ||
(tempBlock.ProcessNameLengthInBytes < 0))
{
__leave;
}
// Allocate memory to read the remote process' memory into
appDomainInfoBlockLength = tempBlock.SizeInBytes;
// Check other invariants.
if (appDomainInfoBlockLength != tempBlock.TotalSlots * sizeof(AppDomainInfo))
{
__leave;
}
appDomainInfoBlock = (AppDomainInfo*)PhAllocate(appDomainInfoBlockLength);
memset(appDomainInfoBlock, 0, appDomainInfoBlockLength);
if (!NT_SUCCESS(NtReadVirtualMemory(
ProcessHandle,
tempBlock.ListOfAppDomains,
appDomainInfoBlock,
appDomainInfoBlockLength,
NULL
)))
{
PhFree(appDomainInfoBlock);
__leave;
}
// Collect all the AppDomain names into a list of strings.
for (INT i = 0; i < tempBlock.NumOfUsedSlots; i++)
{
SIZE_T appDomainNameLength;
PVOID appDomainName;
if (!appDomainInfoBlock[i].AppDomainName)
continue;
// Should be positive, and at least have a null-terminator character.
if (appDomainInfoBlock[i].NameLengthInBytes <= 1)
continue;
// Make sure buffer has right geometry.
if (appDomainInfoBlock[i].NameLengthInBytes < 0)
continue;
// If it's not on a WCHAR boundary, then we may have a 1-byte buffer-overflow.
appDomainNameLength = appDomainInfoBlock[i].NameLengthInBytes / sizeof(WCHAR);
if ((appDomainNameLength * sizeof(WCHAR)) != appDomainInfoBlock[i].NameLengthInBytes)
continue;
// It should at least have 1 char for the null terminator.
if (appDomainNameLength < 1)
continue;
// We know the string is a well-formed null-terminated string,
// but beyond that, we can't verify that the data is actually truthful.
appDomainName = PhAllocate(appDomainInfoBlock[i].NameLengthInBytes + 1);
memset(appDomainName, 0, appDomainInfoBlock[i].NameLengthInBytes + 1);
if (!NT_SUCCESS(NtReadVirtualMemory(
ProcessHandle,
appDomainInfoBlock[i].AppDomainName,
appDomainName,
appDomainInfoBlock[i].NameLengthInBytes,
NULL
)))
{
PhFree(appDomainName);
continue;
}
PhAddItemList(appDomainsList, appDomainName);
}
}
__finally
{
if (appDomainInfoBlock)
{
PhFree(appDomainInfoBlock);
}
if (legacyPrivateBlockMutexHandle)
{
NtReleaseMutant(legacyPrivateBlockMutexHandle, NULL);
NtClose(legacyPrivateBlockMutexHandle);
}
}
return appDomainsList;
}
PPH_LIST EnumAppDomainIpcBlockWow64(
_In_ HANDLE ProcessHandle,
_In_ AppDomainEnumerationIPCBlock_Wow64* AppDomainIpcBlock
)
{
LARGE_INTEGER timeout;
SIZE_T appDomainInfoBlockLength;
HANDLE legacyPrivateBlockMutexHandle = NULL;
AppDomainEnumerationIPCBlock_Wow64 tempBlock;
AppDomainInfo_Wow64* appDomainInfoBlock = NULL;
PPH_LIST appDomainsList = PhCreateList(1);
__try
{
// If the mutex isn't filled in, the CLR is either starting up or shutting down
if (!AppDomainIpcBlock->Mutex)
{
__leave;
}
// Dup the valid mutex handle into this process.
if (!NT_SUCCESS(NtDuplicateObject(
ProcessHandle,
UlongToHandle(AppDomainIpcBlock->Mutex),
NtCurrentProcess(),
&legacyPrivateBlockMutexHandle,
GENERIC_ALL,
0,
DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES
)))
{
__leave;
}
// Acquire the mutex, only waiting two seconds.
// We can't actually gaurantee that the target put a mutex object in here.
if (NtWaitForSingleObject(
legacyPrivateBlockMutexHandle,
FALSE,
PhTimeoutFromMilliseconds(&timeout, 2000)
) == STATUS_WAIT_0)
{
// Make sure the mutex handle is still valid. If its not, then we lost a shutdown race.
if (!AppDomainIpcBlock->Mutex)
{
__leave;
}
}
else
{
// Again, landing here is most probably a shutdown race.
__leave;
}
// Beware: If the target pid is not properly honoring the mutex, the data in the IPC block may still shift underneath us.
// If we get here, then hMutex is held by this process.
// Make a copy of the IPC block so that we can gaurantee that it's not changing on us.
memcpy(&tempBlock, AppDomainIpcBlock, sizeof(AppDomainEnumerationIPCBlock_Wow64));
// It's possible the process will not have any appdomains.
if ((tempBlock.ListOfAppDomains == 0) != (tempBlock.SizeInBytes == 0))
{
__leave;
}
// All the data in the IPC block is signed integers. They should never be negative,
// so check that now.
if ((tempBlock.TotalSlots < 0) ||
(tempBlock.NumOfUsedSlots < 0) ||
(tempBlock.LastFreedSlot < 0) ||
(tempBlock.SizeInBytes < 0) ||
(tempBlock.ProcessNameLengthInBytes < 0))
{
__leave;
}
// Allocate memory to read the remote process' memory into
appDomainInfoBlockLength = tempBlock.SizeInBytes;
// Check other invariants.
if (appDomainInfoBlockLength != tempBlock.TotalSlots * sizeof(AppDomainInfo_Wow64))
{
__leave;
}
appDomainInfoBlock = (AppDomainInfo_Wow64*)PhAllocate(appDomainInfoBlockLength);
memset(appDomainInfoBlock, 0, appDomainInfoBlockLength);
if (!NT_SUCCESS(NtReadVirtualMemory(
ProcessHandle,
UlongToPtr(tempBlock.ListOfAppDomains),
appDomainInfoBlock,
appDomainInfoBlockLength,
NULL
)))
{
PhFree(appDomainInfoBlock);
__leave;
}
// Collect all the AppDomain names into a list of strings.
for (INT i = 0; i < tempBlock.NumOfUsedSlots; i++)
{
SIZE_T appDomainNameLength;
PVOID appDomainName;
if (!appDomainInfoBlock[i].AppDomainName)
continue;
// Should be positive, and at least have a null-terminator character.
if (appDomainInfoBlock[i].NameLengthInBytes <= 1)
continue;
// Make sure buffer has right geometry.
if (appDomainInfoBlock[i].NameLengthInBytes < 0)
continue;
// If it's not on a WCHAR boundary, then we may have a 1-byte buffer-overflow.
appDomainNameLength = appDomainInfoBlock[i].NameLengthInBytes / sizeof(WCHAR);
if ((appDomainNameLength * sizeof(WCHAR)) != appDomainInfoBlock[i].NameLengthInBytes)
continue;
// It should at least have 1 char for the null terminator.
if (appDomainNameLength < 1)
continue;
// We know the string is a well-formed null-terminated string,
// but beyond that, we can't verify that the data is actually truthful.
appDomainName = PhAllocate(appDomainInfoBlock[i].NameLengthInBytes + 1);
memset(appDomainName, 0, appDomainInfoBlock[i].NameLengthInBytes + 1);
if (!NT_SUCCESS(NtReadVirtualMemory(
ProcessHandle,
UlongToPtr(appDomainInfoBlock[i].AppDomainName),
appDomainName,
appDomainInfoBlock[i].NameLengthInBytes,
NULL
)))
{
PhFree(appDomainName);
continue;
}
PhAddItemList(appDomainsList, appDomainName);
}
}
__finally
{
if (appDomainInfoBlock)
{
PhFree(appDomainInfoBlock);
}
if (legacyPrivateBlockMutexHandle)
{
NtReleaseMutant(legacyPrivateBlockMutexHandle, NULL);
NtClose(legacyPrivateBlockMutexHandle);
}
}
return appDomainsList;
}
BOOLEAN OpenDotNetPublicControlBlock_V2(
_In_ HANDLE ProcessId,
_Out_ HANDLE* BlockTableHandle,
_Out_ PVOID* BlockTableAddress
)
{
BOOLEAN result = FALSE;
HANDLE blockTableHandle = NULL;
PVOID blockTableAddress = NULL;
UNICODE_STRING sectionNameUs;
OBJECT_ATTRIBUTES objectAttributes;
LARGE_INTEGER sectionOffset = { 0 };
SIZE_T viewSize = 0;
__try
{
if (!PhStringRefToUnicodeString(&GenerateLegacyPublicName(ProcessId)->sr, &sectionNameUs))
__leave;
InitializeObjectAttributes(
&objectAttributes,
&sectionNameUs,
0,
NULL,
NULL
);
if (!NT_SUCCESS(NtOpenSection(
&blockTableHandle,
SECTION_MAP_READ,
&objectAttributes
)))
{
__leave;
}
if (!NT_SUCCESS(NtMapViewOfSection(
blockTableHandle,
NtCurrentProcess(),
&blockTableAddress,
0,
viewSize,
&sectionOffset,
&viewSize,
ViewShare,
0,
PAGE_READONLY
)))
{
__leave;
}
*BlockTableHandle = blockTableHandle;
*BlockTableAddress = blockTableAddress;
result = TRUE;
}
__finally
{
if (!result)
{
if (blockTableHandle)
{
NtClose(blockTableHandle);
}
if (blockTableAddress)
{
NtUnmapViewOfSection(NtCurrentProcess(), blockTableAddress);
}
*BlockTableHandle = NULL;
*BlockTableAddress = NULL;
}
}
return result;
}
BOOLEAN OpenDotNetPublicControlBlock_V4(
_In_ BOOLEAN IsImmersive,
_In_ HANDLE ProcessHandle,
_In_ HANDLE ProcessId,
_Out_ HANDLE* BlockTableHandle,
_Out_ PVOID* BlockTableAddress
)
{
BOOLEAN result = FALSE;
PVOID boundaryDescriptorHandle = NULL;
HANDLE privateNamespaceHandle = NULL;
HANDLE blockTableHandle = NULL;
HANDLE tokenHandle = NULL;
PSID everyoneSIDHandle = NULL;
PVOID blockTableAddress = NULL;
LARGE_INTEGER sectionOffset = { 0 };
SIZE_T viewSize = 0;
PTOKEN_APPCONTAINER_INFORMATION appContainerInfo = NULL;
SID_IDENTIFIER_AUTHORITY SIDWorldAuth = SECURITY_WORLD_SID_AUTHORITY;
__try
{
if (WindowsVersion < WINDOWS_VISTA)
{
UNICODE_STRING sectionNameUs;
OBJECT_ATTRIBUTES objectAttributes;
if (!PhStringRefToUnicodeString(&GenerateSxSPublicNameV4(ProcessId)->sr, &sectionNameUs))
__leave;
InitializeObjectAttributes(
&objectAttributes,
&sectionNameUs,
0,
NULL,
NULL
);
if (!NT_SUCCESS(NtOpenSection(
&blockTableHandle,
SECTION_MAP_READ,
&objectAttributes
)))
{
__leave;
}
}
else
{
static PH_INITONCE initOnce = PH_INITONCE_INIT;
UNICODE_STRING prefixNameUs;
UNICODE_STRING sectionNameUs;
UNICODE_STRING boundaryNameUs;
OBJECT_ATTRIBUTES namespaceObjectAttributes;
OBJECT_ATTRIBUTES sectionObjectAttributes;
if (PhBeginInitOnce(&initOnce))
{
PVOID ntdll;
ntdll = PhGetDllHandle(L"ntdll.dll");
NtOpenPrivateNamespace_I = PhGetProcedureAddress(ntdll, "NtOpenPrivateNamespace", 0);
RtlCreateBoundaryDescriptor_I = PhGetProcedureAddress(ntdll, "RtlCreateBoundaryDescriptor", 0);
RtlDeleteBoundaryDescriptor_I = PhGetProcedureAddress(ntdll, "RtlDeleteBoundaryDescriptor", 0);
RtlAddSIDToBoundaryDescriptor_I = PhGetProcedureAddress(ntdll, "RtlAddSIDToBoundaryDescriptor", 0);
PhEndInitOnce(&initOnce);
}
if (!PhStringRefToUnicodeString(&GenerateBoundaryDescriptorName(ProcessId)->sr, &boundaryNameUs))
__leave;
if (!(boundaryDescriptorHandle = RtlCreateBoundaryDescriptor_I(&boundaryNameUs, 0)))
__leave;
if (!NT_SUCCESS(RtlAllocateAndInitializeSid(&SIDWorldAuth, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &everyoneSIDHandle)))
__leave;
if (!NT_SUCCESS(RtlAddSIDToBoundaryDescriptor_I(&boundaryDescriptorHandle, everyoneSIDHandle)))
__leave;
if (WINDOWS_HAS_IMMERSIVE && IsImmersive)
{
if (NT_SUCCESS(NtOpenProcessToken(&tokenHandle, TOKEN_QUERY, ProcessHandle)))
{
ULONG returnLength = 0;
if (NtQueryInformationToken(
tokenHandle,
TokenAppContainerSid,
NULL,
0,
&returnLength
) != STATUS_BUFFER_TOO_SMALL)
{
__leave;
}
if (returnLength < 1)
__leave;
appContainerInfo = PhAllocate(returnLength);
if (!NT_SUCCESS(NtQueryInformationToken(
tokenHandle,
TokenAppContainerSid,
appContainerInfo,
returnLength,
&returnLength
)))
{
__leave;
}
if (!appContainerInfo->TokenAppContainer)
__leave;
if (!NT_SUCCESS(RtlAddSIDToBoundaryDescriptor_I(&boundaryDescriptorHandle, appContainerInfo->TokenAppContainer)))
__leave;
}
}
RtlInitUnicodeString(&prefixNameUs, CorSxSReaderPrivateNamespacePrefix);
InitializeObjectAttributes(
&namespaceObjectAttributes,
&prefixNameUs,
OBJ_CASE_INSENSITIVE,
boundaryDescriptorHandle,
NULL
);
if (!NT_SUCCESS(NtOpenPrivateNamespace_I(
&privateNamespaceHandle,
MAXIMUM_ALLOWED,
&namespaceObjectAttributes,
boundaryDescriptorHandle
)))
{
__leave;
}
RtlInitUnicodeString(&sectionNameUs, CorSxSVistaPublicIPCBlock);
InitializeObjectAttributes(
&sectionObjectAttributes,
&sectionNameUs,
OBJ_CASE_INSENSITIVE,
privateNamespaceHandle,
NULL
);
if (!NT_SUCCESS(NtOpenSection(
&blockTableHandle,
SECTION_MAP_READ,
&sectionObjectAttributes
)))
{
__leave;
}
}
if (!NT_SUCCESS(NtMapViewOfSection(
blockTableHandle,
NtCurrentProcess(),
&blockTableAddress,
0,
viewSize,
&sectionOffset,
&viewSize,
ViewShare,
0,
PAGE_READONLY
)))
{
__leave;
}
*BlockTableHandle = blockTableHandle;
*BlockTableAddress = blockTableAddress;
result = TRUE;
}
__finally
{
if (!result)
{
if (blockTableHandle)
{
NtClose(blockTableHandle);
}
if (blockTableAddress)
{
NtUnmapViewOfSection(NtCurrentProcess(), blockTableAddress);
}
*BlockTableHandle = NULL;
*BlockTableAddress = NULL;
}
if (tokenHandle)
{
NtClose(tokenHandle);
}
if (appContainerInfo)
{
PhFree(appContainerInfo);
}
if (privateNamespaceHandle)
{
NtClose(privateNamespaceHandle);
}
if (everyoneSIDHandle)
{
RtlFreeSid(everyoneSIDHandle);
}
if (RtlDeleteBoundaryDescriptor_I && boundaryDescriptorHandle)
{
RtlDeleteBoundaryDescriptor_I(boundaryDescriptorHandle);
}
}
return result;
}
PPH_LIST QueryDotNetAppDomainsForPid_V2(
_In_ BOOLEAN Wow64,
_In_ HANDLE ProcessHandle,
_In_ HANDLE ProcessId
)
{
LARGE_INTEGER sectionOffset = { 0 };
SIZE_T viewSize = 0;
OBJECT_ATTRIBUTES objectAttributes;
UNICODE_STRING sectionNameUs;
HANDLE legacyPrivateBlockHandle = NULL;
PVOID ipcControlBlockTable = NULL;
PPH_LIST appDomainsList = NULL;
__try
{
if (!PhStringRefToUnicodeString(&GeneratePrivateName(ProcessId)->sr, &sectionNameUs))
__leave;
InitializeObjectAttributes(
&objectAttributes,
&sectionNameUs,
0,
NULL,
NULL
);
if (!NT_SUCCESS(NtOpenSection(
&legacyPrivateBlockHandle,
SECTION_MAP_READ,
&objectAttributes
)))
{
__leave;
}
if (!NT_SUCCESS(NtMapViewOfSection(
legacyPrivateBlockHandle,
NtCurrentProcess(),
&ipcControlBlockTable,
0,
viewSize,
&sectionOffset,
&viewSize,
ViewShare,
0,
PAGE_READONLY
)))
{
__leave;
}
if (Wow64)
{
LegacyPrivateIPCControlBlock_Wow64* legacyPrivateBlock;
AppDomainEnumerationIPCBlock_Wow64* appDomainEnumBlock;
legacyPrivateBlock = (LegacyPrivateIPCControlBlock_Wow64*)ipcControlBlockTable;
// NOTE: .NET 2.0 processes do not have the IPC_FLAG_INITIALIZED flag.
// Check the IPCControlBlock version is valid.
if (legacyPrivateBlock->FullIPCHeader.Header.Version > VER_LEGACYPRIVATE_IPC_BLOCK)
{
__leave;
}
appDomainEnumBlock = GetLegacyBlockTableEntry(
Wow64,
ipcControlBlockTable,
eLegacyPrivateIPC_AppDomain
);
appDomainsList = EnumAppDomainIpcBlockWow64(
ProcessHandle,
appDomainEnumBlock
);
}
else
{
LegacyPrivateIPCControlBlock* legacyPrivateBlock;
AppDomainEnumerationIPCBlock* appDomainEnumBlock;
legacyPrivateBlock = (LegacyPrivateIPCControlBlock*)ipcControlBlockTable;
// NOTE: .NET 2.0 processes do not have the IPC_FLAG_INITIALIZED flag.
// Check the IPCControlBlock version is valid.
if (legacyPrivateBlock->FullIPCHeader.Header.Version > VER_LEGACYPRIVATE_IPC_BLOCK)
{
__leave;
}
appDomainEnumBlock = GetLegacyBlockTableEntry(
Wow64,
ipcControlBlockTable,
eLegacyPrivateIPC_AppDomain
);
appDomainsList = EnumAppDomainIpcBlock(
ProcessHandle,
appDomainEnumBlock
);
}
}
__finally
{
if (ipcControlBlockTable)
{
NtUnmapViewOfSection(NtCurrentProcess(), ipcControlBlockTable);
}
if (legacyPrivateBlockHandle)
{
NtClose(legacyPrivateBlockHandle);
}
}
return appDomainsList;
}
PPH_LIST QueryDotNetAppDomainsForPid_V4(
_In_ BOOLEAN Wow64,
_In_ HANDLE ProcessHandle,
_In_ HANDLE ProcessId
)
{
HANDLE legacyPrivateBlockHandle = NULL;
PVOID ipcControlBlockTable = NULL;
LARGE_INTEGER sectionOffset = { 0 };
SIZE_T viewSize = 0;
OBJECT_ATTRIBUTES objectAttributes;
UNICODE_STRING sectionNameUs;
PPH_LIST appDomainsList = NULL;
__try
{
if (!PhStringRefToUnicodeString(&GeneratePrivateNameV4(ProcessId)->sr, &sectionNameUs))
__leave;
InitializeObjectAttributes(
&objectAttributes,
&sectionNameUs,
0,
NULL,
NULL
);
if (!NT_SUCCESS(NtOpenSection(
&legacyPrivateBlockHandle,
SECTION_MAP_READ,
&objectAttributes
)))
{
__leave;
}
if (!NT_SUCCESS(NtMapViewOfSection(
legacyPrivateBlockHandle,
NtCurrentProcess(),
&ipcControlBlockTable,
0,
viewSize,
&sectionOffset,
&viewSize,
ViewShare,
0,
PAGE_READONLY
)))
{
__leave;
}
if (Wow64)
{
LegacyPrivateIPCControlBlock_Wow64* legacyPrivateBlock;
AppDomainEnumerationIPCBlock_Wow64* appDomainEnumBlock;
legacyPrivateBlock = (LegacyPrivateIPCControlBlock_Wow64*)ipcControlBlockTable;
appDomainEnumBlock = &legacyPrivateBlock->AppDomainBlock;
// Check the IPCControlBlock is initialized.
if ((legacyPrivateBlock->FullIPCHeader.Header.Flags & IPC_FLAG_INITIALIZED) != IPC_FLAG_INITIALIZED)
{
__leave;
}
// Check the IPCControlBlock version is valid.
if (legacyPrivateBlock->FullIPCHeader.Header.Version > VER_LEGACYPRIVATE_IPC_BLOCK)
{
__leave;
}
appDomainsList = EnumAppDomainIpcBlockWow64(
ProcessHandle,
appDomainEnumBlock
);
}
else
{
LegacyPrivateIPCControlBlock* legacyPrivateBlock;
AppDomainEnumerationIPCBlock* appDomainEnumBlock;
legacyPrivateBlock = (LegacyPrivateIPCControlBlock*)ipcControlBlockTable;
appDomainEnumBlock = &legacyPrivateBlock->AppDomainBlock;
// Check the IPCControlBlock is initialized.
if ((legacyPrivateBlock->FullIPCHeader.Header.Flags & IPC_FLAG_INITIALIZED) != IPC_FLAG_INITIALIZED)
{
__leave;
}
// Check the IPCControlBlock version is valid.
if (legacyPrivateBlock->FullIPCHeader.Header.Version > VER_LEGACYPRIVATE_IPC_BLOCK)
{
__leave;
}
appDomainsList = EnumAppDomainIpcBlock(
ProcessHandle,
appDomainEnumBlock
);
}
}
__finally
{
if (ipcControlBlockTable)
{
NtUnmapViewOfSection(NtCurrentProcess(), ipcControlBlockTable);
}
if (legacyPrivateBlockHandle)
{
NtClose(legacyPrivateBlockHandle);
}
}
return appDomainsList;
}