As being taking charge of CER project, reviewing some basic knowledge of callstack should be useful. Here is a demo code used to walk callstack when an unhandled exception is raised. In fact, the demo code of StackWalk1 should be able to walk in a common case. It’s just difficult to obtain a thread-related CONTEXT. Win32 API GetThreadContext will only work as expected when the thread being inspected is suppended.
[principle]
I think the principle is supposed to be easy enough, just using ebp register, and traverse the list that ebp represents.
[Note]
The demo code is CPU specific. I am working on an X86 duel core CPU.
The demo code should be running with out FPO (Frame Pointer Omit) optimization.
[Shortcoming]
The callstack dumped out is shown as raw linear address.
#include "stdafx.h"
#include "windows.h"
#include "windows.h"
LPTOP_LEVEL_EXCEPTION_FILTER previousFilter;
void StackWalk1(PCONTEXT pContext);
LONG WINAPI MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo);
LONG WINAPI MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo);
int _tmain(int argc, _TCHAR* argv[])
{
previousFilter = SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
{
previousFilter = SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
// cause a EXCEPTION_FLT_DIVIDE_BY_ZERO
int a = 0;
int b = 1 / a;
return 0;
}
LONG WINAPI MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo)
{
PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
_tprintf(_T("Exception code: [%08x]\n"),
pExceptionRecord->ExceptionCode);
int a = 0;
int b = 1 / a;
return 0;
}
LONG WINAPI MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo)
{
PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
_tprintf(_T("Exception code: [%08x]\n"),
pExceptionRecord->ExceptionCode);
StackWalk1(pExceptionInfo->ContextRecord);
if (previousFilter)
return previousFilter(pExceptionInfo); // bypass to the user-installed handler
else
return EXCEPTION_CONTINUE_SEARCH; // transfer the control to operating system
}
//
// stack walker version 1, without symbol information
// should not be FPO optimization
//
void StackWalk1(PCONTEXT pContext)
{
_tprintf(_T("Callstack:\n"));
_tprintf(_T("Address Frame Module\n"));
return previousFilter(pExceptionInfo); // bypass to the user-installed handler
else
return EXCEPTION_CONTINUE_SEARCH; // transfer the control to operating system
}
//
// stack walker version 1, without symbol information
// should not be FPO optimization
//
void StackWalk1(PCONTEXT pContext)
{
_tprintf(_T("Callstack:\n"));
_tprintf(_T("Address Frame Module\n"));
// instruction pointer (EIP) at the time of the exception
DWORD pc = pContext->Eip;
PDWORD pFrame, pPrevFrame;
DWORD pc = pContext->Eip;
PDWORD pFrame, pPrevFrame;
pFrame = (PDWORD)pContext->Ebp;
do
{
TCHAR szModule[MAX_PATH] = { 0 };
{
TCHAR szModule[MAX_PATH] = { 0 };
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery((PVOID)pc, &mbi, sizeof(mbi));
GetModuleFileName((HMODULE)mbi.AllocationBase, szModule, sizeof(szModule));
VirtualQuery((PVOID)pc, &mbi, sizeof(mbi));
GetModuleFileName((HMODULE)mbi.AllocationBase, szModule, sizeof(szModule));
_tprintf(_T("%08x %08x %s\n"), pc, pFrame, szModule);
// push ebp
// mov ebp, esp, looking at the following snapshot of a stack
// [0x…]return address pContext->Ebp[1]
// [0x…]ebp pContext->Ebp[0]
pc = pFrame[1];
pPrevFrame = pFrame;
pFrame = (PDWORD)pFrame[0];
// mov ebp, esp, looking at the following snapshot of a stack
// [0x…]return address pContext->Ebp[1]
// [0x…]ebp pContext->Ebp[0]
pc = pFrame[1];
pPrevFrame = pFrame;
pFrame = (PDWORD)pFrame[0];
// check if the ebp is still valid, otherwise stop walking
if ((DWORD)pFrame & 3)
break;
if (pFrame <= pPrevFrame)
break;
if ((DWORD)pFrame & 3)
break;
if (pFrame <= pPrevFrame)
break;
// check if the address pointed to by ebp is committed
if (IsBadWritePtr(pFrame, sizeof(PVOID) * 2))
break;
if (IsBadWritePtr(pFrame, sizeof(PVOID) * 2))
break;
} while (1);
// when to stop the walking?
// when it encounters a frame address that doesn’t look valid
}
// when to stop the walking?
// when it encounters a frame address that doesn’t look valid
}
[Result/Output]
Exception code: [c0000094]
Callstack:
Address Frame Module
00bb1414 0016f8a0 d:\Visual Studio 2008\VectoredExceptDemo\Debug\MyStackwalker.exe
00bb25b8 0016f8f0 d:\Visual Studio 2008\VectoredExceptDemo\Debug\MyStackwalker.exe
00bb23ff 0016f8f8 d:\Visual Studio 2008\VectoredExceptDemo\Debug\MyStackwalker.exe
76d84911 0016f904 C:\Windows\system32\kernel32.dll
76e5e4b6 0016f944 C:\Windows\system32\ntdll.dll
76e5e489 0016f95c C:\Windows\system32\ntdll.dll
Callstack:
Address Frame Module
00bb1414 0016f8a0 d:\Visual Studio 2008\VectoredExceptDemo\Debug\MyStackwalker.exe
00bb25b8 0016f8f0 d:\Visual Studio 2008\VectoredExceptDemo\Debug\MyStackwalker.exe
00bb23ff 0016f8f8 d:\Visual Studio 2008\VectoredExceptDemo\Debug\MyStackwalker.exe
76d84911 0016f904 C:\Windows\system32\kernel32.dll
76e5e4b6 0016f944 C:\Windows\system32\ntdll.dll
76e5e489 0016f95c C:\Windows\system32\ntdll.dll