/* * 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 . */ #include "dn.h" #include "clrsup.h" #include "svcext.h" #include 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 }