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

576 lines
13 KiB
C

/*
* Process Hacker .NET Tools -
* CLR data access functions
*
* Copyright (C) 2011-2015 wj32
*
* 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 "clrsup.h"
static GUID IID_ICLRDataTarget_I = { 0x3e11ccee, 0xd08b, 0x43e5, { 0xaf, 0x01, 0x32, 0x71, 0x7a, 0x64, 0xda, 0x03 } };
static GUID IID_IXCLRDataProcess = { 0x5c552ab6, 0xfc09, 0x4cb3, { 0x8e, 0x36, 0x22, 0xfa, 0x03, 0xc7, 0x98, 0xb7 } };
static ICLRDataTargetVtbl DnCLRDataTarget_VTable =
{
DnCLRDataTarget_QueryInterface,
DnCLRDataTarget_AddRef,
DnCLRDataTarget_Release,
DnCLRDataTarget_GetMachineType,
DnCLRDataTarget_GetPointerSize,
DnCLRDataTarget_GetImageBase,
DnCLRDataTarget_ReadVirtual,
DnCLRDataTarget_WriteVirtual,
DnCLRDataTarget_GetTLSValue,
DnCLRDataTarget_SetTLSValue,
DnCLRDataTarget_GetCurrentThreadID,
DnCLRDataTarget_GetThreadContext,
DnCLRDataTarget_SetThreadContext,
DnCLRDataTarget_Request
};
PCLR_PROCESS_SUPPORT CreateClrProcessSupport(
_In_ HANDLE ProcessId
)
{
PCLR_PROCESS_SUPPORT support;
ICLRDataTarget *dataTarget;
IXCLRDataProcess *dataProcess;
dataTarget = DnCLRDataTarget_Create(ProcessId);
if (!dataTarget)
return NULL;
dataProcess = NULL;
CreateXCLRDataProcess(ProcessId, dataTarget, &dataProcess);
ICLRDataTarget_Release(dataTarget);
if (!dataProcess)
return NULL;
support = PhAllocate(sizeof(CLR_PROCESS_SUPPORT));
support->DataProcess = dataProcess;
return support;
}
VOID FreeClrProcessSupport(
_In_ PCLR_PROCESS_SUPPORT Support
)
{
IXCLRDataProcess_Release(Support->DataProcess);
PhFree(Support);
}
PPH_STRING GetRuntimeNameByAddressClrProcess(
_In_ PCLR_PROCESS_SUPPORT Support,
_In_ ULONG64 Address,
_Out_opt_ PULONG64 Displacement
)
{
PPH_STRING buffer;
ULONG bufferLength;
ULONG returnLength;
ULONG64 displacement;
bufferLength = 33;
buffer = PhCreateStringEx(NULL, (bufferLength - 1) * 2);
returnLength = 0;
if (!SUCCEEDED(IXCLRDataProcess_GetRuntimeNameByAddress(
Support->DataProcess,
Address,
0,
bufferLength,
&returnLength,
buffer->Buffer,
&displacement
)))
{
PhDereferenceObject(buffer);
return NULL;
}
// Try again if our buffer was too small.
if (returnLength > bufferLength)
{
PhDereferenceObject(buffer);
bufferLength = returnLength;
buffer = PhCreateStringEx(NULL, (bufferLength - 1) * 2);
if (!SUCCEEDED(IXCLRDataProcess_GetRuntimeNameByAddress(
Support->DataProcess,
Address,
0,
bufferLength,
&returnLength,
buffer->Buffer,
&displacement
)))
{
PhDereferenceObject(buffer);
return NULL;
}
}
if (Displacement)
*Displacement = displacement;
buffer->Length = (returnLength - 1) * 2;
return buffer;
}
PPH_STRING GetNameXClrDataAppDomain(
_In_ PVOID AppDomain
)
{
IXCLRDataAppDomain *appDomain;
PPH_STRING buffer;
ULONG bufferLength;
ULONG returnLength;
appDomain = AppDomain;
bufferLength = 33;
buffer = PhCreateStringEx(NULL, (bufferLength - 1) * 2);
returnLength = 0;
if (!SUCCEEDED(IXCLRDataAppDomain_GetName(appDomain, bufferLength, &returnLength, buffer->Buffer)))
{
PhDereferenceObject(buffer);
return NULL;
}
// Try again if our buffer was too small.
if (returnLength > bufferLength)
{
PhDereferenceObject(buffer);
bufferLength = returnLength;
buffer = PhCreateStringEx(NULL, (bufferLength - 1) * 2);
if (!SUCCEEDED(IXCLRDataAppDomain_GetName(appDomain, bufferLength, &returnLength, buffer->Buffer)))
{
PhDereferenceObject(buffer);
return NULL;
}
}
buffer->Length = (returnLength - 1) * 2;
return buffer;
}
PVOID LoadMscordacwks(
_In_ BOOLEAN IsClrV4
)
{
PVOID dllBase;
PH_STRINGREF systemRootString;
PH_STRINGREF mscordacwksPathString;
PPH_STRING mscordacwksFileName;
LoadLibrary(L"mscoree.dll");
PhGetSystemRoot(&systemRootString);
if (IsClrV4)
{
#ifdef _WIN64
PhInitializeStringRef(&mscordacwksPathString, L"\\Microsoft.NET\\Framework64\\v4.0.30319\\mscordacwks.dll");
#else
PhInitializeStringRef(&mscordacwksPathString, L"\\Microsoft.NET\\Framework\\v4.0.30319\\mscordacwks.dll");
#endif
}
else
{
#ifdef _WIN64
PhInitializeStringRef(&mscordacwksPathString, L"\\Microsoft.NET\\Framework64\\v2.0.50727\\mscordacwks.dll");
#else
PhInitializeStringRef(&mscordacwksPathString, L"\\Microsoft.NET\\Framework\\v2.0.50727\\mscordacwks.dll");
#endif
}
mscordacwksFileName = PhConcatStringRef2(&systemRootString, &mscordacwksPathString);
dllBase = LoadLibrary(mscordacwksFileName->Buffer);
PhDereferenceObject(mscordacwksFileName);
return dllBase;
}
HRESULT CreateXCLRDataProcess(
_In_ HANDLE ProcessId,
_In_ ICLRDataTarget *Target,
_Out_ struct IXCLRDataProcess **DataProcess
)
{
ULONG flags;
BOOLEAN clrV4;
HMODULE dllBase;
HRESULT (__stdcall *clrDataCreateInstance)(REFIID, ICLRDataTarget *, void **);
clrV4 = FALSE;
if (NT_SUCCESS(PhGetProcessIsDotNetEx(ProcessId, NULL, 0, NULL, &flags)))
{
if (flags & PH_CLR_VERSION_4_ABOVE)
clrV4 = TRUE;
}
// Load the correct version of mscordacwks.dll.
if (clrV4)
{
static PH_INITONCE initOnce = PH_INITONCE_INIT;
static HMODULE mscordacwksDllBase;
if (PhBeginInitOnce(&initOnce))
{
mscordacwksDllBase = LoadMscordacwks(TRUE);
PhEndInitOnce(&initOnce);
}
dllBase = mscordacwksDllBase;
}
else
{
static PH_INITONCE initOnce = PH_INITONCE_INIT;
static HMODULE mscordacwksDllBase;
if (PhBeginInitOnce(&initOnce))
{
mscordacwksDllBase = LoadMscordacwks(FALSE);
PhEndInitOnce(&initOnce);
}
dllBase = mscordacwksDllBase;
}
if (!dllBase)
return E_FAIL;
clrDataCreateInstance = PhGetProcedureAddress(dllBase, "CLRDataCreateInstance", 0);
if (!clrDataCreateInstance)
return E_FAIL;
return clrDataCreateInstance(&IID_IXCLRDataProcess, Target, DataProcess);
}
ICLRDataTarget *DnCLRDataTarget_Create(
_In_ HANDLE ProcessId
)
{
DnCLRDataTarget *dataTarget;
HANDLE processHandle;
BOOLEAN isWow64;
if (!NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess | PROCESS_VM_READ, ProcessId)))
return NULL;
#ifdef _WIN64
if (!NT_SUCCESS(PhGetProcessIsWow64(processHandle, &isWow64)))
{
NtClose(processHandle);
return NULL;
}
#else
isWow64 = FALSE;
#endif
dataTarget = PhAllocate(sizeof(DnCLRDataTarget));
dataTarget->VTable = &DnCLRDataTarget_VTable;
dataTarget->RefCount = 1;
dataTarget->ProcessId = ProcessId;
dataTarget->ProcessHandle = processHandle;
dataTarget->IsWow64 = isWow64;
return (ICLRDataTarget *)dataTarget;
}
HRESULT STDMETHODCALLTYPE DnCLRDataTarget_QueryInterface(
_In_ ICLRDataTarget *This,
_In_ REFIID Riid,
_Out_ PVOID *Object
)
{
if (
IsEqualIID(Riid, &IID_IUnknown) ||
IsEqualIID(Riid, &IID_ICLRDataTarget_I)
)
{
DnCLRDataTarget_AddRef(This);
*Object = This;
return S_OK;
}
*Object = NULL;
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE DnCLRDataTarget_AddRef(
_In_ ICLRDataTarget *This
)
{
DnCLRDataTarget *this = (DnCLRDataTarget *)This;
this->RefCount++;
return this->RefCount;
}
ULONG STDMETHODCALLTYPE DnCLRDataTarget_Release(
_In_ ICLRDataTarget *This
)
{
DnCLRDataTarget *this = (DnCLRDataTarget *)This;
this->RefCount--;
if (this->RefCount == 0)
{
NtClose(this->ProcessHandle);
PhFree(this);
return 0;
}
return this->RefCount;
}
HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetMachineType(
_In_ ICLRDataTarget *This,
_Out_ ULONG32 *machineType
)
{
DnCLRDataTarget *this = (DnCLRDataTarget *)This;
#ifdef _WIN64
if (!this->IsWow64)
*machineType = IMAGE_FILE_MACHINE_AMD64;
else
*machineType = IMAGE_FILE_MACHINE_I386;
#else
*machineType = IMAGE_FILE_MACHINE_I386;
#endif
return S_OK;
}
HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetPointerSize(
_In_ ICLRDataTarget *This,
_Out_ ULONG32 *pointerSize
)
{
DnCLRDataTarget *this = (DnCLRDataTarget *)This;
#ifdef _WIN64
if (!this->IsWow64)
#endif
*pointerSize = sizeof(PVOID);
#ifdef _WIN64
else
*pointerSize = sizeof(ULONG);
#endif
return S_OK;
}
BOOLEAN NTAPI PhpGetImageBaseCallback(
_In_ PLDR_DATA_TABLE_ENTRY Module,
_In_opt_ PVOID Context
)
{
PPHP_GET_IMAGE_BASE_CONTEXT context = Context;
if (RtlEqualUnicodeString(&Module->FullDllName, &context->ImagePath, TRUE) ||
RtlEqualUnicodeString(&Module->BaseDllName, &context->ImagePath, TRUE))
{
context->BaseAddress = Module->DllBase;
return FALSE;
}
return TRUE;
}
HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetImageBase(
_In_ ICLRDataTarget *This,
_In_ LPCWSTR imagePath,
_Out_ CLRDATA_ADDRESS *baseAddress
)
{
DnCLRDataTarget *this = (DnCLRDataTarget *)This;
PHP_GET_IMAGE_BASE_CONTEXT context;
RtlInitUnicodeString(&context.ImagePath, (PWSTR)imagePath);
context.BaseAddress = NULL;
PhEnumProcessModules(this->ProcessHandle, PhpGetImageBaseCallback, &context);
#ifdef _WIN64
if (this->IsWow64)
PhEnumProcessModules32(this->ProcessHandle, PhpGetImageBaseCallback, &context);
#endif
if (context.BaseAddress)
{
*baseAddress = (CLRDATA_ADDRESS)context.BaseAddress;
return S_OK;
}
else
{
return E_FAIL;
}
}
HRESULT STDMETHODCALLTYPE DnCLRDataTarget_ReadVirtual(
_In_ ICLRDataTarget *This,
_In_ CLRDATA_ADDRESS address,
_Out_ BYTE *buffer,
_In_ ULONG32 bytesRequested,
_Out_ ULONG32 *bytesRead
)
{
DnCLRDataTarget *this = (DnCLRDataTarget *)This;
NTSTATUS status;
SIZE_T numberOfBytesRead;
if (NT_SUCCESS(status = NtReadVirtualMemory(
this->ProcessHandle,
(PVOID)address,
buffer,
bytesRequested,
&numberOfBytesRead
)))
{
*bytesRead = (ULONG32)numberOfBytesRead;
return S_OK;
}
else
{
ULONG result;
result = RtlNtStatusToDosError(status);
return HRESULT_FROM_WIN32(result);
}
}
HRESULT STDMETHODCALLTYPE DnCLRDataTarget_WriteVirtual(
_In_ ICLRDataTarget *This,
_In_ CLRDATA_ADDRESS address,
_In_ BYTE *buffer,
_In_ ULONG32 bytesRequested,
_Out_ ULONG32 *bytesWritten
)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetTLSValue(
_In_ ICLRDataTarget *This,
_In_ ULONG32 threadID,
_In_ ULONG32 index,
_Out_ CLRDATA_ADDRESS *value
)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE DnCLRDataTarget_SetTLSValue(
_In_ ICLRDataTarget *This,
_In_ ULONG32 threadID,
_In_ ULONG32 index,
_In_ CLRDATA_ADDRESS value
)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetCurrentThreadID(
_In_ ICLRDataTarget *This,
_Out_ ULONG32 *threadID
)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetThreadContext(
_In_ ICLRDataTarget *This,
_In_ ULONG32 threadID,
_In_ ULONG32 contextFlags,
_In_ ULONG32 contextSize,
_Out_ BYTE *context
)
{
NTSTATUS status;
HANDLE threadHandle;
CONTEXT buffer;
if (contextSize < sizeof(CONTEXT))
return E_INVALIDARG;
memset(&buffer, 0, sizeof(CONTEXT));
buffer.ContextFlags = contextFlags;
if (NT_SUCCESS(status = PhOpenThread(&threadHandle, THREAD_GET_CONTEXT, UlongToHandle(threadID))))
{
status = NtGetContextThread(threadHandle, &buffer);
NtClose(threadHandle);
}
if (NT_SUCCESS(status))
{
memcpy(context, &buffer, sizeof(CONTEXT));
return S_OK;
}
else
{
return HRESULT_FROM_WIN32(RtlNtStatusToDosError(status));
}
}
HRESULT STDMETHODCALLTYPE DnCLRDataTarget_SetThreadContext(
_In_ ICLRDataTarget *This,
_In_ ULONG32 threadID,
_In_ ULONG32 contextSize,
_In_ BYTE *context
)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE DnCLRDataTarget_Request(
_In_ ICLRDataTarget *This,
_In_ ULONG32 reqCode,
_In_ ULONG32 inBufferSize,
_In_ BYTE *inBuffer,
_In_ ULONG32 outBufferSize,
_Out_ BYTE *outBuffer
)
{
return E_NOTIMPL;
}