Last active
October 22, 2024 01:26
-
-
Save mmozeiko/22ad8ac98a4391ba185d313bb11d02b9 to your computer and use it in GitHub Desktop.
dumps callstack of all threads in a single process
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <windows.h> | |
#include <dbghelp.h> | |
#include <tlhelp32.h> | |
#include <stdlib.h> | |
#include <stdio.h> | |
#pragma comment (lib, "dbghelp") | |
#pragma comment (lib, "advapi32") | |
int main(int argc, char* argv[]) | |
{ | |
if (argc != 2) | |
{ | |
printf("Usage: %s process_id\n", argv[0]); | |
return 1; | |
} | |
// enable debug privilege, to be able to call SuspendThread on other processes | |
HANDLE token; | |
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token)) | |
{ | |
TOKEN_PRIVILEGES tp = | |
{ | |
.PrivilegeCount = 1, | |
.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED, | |
}; | |
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid) || | |
!AdjustTokenPrivileges(token, FALSE, &tp, 0, NULL, 0)) | |
{ | |
printf("ERROR: cannot enable debug privilege!\n"); | |
return 1; | |
} | |
CloseHandle(token); | |
} | |
DWORD processId = atoi(argv[1]); | |
HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId); | |
BOOL isWow64 = FALSE; | |
#if defined(_M_AMD64) | |
IsWow64Process(process, &isWow64); | |
#endif | |
SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_EXACT_SYMBOLS | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_UNDNAME); | |
// TRUE here means that dbghelp will enumerate modules for the process | |
// and initialize info needed to lookup addresses & symbols from this process | |
// if you want to lookup info from modules that are loaded later you will need to | |
// either manually refresh module list with SymRefreshModuleList, or implement custom callbacks | |
// for StackWalk64 call that loads module info (SymLoadModuleEx) for addresses it needs | |
if (!SymInitializeW(process, NULL, TRUE)) | |
{ | |
printf("ERROR: cannot initialize dbghelp\n"); | |
} | |
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); | |
if (snapshot) | |
{ | |
THREADENTRY32 entry = | |
{ | |
.dwSize = sizeof(entry), | |
}; | |
if (Thread32First(snapshot, &entry)) | |
{ | |
do | |
{ | |
if (entry.th32OwnerProcessID != processId) | |
{ | |
continue; | |
} | |
DWORD threadId = entry.th32ThreadID; | |
printf("Thread %lu\n", threadId); | |
HANDLE thread = OpenThread(THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT, FALSE, threadId); | |
if (thread) | |
{ | |
if (isWow64 ? Wow64SuspendThread(thread) : SuspendThread(thread)) | |
{ | |
STACKFRAME64 frame = | |
{ | |
.AddrPC.Mode = AddrModeFlat, | |
.AddrStack.Mode = AddrModeFlat, | |
.AddrFrame.Mode = AddrModeFlat, | |
}; | |
DWORD machine = IMAGE_FILE_MACHINE_UNKNOWN; | |
CONTEXT context; | |
LPVOID contextPtr = NULL; | |
#if defined(_M_ARM64) | |
machine = IMAGE_FILE_MACHINE_ARM64; | |
context.ContextFlags = CONTEXT_CONTROL; | |
if (GetThreadContext(thread, &context)) | |
{ | |
frame.AddrPC.Offset = context.Pc; | |
frame.AddrStack.Offset = context.Sp; | |
frame.AddrFrame.Offset = context.Fp; | |
contextPtr = &context; | |
} | |
#elif defined(_M_AMD64) | |
machine = isWow64 ? IMAGE_FILE_MACHINE_I386 : IMAGE_FILE_MACHINE_AMD64; | |
WOW64_CONTEXT contextWow64; | |
if (isWow64) | |
{ | |
contextWow64.ContextFlags = WOW64_CONTEXT_CONTROL; | |
if (Wow64GetThreadContext(thread, &contextWow64)) | |
{ | |
frame.AddrPC.Offset = contextWow64.Eip; | |
frame.AddrStack.Offset = contextWow64.Esp; | |
frame.AddrFrame.Offset = contextWow64.Ebp; | |
contextPtr = &contextWow64; | |
} | |
} | |
else | |
{ | |
context.ContextFlags = CONTEXT_CONTROL; | |
if (GetThreadContext(thread, &context)) | |
{ | |
frame.AddrPC.Offset = context.Rip; | |
frame.AddrStack.Offset = context.Rsp; | |
contextPtr = &context; | |
} | |
} | |
#elif defined(_M_IX86) | |
machine = IMAGE_FILE_MACHINE_I386; | |
context.ContextFlags = CONTEXT_CONTROL; | |
if (GetThreadContext(thread, &context)) | |
{ | |
frame.AddrPC.Offset = context.Eip; | |
frame.AddrStack.Offset = context.Esp; | |
frame.AddrFrame.Offset = context.Ebp; | |
contextPtr = &context; | |
} | |
#else | |
#error not unsupported | |
#endif | |
if (contextPtr) | |
{ | |
for (int idx = 0; /* empty */; idx++) | |
{ | |
if (!StackWalk64(machine, process, thread, &frame, contextPtr, 0, &SymFunctionTableAccess64, &SymGetModuleBase64, 0)) | |
{ | |
break; | |
} | |
if (frame.AddrPC.Offset == 0) | |
{ | |
break; | |
} | |
printf("%d. %016I64x\n", idx, frame.AddrPC.Offset); | |
} | |
} | |
else | |
{ | |
printf("[error] cannot get thread context\n"); | |
} | |
ResumeThread(thread); | |
} | |
else | |
{ | |
printf("[error] cannot suspend thread\n"); | |
} | |
CloseHandle(thread); | |
} | |
else | |
{ | |
printf("[error] cannot open thread\n"); | |
} | |
} | |
while (Thread32Next(snapshot, &entry)); | |
} | |
CloseHandle(snapshot); | |
} | |
SymCleanup(process); | |
CloseHandle(process); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment