1044 lines
30 KiB
C
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, §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;
|
|
} |