/* * 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 . */ #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, §ionNameUs)) __leave; InitializeObjectAttributes( &objectAttributes, §ionNameUs, 0, NULL, NULL ); if (!NT_SUCCESS(NtOpenSection( &blockTableHandle, SECTION_MAP_READ, &objectAttributes ))) { __leave; } if (!NT_SUCCESS(NtMapViewOfSection( blockTableHandle, NtCurrentProcess(), &blockTableAddress, 0, viewSize, §ionOffset, &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, §ionNameUs)) __leave; InitializeObjectAttributes( &objectAttributes, §ionNameUs, 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(§ionNameUs, CorSxSVistaPublicIPCBlock); InitializeObjectAttributes( §ionObjectAttributes, §ionNameUs, OBJ_CASE_INSENSITIVE, privateNamespaceHandle, NULL ); if (!NT_SUCCESS(NtOpenSection( &blockTableHandle, SECTION_MAP_READ, §ionObjectAttributes ))) { __leave; } } if (!NT_SUCCESS(NtMapViewOfSection( blockTableHandle, NtCurrentProcess(), &blockTableAddress, 0, viewSize, §ionOffset, &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, §ionNameUs)) __leave; InitializeObjectAttributes( &objectAttributes, §ionNameUs, 0, NULL, NULL ); if (!NT_SUCCESS(NtOpenSection( &legacyPrivateBlockHandle, SECTION_MAP_READ, &objectAttributes ))) { __leave; } if (!NT_SUCCESS(NtMapViewOfSection( legacyPrivateBlockHandle, NtCurrentProcess(), &ipcControlBlockTable, 0, viewSize, §ionOffset, &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, §ionNameUs)) __leave; InitializeObjectAttributes( &objectAttributes, §ionNameUs, 0, NULL, NULL ); if (!NT_SUCCESS(NtOpenSection( &legacyPrivateBlockHandle, SECTION_MAP_READ, &objectAttributes ))) { __leave; } if (!NT_SUCCESS(NtMapViewOfSection( legacyPrivateBlockHandle, NtCurrentProcess(), &ipcControlBlockTable, 0, viewSize, §ionOffset, &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; }