Process Hacker
stackext.c
Go to the documentation of this file.
1 /*
2  * Process Hacker .NET Tools -
3  * thread stack extensions
4  *
5  * Copyright (C) 2011-2015 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include "dn.h"
24 #include "clrsup.h"
25 #include "svcext.h"
26 #include <symprv.h>
27 
28 typedef struct _THREAD_STACK_CONTEXT
29 {
30  PCLR_PROCESS_SUPPORT Support;
31  HANDLE ProcessId;
32  HANDLE ThreadId;
33  HANDLE ThreadHandle;
34 
35  PVOID PredictedEip;
36  PVOID PredictedEbp;
37  PVOID PredictedEsp;
38 
39 #ifdef _WIN64
40  BOOLEAN IsWow64;
41  BOOLEAN ConnectedToPhSvc;
42 #endif
44 
45 static PPH_HASHTABLE ContextHashtable;
46 static PH_QUEUED_LOCK ContextHashtableLock = PH_QUEUED_LOCK_INIT;
47 static PH_INITONCE ContextHashtableInitOnce = PH_INITONCE_INIT;
48 
50  _In_ PVOID UniqueKey
51  )
52 {
53  PTHREAD_STACK_CONTEXT context;
54  PVOID *item;
55 
56  PhAcquireQueuedLockExclusive(&ContextHashtableLock);
57 
58  item = PhFindItemSimpleHashtable(ContextHashtable, UniqueKey);
59 
60  if (item)
61  context = *item;
62  else
63  context = NULL;
64 
65  PhReleaseQueuedLockExclusive(&ContextHashtableLock);
66 
67  return context;
68 }
69 
72  )
73 {
74  if (PhBeginInitOnce(&ContextHashtableInitOnce))
75  {
76  ContextHashtable = PhCreateSimpleHashtable(4);
77  PhEndInitOnce(&ContextHashtableInitOnce);
78  }
79 
80  switch (Control->Type)
81  {
83  {
84  PTHREAD_STACK_CONTEXT context;
85 #if _WIN64
86  HANDLE processHandle;
87 #endif
88 
89  context = PhAllocate(sizeof(THREAD_STACK_CONTEXT));
90  memset(context, 0, sizeof(THREAD_STACK_CONTEXT));
91  context->ProcessId = Control->u.Initializing.ProcessId;
92  context->ThreadId = Control->u.Initializing.ThreadId;
93  context->ThreadHandle = Control->u.Initializing.ThreadHandle;
94 
95 #if _WIN64
96  if (NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess, Control->u.Initializing.ProcessId)))
97  {
98  PhGetProcessIsWow64(processHandle, &context->IsWow64);
99  NtClose(processHandle);
100  }
101 #endif
102 
103  PhAcquireQueuedLockExclusive(&ContextHashtableLock);
104  PhAddItemSimpleHashtable(ContextHashtable, Control->UniqueKey, context);
105  PhReleaseQueuedLockExclusive(&ContextHashtableLock);
106  }
107  break;
109  {
110  PTHREAD_STACK_CONTEXT context = FindThreadStackContext(Control->UniqueKey);
111 
112  if (!context)
113  return;
114 
115  PhFree(context);
116 
117  PhAcquireQueuedLockExclusive(&ContextHashtableLock);
118  PhRemoveItemSimpleHashtable(ContextHashtable, Control->UniqueKey);
119  PhReleaseQueuedLockExclusive(&ContextHashtableLock);
120  }
121  break;
123  {
124  PTHREAD_STACK_CONTEXT context = FindThreadStackContext(Control->UniqueKey);
125  PPH_STRING managedSymbol = NULL;
126  ULONG64 displacement;
127 
128  if (!context)
129  return;
130 
131  if (context->Support)
132  {
133 #ifndef _WIN64
134  PVOID predictedEip;
135  PVOID predictedEbp;
136  PVOID predictedEsp;
137 
138  predictedEip = context->PredictedEip;
139  predictedEbp = context->PredictedEbp;
140  predictedEsp = context->PredictedEsp;
141 
143  context->Support,
144  context->ThreadId,
145  Control->u.ResolveSymbol.StackFrame->PcAddress,
146  Control->u.ResolveSymbol.StackFrame->FrameAddress,
147  Control->u.ResolveSymbol.StackFrame->StackAddress,
148  &context->PredictedEip,
149  &context->PredictedEbp,
150  &context->PredictedEsp
151  );
152 
153  // Fix up dbghelp EBP with real EBP given by the CLR data routines.
154  if (Control->u.ResolveSymbol.StackFrame->PcAddress == predictedEip)
155  {
156  Control->u.ResolveSymbol.StackFrame->FrameAddress = predictedEbp;
157  Control->u.ResolveSymbol.StackFrame->StackAddress = predictedEsp;
158  }
159 #endif
160 
161  managedSymbol = GetRuntimeNameByAddressClrProcess(
162  context->Support,
163  (ULONG64)Control->u.ResolveSymbol.StackFrame->PcAddress,
164  &displacement
165  );
166  }
167 #ifdef _WIN64
168  else if (context->IsWow64 && context->ConnectedToPhSvc)
169  {
170  PVOID predictedEip;
171  PVOID predictedEbp;
172  PVOID predictedEsp;
173 
174  predictedEip = context->PredictedEip;
175  predictedEbp = context->PredictedEbp;
176  predictedEsp = context->PredictedEsp;
177 
179  context->ProcessId,
180  context->ThreadId,
181  Control->u.ResolveSymbol.StackFrame->PcAddress,
182  Control->u.ResolveSymbol.StackFrame->FrameAddress,
183  Control->u.ResolveSymbol.StackFrame->StackAddress,
184  &context->PredictedEip,
185  &context->PredictedEbp,
186  &context->PredictedEsp
187  );
188 
189  // Fix up dbghelp EBP with real EBP given by the CLR data routines.
190  if (Control->u.ResolveSymbol.StackFrame->PcAddress == predictedEip)
191  {
192  Control->u.ResolveSymbol.StackFrame->FrameAddress = predictedEbp;
193  Control->u.ResolveSymbol.StackFrame->StackAddress = predictedEsp;
194  }
195 
196  managedSymbol = CallGetRuntimeNameByAddress(
197  context->ProcessId,
198  (ULONG64)Control->u.ResolveSymbol.StackFrame->PcAddress,
199  &displacement
200  );
201  }
202 #endif
203 
204  if (managedSymbol)
205  {
206  if (displacement != 0)
207  PhMoveReference(&managedSymbol, PhFormatString(L"%s + 0x%I64x", managedSymbol->Buffer, displacement));
208 
209  if (Control->u.ResolveSymbol.Symbol)
210  PhMoveReference(&managedSymbol, PhFormatString(L"%s <-- %s", managedSymbol->Buffer, Control->u.ResolveSymbol.Symbol->Buffer));
211 
212  PhMoveReference(&Control->u.ResolveSymbol.Symbol, managedSymbol);
213  }
214  }
215  break;
217  {
218  PTHREAD_STACK_CONTEXT context = FindThreadStackContext(Control->UniqueKey);
219  BOOLEAN isDotNet;
220 
221  if (!context)
222  return;
223 
224  if (!NT_SUCCESS(PhGetProcessIsDotNet(context->ProcessId, &isDotNet)) || !isDotNet)
225  return;
226 
227  context->Support = CreateClrProcessSupport(context->ProcessId);
228 
229 #ifdef _WIN64
230  if (context->IsWow64)
231  context->ConnectedToPhSvc = PhUiConnectToPhSvcEx(NULL, Wow64PhSvcMode, FALSE);
232 #endif
233  }
234  break;
236  {
237  PTHREAD_STACK_CONTEXT context = FindThreadStackContext(Control->UniqueKey);
238 
239  if (!context)
240  return;
241 
242  if (context->Support)
243  {
244  FreeClrProcessSupport(context->Support);
245  context->Support = NULL;
246  }
247 
248 #ifdef _WIN64
249  if (context->ConnectedToPhSvc)
250  {
252  context->ConnectedToPhSvc = FALSE;
253  }
254 #endif
255  }
256  break;
257  }
258 }
259 
261  _In_ PCLR_PROCESS_SUPPORT Support,
262  _In_ HANDLE ThreadId,
263  _In_ PVOID PcAddress,
264  _In_ PVOID FrameAddress,
265  _In_ PVOID StackAddress,
266  _Out_ PVOID *PredictedEip,
267  _Out_ PVOID *PredictedEbp,
268  _Out_ PVOID *PredictedEsp
269  )
270 {
271 #ifdef _WIN64
272  *PredictedEip = NULL;
273  *PredictedEbp = NULL;
274  *PredictedEsp = NULL;
275 #else
276  IXCLRDataTask *task;
277 
278  *PredictedEip = NULL;
279  *PredictedEbp = NULL;
280  *PredictedEsp = NULL;
281 
283  Support->DataProcess,
284  HandleToUlong(ThreadId),
285  &task
286  )))
287  {
288  IXCLRDataStackWalk *stackWalk;
289 
290  if (SUCCEEDED(IXCLRDataTask_CreateStackWalk(task, 0xf, &stackWalk)))
291  {
292  HRESULT result;
293  BOOLEAN firstTime = TRUE;
294  CONTEXT context;
295  ULONG contextSize;
296 
297  memset(&context, 0, sizeof(CONTEXT));
298  context.ContextFlags = CONTEXT_CONTROL;
299  context.Eip = PtrToUlong(PcAddress);
300  context.Ebp = PtrToUlong(FrameAddress);
301  context.Esp = PtrToUlong(StackAddress);
302 
303  result = IXCLRDataStackWalk_SetContext2(stackWalk, CLRDATA_STACK_SET_CURRENT_CONTEXT, sizeof(CONTEXT), (BYTE *)&context);
304 
305  if (SUCCEEDED(result = IXCLRDataStackWalk_Next(stackWalk)) && result != S_FALSE)
306  {
307  if (SUCCEEDED(IXCLRDataStackWalk_GetContext(stackWalk, CONTEXT_CONTROL, sizeof(CONTEXT), &contextSize, (BYTE *)&context)))
308  {
309  *PredictedEip = UlongToPtr(context.Eip);
310  *PredictedEbp = UlongToPtr(context.Ebp);
311  *PredictedEsp = UlongToPtr(context.Esp);
312  }
313  }
314 
315  IXCLRDataStackWalk_Release(stackWalk);
316  }
317 
318  IXCLRDataTask_Release(task);
319  }
320 #endif
321 }