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

322 lines
10 KiB
C

/*
* Process Hacker .NET Tools -
* thread stack extensions
*
* 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"
#include "svcext.h"
#include <symprv.h>
typedef struct _THREAD_STACK_CONTEXT
{
PCLR_PROCESS_SUPPORT Support;
HANDLE ProcessId;
HANDLE ThreadId;
HANDLE ThreadHandle;
PVOID PredictedEip;
PVOID PredictedEbp;
PVOID PredictedEsp;
#ifdef _WIN64
BOOLEAN IsWow64;
BOOLEAN ConnectedToPhSvc;
#endif
} THREAD_STACK_CONTEXT, *PTHREAD_STACK_CONTEXT;
static PPH_HASHTABLE ContextHashtable;
static PH_QUEUED_LOCK ContextHashtableLock = PH_QUEUED_LOCK_INIT;
static PH_INITONCE ContextHashtableInitOnce = PH_INITONCE_INIT;
PTHREAD_STACK_CONTEXT FindThreadStackContext(
_In_ PVOID UniqueKey
)
{
PTHREAD_STACK_CONTEXT context;
PVOID *item;
PhAcquireQueuedLockExclusive(&ContextHashtableLock);
item = PhFindItemSimpleHashtable(ContextHashtable, UniqueKey);
if (item)
context = *item;
else
context = NULL;
PhReleaseQueuedLockExclusive(&ContextHashtableLock);
return context;
}
VOID ProcessThreadStackControl(
_In_ PPH_PLUGIN_THREAD_STACK_CONTROL Control
)
{
if (PhBeginInitOnce(&ContextHashtableInitOnce))
{
ContextHashtable = PhCreateSimpleHashtable(4);
PhEndInitOnce(&ContextHashtableInitOnce);
}
switch (Control->Type)
{
case PluginThreadStackInitializing:
{
PTHREAD_STACK_CONTEXT context;
#if _WIN64
HANDLE processHandle;
#endif
context = PhAllocate(sizeof(THREAD_STACK_CONTEXT));
memset(context, 0, sizeof(THREAD_STACK_CONTEXT));
context->ProcessId = Control->u.Initializing.ProcessId;
context->ThreadId = Control->u.Initializing.ThreadId;
context->ThreadHandle = Control->u.Initializing.ThreadHandle;
#if _WIN64
if (NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess, Control->u.Initializing.ProcessId)))
{
PhGetProcessIsWow64(processHandle, &context->IsWow64);
NtClose(processHandle);
}
#endif
PhAcquireQueuedLockExclusive(&ContextHashtableLock);
PhAddItemSimpleHashtable(ContextHashtable, Control->UniqueKey, context);
PhReleaseQueuedLockExclusive(&ContextHashtableLock);
}
break;
case PluginThreadStackUninitializing:
{
PTHREAD_STACK_CONTEXT context = FindThreadStackContext(Control->UniqueKey);
if (!context)
return;
PhFree(context);
PhAcquireQueuedLockExclusive(&ContextHashtableLock);
PhRemoveItemSimpleHashtable(ContextHashtable, Control->UniqueKey);
PhReleaseQueuedLockExclusive(&ContextHashtableLock);
}
break;
case PluginThreadStackResolveSymbol:
{
PTHREAD_STACK_CONTEXT context = FindThreadStackContext(Control->UniqueKey);
PPH_STRING managedSymbol = NULL;
ULONG64 displacement;
if (!context)
return;
if (context->Support)
{
#ifndef _WIN64
PVOID predictedEip;
PVOID predictedEbp;
PVOID predictedEsp;
predictedEip = context->PredictedEip;
predictedEbp = context->PredictedEbp;
predictedEsp = context->PredictedEsp;
PredictAddressesFromClrData(
context->Support,
context->ThreadId,
Control->u.ResolveSymbol.StackFrame->PcAddress,
Control->u.ResolveSymbol.StackFrame->FrameAddress,
Control->u.ResolveSymbol.StackFrame->StackAddress,
&context->PredictedEip,
&context->PredictedEbp,
&context->PredictedEsp
);
// Fix up dbghelp EBP with real EBP given by the CLR data routines.
if (Control->u.ResolveSymbol.StackFrame->PcAddress == predictedEip)
{
Control->u.ResolveSymbol.StackFrame->FrameAddress = predictedEbp;
Control->u.ResolveSymbol.StackFrame->StackAddress = predictedEsp;
}
#endif
managedSymbol = GetRuntimeNameByAddressClrProcess(
context->Support,
(ULONG64)Control->u.ResolveSymbol.StackFrame->PcAddress,
&displacement
);
}
#ifdef _WIN64
else if (context->IsWow64 && context->ConnectedToPhSvc)
{
PVOID predictedEip;
PVOID predictedEbp;
PVOID predictedEsp;
predictedEip = context->PredictedEip;
predictedEbp = context->PredictedEbp;
predictedEsp = context->PredictedEsp;
CallPredictAddressesFromClrData(
context->ProcessId,
context->ThreadId,
Control->u.ResolveSymbol.StackFrame->PcAddress,
Control->u.ResolveSymbol.StackFrame->FrameAddress,
Control->u.ResolveSymbol.StackFrame->StackAddress,
&context->PredictedEip,
&context->PredictedEbp,
&context->PredictedEsp
);
// Fix up dbghelp EBP with real EBP given by the CLR data routines.
if (Control->u.ResolveSymbol.StackFrame->PcAddress == predictedEip)
{
Control->u.ResolveSymbol.StackFrame->FrameAddress = predictedEbp;
Control->u.ResolveSymbol.StackFrame->StackAddress = predictedEsp;
}
managedSymbol = CallGetRuntimeNameByAddress(
context->ProcessId,
(ULONG64)Control->u.ResolveSymbol.StackFrame->PcAddress,
&displacement
);
}
#endif
if (managedSymbol)
{
if (displacement != 0)
PhMoveReference(&managedSymbol, PhFormatString(L"%s + 0x%I64x", managedSymbol->Buffer, displacement));
if (Control->u.ResolveSymbol.Symbol)
PhMoveReference(&managedSymbol, PhFormatString(L"%s <-- %s", managedSymbol->Buffer, Control->u.ResolveSymbol.Symbol->Buffer));
PhMoveReference(&Control->u.ResolveSymbol.Symbol, managedSymbol);
}
}
break;
case PluginThreadStackBeginDefaultWalkStack:
{
PTHREAD_STACK_CONTEXT context = FindThreadStackContext(Control->UniqueKey);
BOOLEAN isDotNet;
if (!context)
return;
if (!NT_SUCCESS(PhGetProcessIsDotNet(context->ProcessId, &isDotNet)) || !isDotNet)
return;
context->Support = CreateClrProcessSupport(context->ProcessId);
#ifdef _WIN64
if (context->IsWow64)
context->ConnectedToPhSvc = PhUiConnectToPhSvcEx(NULL, Wow64PhSvcMode, FALSE);
#endif
}
break;
case PluginThreadStackEndDefaultWalkStack:
{
PTHREAD_STACK_CONTEXT context = FindThreadStackContext(Control->UniqueKey);
if (!context)
return;
if (context->Support)
{
FreeClrProcessSupport(context->Support);
context->Support = NULL;
}
#ifdef _WIN64
if (context->ConnectedToPhSvc)
{
PhUiDisconnectFromPhSvc();
context->ConnectedToPhSvc = FALSE;
}
#endif
}
break;
}
}
VOID PredictAddressesFromClrData(
_In_ PCLR_PROCESS_SUPPORT Support,
_In_ HANDLE ThreadId,
_In_ PVOID PcAddress,
_In_ PVOID FrameAddress,
_In_ PVOID StackAddress,
_Out_ PVOID *PredictedEip,
_Out_ PVOID *PredictedEbp,
_Out_ PVOID *PredictedEsp
)
{
#ifdef _WIN64
*PredictedEip = NULL;
*PredictedEbp = NULL;
*PredictedEsp = NULL;
#else
IXCLRDataTask *task;
*PredictedEip = NULL;
*PredictedEbp = NULL;
*PredictedEsp = NULL;
if (SUCCEEDED(IXCLRDataProcess_GetTaskByOSThreadID(
Support->DataProcess,
HandleToUlong(ThreadId),
&task
)))
{
IXCLRDataStackWalk *stackWalk;
if (SUCCEEDED(IXCLRDataTask_CreateStackWalk(task, 0xf, &stackWalk)))
{
HRESULT result;
BOOLEAN firstTime = TRUE;
CONTEXT context;
ULONG contextSize;
memset(&context, 0, sizeof(CONTEXT));
context.ContextFlags = CONTEXT_CONTROL;
context.Eip = PtrToUlong(PcAddress);
context.Ebp = PtrToUlong(FrameAddress);
context.Esp = PtrToUlong(StackAddress);
result = IXCLRDataStackWalk_SetContext2(stackWalk, CLRDATA_STACK_SET_CURRENT_CONTEXT, sizeof(CONTEXT), (BYTE *)&context);
if (SUCCEEDED(result = IXCLRDataStackWalk_Next(stackWalk)) && result != S_FALSE)
{
if (SUCCEEDED(IXCLRDataStackWalk_GetContext(stackWalk, CONTEXT_CONTROL, sizeof(CONTEXT), &contextSize, (BYTE *)&context)))
{
*PredictedEip = UlongToPtr(context.Eip);
*PredictedEbp = UlongToPtr(context.Ebp);
*PredictedEsp = UlongToPtr(context.Esp);
}
}
IXCLRDataStackWalk_Release(stackWalk);
}
IXCLRDataTask_Release(task);
}
#endif
}