Process Hacker
clrsup.c
Go to the documentation of this file.
1 /*
2  * Process Hacker .NET Tools -
3  * CLR data access functions
4  *
5  * Copyright (C) 2011-2013 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 
26 static GUID IID_ICLRDataTarget_I = { 0x3e11ccee, 0xd08b, 0x43e5, { 0xaf, 0x01, 0x32, 0x71, 0x7a, 0x64, 0xda, 0x03 } };
27 static GUID IID_IXCLRDataProcess = { 0x5c552ab6, 0xfc09, 0x4cb3, { 0x8e, 0x36, 0x22, 0xfa, 0x03, 0xc7, 0x98, 0xb7 } };
28 
29 static ICLRDataTargetVtbl DnCLRDataTarget_VTable =
30 {
45 };
46 
48  _In_ HANDLE ProcessId
49  )
50 {
51  PCLR_PROCESS_SUPPORT support;
52  ICLRDataTarget *dataTarget;
53  IXCLRDataProcess *dataProcess;
54 
55  dataTarget = DnCLRDataTarget_Create(ProcessId);
56 
57  if (!dataTarget)
58  return NULL;
59 
60  dataProcess = NULL;
61  CreateXCLRDataProcess(ProcessId, dataTarget, &dataProcess);
62  ICLRDataTarget_Release(dataTarget);
63 
64  if (!dataProcess)
65  return NULL;
66 
67  support = PhAllocate(sizeof(CLR_PROCESS_SUPPORT));
68  support->DataProcess = dataProcess;
69 
70  return support;
71 }
72 
74  _In_ PCLR_PROCESS_SUPPORT Support
75  )
76 {
77  IXCLRDataProcess_Release(Support->DataProcess);
78  PhFree(Support);
79 }
80 
82  _In_ PCLR_PROCESS_SUPPORT Support,
83  _In_ ULONG64 Address,
84  _Out_opt_ PULONG64 Displacement
85  )
86 {
87  PPH_STRING buffer;
88  ULONG bufferLength;
89  ULONG returnLength;
90  ULONG64 displacement;
91 
92  bufferLength = 33;
93  buffer = PhCreateStringEx(NULL, (bufferLength - 1) * 2);
94 
95  returnLength = 0;
96 
98  Support->DataProcess,
99  Address,
100  0,
101  bufferLength,
102  &returnLength,
103  buffer->Buffer,
104  &displacement
105  )))
106  {
107  PhDereferenceObject(buffer);
108  return NULL;
109  }
110 
111  // Try again if our buffer was too small.
112  if (returnLength > bufferLength)
113  {
114  PhDereferenceObject(buffer);
115  bufferLength = returnLength;
116  buffer = PhCreateStringEx(NULL, (bufferLength - 1) * 2);
117 
119  Support->DataProcess,
120  Address,
121  0,
122  bufferLength,
123  &returnLength,
124  buffer->Buffer,
125  &displacement
126  )))
127  {
128  PhDereferenceObject(buffer);
129  return NULL;
130  }
131  }
132 
133  if (Displacement)
134  *Displacement = displacement;
135 
136  buffer->Length = (returnLength - 1) * 2;
137 
138  return buffer;
139 }
140 
142  _In_ PVOID AppDomain
143  )
144 {
145  IXCLRDataAppDomain *appDomain;
146  PPH_STRING buffer;
147  ULONG bufferLength;
148  ULONG returnLength;
149 
150  appDomain = AppDomain;
151 
152  bufferLength = 33;
153  buffer = PhCreateStringEx(NULL, (bufferLength - 1) * 2);
154 
155  returnLength = 0;
156 
157  if (!SUCCEEDED(IXCLRDataAppDomain_GetName(appDomain, bufferLength, &returnLength, buffer->Buffer)))
158  {
159  PhDereferenceObject(buffer);
160  return NULL;
161  }
162 
163  // Try again if our buffer was too small.
164  if (returnLength > bufferLength)
165  {
166  PhDereferenceObject(buffer);
167  bufferLength = returnLength;
168  buffer = PhCreateStringEx(NULL, (bufferLength - 1) * 2);
169 
170  if (!SUCCEEDED(IXCLRDataAppDomain_GetName(appDomain, bufferLength, &returnLength, buffer->Buffer)))
171  {
172  PhDereferenceObject(buffer);
173  return NULL;
174  }
175  }
176 
177  buffer->Length = (returnLength - 1) * 2;
178 
179  return buffer;
180 }
181 
183  _In_ BOOLEAN IsClrV4
184  )
185 {
186  PVOID dllBase;
187  PH_STRINGREF systemRootString;
188  PH_STRINGREF mscordacwksPathString;
189  PPH_STRING mscordacwksFileName;
190 
191  LoadLibrary(L"mscoree.dll");
192 
193  PhGetSystemRoot(&systemRootString);
194 
195  if (IsClrV4)
196  {
197 #ifdef _WIN64
198  PhInitializeStringRef(&mscordacwksPathString, L"\\Microsoft.NET\\Framework64\\v4.0.30319\\mscordacwks.dll");
199 #else
200  PhInitializeStringRef(&mscordacwksPathString, L"\\Microsoft.NET\\Framework\\v4.0.30319\\mscordacwks.dll");
201 #endif
202  }
203  else
204  {
205 #ifdef _WIN64
206  PhInitializeStringRef(&mscordacwksPathString, L"\\Microsoft.NET\\Framework64\\v2.0.50727\\mscordacwks.dll");
207 #else
208  PhInitializeStringRef(&mscordacwksPathString, L"\\Microsoft.NET\\Framework\\v2.0.50727\\mscordacwks.dll");
209 #endif
210  }
211 
212  mscordacwksFileName = PhConcatStringRef2(&systemRootString, &mscordacwksPathString);
213  dllBase = LoadLibrary(mscordacwksFileName->Buffer);
214  PhDereferenceObject(mscordacwksFileName);
215 
216  return dllBase;
217 }
218 
220  _In_ HANDLE ProcessId,
221  _In_ ICLRDataTarget *Target,
222  _Out_ struct IXCLRDataProcess **DataProcess
223  )
224 {
225  ULONG flags;
226  BOOLEAN clrV4;
227  HMODULE dllBase;
228  HRESULT (__stdcall *clrDataCreateInstance)(REFIID, ICLRDataTarget *, void **);
229 
230  clrV4 = FALSE;
231 
232  if (NT_SUCCESS(PhGetProcessIsDotNetEx(ProcessId, NULL, 0, NULL, &flags)))
233  {
234  if (flags & PH_CLR_VERSION_4_ABOVE)
235  clrV4 = TRUE;
236  }
237 
238  // Load the correct version of mscordacwks.dll.
239 
240  if (clrV4)
241  {
242  static PH_INITONCE initOnce = PH_INITONCE_INIT;
243  static HMODULE mscordacwksDllBase;
244 
245  if (PhBeginInitOnce(&initOnce))
246  {
247  mscordacwksDllBase = LoadMscordacwks(TRUE);
248  PhEndInitOnce(&initOnce);
249  }
250 
251  dllBase = mscordacwksDllBase;
252  }
253  else
254  {
255  static PH_INITONCE initOnce = PH_INITONCE_INIT;
256  static HMODULE mscordacwksDllBase;
257 
258  if (PhBeginInitOnce(&initOnce))
259  {
260  mscordacwksDllBase = LoadMscordacwks(FALSE);
261  PhEndInitOnce(&initOnce);
262  }
263 
264  dllBase = mscordacwksDllBase;
265  }
266 
267  if (!dllBase)
268  return E_FAIL;
269 
270  clrDataCreateInstance = (PVOID)GetProcAddress(dllBase, "CLRDataCreateInstance");
271 
272  if (!clrDataCreateInstance)
273  return E_FAIL;
274 
275  return clrDataCreateInstance(&IID_IXCLRDataProcess, Target, DataProcess);
276 }
277 
278 ICLRDataTarget *DnCLRDataTarget_Create(
279  _In_ HANDLE ProcessId
280  )
281 {
282  DnCLRDataTarget *dataTarget;
283  HANDLE processHandle;
284  BOOLEAN isWow64;
285 
286  if (!NT_SUCCESS(PhOpenProcess(&processHandle, ProcessQueryAccess | PROCESS_VM_READ, ProcessId)))
287  return NULL;
288 
289 #ifdef _WIN64
290  if (!NT_SUCCESS(PhGetProcessIsWow64(processHandle, &isWow64)))
291  {
292  NtClose(processHandle);
293  return NULL;
294  }
295 #else
296  isWow64 = FALSE;
297 #endif
298 
299  dataTarget = PhAllocate(sizeof(DnCLRDataTarget));
300  dataTarget->VTable = &DnCLRDataTarget_VTable;
301  dataTarget->RefCount = 1;
302 
303  dataTarget->ProcessId = ProcessId;
304  dataTarget->ProcessHandle = processHandle;
305  dataTarget->IsWow64 = isWow64;
306 
307  return (ICLRDataTarget *)dataTarget;
308 }
309 
310 HRESULT STDMETHODCALLTYPE DnCLRDataTarget_QueryInterface(
311  _In_ ICLRDataTarget *This,
312  _In_ REFIID Riid,
313  _Out_ PVOID *Object
314  )
315 {
316  if (
317  IsEqualIID(Riid, &IID_IUnknown) ||
318  IsEqualIID(Riid, &IID_ICLRDataTarget_I)
319  )
320  {
322  *Object = This;
323  return S_OK;
324  }
325 
326  *Object = NULL;
327  return E_NOINTERFACE;
328 }
329 
330 ULONG STDMETHODCALLTYPE DnCLRDataTarget_AddRef(
331  _In_ ICLRDataTarget *This
332  )
333 {
334  DnCLRDataTarget *this = (DnCLRDataTarget *)This;
335 
336  this->RefCount++;
337 
338  return this->RefCount;
339 }
340 
341 ULONG STDMETHODCALLTYPE DnCLRDataTarget_Release(
342  _In_ ICLRDataTarget *This
343  )
344 {
345  DnCLRDataTarget *this = (DnCLRDataTarget *)This;
346 
347  this->RefCount--;
348 
349  if (this->RefCount == 0)
350  {
351  NtClose(this->ProcessHandle);
352 
353  PhFree(this);
354 
355  return 0;
356  }
357 
358  return this->RefCount;
359 }
360 
361 HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetMachineType(
362  _In_ ICLRDataTarget *This,
363  _Out_ ULONG32 *machineType
364  )
365 {
366  DnCLRDataTarget *this = (DnCLRDataTarget *)This;
367 
368 #ifdef _WIN64
369  if (!this->IsWow64)
370  *machineType = IMAGE_FILE_MACHINE_AMD64;
371  else
372  *machineType = IMAGE_FILE_MACHINE_I386;
373 #else
374  *machineType = IMAGE_FILE_MACHINE_I386;
375 #endif
376 
377  return S_OK;
378 }
379 
380 HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetPointerSize(
381  _In_ ICLRDataTarget *This,
382  _Out_ ULONG32 *pointerSize
383  )
384 {
385  DnCLRDataTarget *this = (DnCLRDataTarget *)This;
386 
387 #ifdef _WIN64
388  if (!this->IsWow64)
389 #endif
390  *pointerSize = sizeof(PVOID);
391 #ifdef _WIN64
392  else
393  *pointerSize = sizeof(ULONG);
394 #endif
395 
396  return S_OK;
397 }
398 
400  _In_ PLDR_DATA_TABLE_ENTRY Module,
401  _In_opt_ PVOID Context
402  )
403 {
404  PPHP_GET_IMAGE_BASE_CONTEXT context = Context;
405 
406  if (RtlEqualUnicodeString(&Module->FullDllName, &context->ImagePath, TRUE) ||
407  RtlEqualUnicodeString(&Module->BaseDllName, &context->ImagePath, TRUE))
408  {
409  context->BaseAddress = Module->DllBase;
410  return FALSE;
411  }
412 
413  return TRUE;
414 }
415 
416 HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetImageBase(
417  _In_ ICLRDataTarget *This,
418  _In_ LPCWSTR imagePath,
419  _Out_ CLRDATA_ADDRESS *baseAddress
420  )
421 {
422  DnCLRDataTarget *this = (DnCLRDataTarget *)This;
424 
425  RtlInitUnicodeString(&context.ImagePath, (PWSTR)imagePath);
426  context.BaseAddress = NULL;
427  PhEnumProcessModules(this->ProcessHandle, PhpGetImageBaseCallback, &context);
428 
429 #ifdef _WIN64
430  if (this->IsWow64)
431  PhEnumProcessModules32(this->ProcessHandle, PhpGetImageBaseCallback, &context);
432 #endif
433 
434  if (context.BaseAddress)
435  {
436  *baseAddress = (CLRDATA_ADDRESS)context.BaseAddress;
437 
438  return S_OK;
439  }
440  else
441  {
442  return E_FAIL;
443  }
444 }
445 
446 HRESULT STDMETHODCALLTYPE DnCLRDataTarget_ReadVirtual(
447  _In_ ICLRDataTarget *This,
448  _In_ CLRDATA_ADDRESS address,
449  _Out_ BYTE *buffer,
450  _In_ ULONG32 bytesRequested,
451  _Out_ ULONG32 *bytesRead
452  )
453 {
454  DnCLRDataTarget *this = (DnCLRDataTarget *)This;
455  NTSTATUS status;
456  SIZE_T numberOfBytesRead;
457 
458  if (NT_SUCCESS(status = PhReadVirtualMemory(
459  this->ProcessHandle,
460  (PVOID)address,
461  buffer,
462  bytesRequested,
463  &numberOfBytesRead
464  )))
465  {
466  *bytesRead = (ULONG32)numberOfBytesRead;
467 
468  return S_OK;
469  }
470  else
471  {
472  ULONG result;
473 
474  result = RtlNtStatusToDosError(status);
475 
476  return HRESULT_FROM_WIN32(result);
477  }
478 }
479 
480 HRESULT STDMETHODCALLTYPE DnCLRDataTarget_WriteVirtual(
481  _In_ ICLRDataTarget *This,
482  _In_ CLRDATA_ADDRESS address,
483  _In_ BYTE *buffer,
484  _In_ ULONG32 bytesRequested,
485  _Out_ ULONG32 *bytesWritten
486  )
487 {
488  return E_NOTIMPL;
489 }
490 
491 HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetTLSValue(
492  _In_ ICLRDataTarget *This,
493  _In_ ULONG32 threadID,
494  _In_ ULONG32 index,
495  _Out_ CLRDATA_ADDRESS *value
496  )
497 {
498  return E_NOTIMPL;
499 }
500 
501 HRESULT STDMETHODCALLTYPE DnCLRDataTarget_SetTLSValue(
502  _In_ ICLRDataTarget *This,
503  _In_ ULONG32 threadID,
504  _In_ ULONG32 index,
505  _In_ CLRDATA_ADDRESS value
506  )
507 {
508  return E_NOTIMPL;
509 }
510 
511 HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetCurrentThreadID(
512  _In_ ICLRDataTarget *This,
513  _Out_ ULONG32 *threadID
514  )
515 {
516  return E_NOTIMPL;
517 }
518 
519 HRESULT STDMETHODCALLTYPE DnCLRDataTarget_GetThreadContext(
520  _In_ ICLRDataTarget *This,
521  _In_ ULONG32 threadID,
522  _In_ ULONG32 contextFlags,
523  _In_ ULONG32 contextSize,
524  _Out_ BYTE *context
525  )
526 {
527  NTSTATUS status;
528  HANDLE threadHandle;
529  CONTEXT buffer;
530 
531  if (contextSize < sizeof(CONTEXT))
532  return E_INVALIDARG;
533 
534  memset(&buffer, 0, sizeof(CONTEXT));
535  buffer.ContextFlags = contextFlags;
536 
537  if (NT_SUCCESS(status = PhOpenThread(&threadHandle, THREAD_GET_CONTEXT, ULongToHandle(threadID))))
538  {
539  status = PhGetThreadContext(threadHandle, &buffer);
540  NtClose(threadHandle);
541  }
542 
543  if (NT_SUCCESS(status))
544  {
545  memcpy(context, &buffer, sizeof(CONTEXT));
546 
547  return S_OK;
548  }
549  else
550  {
551  return HRESULT_FROM_WIN32(RtlNtStatusToDosError(status));
552  }
553 }
554 
555 HRESULT STDMETHODCALLTYPE DnCLRDataTarget_SetThreadContext(
556  _In_ ICLRDataTarget *This,
557  _In_ ULONG32 threadID,
558  _In_ ULONG32 contextSize,
559  _In_ BYTE *context
560  )
561 {
562  return E_NOTIMPL;
563 }
564 
565 HRESULT STDMETHODCALLTYPE DnCLRDataTarget_Request(
566  _In_ ICLRDataTarget *This,
567  _In_ ULONG32 reqCode,
568  _In_ ULONG32 inBufferSize,
569  _In_ BYTE *inBuffer,
570  _In_ ULONG32 outBufferSize,
571  _Out_ BYTE *outBuffer
572  )
573 {
574  return E_NOTIMPL;
575 }