How to get thread stack information on Windows?
Asked Answered
Q

5

8

I enumerate all threads in a process through the CreateToolhelp32Snapshot function. I would like to get some basic stack information for each thread. More specifically I would like to get stack bottom address and if possible I would like to get current stack top address. Basically this is the information displayed with the ~*k command in WinDbg. So how can I obtain the stack information from the thread's ID or HANDLE?

Quackery answered 12/10, 2010 at 19:42 Comment(0)
S
10

(Definitions can be found here.)

To get stack boundaries:

THREAD_BASIC_INFORMATION basicInfo;
NT_TIB tib;

// Get TEB address
NtQueryInformationThread(YOUR_THREAD_HANDLE, ThreadBasicInformation, &basicInfo, sizeof(THREAD_BASIC_INFORMATION), NULL);
// Read TIB
NtReadVirtualMemory(YOUR_PROCESS_HANDLE, basicInfo.TebBaseAddress, &tib, sizeof(NT_TIB), NULL);
// Check tib.StackBase and tib.StackLimit

To get the value of esp, simply use GetThreadContext.

Sussna answered 13/10, 2010 at 8:36 Comment(1)
Thanks wj32! I will have a look at the link you provided.Quackery
R
2

An easier way without having to involve the Windows Driver Kit is as so:

NT_TIB* tib = (NT_TIB*)__readfsdword(0x18);
size_t* stackBottom = (size_t*)tib->StackLimit;
size_t* stackTop = (size_t*)tib->StackBase;
Ribal answered 6/7, 2012 at 16:23 Comment(1)
somehow I'd expect, threads are to be enumerated to get all stacks, right?Payroll
L
2

__readfsdword() works only for the current thread. So, the variant with NtQueryInformationThread() is more flexible.

Added some declarations which are missed in ntdll.h:

typedef enum _THREADINFOCLASS {
    ThreadBasicInformation = 0,
} THREADINFOCLASS;

typedef LONG KPRIORITY;

typedef struct _CLIENT_ID {
    HANDLE UniqueProcess;
    HANDLE UniqueThread;
} CLIENT_ID;
typedef CLIENT_ID *PCLIENT_ID;

typedef struct _THREAD_BASIC_INFORMATION
{
  NTSTATUS                ExitStatus;
  PVOID                   TebBaseAddress;
  CLIENT_ID               ClientId;
  KAFFINITY               AffinityMask;
  KPRIORITY               Priority;
  KPRIORITY               BasePriority;
} THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION;
Love answered 24/7, 2013 at 13:30 Comment(1)
how to deploy for a set of threads?Payroll
P
1

Here's an easy way for the current thread (portable Win32 x86/x64 version):

#include <intrin.h>

NT_TIB* getTIB() {
#ifdef _M_IX86
  return (NT_TIB*)__readfsdword(0x18);
#elif _M_AMD64
  return (NT_TIB*)__readgsqword(0x30);
#else
#error unsupported architecture
#endif
}

  NT_TIB* tib = getTIB();
  void* stackBase = tib->StackBase;
  void* stackLimit = tib->StackLimit;

Note: stackLimit < stackBase (as stack grows downwards).

For more details refer to Win32 TIB.

Parrotfish answered 8/4, 2021 at 14:13 Comment(1)
if you have more than one thread?Payroll
F
0

As fas as I know, Toolhelp works by making a copy of basic information on heaps, modules, processes and threads. This does not include the TEB block that contains the stack bottom address. I think you need to use another API, the debugger engine API, which offers functions to examine the stacks

Farceuse answered 13/10, 2010 at 8:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.