Introduction
This post is a \”how-to\” for writing Win32 code for performing a stackwalk on both x86 and x64 architectures and along the way we will learn the theory behind some of the concepts associated with the stack. In fact this is a quick note created for myself when I started working on designing a runtime anomaly detection using call stack as the telemetry for the data. I hope this will save a lot of googling!
Check out following posts to take a deep dive into stack internals.
contexts & Wow64
Windows Wow64 API set provides a lot functionalities to perform various operations on x86 address space from an x64 space. The function IsWow64Process is very useful in identifying 32 bit processes running on x64 system. Process level operations can then be performed with ease and more flexibility, a decision can be made by calling this specific API and use appropriate x86 data structures to do something interesting (whatever that is!) from an x64 process.
Sometimes we need to capture a context of the running thread in events like error handling, state analysis to do something interesting like stack walk or even some of the win32 APIs expect contexts to be passed as input. For starters, a context is a snapshot of the CPU at a given time, context can be seen as a structure whose members are all available registers in the CPU and other architectural elements that make up the \”state\” of the system. Following two images show two types of CONTEXT structures supported by Windows, the _WOW64_CONTEXT structure reflects x86 system and _CONTEXT structure reflects x64 one. We can pick one based on the architecture we are targeting in our application. It is evident from the images that both context structures are very much closely tied to its internal architectural details, hence these cannot be used interchangeably.
Following APIs will help us to capture the context (not limited to this set):
- GetThreadContext
- Wow64GetThreadContext
- RtlCaptureContext
stack walking using win32 api
basic idea
I am not going to reiterate the working of stack and stack walking or tracing and its uses as I have already covered it in my previous posts and it has been a recurring theme in my recent posts.
If we are planning to do a stack trace then we don\’t have to cook up our own code, Windows provides us with two elegant APIs to do it.
- StackWalk64
- StackWalk
- ..and a hidden gem RtlWalkFrameChain (not going to be covered in this post, explore this on your own 😉 )
Why it is always a good idea to use above APIs is we don\’t have to worry about any architectural details of stack implementation, the API will take care of everything for us. These APIs StackWalk64 and StackWalk need to be enclosed in an infinite loop, this functions will fail when they are done walking the last frame on the call stack. The code structure is shown below:
do
{
if(StackWalkXX(...))
{
//Execution comes here after walking the last frame in the chain
break;
}
//do something interesting with the data obtained from stack walk
}while(1)
Note: Each iteration represents a stack frame in the call chain
Interestingly the output from the API is a \”stackframe\” represented by following structures:
- STACKFRAME
- STACKFRAME64
Refer to the documentation to learn more about the members and types.
Initialization of STACKFRAME/STACKFRAME64
One very important step is initialization of STACKFRAME structures before calling the APIs. The context information retrieved from API like GetThreadContext needs to be used to initialize the internal members in STACKFRAME/STACKFRAME64 required for the functioning of StackWalk/StackWalk64 apis. Below images show the initialization step for x32 and x64 respectively.
x32 System
Few things to keep in mind while using it:
- The first parameter should be IMAGE_FILE_MACHINE_I386 (0x014c)
- The input stackframe (4th parameter) must be of the type LPSTACKFRAME
- The 7th parameter FunctionTableAccessRoutine should be NULL, this is required for 64 systems only.
- The 8th parameter should be a pointer to the function SymGetModuleBase
Example
x64 system
- The first parameter should be IMAGE_FILE_MACHINE_AMD64 (0x8664)
- The input stackframe (4th parameter) must be of the type LPSTACKFRAME64
- The 7th parameter FunctionTableAccessRoutine should be pointer to SymFunctionTableAccess64 function.
- The 8th parameter should be a pointer to the function SymGetModuleBase64
Example
code examples from web
Code Samples
https://cpp.hotexamples.com/examples/-/-/StackWalk64/cpp-stackwalk64-function-examples.html
Leave a Reply