1 /**
2  * UGENE - Integrated Bioinformatics Tools.
3  * Copyright (C) 2008-2021 UniPro <ugene@unipro.ru>
4  * http://ugene.net
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  * MA 02110-1301, USA.
20  */
21 
22 /**********************************************************************
23  * LICENSE (http://www.opensource.org/licenses/bsd-license.php)
24  *
25  *   Copyright (c) 2005-2011, Jochen Kalmbach
26  *   All rights reserved.
27  *
28  *   Redistribution and use in source and binary forms, with or without modification,
29  *   are permitted provided that the following conditions are met:
30  *
31  *   Redistributions of source code must retain the above copyright notice,
32  *   this list of conditions and the following disclaimer.
33  *   Redistributions in binary form must reproduce the above copyright notice,
34  *   this list of conditions and the following disclaimer in the documentation
35  *   and/or other materials provided with the distribution.
36  *   Neither the name of Jochen Kalmbach nor the names of its contributors may be
37  *   used to endorse or promote products derived from this software without
38  *   specific prior written permission.
39  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
40  *   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
41  *   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
42  *   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
43  *   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
44  *   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
45  *   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
46  *   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
47  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
48  *   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
49  *
50  **********************************************************************/
51 
52 #include "StackWalker.h"
53 #if defined(Q_OS_WIN)
54 
55 #    pragma comment(lib, "version.lib")  // for "VerQueryValue"
56 
57 #    include <dbghelp.h>
58 #    include <fstream>
59 #    include <stdio.h>
60 #    include <tchar.h>
61 
62 #    include <QFile>
63 #    include <QStringList>
64 
65 #    define USED_CONTEXT_FLAGS CONTEXT_ALL
66 
67 namespace U2 {
68 
69 class StackWalkerInternal {
70 public:
StackWalkerInternal(StackWalker * parent,HANDLE hProcess)71     StackWalkerInternal(StackWalker *parent, HANDLE hProcess) {
72         m_parent = parent;
73         m_hDbhHelp = nullptr;
74         pSC = nullptr;
75         m_hProcess = hProcess;
76         m_szSymPath = nullptr;
77         pSFTA = nullptr;
78         pSGLFA = nullptr;
79         pSGMB = nullptr;
80         pSGMI = nullptr;
81         pSGO = nullptr;
82         pSGSFA = nullptr;
83         pSI = nullptr;
84         pSLM = nullptr;
85         pSSO = nullptr;
86         pSW = nullptr;
87         pUDSN = nullptr;
88         pSGSP = nullptr;
89     }
~StackWalkerInternal()90     ~StackWalkerInternal() {
91         if (pSC != nullptr) {
92             pSC(m_hProcess);  // SymCleanup
93         }
94         if (m_hDbhHelp != nullptr) {
95             FreeLibrary(m_hDbhHelp);
96         }
97         m_hDbhHelp = nullptr;
98         m_parent = nullptr;
99         if (m_szSymPath != nullptr) {
100             free(m_szSymPath);
101         }
102         m_szSymPath = nullptr;
103     }
Init(LPCSTR szSymPath)104     BOOL Init(LPCSTR szSymPath) {
105         if (m_parent == nullptr) {
106             return FALSE;
107         }
108         // Dynamically load the Entry-Points for dbghelp.dll:
109         // First try to load the newest one from
110         wchar_t szTemp[4096];
111         // But before we do this, we first check if the ".local" file exists
112         if (GetModuleFileName(nullptr, szTemp, 4096) > 0) {
113             wcscat_s(szTemp, TEXT(".local"));
114             if (GetFileAttributes(szTemp) == INVALID_FILE_ATTRIBUTES) {
115                 // ".local" file does not exist, so we can try to load the dbghelp.dll from the "Debugging Tools for Windows"
116                 if (GetEnvironmentVariable(TEXT("ProgramFiles"), szTemp, 4096) > 0) {
117                     wcscat_s(szTemp, TEXT("\\Debugging Tools for Windows\\dbghelp.dll"));
118                     // now check if the file exists:
119                     if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) {
120                         m_hDbhHelp = LoadLibrary(szTemp);
121                     }
122                 }
123                 // Still not found? Then try to load the 64-Bit version:
124                 if ((m_hDbhHelp == nullptr) && (GetEnvironmentVariable(TEXT("ProgramFiles"), szTemp, 4096) > 0)) {
125                     wcscat_s(szTemp, TEXT("\\Debugging Tools for Windows 64-Bit\\dbghelp.dll"));
126                     if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) {
127                         m_hDbhHelp = LoadLibrary(szTemp);
128                     }
129                 }
130             }
131         }
132         if (m_hDbhHelp == nullptr) {  // if not already loaded, try to load a default-one
133             m_hDbhHelp = LoadLibrary(TEXT("dbghelp.dll"));
134         }
135         if (m_hDbhHelp == nullptr) {
136             return FALSE;
137         }
138         pSI = (tSI)GetProcAddress(m_hDbhHelp, "SymInitialize");
139         pSC = (tSC)GetProcAddress(m_hDbhHelp, "SymCleanup");
140 
141         pSW = (tSW)GetProcAddress(m_hDbhHelp, "StackWalk64");
142         pSGO = (tSGO)GetProcAddress(m_hDbhHelp, "SymGetOptions");
143         pSSO = (tSSO)GetProcAddress(m_hDbhHelp, "SymSetOptions");
144 
145         pSFTA = (tSFTA)GetProcAddress(m_hDbhHelp, "SymFunctionTableAccess64");
146         pSGLFA = (tSGLFA)GetProcAddress(m_hDbhHelp, "SymGetLineFromAddr64");
147         pSGMB = (tSGMB)GetProcAddress(m_hDbhHelp, "SymGetModuleBase64");
148         pSGMI = (tSGMI)GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64");
149         // pSGMI_V3 = (tSGMI_V3) GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64" );
150         pSGSFA = (tSGSFA)GetProcAddress(m_hDbhHelp, "SymGetSymFromAddr64");
151         pUDSN = (tUDSN)GetProcAddress(m_hDbhHelp, "UnDecorateSymbolName");
152         pSLM = (tSLM)GetProcAddress(m_hDbhHelp, "SymLoadModule64");
153         pSGSP = (tSGSP)GetProcAddress(m_hDbhHelp, "SymGetSearchPath");
154 
155         if (pSC == nullptr || pSFTA == nullptr || pSGMB == nullptr || pSGMI == nullptr ||
156             pSGO == nullptr || pSGSFA == nullptr || pSI == nullptr || pSSO == nullptr ||
157             pSW == nullptr || pUDSN == nullptr || pSLM == nullptr) {
158             FreeLibrary(m_hDbhHelp);
159             m_hDbhHelp = nullptr;
160             pSC = nullptr;
161             return FALSE;
162         }
163 
164         // SymInitialize
165         if (szSymPath != nullptr) {
166             m_szSymPath = _strdup(szSymPath);
167         }
168         if (this->pSI(m_hProcess, m_szSymPath, FALSE) == FALSE) {
169             this->m_parent->OnDbgHelpErr("SymInitialize", GetLastError(), 0);
170         }
171 
172         DWORD symOptions = this->pSGO();  // SymGetOptions
173         symOptions |= SYMOPT_LOAD_LINES;
174         symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS;
175         // SymSetOptions
176         symOptions = this->pSSO(symOptions);
177 
178         char buf[StackWalker::STACKWALK_MAX_NAMELEN] = {0};
179         if (this->pSGSP != nullptr) {
180             if (this->pSGSP(m_hProcess, buf, StackWalker::STACKWALK_MAX_NAMELEN) == FALSE) {
181                 this->m_parent->OnDbgHelpErr("SymGetSearchPath", GetLastError(), 0);
182             }
183         }
184         char szUserName[1024] = {0};
185         DWORD dwSize = 1024;
186         GetUserNameA(szUserName, &dwSize);
187         this->m_parent->OnSymInit(buf, symOptions, szUserName);
188 
189         return TRUE;
190     }
191 
192     StackWalker *m_parent;
193 
194     HMODULE m_hDbhHelp;
195     HANDLE m_hProcess;
196     LPSTR m_szSymPath;
197 
198     typedef struct _IMAGEHLP_MODULE64_V2 {
199         DWORD SizeOfStruct;  // set to sizeof(IMAGEHLP_MODULE64)
200         DWORD64 BaseOfImage;  // base load address of module
201         DWORD ImageSize;  // virtual size of the loaded module
202         DWORD TimeDateStamp;  // date/time stamp from pe header
203         DWORD CheckSum;  // checksum from the pe header
204         DWORD NumSyms;  // number of symbols in the symbol table
205         SYM_TYPE SymType;  // type of symbols loaded
206         CHAR ModuleName[32];  // module name
207         CHAR ImageName[256];  // image name
208         CHAR LoadedImageName[256];  // symbol file name
209     } IMAGEHLP_MODULE64_V2;
210 
211     // SymCleanup()
212     typedef BOOL(__stdcall *tSC)(IN HANDLE hProcess);
213     tSC pSC;
214 
215     // SymFunctionTableAccess64()
216     typedef PVOID(__stdcall *tSFTA)(HANDLE hProcess, DWORD64 AddrBase);
217     tSFTA pSFTA;
218 
219     // SymGetLineFromAddr64()
220     typedef BOOL(__stdcall *tSGLFA)(IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line);
221     tSGLFA pSGLFA;
222 
223     // SymGetModuleBase64()
224     typedef DWORD64(__stdcall *tSGMB)(IN HANDLE hProcess, IN DWORD64 dwAddr);
225     tSGMB pSGMB;
226 
227     // SymGetModuleInfo64()
228     typedef BOOL(__stdcall *tSGMI)(IN HANDLE hProcess, IN DWORD64 dwAddr, OUT IMAGEHLP_MODULE64_V2 *ModuleInfo);
229     tSGMI pSGMI;
230 
231     // SymGetOptions()
232     typedef DWORD(__stdcall *tSGO)(VOID);
233     tSGO pSGO;
234 
235     // SymGetSymFromAddr64()
236     typedef BOOL(__stdcall *tSGSFA)(IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PDWORD64 pdwDisplacement, OUT PIMAGEHLP_SYMBOL64 Symbol);
237     tSGSFA pSGSFA;
238 
239     // SymInitialize()
240     typedef BOOL(__stdcall *tSI)(IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess);
241     tSI pSI;
242 
243     // SymLoadModule64()
244     typedef DWORD64(__stdcall *tSLM)(IN HANDLE hProcess, IN HANDLE hFile, IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll);
245     tSLM pSLM;
246 
247     // SymSetOptions()
248     typedef DWORD(__stdcall *tSSO)(IN DWORD SymOptions);
249     tSSO pSSO;
250 
251     // StackWalk64()
252     typedef BOOL(__stdcall *tSW)(
253         DWORD MachineType,
254         HANDLE hProcess,
255         HANDLE hThread,
256         LPSTACKFRAME64 StackFrame,
257         PVOID ContextRecord,
258         PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
259         PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
260         PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
261         PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress);
262     tSW pSW;
263 
264     // UnDecorateSymbolName()
265     typedef DWORD(__stdcall WINAPI *tUDSN)(PCSTR DecoratedName, PSTR UnDecoratedName, DWORD UndecoratedLength, DWORD Flags);
266     tUDSN pUDSN;
267 
268     typedef BOOL(__stdcall WINAPI *tSGSP)(HANDLE hProcess, PSTR SearchPath, DWORD SearchPathLength);
269     tSGSP pSGSP;
270 
271 private:
272     // **************************************** ToolHelp32 ************************
273 #    define MAX_MODULE_NAME32 255
274 #    define TH32CS_SNAPMODULE 0x00000008
275 #    pragma pack(push, 8)
276     typedef struct tagMODULEENTRY32 {
277         DWORD dwSize;
278         DWORD th32ModuleID;  // This module
279         DWORD th32ProcessID;  // owning process
280         DWORD GlblcntUsage;  // Global usage count on the module
281         DWORD ProccntUsage;  // Module usage count in th32ProcessID's context
282         BYTE *modBaseAddr;  // Base address of module in th32ProcessID's context
283         DWORD modBaseSize;  // Size in bytes of module starting at modBaseAddr
284         HMODULE hModule;  // The hModule of this module in th32ProcessID's context
285         char szModule[MAX_MODULE_NAME32 + 1];
286         char szExePath[MAX_PATH];
287     } MODULEENTRY32;
288     typedef MODULEENTRY32 *PMODULEENTRY32;
289     typedef MODULEENTRY32 *LPMODULEENTRY32;
290 #    pragma pack(pop)
291 
GetModuleListTH32(HANDLE hProcess,DWORD pid)292     BOOL GetModuleListTH32(HANDLE hProcess, DWORD pid) {
293         // CreateToolhelp32Snapshot()
294         typedef HANDLE(__stdcall * tCT32S)(DWORD dwFlags, DWORD th32ProcessID);
295         // Module32First()
296         typedef BOOL(__stdcall * tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
297         // Module32Next()
298         typedef BOOL(__stdcall * tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
299 
300         // try both dlls...
301         const wchar_t *dllname[] = {TEXT("kernel32.dll"), TEXT("tlhelp32.dll")};
302         HINSTANCE hToolhelp = nullptr;
303         tCT32S pCT32S = nullptr;
304         tM32F pM32F = nullptr;
305         tM32N pM32N = nullptr;
306 
307         HANDLE hSnap;
308         MODULEENTRY32 me;
309         me.dwSize = sizeof(me);
310         BOOL keepGoing;
311         size_t i;
312 
313         for (i = 0; i < (sizeof(dllname) / sizeof(dllname[0])); i++) {
314             hToolhelp = LoadLibrary(dllname[i]);
315             if (hToolhelp == nullptr) {
316                 continue;
317             }
318             pCT32S = (tCT32S)GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");
319             pM32F = (tM32F)GetProcAddress(hToolhelp, "Module32First");
320             pM32N = (tM32N)GetProcAddress(hToolhelp, "Module32Next");
321             if ((pCT32S != nullptr) && (pM32F != nullptr) && (pM32N != nullptr)) {
322                 break;  // found the functions!
323             }
324             FreeLibrary(hToolhelp);
325             hToolhelp = nullptr;
326         }
327 
328         if (hToolhelp == nullptr) {
329             return FALSE;
330         }
331 
332         hSnap = pCT32S(TH32CS_SNAPMODULE, pid);
333         if (hSnap == (HANDLE)-1) {
334             return FALSE;
335         }
336 
337         keepGoing = !!pM32F(hSnap, &me);
338         int cnt = 0;
339         while (keepGoing) {
340             this->LoadModule(hProcess, me.szExePath, me.szModule, (DWORD64)me.modBaseAddr, me.modBaseSize);
341             cnt++;
342             keepGoing = !!pM32N(hSnap, &me);
343         }
344         CloseHandle(hSnap);
345         FreeLibrary(hToolhelp);
346         if (cnt <= 0) {
347             return FALSE;
348         }
349         return TRUE;
350     }  // GetModuleListTH32
351 
352     // **************************************** PSAPI ************************
353     typedef struct _MODULEINFO {
354         LPVOID lpBaseOfDll;
355         DWORD SizeOfImage;
356         LPVOID EntryPoint;
357     } MODULEINFO, *LPMODULEINFO;
358 
GetModuleListPSAPI(HANDLE hProcess)359     BOOL GetModuleListPSAPI(HANDLE hProcess) {
360         // EnumProcessModules()
361         typedef BOOL(__stdcall * tEPM)(HANDLE hProcess, HMODULE * lphModule, DWORD cb, LPDWORD lpcbNeeded);
362         // GetModuleFileNameEx()
363         typedef DWORD(__stdcall * tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize);
364         // GetModuleBaseName()
365         typedef DWORD(__stdcall * tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize);
366         // GetModuleInformation()
367         typedef BOOL(__stdcall * tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize);
368 
369         HINSTANCE hPsapi;
370         tEPM pEPM;
371         tGMFNE pGMFNE;
372         tGMBN pGMBN;
373         tGMI pGMI;
374 
375         DWORD i;
376         // ModuleEntry e;
377         DWORD cbNeeded;
378         MODULEINFO mi;
379         HMODULE *hMods = 0;
380         char *tt = nullptr;
381         char *tt2 = nullptr;
382         const SIZE_T TTBUFLEN = 8096;
383         int cnt = 0;
384 
385         hPsapi = LoadLibrary(TEXT("psapi.dll"));
386         if (hPsapi == nullptr) {
387             return FALSE;
388         }
389 
390         pEPM = (tEPM)GetProcAddress(hPsapi, "EnumProcessModules");
391         pGMFNE = (tGMFNE)GetProcAddress(hPsapi, "GetModuleFileNameExA");
392         pGMBN = (tGMFNE)GetProcAddress(hPsapi, "GetModuleBaseNameA");
393         pGMI = (tGMI)GetProcAddress(hPsapi, "GetModuleInformation");
394         if ((pEPM == nullptr) || (pGMFNE == nullptr) || (pGMBN == nullptr) || (pGMI == nullptr)) {
395             // we couldn't find all functions
396             FreeLibrary(hPsapi);
397             return FALSE;
398         }
399 
400         hMods = (HMODULE *)malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof HMODULE));
401         tt = (char *)malloc(sizeof(char) * TTBUFLEN);
402         tt2 = (char *)malloc(sizeof(char) * TTBUFLEN);
403         if (!(((hMods == nullptr) || (tt == nullptr) || (tt2 == nullptr)) || !pEPM(hProcess, hMods, TTBUFLEN, &cbNeeded) || cbNeeded > TTBUFLEN)) {
404             for (i = 0; i < cbNeeded / sizeof hMods[0]; i++) {
405                 // base address, size
406                 pGMI(hProcess, hMods[i], &mi, sizeof mi);
407                 // image file name
408                 tt[0] = 0;
409                 pGMFNE(hProcess, hMods[i], tt, TTBUFLEN);
410                 // module name
411                 tt2[0] = 0;
412                 pGMBN(hProcess, hMods[i], tt2, TTBUFLEN);
413 
414                 DWORD dwRes = this->LoadModule(hProcess, tt, tt2, (DWORD64)mi.lpBaseOfDll, mi.SizeOfImage);
415                 if (dwRes != ERROR_SUCCESS)
416                     this->m_parent->OnDbgHelpErr("LoadModule", dwRes, 0);
417                 cnt++;
418             }
419         }
420 
421         if (hPsapi != nullptr)
422             FreeLibrary(hPsapi);
423         if (tt2 != nullptr)
424             free(tt2);
425         if (tt != nullptr)
426             free(tt);
427         if (hMods != nullptr)
428             free(hMods);
429 
430         return cnt != 0;
431     }  // GetModuleListPSAPI
432 
LoadModule(HANDLE hProcess,LPCSTR img,LPCSTR mod,DWORD64 baseAddr,DWORD size)433     DWORD LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size) {
434         CHAR *szImg = _strdup(img);
435         CHAR *szMod = _strdup(mod);
436         DWORD result = ERROR_SUCCESS;
437         if ((szImg == nullptr) || (szMod == nullptr))
438             result = ERROR_NOT_ENOUGH_MEMORY;
439         else {
440             if (pSLM(hProcess, 0, szImg, szMod, baseAddr, size) == 0)
441                 result = GetLastError();
442         }
443         ULONGLONG fileVersion = 0;
444         if ((m_parent != nullptr) && (szImg != nullptr)) {
445             // try to retrive the file-version:
446             if ((this->m_parent->m_options & StackWalker::RetrieveFileVersion) != 0) {
447                 VS_FIXEDFILEINFO *fInfo = nullptr;
448                 DWORD dwHandle;
449                 DWORD dwSize = GetFileVersionInfoSizeA(szImg, &dwHandle);
450                 if (dwSize > 0) {
451                     LPVOID vData = malloc(dwSize);
452                     if (vData != nullptr) {
453                         if (GetFileVersionInfoA(szImg, dwHandle, dwSize, vData) != 0) {
454                             UINT len;
455                             TCHAR szSubBlock[] = TEXT("\\");
456                             if (VerQueryValue(vData, szSubBlock, (LPVOID *)&fInfo, &len) == 0)
457                                 fInfo = nullptr;
458                             else {
459                                 fileVersion = ((ULONGLONG)fInfo->dwFileVersionLS) + ((ULONGLONG)fInfo->dwFileVersionMS << 32);
460                             }
461                         }
462                         free(vData);
463                     }
464                 }
465             }
466 
467             // Retrive some additional-infos about the module
468             IMAGEHLP_MODULE64_V2 Module;
469             const char *szSymType = "-unknown-";
470             if (this->GetModuleInfo(hProcess, baseAddr, &Module) != FALSE) {
471                 switch (Module.SymType) {
472                     case SymNone:
473                         szSymType = "-nosymbols-";
474                         break;
475                     case SymCoff:
476                         szSymType = "COFF";
477                         break;
478                     case SymCv:
479                         szSymType = "CV";
480                         break;
481                     case SymPdb:
482                         szSymType = "PDB";
483                         break;
484                     case SymExport:
485                         szSymType = "-exported-";
486                         break;
487                     case SymDeferred:
488                         szSymType = "-deferred-";
489                         break;
490                     case SymSym:
491                         szSymType = "SYM";
492                         break;
493                     case 8:  // SymVirtual:
494                         szSymType = "Virtual";
495                         break;
496                     case 9:  // SymDia:
497                         szSymType = "DIA";
498                         break;
499                 }
500             }
501             this->m_parent->OnLoadModule(img, mod, baseAddr, size, result, szSymType, Module.LoadedImageName, fileVersion);
502         }
503         if (szImg != nullptr)
504             free(szImg);
505         if (szMod != nullptr)
506             free(szMod);
507         return result;
508     }
509 
510 public:
LoadModules(HANDLE hProcess,DWORD dwProcessId)511     BOOL LoadModules(HANDLE hProcess, DWORD dwProcessId) {
512         // first try toolhelp32
513         if (GetModuleListTH32(hProcess, dwProcessId)) {
514             return true;
515         }
516         // then try psapi
517         return GetModuleListPSAPI(hProcess);
518     }
519 
GetModuleInfo(HANDLE hProcess,DWORD64 baseAddr,IMAGEHLP_MODULE64_V2 * pModuleInfo)520     bool GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64_V2 *pModuleInfo) {
521         if (this->pSGMI == nullptr) {
522             SetLastError(ERROR_DLL_INIT_FAILED);
523             return FALSE;
524         }
525         pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);
526         void *pData = malloc(4096);  // reserve enough memory, so the bug in v6.3.5.1 does not lead to memory-overwrites...
527         if (pData == nullptr) {
528             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
529             return FALSE;
530         }
531         memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V2));
532         if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V2 *)pData) != FALSE) {
533             // only copy as much memory as is reserved...
534             memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V2));
535             pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);
536             free(pData);
537             return TRUE;
538         }
539         free(pData);
540         SetLastError(ERROR_DLL_INIT_FAILED);
541         return FALSE;
542     }
543 };
544 
545 // #############################################################
StackWalker(DWORD dwProcessId,HANDLE hProcess)546 StackWalker::StackWalker(DWORD dwProcessId, HANDLE hProcess) {
547     this->m_options = OptionsAll;
548     this->m_modulesLoaded = FALSE;
549     this->m_hProcess = hProcess;
550     this->m_sw = new StackWalkerInternal(this, this->m_hProcess);
551     this->m_dwProcessId = dwProcessId;
552     this->m_szSymPath = nullptr;
553 }
StackWalker(int options,LPCSTR szSymPath,DWORD dwProcessId,HANDLE hProcess)554 StackWalker::StackWalker(int options, LPCSTR szSymPath, DWORD dwProcessId, HANDLE hProcess) {
555     this->m_options = options;
556     this->m_modulesLoaded = FALSE;
557     this->m_hProcess = hProcess;
558     this->m_sw = new StackWalkerInternal(this, this->m_hProcess);
559     this->m_dwProcessId = dwProcessId;
560     if (szSymPath != nullptr) {
561         this->m_szSymPath = _strdup(szSymPath);
562         this->m_options |= SymBuildPath;
563     } else {
564         this->m_szSymPath = nullptr;
565     }
566 }
567 
~StackWalker()568 StackWalker::~StackWalker() {
569     if (m_szSymPath != nullptr) {
570         free(m_szSymPath);
571     }
572     m_szSymPath = nullptr;
573     if (this->m_sw != nullptr) {
574         delete this->m_sw;
575     }
576     this->m_sw = nullptr;
577 }
578 
LoadModules()579 BOOL StackWalker::LoadModules() {
580     if (this->m_sw == nullptr) {
581         SetLastError(ERROR_DLL_INIT_FAILED);
582         return FALSE;
583     }
584     if (m_modulesLoaded != FALSE) {
585         return TRUE;
586     }
587 
588     // Build the sym-path:
589     char *szSymPath = nullptr;
590     if ((this->m_options & SymBuildPath) != 0) {
591         const size_t nSymPathLen = 4096;
592         szSymPath = (char *)malloc(nSymPathLen);
593         if (szSymPath == nullptr) {
594             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
595             return FALSE;
596         }
597         szSymPath[0] = 0;
598         // Now first add the (optional) provided sympath:
599         if (this->m_szSymPath != nullptr) {
600             strcat_s(szSymPath, nSymPathLen, this->m_szSymPath);
601             strcat_s(szSymPath, nSymPathLen, ";");
602         }
603 
604         strcat_s(szSymPath, nSymPathLen, ".;");
605 
606         const size_t nTempLen = 1024;
607         char szTemp[nTempLen];
608         // Now add the current folder:
609         if (GetCurrentDirectoryA(nTempLen, szTemp) > 0) {
610             szTemp[nTempLen - 1] = 0;
611             strcat_s(szSymPath, nSymPathLen, szTemp);
612             strcat_s(szSymPath, nSymPathLen, ";");
613         }
614 
615         // Now add the path for the main-module:
616         if (GetModuleFileNameA(nullptr, szTemp, nTempLen) > 0) {
617             szTemp[nTempLen - 1] = 0;
618             for (char *p = (szTemp + strlen(szTemp) - 1); p >= szTemp; --p) {
619                 // locate the rightmost path separator
620                 if ((*p == '\\') || (*p == '/') || (*p == ':')) {
621                     *p = 0;
622                     break;
623                 }
624             }  // for (search for path separator...)
625             if (strlen(szTemp) > 0) {
626                 strcat_s(szSymPath, nSymPathLen, szTemp);
627                 strcat_s(szSymPath, nSymPathLen, ";");
628             }
629         }
630         if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0) {
631             szTemp[nTempLen - 1] = 0;
632             strcat_s(szSymPath, nSymPathLen, szTemp);
633             strcat_s(szSymPath, nSymPathLen, ";");
634         }
635         if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0) {
636             szTemp[nTempLen - 1] = 0;
637             strcat_s(szSymPath, nSymPathLen, szTemp);
638             strcat_s(szSymPath, nSymPathLen, ";");
639         }
640         if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0) {
641             szTemp[nTempLen - 1] = 0;
642             strcat_s(szSymPath, nSymPathLen, szTemp);
643             strcat_s(szSymPath, nSymPathLen, ";");
644             // also add the "system32"-folder:
645             strcat_s(szTemp, nTempLen, "\\system32");
646             strcat_s(szSymPath, nSymPathLen, szTemp);
647             strcat_s(szSymPath, nSymPathLen, ";");
648         }
649 
650         if ((this->m_options & SymBuildPath) != 0) {
651             if (GetEnvironmentVariableA("SYSTEMDRIVE", szTemp, nTempLen) > 0) {
652                 szTemp[nTempLen - 1] = 0;
653                 strcat_s(szSymPath, nSymPathLen, "SRV*");
654                 strcat_s(szSymPath, nSymPathLen, szTemp);
655                 strcat_s(szSymPath, nSymPathLen, "\\websymbols");
656                 strcat_s(szSymPath, nSymPathLen, "*http://msdl.microsoft.com/download/symbols;");
657             } else {
658                 strcat_s(szSymPath, nSymPathLen, "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols;");
659             }
660         }
661     }
662 
663     // First Init the whole stuff...
664     BOOL bRet = this->m_sw->Init(szSymPath);
665     if (szSymPath != nullptr) {
666         free(szSymPath);
667     }
668     szSymPath = nullptr;
669     if (bRet == FALSE) {
670         this->OnDbgHelpErr("Error while initializing dbghelp.dll", 0, 0);
671         SetLastError(ERROR_DLL_INIT_FAILED);
672         return FALSE;
673     }
674 
675     bRet = this->m_sw->LoadModules(this->m_hProcess, this->m_dwProcessId);
676     if (bRet != FALSE) {
677         m_modulesLoaded = TRUE;
678     }
679     return bRet;
680 }
681 
682 // The following is used to pass the "userData"-Pointer to the user-provided readMemoryFunction
683 // This has to be done due to a problem with the "hProcess"-parameter in x64...
684 // Because this class is in no case multi-threading-enabled (because of the limitations
685 // of dbghelp.dll) it is "safe" to use a static-variable
686 static StackWalker::PReadProcessMemoryRoutine s_readMemoryFunction = nullptr;
687 static LPVOID s_readMemoryFunction_UserData = nullptr;
688 
ShowCallstack(HANDLE hThread,const CONTEXT * context,PReadProcessMemoryRoutine readMemoryFunction,LPVOID pUserData)689 bool StackWalker::ShowCallstack(HANDLE hThread, const CONTEXT *context, PReadProcessMemoryRoutine readMemoryFunction, LPVOID pUserData) {
690     CONTEXT c;
691     CallstackEntry csEntry;
692     IMAGEHLP_SYMBOL64 *pSym = nullptr;
693     StackWalkerInternal::IMAGEHLP_MODULE64_V2 Module;
694     IMAGEHLP_LINE64 Line;
695     int frameNum;
696 
697     if (m_modulesLoaded == FALSE)
698         this->LoadModules();  // ignore the result...
699 
700     if (this->m_sw->m_hDbhHelp == nullptr) {
701         SetLastError(ERROR_DLL_INIT_FAILED);
702         return FALSE;
703     }
704 
705     s_readMemoryFunction = readMemoryFunction;
706     s_readMemoryFunction_UserData = pUserData;
707 
708     c = *context;
709 
710     // init STACKFRAME for first call
711     STACKFRAME64 s;  // in/out stackframe
712     memset(&s, 0, sizeof(s));
713     DWORD imageType;
714 #    ifdef _M_IX86
715     // normally, call ImageNtHeader() and use machine info from PE header
716     imageType = IMAGE_FILE_MACHINE_I386;
717     s.AddrPC.Offset = c.Eip;
718     s.AddrPC.Mode = AddrModeFlat;
719     s.AddrFrame.Offset = c.Ebp;
720     s.AddrFrame.Mode = AddrModeFlat;
721     s.AddrStack.Offset = c.Esp;
722     s.AddrStack.Mode = AddrModeFlat;
723 #    elif _M_X64
724     imageType = IMAGE_FILE_MACHINE_AMD64;
725     s.AddrPC.Offset = c.Rip;
726     s.AddrPC.Mode = AddrModeFlat;
727     s.AddrFrame.Offset = c.Rsp;
728     s.AddrFrame.Mode = AddrModeFlat;
729     s.AddrStack.Offset = c.Rsp;
730     s.AddrStack.Mode = AddrModeFlat;
731 #    elif _M_IA64
732     imageType = IMAGE_FILE_MACHINE_IA64;
733     s.AddrPC.Offset = c.StIIP;
734     s.AddrPC.Mode = AddrModeFlat;
735     s.AddrFrame.Offset = c.IntSp;
736     s.AddrFrame.Mode = AddrModeFlat;
737     s.AddrBStore.Offset = c.RsBSP;
738     s.AddrBStore.Mode = AddrModeFlat;
739     s.AddrStack.Offset = c.IntSp;
740     s.AddrStack.Mode = AddrModeFlat;
741 #    else
742 #        error "Platform not supported!"
743 #    endif
744 
745     pSym = (IMAGEHLP_SYMBOL64 *)malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
746     if (!pSym)
747         goto cleanup;  // not enough memory...
748     memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
749     pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
750     pSym->MaxNameLength = STACKWALK_MAX_NAMELEN;
751 
752     memset(&Line, 0, sizeof(Line));
753     Line.SizeOfStruct = sizeof(Line);
754 
755     memset(&Module, 0, sizeof(Module));
756     Module.SizeOfStruct = sizeof(Module);
757 
758     for (frameNum = 0;; ++frameNum) {
759         // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64())
760         // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can
761         // assume that either you are done, or that the stack is so hosed that the next
762         // deeper frame could not be found.
763         // CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386!
764         if (!this->m_sw->pSW(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem, this->m_sw->pSFTA, this->m_sw->pSGMB, nullptr)) {
765             this->OnDbgHelpErr("StackWalk64", GetLastError(), s.AddrPC.Offset);
766             break;
767         }
768 
769         csEntry.offset = s.AddrPC.Offset;
770         csEntry.name[0] = 0;
771         csEntry.undName[0] = 0;
772         csEntry.undFullName[0] = 0;
773         csEntry.offsetFromSmybol = 0;
774         csEntry.offsetFromLine = 0;
775         csEntry.lineFileName[0] = 0;
776         csEntry.lineNumber = 0;
777         csEntry.loadedImageName[0] = 0;
778         csEntry.moduleName[0] = 0;
779         if (s.AddrPC.Offset == s.AddrReturn.Offset) {
780             this->OnDbgHelpErr("StackWalk64-Endless-Callstack!", 0, s.AddrPC.Offset);
781             break;
782         }
783         if (s.AddrPC.Offset != 0) {
784             // we seem to have a valid PC
785             // show procedure info (SymGetSymFromAddr64())
786             if (this->m_sw->pSGSFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol), pSym) != FALSE) {
787                 // TODO: Mache dies sicher...!
788                 strcpy_s(csEntry.name, pSym->Name);
789                 // UnDecorateSymbolName()
790                 this->m_sw->pUDSN(pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY);
791                 this->m_sw->pUDSN(pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE);
792             } else {
793                 this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), s.AddrPC.Offset);
794             }
795 
796             // show line number info, NT5.0-method (SymGetLineFromAddr64())
797             if (this->m_sw->pSGLFA != nullptr) {  // yes, we have SymGetLineFromAddr64()
798                 if (this->m_sw->pSGLFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromLine), &Line) != FALSE) {
799                     csEntry.lineNumber = Line.LineNumber;
800                     // TODO: Mache dies sicher...!
801                     strcpy_s(csEntry.lineFileName, Line.FileName);
802                 } else {
803                     this->OnDbgHelpErr("SymGetLineFromAddr64", GetLastError(), s.AddrPC.Offset);
804                 }
805             }  // yes, we have SymGetLineFromAddr64()
806 
807             // show module info (SymGetModuleInfo64())
808             if (this->m_sw->GetModuleInfo(this->m_hProcess, s.AddrPC.Offset, &Module) != FALSE) {  // got module info OK
809                 switch (Module.SymType) {
810                     case SymNone:
811                         csEntry.symTypeString = "-nosymbols-";
812                         break;
813                     case SymCoff:
814                         csEntry.symTypeString = "COFF";
815                         break;
816                     case SymCv:
817                         csEntry.symTypeString = "CV";
818                         break;
819                     case SymPdb:
820                         csEntry.symTypeString = "PDB";
821                         break;
822                     case SymExport:
823                         csEntry.symTypeString = "-exported-";
824                         break;
825                     case SymDeferred:
826                         csEntry.symTypeString = "-deferred-";
827                         break;
828                     case SymSym:
829                         csEntry.symTypeString = "SYM";
830                         break;
831 #    if API_VERSION_NUMBER >= 9
832                     case SymDia:
833                         csEntry.symTypeString = "DIA";
834                         break;
835 #    endif
836                     case 8:  // SymVirtual:
837                         csEntry.symTypeString = "Virtual";
838                         break;
839                     default:
840                         //_snprintf( ty, sizeof ty, "symtype=%ld", (long) Module.SymType );
841                         csEntry.symTypeString = nullptr;
842                         break;
843                 }
844 
845                 // TODO: Mache dies sicher...!
846                 strcpy_s(csEntry.moduleName, Module.ModuleName);
847                 csEntry.baseOfImage = Module.BaseOfImage;
848                 strcpy_s(csEntry.loadedImageName, Module.LoadedImageName);
849             }  // got module info OK
850             else {
851                 this->OnDbgHelpErr("SymGetModuleInfo64", GetLastError(), s.AddrPC.Offset);
852             }
853         }  // we seem to have a valid PC
854 
855         CallstackEntryType et = nextEntry;
856         if (frameNum == 0)
857             et = firstEntry;
858         this->OnCallstackEntry(et, csEntry);
859 
860         if (s.AddrReturn.Offset == 0) {
861             this->OnCallstackEntry(lastEntry, csEntry);
862             SetLastError(ERROR_SUCCESS);
863             break;
864         }
865     }  // for ( frameNum )
866 
867 cleanup:
868     if (pSym)
869         free(pSym);
870 
871     if (context == nullptr)
872         ResumeThread(hThread);
873 
874     return TRUE;
875 }
876 
myReadProcMem(HANDLE hProcess,DWORD64 qwBaseAddress,PVOID lpBuffer,DWORD nSize,LPDWORD lpNumberOfBytesRead)877 BOOL __stdcall StackWalker::myReadProcMem(
878     HANDLE hProcess,
879     DWORD64 qwBaseAddress,
880     PVOID lpBuffer,
881     DWORD nSize,
882     LPDWORD lpNumberOfBytesRead) {
883     if (s_readMemoryFunction == nullptr) {
884         SIZE_T st;
885         BOOL bRet = ReadProcessMemory(hProcess, (LPVOID)qwBaseAddress, lpBuffer, nSize, &st);
886         *lpNumberOfBytesRead = (DWORD)st;
887         return bRet;
888     } else {
889         return s_readMemoryFunction(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, s_readMemoryFunction_UserData);
890     }
891 }
892 
OnLoadModule(LPCSTR img,LPCSTR mod,DWORD64 baseAddr,DWORD size,DWORD result,LPCSTR symType,LPCSTR pdbName,ULONGLONG fileVersion)893 void StackWalker::OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion) {
894     libraries[(LPVOID)baseAddr] = QString(img);
895 }
896 
OnCallstackEntry(CallstackEntryType eType,CallstackEntry & entry)897 void StackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) {
898     QMap<LPVOID, QString>::iterator it = libraries.begin();
899     while (entry.offset > (DWORD)it.key() && it != libraries.end()) {
900         it++;
901     }
902     it--;
903     QString library = it.value();
904     DWORD libAddr = (DWORD)it.key();
905     QString binaryName = library.split("\\").last();
906     library.replace("dll", "map");
907     library.replace("exe", "map");
908     QFile file(library);
909     if (!file.exists()) {
910         QString str;
911         buffer += str.sprintf("%p", entry.offset) + ": (" + binaryName + ") " + entry.name + "\n";
912         return;
913     }
914     file.open(QIODevice::ReadOnly);
915     DWORD preferredAddress = 0x10000000;
916 
917     QString name;
918     DWORD offset = 0;
919     while (!file.readLine().contains("Rva+Base") && !file.atEnd())
920         ;
921     file.readLine();
922     while (!file.atEnd()) {
923         QString line = file.readLine();
924         QStringList list = line.split(" ", QString::SkipEmptyParts);
925         if (list.size() > 3) {
926             DWORD addr = list[2].toInt(nullptr, 16);
927             DWORD actualAddress = preferredAddress + entry.offset - libAddr;
928             if (actualAddress < addr) {
929                 break;
930             }
931             name = list[1];
932             offset = actualAddress - addr;
933         }
934     }
935     if (file.atEnd()) {
936         name = "Some static function";
937     }
938     file.close();
939     QString str;
940     buffer += str.sprintf("%p", entry.offset) + ": (" + binaryName + ") " + name.replace("@", ":") + " + " + str.sprintf("%p", offset) + "\n";
941 }
942 
OnDbgHelpErr(LPCSTR szFuncName,DWORD gle,DWORD64 addr)943 void StackWalker::OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) {
944     // CHAR buffer[STACKWALK_MAX_NAMELEN];
945     //_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "ERROR: %s, GetLastError: %d (Address: %p)\n", szFuncName, gle, (LPVOID) addr);
946     // OnOutput("--");
947 }
948 
OnSymInit(LPCSTR szSearchPath,DWORD symOptions,LPCSTR szUserName)949 void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName) {
950     CHAR buffer[STACKWALK_MAX_NAMELEN];
951     _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n", szSearchPath, symOptions, szUserName);
952     // Also display the OS-version
953     OSVERSIONINFOEXA ver;
954     ZeroMemory(&ver, sizeof(OSVERSIONINFOEXA));
955     ver.dwOSVersionInfoSize = sizeof(ver);
956     if (GetVersionExA((OSVERSIONINFOA *)&ver) != FALSE) {
957         _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n", ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber, ver.szCSDVersion, ver.wSuiteMask, ver.wProductType);
958         OnOutput(buffer);
959     }
960 }
961 
OnOutput(LPCSTR _buffer)962 void StackWalker::OnOutput(LPCSTR _buffer) {
963     buffer.append(_buffer);
964 }
965 
966 }  // namespace U2
967 
968 #endif
969