Process Hacker
symprv.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * symbol provider
4  *
5  * Copyright (C) 2010-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 <ph.h>
24 #include <kphuser.h>
25 #include <dbghelp.h>
26 #include <symprv.h>
27 #include <symprvp.h>
28 
29 typedef struct _PH_SYMBOL_MODULE
30 {
31  LIST_ENTRY ListEntry;
32  PH_AVL_LINKS Links;
33  ULONG64 BaseAddress;
34  ULONG Size;
35  PPH_STRING FileName;
36  ULONG BaseNameIndex;
38 
40  _In_ PVOID Object,
41  _In_ ULONG Flags
42  );
43 
45  _In_opt_ PPH_SYMBOL_PROVIDER SymbolProvider
46  );
47 
49  _In_ PPH_SYMBOL_MODULE SymbolModule
50  );
51 
53  _In_ PPH_AVL_LINKS Links1,
55  );
56 
58 
59 static PH_INITONCE PhSymInitOnce = PH_INITONCE_INIT;
60 DECLSPEC_SELECTANY PH_CALLBACK_DECLARE(PhSymInitCallback);
61 
62 static HANDLE PhNextFakeHandle = (HANDLE)0;
63 static PH_FAST_LOCK PhSymMutex = PH_FAST_LOCK_INIT;
64 
65 #define PH_LOCK_SYMBOLS() PhAcquireFastLockExclusive(&PhSymMutex)
66 #define PH_UNLOCK_SYMBOLS() PhReleaseFastLockExclusive(&PhSymMutex)
67 
94 
96  VOID
97  )
98 {
99  PhSymbolProviderType = PhCreateObjectType(L"SymbolProvider", 0, PhpSymbolProviderDeleteProcedure);
100 
101  return TRUE;
102 }
103 
105  _In_opt_ PVOID DbgHelpBase
106  )
107 {
108  HMODULE dbghelpHandle;
109  HMODULE symsrvHandle;
110 
111  // The user should have loaded dbghelp.dll and symsrv.dll already. If not, it's not our problem.
112 
113  // The Unicode versions aren't available in dbghelp.dll 5.1, so we fallback on the ANSI versions.
114 
115  if (DbgHelpBase)
116  dbghelpHandle = DbgHelpBase;
117  else
118  dbghelpHandle = GetModuleHandle(L"dbghelp.dll");
119 
120  symsrvHandle = GetModuleHandle(L"symsrv.dll");
121 
122  SymInitialize_I = (PVOID)GetProcAddress(dbghelpHandle, "SymInitialize");
123  SymCleanup_I = (PVOID)GetProcAddress(dbghelpHandle, "SymCleanup");
124  if (!(SymEnumSymbolsW_I = (PVOID)GetProcAddress(dbghelpHandle, "SymEnumSymbolsW")))
125  SymEnumSymbols_I = (PVOID)GetProcAddress(dbghelpHandle, "SymEnumSymbols");
126  if (!(SymFromAddrW_I = (PVOID)GetProcAddress(dbghelpHandle, "SymFromAddrW")))
127  SymFromAddr_I = (PVOID)GetProcAddress(dbghelpHandle, "SymFromAddr");
128  if (!(SymFromNameW_I = (PVOID)GetProcAddress(dbghelpHandle, "SymFromNameW")))
129  SymFromName_I = (PVOID)GetProcAddress(dbghelpHandle, "SymFromName");
130  if (!(SymGetLineFromAddrW64_I = (PVOID)GetProcAddress(dbghelpHandle, "SymGetLineFromAddrW64")))
131  SymGetLineFromAddr64_I = (PVOID)GetProcAddress(dbghelpHandle, "SymGetLineFromAddr64");
132  if (!(SymLoadModuleExW_I = (PVOID)GetProcAddress(dbghelpHandle, "SymLoadModuleExW")))
133  SymLoadModule64_I = (PVOID)GetProcAddress(dbghelpHandle, "SymLoadModule64");
134  SymGetOptions_I = (PVOID)GetProcAddress(dbghelpHandle, "SymGetOptions");
135  SymSetOptions_I = (PVOID)GetProcAddress(dbghelpHandle, "SymSetOptions");
136  if (!(SymGetSearchPathW_I = (PVOID)GetProcAddress(dbghelpHandle, "SymGetSearchPathW")))
137  SymGetSearchPath_I = (PVOID)GetProcAddress(dbghelpHandle, "SymGetSearchPath");
138  if (!(SymSetSearchPathW_I = (PVOID)GetProcAddress(dbghelpHandle, "SymSetSearchPathW")))
139  SymSetSearchPath_I = (PVOID)GetProcAddress(dbghelpHandle, "SymSetSearchPath");
140  SymUnloadModule64_I = (PVOID)GetProcAddress(dbghelpHandle, "SymUnloadModule64");
141  SymFunctionTableAccess64_I = (PVOID)GetProcAddress(dbghelpHandle, "SymFunctionTableAccess64");
142  SymGetModuleBase64_I = (PVOID)GetProcAddress(dbghelpHandle, "SymGetModuleBase64");
143  SymRegisterCallbackW64_I = (PVOID)GetProcAddress(dbghelpHandle, "SymRegisterCallbackW64");
144  StackWalk64_I = (PVOID)GetProcAddress(dbghelpHandle, "StackWalk64");
145  MiniDumpWriteDump_I = (PVOID)GetProcAddress(dbghelpHandle, "MiniDumpWriteDump");
146  SymbolServerGetOptions = (PVOID)GetProcAddress(symsrvHandle, "SymbolServerGetOptions");
147  SymbolServerSetOptions = (PVOID)GetProcAddress(symsrvHandle, "SymbolServerSetOptions");
148 
150  SymSetOptions_I(SymGetOptions_I() | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED);
151 }
152 
154  _In_opt_ HANDLE ProcessId
155  )
156 {
157  PPH_SYMBOL_PROVIDER symbolProvider;
158 
159  symbolProvider = PhCreateObject(sizeof(PH_SYMBOL_PROVIDER), PhSymbolProviderType);
160  memset(symbolProvider, 0, sizeof(PH_SYMBOL_PROVIDER));
161  InitializeListHead(&symbolProvider->ModulesListHead);
162  PhInitializeQueuedLock(&symbolProvider->ModulesListLock);
164  PhInitializeCallback(&symbolProvider->EventCallback);
165  PhInitializeInitOnce(&symbolProvider->InitOnce);
166 
167  if (ProcessId)
168  {
169  static ACCESS_MASK accesses[] =
170  {
171  STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xfff, // pre-Vista full access
172  PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_DUP_HANDLE,
174  MAXIMUM_ALLOWED
175  };
176 
177  ULONG i;
178 
179  // Try to open the process with many different accesses.
180  // This handle will be re-used when walking stacks, and doing various other things.
181  for (i = 0; i < sizeof(accesses) / sizeof(ACCESS_MASK); i++)
182  {
183  if (NT_SUCCESS(PhOpenProcess(&symbolProvider->ProcessHandle, accesses[i], ProcessId)))
184  {
185  symbolProvider->IsRealHandle = TRUE;
186  break;
187  }
188  }
189  }
190 
191  if (!symbolProvider->IsRealHandle)
192  {
193  HANDLE fakeHandle;
194 
195  // Just generate a fake handle.
196  fakeHandle = (HANDLE)_InterlockedExchangeAddPointer((PLONG_PTR)&PhNextFakeHandle, 4);
197  // Add one to make sure it isn't divisible by 4 (so it can't be mistaken for a real handle).
198  symbolProvider->ProcessHandle = (HANDLE)((ULONG_PTR)fakeHandle + 1);
199  }
200 
201  return symbolProvider;
202 }
203 
205  _In_ PVOID Object,
206  _In_ ULONG Flags
207  )
208 {
209  PPH_SYMBOL_PROVIDER symbolProvider = (PPH_SYMBOL_PROVIDER)Object;
210  PLIST_ENTRY listEntry;
211 
212  PhDeleteCallback(&symbolProvider->EventCallback);
213 
214  if (SymCleanup_I)
215  {
216  PH_LOCK_SYMBOLS();
217 
218  if (symbolProvider->IsRegistered)
219  SymCleanup_I(symbolProvider->ProcessHandle);
220 
222  }
223 
224  listEntry = symbolProvider->ModulesListHead.Flink;
225 
226  while (listEntry != &symbolProvider->ModulesListHead)
227  {
228  PPH_SYMBOL_MODULE module;
229 
230  module = CONTAINING_RECORD(listEntry, PH_SYMBOL_MODULE, ListEntry);
231  listEntry = listEntry->Flink;
232 
233  PhpFreeSymbolModule(module);
234  }
235 
236  if (symbolProvider->IsRealHandle) NtClose(symbolProvider->ProcessHandle);
237 }
238 
240  _In_ PVOID Parameter
241  )
242 {
243  PPH_SYMBOL_EVENT_DATA data = Parameter;
244 
245  dprintf("symbol event %d: %S\n", data->Type, data->FileName->Buffer);
247  PhClearReference(&data->FileName);
248  PhDereferenceObject(data);
249 
250  return STATUS_SUCCESS;
251 }
252 
254  _In_ HANDLE hProcess,
255  _In_ ULONG ActionCode,
256  _In_opt_ ULONG64 CallbackData,
257  _In_opt_ ULONG64 UserContext
258  )
259 {
260  PPH_SYMBOL_PROVIDER symbolProvider = (PPH_SYMBOL_PROVIDER)UserContext;
262  PIMAGEHLP_DEFERRED_SYMBOL_LOADW64 callbackData;
263 
264  if (!IsListEmpty(&symbolProvider->EventCallback.ListHead))
265  {
266  switch (ActionCode)
267  {
273  data = PhCreateAlloc(sizeof(PH_SYMBOL_EVENT_DATA));
274  memset(data, 0, sizeof(PH_SYMBOL_EVENT_DATA));
275  data->SymbolProvider = symbolProvider;
276  data->Type = ActionCode;
277 
278  if (ActionCode != SymbolSymbolsUnloaded)
279  {
280  callbackData = (PIMAGEHLP_DEFERRED_SYMBOL_LOADW64)CallbackData;
281  data->BaseAddress = callbackData->BaseOfImage;
282  data->CheckSum = callbackData->CheckSum;
283  data->TimeStamp = callbackData->TimeDateStamp;
284  data->FileName = PhCreateString(callbackData->FileName);
285  }
286 
288 
289  break;
290  }
291  }
292 
293  return FALSE;
294 }
295 
297  _In_opt_ PPH_SYMBOL_PROVIDER SymbolProvider
298  )
299 {
300  if (PhBeginInitOnce(&PhSymInitOnce))
301  {
303  PhEndInitOnce(&PhSymInitOnce);
304  }
305 
306  if (!SymbolProvider)
307  return;
308 
309  if (PhBeginInitOnce(&SymbolProvider->InitOnce))
310  {
311  if (SymInitialize_I)
312  {
313  PH_LOCK_SYMBOLS();
314 
315  SymInitialize_I(SymbolProvider->ProcessHandle, NULL, FALSE);
316 
318  SymRegisterCallbackW64_I(SymbolProvider->ProcessHandle, PhpSymbolCallbackFunction, (ULONG64)SymbolProvider);
319 
321 
322  SymbolProvider->IsRegistered = TRUE;
323  }
324 
325  PhEndInitOnce(&SymbolProvider->InitOnce);
326  }
327 }
328 
330  _In_ PPH_SYMBOL_MODULE SymbolModule
331  )
332 {
333  if (SymbolModule->FileName) PhDereferenceObject(SymbolModule->FileName);
334 
335  PhFree(SymbolModule);
336 }
337 
339  _In_ PPH_AVL_LINKS Links1,
340  _In_ PPH_AVL_LINKS Links2
341  )
342 {
343  PPH_SYMBOL_MODULE symbolModule1 = CONTAINING_RECORD(Links1, PH_SYMBOL_MODULE, Links);
344  PPH_SYMBOL_MODULE symbolModule2 = CONTAINING_RECORD(Links2, PH_SYMBOL_MODULE, Links);
345 
346  return uint64cmp(symbolModule1->BaseAddress, symbolModule2->BaseAddress);
347 }
348 
350  _In_ PPH_SYMBOL_PROVIDER SymbolProvider,
351  _In_ ULONG64 Address,
352  _Out_ PPH_STRING *FileName,
353  _Out_opt_ PULONG Displacement,
354  _Out_opt_ PPH_SYMBOL_LINE_INFORMATION Information
355  )
356 {
357  IMAGEHLP_LINEW64 line;
358  BOOL result;
359  ULONG displacement;
360  PPH_STRING fileName;
361 
362  PhpRegisterSymbolProvider(SymbolProvider);
363 
365  return FALSE;
366 
367  line.SizeOfStruct = sizeof(IMAGEHLP_LINEW64);
368 
369  PH_LOCK_SYMBOLS();
370 
372  {
373  result = SymGetLineFromAddrW64_I(
374  SymbolProvider->ProcessHandle,
375  Address,
376  &displacement,
377  &line
378  );
379 
380  if (result)
381  fileName = PhCreateString(line.FileName);
382  }
383  else
384  {
385  IMAGEHLP_LINE64 lineA;
386 
387  lineA.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
388 
389  result = SymGetLineFromAddr64_I(
390  SymbolProvider->ProcessHandle,
391  Address,
392  &displacement,
393  &lineA
394  );
395 
396  if (result)
397  {
398  fileName = PhConvertMultiByteToUtf16(lineA.FileName);
399  line.LineNumber = lineA.LineNumber;
400  line.Address = lineA.Address;
401  }
402  }
403 
405 
406  if (!result)
407  return FALSE;
408 
409  *FileName = fileName;
410 
411  if (Displacement)
412  *Displacement = displacement;
413 
414  if (Information)
415  {
416  Information->LineNumber = line.LineNumber;
417  Information->Address = line.Address;
418  }
419 
420  return TRUE;
421 }
422 
424  _In_ PPH_SYMBOL_PROVIDER SymbolProvider,
425  _In_ ULONG64 Address,
426  _Out_opt_ PPH_STRING *FileName
427  )
428 {
429  PH_SYMBOL_MODULE lookupModule;
430  PPH_AVL_LINKS links;
431  PPH_SYMBOL_MODULE module;
432  LONG result;
433  PPH_STRING foundFileName;
434  ULONG64 foundBaseAddress;
435 
436  module = NULL;
437  foundFileName = NULL;
438  foundBaseAddress = 0;
439 
440  // Do an approximate search on the modules set to locate the module with the largest
441  // base address that is still smaller than the given address.
442  lookupModule.BaseAddress = Address;
443 
444  PhAcquireQueuedLockShared(&SymbolProvider->ModulesListLock);
445 
446  links = PhFindElementAvlTree2(&SymbolProvider->ModulesSet, &lookupModule.Links, &result);
447 
448  if (links)
449  {
450  if (result == 0)
451  {
452  // Exact match.
453  }
454  else if (result < 0)
455  {
456  // The base of the closest module is larger than our address. Assume the
457  // preceding element (which is going to be smaller than our address) is the
458  // one we're looking for.
459 
460  links = PhPredecessorElementAvlTree(links);
461  }
462  else
463  {
464  // The base of the closest module is smaller than our address. Assume this
465  // is the element we're looking for.
466  }
467 
468  if (links)
469  {
470  module = CONTAINING_RECORD(links, PH_SYMBOL_MODULE, Links);
471  }
472  }
473  else
474  {
475  // No modules loaded.
476  }
477 
478  if (module && Address < module->BaseAddress + module->Size)
479  {
480  PhSetReference(&foundFileName, module->FileName);
481  foundBaseAddress = module->BaseAddress;
482  }
483 
484  PhReleaseQueuedLockShared(&SymbolProvider->ModulesListLock);
485 
486  if (foundFileName)
487  {
488  if (FileName)
489  {
490  *FileName = foundFileName;
491  }
492  else
493  {
494  PhDereferenceObject(foundFileName);
495  }
496  }
497 
498  return foundBaseAddress;
499 }
500 
502  _Out_ PSYMBOL_INFOW SymbolInfoW,
503  _In_ PSYMBOL_INFO SymbolInfoA
504  )
505 {
506  SymbolInfoW->TypeIndex = SymbolInfoA->TypeIndex;
507  SymbolInfoW->Index = SymbolInfoA->Index;
508  SymbolInfoW->Size = SymbolInfoA->Size;
509  SymbolInfoW->ModBase = SymbolInfoA->ModBase;
510  SymbolInfoW->Flags = SymbolInfoA->Flags;
511  SymbolInfoW->Value = SymbolInfoA->Value;
512  SymbolInfoW->Address = SymbolInfoA->Address;
513  SymbolInfoW->Register = SymbolInfoA->Register;
514  SymbolInfoW->Scope = SymbolInfoA->Scope;
515  SymbolInfoW->Tag = SymbolInfoA->Tag;
516  SymbolInfoW->NameLen = 0;
517 
518  if (SymbolInfoA->NameLen != 0 && SymbolInfoW->MaxNameLen != 0)
519  {
520  ULONG copyCount;
521 
522  copyCount = min(SymbolInfoA->NameLen, SymbolInfoW->MaxNameLen - 1);
523 
525  SymbolInfoA->Name,
526  copyCount,
527  SymbolInfoW->Name,
528  SymbolInfoW->MaxNameLen,
529  NULL
530  ))
531  {
532  SymbolInfoW->NameLen = copyCount;
533  }
534  }
535 }
536 
538  _In_ PPH_SYMBOL_PROVIDER SymbolProvider,
539  _In_ ULONG64 Address,
540  _Out_opt_ PPH_SYMBOL_RESOLVE_LEVEL ResolveLevel,
541  _Out_opt_ PPH_STRING *FileName,
542  _Out_opt_ PPH_STRING *SymbolName,
543  _Out_opt_ PULONG64 Displacement
544  )
545 {
546  PSYMBOL_INFOW symbolInfo;
547  ULONG nameLength;
548  PPH_STRING symbol = NULL;
549  PH_SYMBOL_RESOLVE_LEVEL resolveLevel;
550  ULONG64 displacement;
551  PPH_STRING modFileName = NULL;
552  PPH_STRING modBaseName = NULL;
553  ULONG64 modBase;
554  PPH_STRING symbolName = NULL;
555 
556  if (Address == 0)
557  {
558  if (ResolveLevel) *ResolveLevel = PhsrlInvalid;
559  if (FileName) *FileName = NULL;
560  if (SymbolName) *SymbolName = NULL;
561  if (Displacement) *Displacement = 0;
562 
563  return NULL;
564  }
565 
566  PhpRegisterSymbolProvider(SymbolProvider);
567 
568  if (!SymFromAddrW_I && !SymFromAddr_I)
569  return NULL;
570 
571  symbolInfo = PhAllocate(FIELD_OFFSET(SYMBOL_INFOW, Name) + PH_MAX_SYMBOL_NAME_LEN * 2);
572  memset(symbolInfo, 0, sizeof(SYMBOL_INFOW));
573  symbolInfo->SizeOfStruct = sizeof(SYMBOL_INFOW);
574  symbolInfo->MaxNameLen = PH_MAX_SYMBOL_NAME_LEN;
575 
576  // Get the symbol name.
577 
578  PH_LOCK_SYMBOLS();
579 
580  // Note that we don't care whether this call
581  // succeeds or not, based on the assumption that
582  // it will not write to the symbolInfo structure
583  // if it fails. We've already zeroed the structure,
584  // so we can deal with it.
585 
586  if (SymFromAddrW_I)
587  {
589  SymbolProvider->ProcessHandle,
590  Address,
591  &displacement,
592  symbolInfo
593  );
594  nameLength = symbolInfo->NameLen;
595 
596  if (nameLength + 1 > PH_MAX_SYMBOL_NAME_LEN)
597  {
598  PhFree(symbolInfo);
599  symbolInfo = PhAllocate(FIELD_OFFSET(SYMBOL_INFOW, Name) + nameLength * 2 + 2);
600  memset(symbolInfo, 0, sizeof(SYMBOL_INFOW));
601  symbolInfo->SizeOfStruct = sizeof(SYMBOL_INFOW);
602  symbolInfo->MaxNameLen = nameLength + 1;
603 
605  SymbolProvider->ProcessHandle,
606  Address,
607  &displacement,
608  symbolInfo
609  );
610  }
611  }
612  else if (SymFromAddr_I)
613  {
614  PSYMBOL_INFO symbolInfoA;
615 
616  symbolInfoA = PhAllocate(FIELD_OFFSET(SYMBOL_INFO, Name) + PH_MAX_SYMBOL_NAME_LEN);
617  memset(symbolInfoA, 0, sizeof(SYMBOL_INFO));
618  symbolInfoA->SizeOfStruct = sizeof(SYMBOL_INFO);
619  symbolInfoA->MaxNameLen = PH_MAX_SYMBOL_NAME_LEN;
620 
622  SymbolProvider->ProcessHandle,
623  Address,
624  &displacement,
625  symbolInfoA
626  );
627  nameLength = symbolInfoA->NameLen;
628 
629  if (nameLength + 1 > PH_MAX_SYMBOL_NAME_LEN)
630  {
631  PhFree(symbolInfoA);
632  symbolInfoA = PhAllocate(FIELD_OFFSET(SYMBOL_INFO, Name) + nameLength + 1);
633  memset(symbolInfoA, 0, sizeof(SYMBOL_INFO));
634  symbolInfoA->SizeOfStruct = sizeof(SYMBOL_INFO);
635  symbolInfoA->MaxNameLen = nameLength + 1;
636 
638  SymbolProvider->ProcessHandle,
639  Address,
640  &displacement,
641  symbolInfoA
642  );
643 
644  // Also reallocate the Unicode-based buffer.
645  PhFree(symbolInfo);
646  symbolInfo = PhAllocate(FIELD_OFFSET(SYMBOL_INFOW, Name) + nameLength * 2 + 2);
647  memset(symbolInfo, 0, sizeof(SYMBOL_INFOW));
648  symbolInfo->SizeOfStruct = sizeof(SYMBOL_INFOW);
649  symbolInfo->MaxNameLen = nameLength + 1;
650  }
651 
652  PhpSymbolInfoAnsiToUnicode(symbolInfo, symbolInfoA);
653  PhFree(symbolInfoA);
654  }
655 
657 
658  // Find the module name.
659 
660  if (symbolInfo->ModBase == 0)
661  {
662  modBase = PhGetModuleFromAddress(
663  SymbolProvider,
664  Address,
665  &modFileName
666  );
667  }
668  else
669  {
670  PH_SYMBOL_MODULE lookupSymbolModule;
671  PPH_AVL_LINKS existingLinks;
672  PPH_SYMBOL_MODULE symbolModule;
673 
674  lookupSymbolModule.BaseAddress = symbolInfo->ModBase;
675 
676  PhAcquireQueuedLockShared(&SymbolProvider->ModulesListLock);
677 
678  existingLinks = PhFindElementAvlTree(&SymbolProvider->ModulesSet, &lookupSymbolModule.Links);
679 
680  if (existingLinks)
681  {
682  symbolModule = CONTAINING_RECORD(existingLinks, PH_SYMBOL_MODULE, Links);
683  PhSetReference(&modFileName, symbolModule->FileName);
684  }
685 
686  PhReleaseQueuedLockShared(&SymbolProvider->ModulesListLock);
687  }
688 
689  // If we don't have a module name, return an address.
690  if (!modFileName)
691  {
692  resolveLevel = PhsrlAddress;
693  symbol = PhCreateStringEx(NULL, PH_PTR_STR_LEN * 2);
694  PhPrintPointer(symbol->Buffer, (PVOID)Address);
696 
697  goto CleanupExit;
698  }
699 
700  modBaseName = PhGetBaseName(modFileName);
701 
702  // If we have a module name but not a symbol name,
703  // return the module plus an offset: module+offset.
704 
705  if (symbolInfo->NameLen == 0)
706  {
707  PH_FORMAT format[3];
708 
709  resolveLevel = PhsrlModule;
710 
711  PhInitFormatSR(&format[0], modBaseName->sr);
712  PhInitFormatS(&format[1], L"+0x");
713  PhInitFormatIX(&format[2], (ULONG_PTR)(Address - modBase));
714  symbol = PhFormat(format, 3, modBaseName->Length + 6 + 32);
715 
716  goto CleanupExit;
717  }
718 
719  // If we have everything, return the full symbol
720  // name: module!symbol+offset.
721 
722  symbolName = PhCreateStringEx(symbolInfo->Name, symbolInfo->NameLen * 2);
723  resolveLevel = PhsrlFunction;
724 
725  if (displacement == 0)
726  {
727  PH_FORMAT format[3];
728 
729  PhInitFormatSR(&format[0], modBaseName->sr);
730  PhInitFormatC(&format[1], '!');
731  PhInitFormatSR(&format[2], symbolName->sr);
732 
733  symbol = PhFormat(format, 3, modBaseName->Length + 2 + symbolName->Length);
734  }
735  else
736  {
737  PH_FORMAT format[5];
738 
739  PhInitFormatSR(&format[0], modBaseName->sr);
740  PhInitFormatC(&format[1], '!');
741  PhInitFormatSR(&format[2], symbolName->sr);
742  PhInitFormatS(&format[3], L"+0x");
743  PhInitFormatIX(&format[4], (ULONG_PTR)displacement);
744 
745  symbol = PhFormat(format, 5, modBaseName->Length + 2 + symbolName->Length + 6 + 32);
746  }
747 
748 CleanupExit:
749 
750  if (ResolveLevel)
751  *ResolveLevel = resolveLevel;
752  if (FileName)
753  PhSetReference(FileName, modFileName);
754  if (SymbolName)
755  PhSetReference(SymbolName, symbolName);
756  if (Displacement)
757  *Displacement = displacement;
758 
759  PhClearReference(&modFileName);
760  PhClearReference(&modBaseName);
761  PhClearReference(&symbolName);
762  PhFree(symbolInfo);
763 
764  return symbol;
765 }
766 
768  _In_ PPH_SYMBOL_PROVIDER SymbolProvider,
769  _In_ PWSTR Name,
770  _Out_ PPH_SYMBOL_INFORMATION Information
771  )
772 {
773  PSYMBOL_INFOW symbolInfo;
774  UCHAR symbolInfoBuffer[FIELD_OFFSET(SYMBOL_INFOW, Name) + PH_MAX_SYMBOL_NAME_LEN * 2];
775  BOOL result;
776 
777  PhpRegisterSymbolProvider(SymbolProvider);
778 
779  if (!SymFromNameW_I && !SymFromName_I)
780  return FALSE;
781 
782  symbolInfo = (PSYMBOL_INFOW)symbolInfoBuffer;
783  memset(symbolInfo, 0, sizeof(SYMBOL_INFOW));
784  symbolInfo->SizeOfStruct = sizeof(SYMBOL_INFOW);
785  symbolInfo->MaxNameLen = PH_MAX_SYMBOL_NAME_LEN;
786 
787  // Get the symbol information.
788 
789  PH_LOCK_SYMBOLS();
790 
791  if (SymFromNameW_I)
792  {
793  result = SymFromNameW_I(
794  SymbolProvider->ProcessHandle,
795  Name,
796  symbolInfo
797  );
798  }
799  else if (SymFromName_I)
800  {
801  UCHAR buffer[FIELD_OFFSET(SYMBOL_INFO, Name) + PH_MAX_SYMBOL_NAME_LEN];
802  PSYMBOL_INFO symbolInfoA;
803  PPH_BYTES name;
804 
805  symbolInfoA = (PSYMBOL_INFO)buffer;
806  memset(symbolInfoA, 0, sizeof(SYMBOL_INFO));
807  symbolInfoA->SizeOfStruct = sizeof(SYMBOL_INFO);
808  symbolInfoA->MaxNameLen = PH_MAX_SYMBOL_NAME_LEN;
809 
810  name = PhConvertUtf16ToMultiByte(Name);
811 
812  if (result = SymFromName_I(
813  SymbolProvider->ProcessHandle,
814  name->Buffer,
815  symbolInfoA
816  ))
817  {
818  PhpSymbolInfoAnsiToUnicode(symbolInfo, symbolInfoA);
819  }
820 
821  PhDereferenceObject(name);
822  }
823  else
824  {
825  result = FALSE;
826  }
827 
829 
830  if (!result)
831  return FALSE;
832 
833  Information->Address = symbolInfo->Address;
834  Information->ModuleBase = symbolInfo->ModBase;
835  Information->Index = symbolInfo->Index;
836  Information->Size = symbolInfo->Size;
837 
838  return TRUE;
839 }
840 
842  _In_ PPH_SYMBOL_PROVIDER SymbolProvider,
843  _In_ PWSTR FileName,
844  _In_ ULONG64 BaseAddress,
845  _In_ ULONG Size
846  )
847 {
848  ULONG64 baseAddress;
849  PPH_SYMBOL_MODULE symbolModule = NULL;
850  PPH_AVL_LINKS existingLinks;
851  PH_SYMBOL_MODULE lookupSymbolModule;
852 
853  PhpRegisterSymbolProvider(SymbolProvider);
854 
856  return FALSE;
857 
858  // Check for duplicates. It is better to do this before calling SymLoadModuleExW, because it
859  // seems to force symbol loading when it is called twice on the same module even if deferred
860  // loading is enabled.
861  PhAcquireQueuedLockExclusive(&SymbolProvider->ModulesListLock);
862  lookupSymbolModule.BaseAddress = BaseAddress;
863  existingLinks = PhFindElementAvlTree(&SymbolProvider->ModulesSet, &lookupSymbolModule.Links);
864  PhReleaseQueuedLockExclusive(&SymbolProvider->ModulesListLock);
865 
866  if (existingLinks)
867  return TRUE;
868 
869  PH_LOCK_SYMBOLS();
870 
871  if (SymLoadModuleExW_I)
872  {
873  baseAddress = SymLoadModuleExW_I(
874  SymbolProvider->ProcessHandle,
875  NULL,
876  FileName,
877  NULL,
878  BaseAddress,
879  Size,
880  NULL,
881  0
882  );
883  }
884  else
885  {
886  PPH_BYTES fileName;
887 
888  fileName = PhConvertUtf16ToMultiByte(FileName);
889  baseAddress = SymLoadModule64_I(
890  SymbolProvider->ProcessHandle,
891  NULL,
892  fileName->Buffer,
893  NULL,
894  BaseAddress,
895  Size
896  );
897  PhDereferenceObject(fileName);
898  }
899 
901 
902  // Add the module to the list, even if we couldn't load symbols for the module.
903 
904  PhAcquireQueuedLockExclusive(&SymbolProvider->ModulesListLock);
905 
906  // Check for duplicates again.
907  lookupSymbolModule.BaseAddress = BaseAddress;
908  existingLinks = PhFindElementAvlTree(&SymbolProvider->ModulesSet, &lookupSymbolModule.Links);
909 
910  if (!existingLinks)
911  {
912  symbolModule = PhAllocate(sizeof(PH_SYMBOL_MODULE));
913  symbolModule->BaseAddress = BaseAddress;
914  symbolModule->Size = Size;
915  symbolModule->FileName = PhGetFullPath(FileName, &symbolModule->BaseNameIndex);
916 
917  existingLinks = PhAddElementAvlTree(&SymbolProvider->ModulesSet, &symbolModule->Links);
918  assert(!existingLinks);
919  InsertTailList(&SymbolProvider->ModulesListHead, &symbolModule->ListEntry);
920  }
921 
922  PhReleaseQueuedLockExclusive(&SymbolProvider->ModulesListLock);
923 
924  if (!baseAddress)
925  {
926  if (GetLastError() != ERROR_SUCCESS)
927  return FALSE;
928  else
929  return TRUE;
930  }
931 
932  return TRUE;
933 }
934 
936  _In_ ULONG Mask,
937  _In_ ULONG Value
938  )
939 {
940  ULONG options;
941 
943 
945  return;
946 
947  PH_LOCK_SYMBOLS();
948 
949  options = SymGetOptions_I();
950  options &= ~Mask;
951  options |= Value;
952  SymSetOptions_I(options);
953 
955 }
956 
958  _In_ PPH_SYMBOL_PROVIDER SymbolProvider,
959  _In_ PWSTR Path
960  )
961 {
962  PhpRegisterSymbolProvider(SymbolProvider);
963 
965  return;
966 
967  PH_LOCK_SYMBOLS();
968 
970  {
971  SymSetSearchPathW_I(SymbolProvider->ProcessHandle, Path);
972  }
973  else if (SymSetSearchPath_I)
974  {
975  PPH_BYTES path;
976 
977  path = PhConvertUtf16ToMultiByte(Path);
978  SymSetSearchPath_I(SymbolProvider->ProcessHandle, path->Buffer);
979  PhDereferenceObject(path);
980  }
981 
983 }
984 
985 #ifdef _WIN64
986 
987 NTSTATUS PhpLookupDynamicFunctionTable(
988  _In_ HANDLE ProcessHandle,
989  _In_ ULONG64 Address,
990  _Out_opt_ PDYNAMIC_FUNCTION_TABLE *FunctionTableAddress,
991  _Out_opt_ PDYNAMIC_FUNCTION_TABLE FunctionTable,
992  _Out_writes_bytes_opt_(OutOfProcessCallbackDllBufferSize) PWCHAR OutOfProcessCallbackDllBuffer,
993  _In_ ULONG OutOfProcessCallbackDllBufferSize,
994  _Out_opt_ PUNICODE_STRING OutOfProcessCallbackDllString
995  )
996 {
997  NTSTATUS status;
998  PLIST_ENTRY (NTAPI *rtlGetFunctionTableListHead)(VOID);
999  PLIST_ENTRY tableListHead;
1000  LIST_ENTRY tableListHeadEntry;
1001  PLIST_ENTRY tableListEntry;
1002  PDYNAMIC_FUNCTION_TABLE functionTableAddress;
1003  DYNAMIC_FUNCTION_TABLE functionTable;
1004  ULONG count;
1005  SIZE_T numberOfBytesRead;
1006  ULONG i;
1007  BOOLEAN foundNull;
1008 
1009  rtlGetFunctionTableListHead = PhGetModuleProcAddress(L"ntdll.dll", "RtlGetFunctionTableListHead");
1010 
1011  if (!rtlGetFunctionTableListHead)
1012  return STATUS_PROCEDURE_NOT_FOUND;
1013 
1014  tableListHead = rtlGetFunctionTableListHead();
1015 
1016  // Find the function table entry for this address.
1017 
1018  if (!NT_SUCCESS(status = PhReadVirtualMemory(
1019  ProcessHandle,
1020  tableListHead,
1021  &tableListHeadEntry,
1022  sizeof(LIST_ENTRY),
1023  NULL
1024  )))
1025  return status;
1026 
1027  tableListEntry = tableListHeadEntry.Flink;
1028  count = 0; // make sure we can't be forced into an infinite loop by crafted data
1029 
1030  while (tableListEntry != tableListHead && count < PH_ENUM_PROCESS_MODULES_LIMIT)
1031  {
1032  functionTableAddress = CONTAINING_RECORD(tableListEntry, DYNAMIC_FUNCTION_TABLE, ListEntry);
1033 
1034  if (!NT_SUCCESS(status = PhReadVirtualMemory(
1035  ProcessHandle,
1036  functionTableAddress,
1037  &functionTable,
1038  sizeof(DYNAMIC_FUNCTION_TABLE),
1039  NULL
1040  )))
1041  return status;
1042 
1043  if (Address >= functionTable.MinimumAddress && Address < functionTable.MaximumAddress)
1044  {
1045  if (OutOfProcessCallbackDllBuffer)
1046  {
1047  if (functionTable.OutOfProcessCallbackDll)
1048  {
1049  // Read the out-of-process callback DLL path. We don't have a length, so we'll
1050  // just have to read as much as possible.
1051 
1052  memset(OutOfProcessCallbackDllBuffer, 0xff, OutOfProcessCallbackDllBufferSize);
1053  status = PhReadVirtualMemory(
1054  ProcessHandle,
1055  functionTable.OutOfProcessCallbackDll,
1056  OutOfProcessCallbackDllBuffer,
1057  OutOfProcessCallbackDllBufferSize,
1058  &numberOfBytesRead
1059  );
1060 
1061  if (status != STATUS_PARTIAL_COPY && !NT_SUCCESS(status))
1062  return status;
1063 
1064  foundNull = FALSE;
1065 
1066  for (i = 0; i < OutOfProcessCallbackDllBufferSize / sizeof(WCHAR); i++)
1067  {
1068  if (OutOfProcessCallbackDllBuffer[i] == 0)
1069  {
1070  foundNull = TRUE;
1071 
1072  if (OutOfProcessCallbackDllString)
1073  {
1074  OutOfProcessCallbackDllString->Buffer = OutOfProcessCallbackDllBuffer;
1075  OutOfProcessCallbackDllString->Length = (USHORT)(i * sizeof(WCHAR));
1076  OutOfProcessCallbackDllString->MaximumLength = OutOfProcessCallbackDllString->Length;
1077  }
1078 
1079  break;
1080  }
1081  }
1082 
1083  // If there was no null terminator, then we didn't read the whole string in.
1084  // Fail the operation.
1085  if (!foundNull)
1086  return STATUS_BUFFER_OVERFLOW;
1087  }
1088  else
1089  {
1090  OutOfProcessCallbackDllBuffer[0] = 0;
1091 
1092  if (OutOfProcessCallbackDllString)
1093  {
1094  OutOfProcessCallbackDllString->Buffer = NULL;
1095  OutOfProcessCallbackDllString->Length = 0;
1096  OutOfProcessCallbackDllString->MaximumLength = 0;
1097  }
1098  }
1099  }
1100 
1101  if (FunctionTableAddress)
1102  *FunctionTableAddress = functionTableAddress;
1103 
1104  if (FunctionTable)
1105  *FunctionTable = functionTable;
1106 
1107  return STATUS_SUCCESS;
1108  }
1109 
1110  tableListEntry = functionTable.ListEntry.Flink;
1111  count++;
1112  }
1113 
1114  return STATUS_NOT_FOUND;
1115 }
1116 
1117 PRUNTIME_FUNCTION PhpLookupFunctionEntry(
1118  _In_ PRUNTIME_FUNCTION Functions,
1119  _In_ ULONG NumberOfFunctions,
1120  _In_ BOOLEAN Sorted,
1121  _In_ ULONG64 RelativeControlPc
1122  )
1123 {
1124  LONG low;
1125  LONG high;
1126  ULONG i;
1127 
1128  if (Sorted)
1129  {
1130  if (NumberOfFunctions == 0)
1131  return NULL;
1132 
1133  low = 0;
1134  high = NumberOfFunctions - 1;
1135 
1136  do
1137  {
1138  i = (low + high) / 2;
1139 
1140  if (RelativeControlPc < Functions[i].BeginAddress)
1141  high = i - 1;
1142  else if (RelativeControlPc >= Functions[i].EndAddress)
1143  low = i + 1;
1144  else
1145  return &Functions[i];
1146  } while (low <= high);
1147  }
1148  else
1149  {
1150  for (i = 0; i < NumberOfFunctions; i++)
1151  {
1152  if (RelativeControlPc >= Functions[i].BeginAddress && RelativeControlPc < Functions[i].EndAddress)
1153  return &Functions[i];
1154  }
1155  }
1156 
1157  return NULL;
1158 }
1159 
1160 NTSTATUS PhpAccessCallbackFunctionTable(
1161  _In_ HANDLE ProcessHandle,
1162  _In_ PVOID FunctionTableAddress,
1163  _In_ PUNICODE_STRING OutOfProcessCallbackDllString,
1164  _Out_ PRUNTIME_FUNCTION *Functions,
1165  _Out_ PULONG NumberOfFunctions
1166  )
1167 {
1168  static PH_STRINGREF knownFunctionTableDllsKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows NT\\CurrentVersion\\KnownFunctionTableDlls");
1169 
1170  NTSTATUS status;
1171  HANDLE keyHandle;
1172  ULONG returnLength;
1173  PVOID dllHandle;
1174  ANSI_STRING outOfProcessFunctionTableCallbackName;
1175  POUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK outOfProcessFunctionTableCallback;
1176 
1177  if (!OutOfProcessCallbackDllString->Buffer)
1178  return STATUS_INVALID_PARAMETER;
1179 
1180  // Don't load DLLs from arbitrary locations. Check if this is a known function table DLL.
1181 
1182  if (!NT_SUCCESS(status = PhOpenKey(
1183  &keyHandle,
1184  KEY_READ,
1186  &knownFunctionTableDllsKeyName,
1187  0
1188  )))
1189  return status;
1190 
1191  status = NtQueryValueKey(keyHandle, OutOfProcessCallbackDllString, KeyValuePartialInformation, NULL, 0, &returnLength);
1192  NtClose(keyHandle);
1193 
1194  if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1195  return STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT;
1196 
1197  status = LdrLoadDll(NULL, NULL, OutOfProcessCallbackDllString, &dllHandle);
1198 
1199  if (!NT_SUCCESS(status))
1200  return status;
1201 
1202  RtlInitAnsiString(&outOfProcessFunctionTableCallbackName, OUT_OF_PROCESS_FUNCTION_TABLE_CALLBACK_EXPORT_NAME);
1203  status = LdrGetProcedureAddress(dllHandle, &outOfProcessFunctionTableCallbackName, 0, (PVOID *)&outOfProcessFunctionTableCallback);
1204 
1205  if (NT_SUCCESS(status))
1206  {
1207  status = outOfProcessFunctionTableCallback(
1208  ProcessHandle,
1209  FunctionTableAddress,
1210  NumberOfFunctions,
1211  Functions
1212  );
1213  }
1214 
1215  LdrUnloadDll(dllHandle);
1216 
1217  return status;
1218 }
1219 
1220 NTSTATUS PhpAccessNormalFunctionTable(
1221  _In_ HANDLE ProcessHandle,
1222  _In_ PDYNAMIC_FUNCTION_TABLE FunctionTable,
1223  _Out_ PRUNTIME_FUNCTION *Functions,
1224  _Out_ PULONG NumberOfFunctions
1225  )
1226 {
1227  NTSTATUS status;
1228  SIZE_T bufferSize;
1229  PRUNTIME_FUNCTION functions;
1230 
1231  // Put a reasonable limit on the number of entries we read.
1232  if (FunctionTable->EntryCount > 0x100000)
1233  return STATUS_BUFFER_OVERFLOW;
1234 
1235  bufferSize = FunctionTable->EntryCount * sizeof(RUNTIME_FUNCTION);
1236  functions = PhAllocatePage(bufferSize, NULL);
1237 
1238  if (!functions)
1239  return STATUS_NO_MEMORY;
1240 
1241  status = PhReadVirtualMemory(ProcessHandle, FunctionTable->FunctionTable, functions, bufferSize, NULL);
1242 
1243  if (NT_SUCCESS(status))
1244  {
1245  *Functions = functions;
1246  *NumberOfFunctions = FunctionTable->EntryCount;
1247  }
1248  else
1249  {
1250  PhFreePage(functions);
1251  }
1252 
1253  return status;
1254 }
1255 
1256 NTSTATUS PhAccessOutOfProcessFunctionEntry(
1257  _In_ HANDLE ProcessHandle,
1258  _In_ ULONG64 ControlPc,
1259  _Out_ PRUNTIME_FUNCTION Function
1260  )
1261 {
1262  NTSTATUS status;
1263  PDYNAMIC_FUNCTION_TABLE functionTableAddress;
1264  DYNAMIC_FUNCTION_TABLE functionTable;
1265  WCHAR outOfProcessCallbackDll[512];
1266  UNICODE_STRING outOfProcessCallbackDllString;
1267  PRUNTIME_FUNCTION functions;
1268  ULONG numberOfFunctions;
1269  PRUNTIME_FUNCTION function;
1270 
1271  if (!NT_SUCCESS(status = PhpLookupDynamicFunctionTable(
1272  ProcessHandle,
1273  ControlPc,
1274  &functionTableAddress,
1275  &functionTable,
1276  outOfProcessCallbackDll,
1277  sizeof(outOfProcessCallbackDll),
1278  &outOfProcessCallbackDllString
1279  )))
1280  {
1281  return status;
1282  }
1283 
1284  if (functionTable.Type == RF_CALLBACK)
1285  {
1286  if (!NT_SUCCESS(status = PhpAccessCallbackFunctionTable(
1287  ProcessHandle,
1288  functionTableAddress,
1289  &outOfProcessCallbackDllString,
1290  &functions,
1291  &numberOfFunctions
1292  )))
1293  {
1294  return status;
1295  }
1296 
1297  function = PhpLookupFunctionEntry(functions, numberOfFunctions, FALSE, ControlPc - functionTable.BaseAddress);
1298 
1299  if (function)
1300  *Function = *function;
1301  else
1302  status = STATUS_NOT_FOUND;
1303 
1304  RtlFreeHeap(RtlProcessHeap(), 0, functions);
1305  }
1306  else
1307  {
1308  if (!NT_SUCCESS(status = PhpAccessNormalFunctionTable(
1309  ProcessHandle,
1310  &functionTable,
1311  &functions,
1312  &numberOfFunctions
1313  )))
1314  {
1315  return status;
1316  }
1317 
1318  function = PhpLookupFunctionEntry(functions, numberOfFunctions, functionTable.Type == RF_SORTED, ControlPc - functionTable.BaseAddress);
1319 
1320  if (function)
1321  *Function = *function;
1322  else
1323  status = STATUS_NOT_FOUND;
1324 
1325  PhFreePage(functions);
1326  }
1327 
1328  return status;
1329 }
1330 
1331 #endif
1332 
1333 ULONG64 __stdcall PhGetModuleBase64(
1334  _In_ HANDLE hProcess,
1335  _In_ DWORD64 dwAddr
1336  )
1337 {
1338  ULONG64 base;
1339 #ifdef _WIN64
1340  DYNAMIC_FUNCTION_TABLE functionTable;
1341 #endif
1342 
1343 #ifdef _WIN64
1344  if (NT_SUCCESS(PhpLookupDynamicFunctionTable(
1345  hProcess,
1346  dwAddr,
1347  NULL,
1348  &functionTable,
1349  NULL,
1350  0,
1351  NULL
1352  )))
1353  {
1354  base = functionTable.BaseAddress;
1355  }
1356  else
1357  {
1358  base = 0;
1359  }
1360 #else
1361  base = 0;
1362 #endif
1363 
1364  if (base == 0 && SymGetModuleBase64_I)
1365  base = SymGetModuleBase64_I(hProcess, dwAddr);
1366 
1367  return base;
1368 }
1369 
1370 PVOID __stdcall PhFunctionTableAccess64(
1371  _In_ HANDLE hProcess,
1372  _In_ DWORD64 AddrBase
1373  )
1374 {
1375 #ifdef _WIN64
1376  static RUNTIME_FUNCTION lastRuntimeFunction;
1377 #endif
1378 
1379  PVOID entry;
1380 
1381 #ifdef _WIN64
1382  if (NT_SUCCESS(PhAccessOutOfProcessFunctionEntry(hProcess, AddrBase, &lastRuntimeFunction)))
1383  entry = &lastRuntimeFunction;
1384  else
1385  entry = NULL;
1386 #else
1387  entry = NULL;
1388 #endif
1389 
1390  if (!entry && SymFunctionTableAccess64_I)
1391  entry = SymFunctionTableAccess64_I(hProcess, AddrBase);
1392 
1393  return entry;
1394 }
1395 
1396 BOOLEAN PhStackWalk(
1397  _In_ ULONG MachineType,
1398  _In_ HANDLE ProcessHandle,
1399  _In_ HANDLE ThreadHandle,
1400  _Inout_ LPSTACKFRAME64 StackFrame,
1401  _Inout_ PVOID ContextRecord,
1402  _In_opt_ PPH_SYMBOL_PROVIDER SymbolProvider,
1403  _In_opt_ PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
1404  _In_opt_ PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
1405  _In_opt_ PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
1406  _In_opt_ PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
1407  )
1408 {
1409  BOOLEAN result;
1410 
1411  PhpRegisterSymbolProvider(SymbolProvider);
1412 
1413  if (!StackWalk64_I)
1414  return FALSE;
1415 
1416  if (!FunctionTableAccessRoutine)
1417  {
1418  if (MachineType == IMAGE_FILE_MACHINE_AMD64)
1419  FunctionTableAccessRoutine = PhFunctionTableAccess64;
1420  else
1421  FunctionTableAccessRoutine = SymFunctionTableAccess64_I;
1422  }
1423 
1424  if (!GetModuleBaseRoutine)
1425  {
1426  if (MachineType == IMAGE_FILE_MACHINE_AMD64)
1427  GetModuleBaseRoutine = PhGetModuleBase64;
1428  else
1429  GetModuleBaseRoutine = SymGetModuleBase64_I;
1430  }
1431 
1432  PH_LOCK_SYMBOLS();
1433  result = StackWalk64_I(
1434  MachineType,
1435  ProcessHandle,
1436  ThreadHandle,
1437  StackFrame,
1438  ContextRecord,
1439  ReadMemoryRoutine,
1440  FunctionTableAccessRoutine,
1441  GetModuleBaseRoutine,
1442  TranslateAddress
1443  );
1445 
1446  return result;
1447 }
1448 
1450  _In_ HANDLE ProcessHandle,
1451  _In_ HANDLE ProcessId,
1452  _In_ HANDLE FileHandle,
1453  _In_ MINIDUMP_TYPE DumpType,
1454  _In_opt_ PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
1455  _In_opt_ PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
1456  _In_opt_ PMINIDUMP_CALLBACK_INFORMATION CallbackParam
1457  )
1458 {
1460 
1461  if (!MiniDumpWriteDump_I)
1462  {
1463  SetLastError(ERROR_PROC_NOT_FOUND);
1464  return FALSE;
1465  }
1466 
1467  return MiniDumpWriteDump_I(
1468  ProcessHandle,
1469  (ULONG)ProcessId,
1470  FileHandle,
1471  DumpType,
1472  ExceptionParam,
1473  UserStreamParam,
1474  CallbackParam
1475  );
1476 }
1477 
1489  _In_ STACKFRAME64 *StackFrame64,
1490  _In_ ULONG Flags,
1491  _Out_ PPH_THREAD_STACK_FRAME ThreadStackFrame
1492  )
1493 {
1494  ULONG i;
1495 
1496  ThreadStackFrame->PcAddress = (PVOID)StackFrame64->AddrPC.Offset;
1497  ThreadStackFrame->ReturnAddress = (PVOID)StackFrame64->AddrReturn.Offset;
1498  ThreadStackFrame->FrameAddress = (PVOID)StackFrame64->AddrFrame.Offset;
1499  ThreadStackFrame->StackAddress = (PVOID)StackFrame64->AddrStack.Offset;
1500  ThreadStackFrame->BStoreAddress = (PVOID)StackFrame64->AddrBStore.Offset;
1501 
1502  for (i = 0; i < 4; i++)
1503  ThreadStackFrame->Params[i] = (PVOID)StackFrame64->Params[i];
1504 
1505  ThreadStackFrame->Flags = Flags;
1506 
1507  if (StackFrame64->FuncTableEntry)
1508  ThreadStackFrame->Flags |= PH_THREAD_STACK_FRAME_FPO_DATA_PRESENT;
1509 }
1510 
1539  _In_ HANDLE ThreadHandle,
1540  _In_opt_ HANDLE ProcessHandle,
1541  _In_opt_ PCLIENT_ID ClientId,
1542  _In_opt_ PPH_SYMBOL_PROVIDER SymbolProvider,
1543  _In_ ULONG Flags,
1544  _In_ PPH_WALK_THREAD_STACK_CALLBACK Callback,
1545  _In_opt_ PVOID Context
1546  )
1547 {
1548  NTSTATUS status = STATUS_SUCCESS;
1549  BOOLEAN suspended = FALSE;
1550  BOOLEAN processOpened = FALSE;
1551  BOOLEAN isCurrentThread = FALSE;
1552  BOOLEAN isSystemProcess = FALSE;
1553  THREAD_BASIC_INFORMATION basicInfo;
1554 
1555  // Open a handle to the process if we weren't given one.
1556  if (!ProcessHandle)
1557  {
1558  if (KphIsConnected() || !ClientId)
1559  {
1560  if (!NT_SUCCESS(status = PhOpenThreadProcess(
1561  &ProcessHandle,
1563  ThreadHandle
1564  )))
1565  return status;
1566  }
1567  else
1568  {
1569  if (!NT_SUCCESS(status = PhOpenProcess(
1570  &ProcessHandle,
1572  ClientId->UniqueProcess
1573  )))
1574  return status;
1575  }
1576 
1577  processOpened = TRUE;
1578  }
1579 
1580  // Determine if the caller specified the current thread.
1581  if (ClientId)
1582  {
1583  if (ClientId->UniqueThread == NtCurrentTeb()->ClientId.UniqueThread)
1584  isCurrentThread = TRUE;
1585  if (ClientId->UniqueProcess == SYSTEM_PROCESS_ID)
1586  isSystemProcess = TRUE;
1587  }
1588  else
1589  {
1590  if (ThreadHandle == NtCurrentThread())
1591  {
1592  isCurrentThread = TRUE;
1593  }
1594  else if (NT_SUCCESS(PhGetThreadBasicInformation(ThreadHandle, &basicInfo)))
1595  {
1596  if (basicInfo.ClientId.UniqueThread == NtCurrentTeb()->ClientId.UniqueThread)
1597  isCurrentThread = TRUE;
1598  if (basicInfo.ClientId.UniqueProcess == SYSTEM_PROCESS_ID)
1599  isSystemProcess = TRUE;
1600  }
1601  }
1602 
1603  // Suspend the thread to avoid inaccurate results. Don't suspend if we're walking
1604  // the stack of the current thread or this is the System process.
1605  if (!isCurrentThread && !isSystemProcess)
1606  {
1607  if (NT_SUCCESS(NtSuspendThread(ThreadHandle, NULL)))
1608  suspended = TRUE;
1609  }
1610 
1611  // Kernel stack walk.
1612  if ((Flags & PH_WALK_KERNEL_STACK) && KphIsConnected())
1613  {
1614  PVOID stack[62 - 1]; // 62 limit for XP and Server 2003.
1615  ULONG capturedFrames;
1616  ULONG i;
1617 
1619  ThreadHandle,
1620  1,
1621  sizeof(stack) / sizeof(PVOID),
1622  stack,
1623  &capturedFrames,
1624  NULL
1625  )))
1626  {
1627  PH_THREAD_STACK_FRAME threadStackFrame;
1628 
1629  memset(&threadStackFrame, 0, sizeof(PH_THREAD_STACK_FRAME));
1630 
1631  for (i = 0; i < capturedFrames; i++)
1632  {
1633  threadStackFrame.PcAddress = stack[i];
1634  threadStackFrame.Flags = PH_THREAD_STACK_FRAME_KERNEL;
1635 
1636  if (!Callback(&threadStackFrame, Context))
1637  {
1638  goto ResumeExit;
1639  }
1640  }
1641  }
1642  }
1643 
1644 #ifdef _WIN64
1645  if (Flags & PH_WALK_AMD64_STACK)
1646  {
1647  STACKFRAME64 stackFrame;
1648  PH_THREAD_STACK_FRAME threadStackFrame;
1649  CONTEXT context;
1650 
1651  context.ContextFlags = CONTEXT_ALL;
1652 
1653  if (!NT_SUCCESS(status = PhGetThreadContext(
1654  ThreadHandle,
1655  &context
1656  )))
1657  goto SkipAmd64Stack;
1658 
1659  memset(&stackFrame, 0, sizeof(STACKFRAME64));
1660  stackFrame.AddrPC.Mode = AddrModeFlat;
1661  stackFrame.AddrPC.Offset = context.Rip;
1662  stackFrame.AddrStack.Mode = AddrModeFlat;
1663  stackFrame.AddrStack.Offset = context.Rsp;
1664  stackFrame.AddrFrame.Mode = AddrModeFlat;
1665  stackFrame.AddrFrame.Offset = context.Rbp;
1666 
1667  while (TRUE)
1668  {
1669  if (!PhStackWalk(
1670  IMAGE_FILE_MACHINE_AMD64,
1671  ProcessHandle,
1672  ThreadHandle,
1673  &stackFrame,
1674  &context,
1675  SymbolProvider,
1676  NULL,
1677  NULL,
1678  NULL,
1679  NULL
1680  ))
1681  break;
1682 
1683  // If we have an invalid instruction pointer, break.
1684  if (!stackFrame.AddrPC.Offset || stackFrame.AddrPC.Offset == -1)
1685  break;
1686 
1687  // Convert the stack frame and execute the callback.
1688 
1689  PhpConvertStackFrame(&stackFrame, PH_THREAD_STACK_FRAME_AMD64, &threadStackFrame);
1690 
1691  if (!Callback(&threadStackFrame, Context))
1692  goto ResumeExit;
1693  }
1694  }
1695 
1696 SkipAmd64Stack:
1697 #endif
1698 
1699  // x86/WOW64 stack walk.
1700  if (Flags & PH_WALK_I386_STACK)
1701  {
1702  STACKFRAME64 stackFrame;
1703  PH_THREAD_STACK_FRAME threadStackFrame;
1704 #ifndef _WIN64
1705  CONTEXT context;
1706 
1707  context.ContextFlags = CONTEXT_ALL;
1708 
1709  if (!NT_SUCCESS(status = PhGetThreadContext(
1710  ThreadHandle,
1711  &context
1712  )))
1713  goto SkipI386Stack;
1714 #else
1715  WOW64_CONTEXT context;
1716 
1717  context.ContextFlags = WOW64_CONTEXT_ALL;
1718 
1719  if (!NT_SUCCESS(status = NtQueryInformationThread(
1720  ThreadHandle,
1721  ThreadWow64Context,
1722  &context,
1723  sizeof(WOW64_CONTEXT),
1724  NULL
1725  )))
1726  goto SkipI386Stack;
1727 #endif
1728 
1729  memset(&stackFrame, 0, sizeof(STACKFRAME64));
1730  stackFrame.AddrPC.Mode = AddrModeFlat;
1731  stackFrame.AddrPC.Offset = context.Eip;
1732  stackFrame.AddrStack.Mode = AddrModeFlat;
1733  stackFrame.AddrStack.Offset = context.Esp;
1734  stackFrame.AddrFrame.Mode = AddrModeFlat;
1735  stackFrame.AddrFrame.Offset = context.Ebp;
1736 
1737  while (TRUE)
1738  {
1739  if (!PhStackWalk(
1740  IMAGE_FILE_MACHINE_I386,
1741  ProcessHandle,
1742  ThreadHandle,
1743  &stackFrame,
1744  &context,
1745  SymbolProvider,
1746  NULL,
1747  NULL,
1748  NULL,
1749  NULL
1750  ))
1751  break;
1752 
1753  // If we have an invalid instruction pointer, break.
1754  if (!stackFrame.AddrPC.Offset || stackFrame.AddrPC.Offset == -1)
1755  break;
1756 
1757  // Convert the stack frame and execute the callback.
1758 
1759  PhpConvertStackFrame(&stackFrame, PH_THREAD_STACK_FRAME_I386, &threadStackFrame);
1760 
1761  if (!Callback(&threadStackFrame, Context))
1762  goto ResumeExit;
1763 
1764  // (x86 only) Allow the user to change Eip, Esp and Ebp.
1765  context.Eip = PtrToUlong(threadStackFrame.PcAddress);
1766  stackFrame.AddrPC.Offset = PtrToUlong(threadStackFrame.PcAddress);
1767  context.Ebp = PtrToUlong(threadStackFrame.FrameAddress);
1768  stackFrame.AddrFrame.Offset = PtrToUlong(threadStackFrame.FrameAddress);
1769  context.Esp = PtrToUlong(threadStackFrame.StackAddress);
1770  stackFrame.AddrStack.Offset = PtrToUlong(threadStackFrame.StackAddress);
1771  }
1772  }
1773 
1774 SkipI386Stack:
1775 
1776 ResumeExit:
1777  if (suspended)
1778  NtResumeThread(ThreadHandle, NULL);
1779 
1780  if (processOpened)
1781  NtClose(ProcessHandle);
1782 
1783  return status;
1784 }