Skip to content

Instantly share code, notes, and snippets.

@mmozeiko
Last active October 22, 2024 01:26
Show Gist options
  • Save mmozeiko/22ad8ac98a4391ba185d313bb11d02b9 to your computer and use it in GitHub Desktop.
Save mmozeiko/22ad8ac98a4391ba185d313bb11d02b9 to your computer and use it in GitHub Desktop.
dumps callstack of all threads in a single process
#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