-
-
Dissecting The Stack Part3 ------- Walking The Callstack
-
发表于: 2007-5-18 14:03 2615
-
Breaking The Secrets ----- Dessecting The Stack Part3
Walking The Callstack
Last time I cover much information about what call stack is, how to trace the callstack with Stack Frame.
It's enough, we've suffered much, now it's the time we get our goal, walk the stack as a real debug does. Here we go!
At first, I thought it's quite easy because I negelect FIO. I think I could walk the stack myself, but I was wrong.
Unless you have greate skill and you have known all secrets of FIO, you won't be able to get the correct address of EIP in stack.
Remember even if you get these skills, I don't mention you can walk the call stack, do I?
Yes, you need match the code line and function name with the DWORD address of your EIP. How can you do this? To my knowledge, you need PDB File unless you use the same technique as Process Explorer does. Let's forget about Process Explorer first.
All right, so give me the PDB File Format, I gonna walk the stack. Unfortunately, PDB File is undocumented.
To my knowlege, it has been updated to PDB8.0 WITH VS2005. The only formal document you can find is Undocumented WIN2000. But it's old PDB Format may be PDB7.0. Without the detail infomation about PDB, it's almost impossable for you to read the PDB File, walking is stack is impossable?
Of course not, Microsoft has a DLL in windows\system32\ which name is DBGHelp.DLL.
With DBGHelp.DLL, you could get the stack frame EIP without knowing anything about FIO, MS has done it for us.
With DBGHelp.DLL, there's no need for you to know the PDB File Format since a new API STACKWALK64 is coming.
Early in March 2002 of Under The Hood, Matt has given us a clear idea what DBGHelp is doing:
http://msdn.microsoft.com/msdnmag/issues/02/03/hood/
The orginal purpose of this article is to update a class which is WheatyExceptionReport.
But it contains a greate feature, which is Walk The Stack.
Actually John Robbins has implemented such feature in his book Debugging Windows And .NET too.
You will find a program which name is MDBG in his code of this book.
Since they have discussed it, why I am here?
Honestly, unless you are a professional, you won't like their code.
If you are good at debugging, there's no need to say too much, just debug the function:
LONG WINAPI WheatyExceptionReport::WheatyUnhandledExceptionFilter(
PEXCEPTION_POINTERS pExceptionInfo )
Oh, really? Can you debug it? You can not do it at all!!
Why not?Because this function is register by API: SetUnhandledExceptionFilter
This is what I get from MSDN.
After calling this function, if an exception occurs in a process that is not being debugged, and the exception makes it to the unhandled exception filter, that filter will call the exception filter function specified by the lpTopLevelExceptionFilter parameter.
LPTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter(
LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
);
Don't you see that when you are running debugger, your WheatyUnhandledExceptionFilter
will not work at all.
Awful. But AddVectoredExceptionHandler API gives us hope.
Simply comment out SetUnhandledExceptionFilter in function:
WheatyExceptionReport::WheatyExceptionReport( )
// m_previousFilter =
// SetUnhandledExceptionFilter(WheatyUnhandledExceptionFilter);
AddVectoredExceptionHandler(1,WheatyUnhandledExceptionFilter);
Replace stdafx.h with: Figour 1.
Compile the code, you gonna have a chance to debug the rogram so that you gonna understand how DBGHelp.DLL
helps you.
What does AddVectoredExceptionHandler do?
Again, under the hood gives you the answer.
New Vectored Exception Handling in Windows XP
http://msdn.microsoft.com/msdnmag/issues/01/09/hood/
Warning, if after AddVectoredExceptionHandler(1,WheatyUnhandledExceptionFilter);
is called, thread swith happens, another thread call it's own addvectoredExceptionHandler,
this will produce a bug.
Is that all? No, there's still 2 thing you muse know:
1. How to get thread context.
I would fist talk about why I do not use John Robbins code.
The most difficult thing is how you can get threadContext.
See Figour 2.
John Robbins has a advantage you and I do not, he is writing a debugger, a debugger will get the
CONTEXT easily from debug event. If you don't believe me, just debug the program WDBG.
What Matt is doing is register a call back function when excpetion is happending and invoke an exception.
Look at the function define, PEXCEPTION_POINTERS pExceptionInfo,the structure contains the thread context.
LONG WINAPI WheatyExceptionReport::WheatyUnhandledExceptionFilter(
PEXCEPTION_POINTERS pExceptionInfo )
The program above can be used when you want to record your callstack without dump file.
Simple create a dll, and export the WheatyExceptionReport class.
Loading the DLL and create the object where you want to see callstack, everything Will Not run perfectly unless you know what I gonna discuss in the end of the debug fight.
How about my suspending the thread to get the thread context?
Honestly there's another MVP who has released his code on CodeProject.
http://www.codeproject.com/threads/StackWalker.asp
But, this time the MVP is wrong. The reason is that he is trying the suspend all the working thread to get thread context. Why he is doing so? Because he is MSDN Fan's.
BOOL GetThreadContext( HANDLE hThread, // handle to thread with context LPCONTEXT lpContext // address of context structure );
You cannot get a valid context for a running thread. Use the SuspendThread function to suspend the thread before calling GetThreadContext.
I would like to warn you that:
Please Never Suspend ALL THE THREAD. The old new thing give you a clear idea why:
http://blogs.msdn.com/oldnewthing/archive/2003/12/09/55988.aspx
The End: A Debugging Fight.
As I said everything won't work perfect unless you know the debugging fight I will discuss.
The first time when I get the code from under hood makes me feel excited, look at the
TestExceptionHandler.RPT, greate, call stack inforamtion is here!!
>>>>Address Frame Function SourceFile
>>>>00401234 0013FBB0 baz+54 f:\vc7\testexceptionhandler.cpp line 77
>>>>00401190 0013FCA8 bar+40 f:\vc7\testexceptionhandler.cpp line 59
>>>>00401103 0013FDCC foo+63 f:\vc7\testexceptionhandler.cpp line 50
>>>>00401041 0013FEDC main+41 f:\vc7\testexceptionhandler.cpp line 37
>>>>004038B0 0013FFC0 mainCRTStartup+170 f:\vs70builds\6030\vc\crtbld\crt\src\crt0.c line 259
>>>>7C816FD7 0013FFF0 RegisterWaitForInputIdle+49
Soon I found the problem, look at the final line, if you has replace the SetUnhandledExceptionFilter as AddVectoredExceptionHandler, run it in VS2005, you will see the correct callstack:
>>>>The callstack from 2005 is:
>>>>> TestExceptionHandler.exe!baz(int x=0x0000000c, float y=51.200001, CRandomStruct * p=0x0013fd08) Line 77 C++
>>>> TestExceptionHandler.exe!bar(int a=0x0000000c, float b=51.200001, char * psz=0x00493e8c) Line 59 + 0x14 bytes C++
>>>> TestExceptionHandler.exe!foo(int i=0x00000004) Line 50 + 0x2a bytes C++
>>>> TestExceptionHandler.exe!main(int argc=0x00000001, char * * argv=0x00b63c30) Line 37 + 0x9 bytes C++
>>>> TestExceptionHandler.exe!__tmainCRTStartup() Line 318 + 0x19 bytes C
>>>> TestExceptionHandler.exe!mainCRTStartup() Line 1
>>>> kernel32.dll!_BaseProcessStart@4() + 0x23 bytes
Don't you see that we lose the BaseProcessStart? What on hell RegisterWaitForInputIdle is?
Who knows!!
At first, I thought may be I'm not using the lastest DBGHELP.DLL.
Soon I downloaded the lastest Debugging Tools For Windows from microsoft and copy the DBGHELP.DLL
DBGHELP.LIB.DBGHELP.H into my project, and cancel include<dbghelp.h>,using include "f:\callstack\dbghelp.h"
It doesn't work at all.
Awful, I'm using a new API, something is wrong here.
I ask my friend Cathy to help me test on her computer, the same bug.
Wow!!!! I'm crazy now. I found John Robbins MDBG to test, he has the same bug!!
I'm using the wrong library absolutely.
I should apprected MS WINDBG TEAM expecially Jen-Lung Chiu and another teacher who is Ramond Zhang(Not WINDBG TEAM).
I sent the email to both of them and what I get is I'm using the wrong library.
Jen has a very very important testing on Vista and WIndows 2003, everything works fine unless WINXP.
In his/her email he/she mentions a dll which is DBGENG.DLL.
Wait, I never copy DBGENG.DLL to my project. He/She told me that DBGENG.DLL has dependency with DBGHELP.DLL. My God, when the lastest DBGHELP.DLL is working, it's loading the default old winxp DBGENG.DLL which is working under the hood. The reason why VISTA and WINDOW 2003 is fine is their default DBGENG.DLL is new enough.
So what I gonna to fix it is only to copy the Lastest DBGENG.DLL into my project.
Are you clear?
Finally let's talk about process explorer. You won't believe this:
process explorer can get detail stack information without DBGHELP.DLL, even without PDB!!
ntkrnlpa.exe!KiSwapContext+0x2f
ntkrnlpa.exe!KiFastCallEntry+0xfc
ntdll.dll!KiFastSystemCallRet
ntdll.dll!NtDelayExecution+0xc
ntdll.dll!RtlpTimerThread+0x47
kernel32.dll!BaseThreadStart+0x37
I have sent one email to Mark, but until now, no reply. If anyone of you know how Process Explorer does,
please let me know.
Written By Jim Secretes
2007/05/15
Figour1
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once
// Modify the following defines if you have to target a platform prior to the ones specified below.
// Refer to MSDN for the latest info on corresponding values for different platforms.
#ifndef WINVER // Allow use of features specific to Windows XP or later.
#define WINVER 0x0501 // Change this to the appropriate value to target other versions of Windows.
#endif
#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later.
#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows.
#endif
#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later.
#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later.
#endif
#ifndef _WIN32_IE // Allow use of features specific to IE 6.0 or later.
#define _WIN32_IE 0x0600 // Change this to the appropriate value to target other versions of IE.
#endif
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>
// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
Figure2
CONTEXT trashableContext = ....// Get Your ThreadContext.
STACKFRAME sf;
memset( &sf, 0, sizeof(sf) );
#ifdef _M_IX86
// Initialize the STACKFRAME structure for the first call. This is only
// necessary for Intel CPUs, and isn't mentioned in the documentation.
sf.AddrPC.Offset = pContext->Eip;
sf.AddrPC.Mode = AddrModeFlat;
sf.AddrStack.Offset = pContext->Esp;
sf.AddrStack.Mode = AddrModeFlat;
sf.AddrFrame.Offset = pContext->Ebp;
sf.AddrFrame.Mode = AddrModeFlat;
Walking The Callstack
Last time I cover much information about what call stack is, how to trace the callstack with Stack Frame.
It's enough, we've suffered much, now it's the time we get our goal, walk the stack as a real debug does. Here we go!
At first, I thought it's quite easy because I negelect FIO. I think I could walk the stack myself, but I was wrong.
Unless you have greate skill and you have known all secrets of FIO, you won't be able to get the correct address of EIP in stack.
Remember even if you get these skills, I don't mention you can walk the call stack, do I?
Yes, you need match the code line and function name with the DWORD address of your EIP. How can you do this? To my knowledge, you need PDB File unless you use the same technique as Process Explorer does. Let's forget about Process Explorer first.
All right, so give me the PDB File Format, I gonna walk the stack. Unfortunately, PDB File is undocumented.
To my knowlege, it has been updated to PDB8.0 WITH VS2005. The only formal document you can find is Undocumented WIN2000. But it's old PDB Format may be PDB7.0. Without the detail infomation about PDB, it's almost impossable for you to read the PDB File, walking is stack is impossable?
Of course not, Microsoft has a DLL in windows\system32\ which name is DBGHelp.DLL.
With DBGHelp.DLL, you could get the stack frame EIP without knowing anything about FIO, MS has done it for us.
With DBGHelp.DLL, there's no need for you to know the PDB File Format since a new API STACKWALK64 is coming.
Early in March 2002 of Under The Hood, Matt has given us a clear idea what DBGHelp is doing:
http://msdn.microsoft.com/msdnmag/issues/02/03/hood/
The orginal purpose of this article is to update a class which is WheatyExceptionReport.
But it contains a greate feature, which is Walk The Stack.
Actually John Robbins has implemented such feature in his book Debugging Windows And .NET too.
You will find a program which name is MDBG in his code of this book.
Since they have discussed it, why I am here?
Honestly, unless you are a professional, you won't like their code.
If you are good at debugging, there's no need to say too much, just debug the function:
LONG WINAPI WheatyExceptionReport::WheatyUnhandledExceptionFilter(
PEXCEPTION_POINTERS pExceptionInfo )
Oh, really? Can you debug it? You can not do it at all!!
Why not?Because this function is register by API: SetUnhandledExceptionFilter
This is what I get from MSDN.
After calling this function, if an exception occurs in a process that is not being debugged, and the exception makes it to the unhandled exception filter, that filter will call the exception filter function specified by the lpTopLevelExceptionFilter parameter.
LPTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter(
LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
);
Don't you see that when you are running debugger, your WheatyUnhandledExceptionFilter
will not work at all.
Awful. But AddVectoredExceptionHandler API gives us hope.
Simply comment out SetUnhandledExceptionFilter in function:
WheatyExceptionReport::WheatyExceptionReport( )
// m_previousFilter =
// SetUnhandledExceptionFilter(WheatyUnhandledExceptionFilter);
AddVectoredExceptionHandler(1,WheatyUnhandledExceptionFilter);
Replace stdafx.h with: Figour 1.
Compile the code, you gonna have a chance to debug the rogram so that you gonna understand how DBGHelp.DLL
helps you.
What does AddVectoredExceptionHandler do?
Again, under the hood gives you the answer.
New Vectored Exception Handling in Windows XP
http://msdn.microsoft.com/msdnmag/issues/01/09/hood/
Warning, if after AddVectoredExceptionHandler(1,WheatyUnhandledExceptionFilter);
is called, thread swith happens, another thread call it's own addvectoredExceptionHandler,
this will produce a bug.
Is that all? No, there's still 2 thing you muse know:
1. How to get thread context.
I would fist talk about why I do not use John Robbins code.
The most difficult thing is how you can get threadContext.
See Figour 2.
John Robbins has a advantage you and I do not, he is writing a debugger, a debugger will get the
CONTEXT easily from debug event. If you don't believe me, just debug the program WDBG.
What Matt is doing is register a call back function when excpetion is happending and invoke an exception.
Look at the function define, PEXCEPTION_POINTERS pExceptionInfo,the structure contains the thread context.
LONG WINAPI WheatyExceptionReport::WheatyUnhandledExceptionFilter(
PEXCEPTION_POINTERS pExceptionInfo )
The program above can be used when you want to record your callstack without dump file.
Simple create a dll, and export the WheatyExceptionReport class.
Loading the DLL and create the object where you want to see callstack, everything Will Not run perfectly unless you know what I gonna discuss in the end of the debug fight.
How about my suspending the thread to get the thread context?
Honestly there's another MVP who has released his code on CodeProject.
http://www.codeproject.com/threads/StackWalker.asp
But, this time the MVP is wrong. The reason is that he is trying the suspend all the working thread to get thread context. Why he is doing so? Because he is MSDN Fan's.
BOOL GetThreadContext( HANDLE hThread, // handle to thread with context LPCONTEXT lpContext // address of context structure );
You cannot get a valid context for a running thread. Use the SuspendThread function to suspend the thread before calling GetThreadContext.
I would like to warn you that:
Please Never Suspend ALL THE THREAD. The old new thing give you a clear idea why:
http://blogs.msdn.com/oldnewthing/archive/2003/12/09/55988.aspx
The End: A Debugging Fight.
As I said everything won't work perfect unless you know the debugging fight I will discuss.
The first time when I get the code from under hood makes me feel excited, look at the
TestExceptionHandler.RPT, greate, call stack inforamtion is here!!
>>>>Address Frame Function SourceFile
>>>>00401234 0013FBB0 baz+54 f:\vc7\testexceptionhandler.cpp line 77
>>>>00401190 0013FCA8 bar+40 f:\vc7\testexceptionhandler.cpp line 59
>>>>00401103 0013FDCC foo+63 f:\vc7\testexceptionhandler.cpp line 50
>>>>00401041 0013FEDC main+41 f:\vc7\testexceptionhandler.cpp line 37
>>>>004038B0 0013FFC0 mainCRTStartup+170 f:\vs70builds\6030\vc\crtbld\crt\src\crt0.c line 259
>>>>7C816FD7 0013FFF0 RegisterWaitForInputIdle+49
Soon I found the problem, look at the final line, if you has replace the SetUnhandledExceptionFilter as AddVectoredExceptionHandler, run it in VS2005, you will see the correct callstack:
>>>>The callstack from 2005 is:
>>>>> TestExceptionHandler.exe!baz(int x=0x0000000c, float y=51.200001, CRandomStruct * p=0x0013fd08) Line 77 C++
>>>> TestExceptionHandler.exe!bar(int a=0x0000000c, float b=51.200001, char * psz=0x00493e8c) Line 59 + 0x14 bytes C++
>>>> TestExceptionHandler.exe!foo(int i=0x00000004) Line 50 + 0x2a bytes C++
>>>> TestExceptionHandler.exe!main(int argc=0x00000001, char * * argv=0x00b63c30) Line 37 + 0x9 bytes C++
>>>> TestExceptionHandler.exe!__tmainCRTStartup() Line 318 + 0x19 bytes C
>>>> TestExceptionHandler.exe!mainCRTStartup() Line 1
>>>> kernel32.dll!_BaseProcessStart@4() + 0x23 bytes
Don't you see that we lose the BaseProcessStart? What on hell RegisterWaitForInputIdle is?
Who knows!!
At first, I thought may be I'm not using the lastest DBGHELP.DLL.
Soon I downloaded the lastest Debugging Tools For Windows from microsoft and copy the DBGHELP.DLL
DBGHELP.LIB.DBGHELP.H into my project, and cancel include<dbghelp.h>,using include "f:\callstack\dbghelp.h"
It doesn't work at all.
Awful, I'm using a new API, something is wrong here.
I ask my friend Cathy to help me test on her computer, the same bug.
Wow!!!! I'm crazy now. I found John Robbins MDBG to test, he has the same bug!!
I'm using the wrong library absolutely.
I should apprected MS WINDBG TEAM expecially Jen-Lung Chiu and another teacher who is Ramond Zhang(Not WINDBG TEAM).
I sent the email to both of them and what I get is I'm using the wrong library.
Jen has a very very important testing on Vista and WIndows 2003, everything works fine unless WINXP.
In his/her email he/she mentions a dll which is DBGENG.DLL.
Wait, I never copy DBGENG.DLL to my project. He/She told me that DBGENG.DLL has dependency with DBGHELP.DLL. My God, when the lastest DBGHELP.DLL is working, it's loading the default old winxp DBGENG.DLL which is working under the hood. The reason why VISTA and WINDOW 2003 is fine is their default DBGENG.DLL is new enough.
So what I gonna to fix it is only to copy the Lastest DBGENG.DLL into my project.
Are you clear?
Finally let's talk about process explorer. You won't believe this:
process explorer can get detail stack information without DBGHELP.DLL, even without PDB!!
ntkrnlpa.exe!KiSwapContext+0x2f
ntkrnlpa.exe!KiFastCallEntry+0xfc
ntdll.dll!KiFastSystemCallRet
ntdll.dll!NtDelayExecution+0xc
ntdll.dll!RtlpTimerThread+0x47
kernel32.dll!BaseThreadStart+0x37
I have sent one email to Mark, but until now, no reply. If anyone of you know how Process Explorer does,
please let me know.
Written By Jim Secretes
2007/05/15
Figour1
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once
// Modify the following defines if you have to target a platform prior to the ones specified below.
// Refer to MSDN for the latest info on corresponding values for different platforms.
#ifndef WINVER // Allow use of features specific to Windows XP or later.
#define WINVER 0x0501 // Change this to the appropriate value to target other versions of Windows.
#endif
#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later.
#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows.
#endif
#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later.
#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later.
#endif
#ifndef _WIN32_IE // Allow use of features specific to IE 6.0 or later.
#define _WIN32_IE 0x0600 // Change this to the appropriate value to target other versions of IE.
#endif
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>
// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
Figure2
CONTEXT trashableContext = ....// Get Your ThreadContext.
STACKFRAME sf;
memset( &sf, 0, sizeof(sf) );
#ifdef _M_IX86
// Initialize the STACKFRAME structure for the first call. This is only
// necessary for Intel CPUs, and isn't mentioned in the documentation.
sf.AddrPC.Offset = pContext->Eip;
sf.AddrPC.Mode = AddrModeFlat;
sf.AddrStack.Offset = pContext->Esp;
sf.AddrStack.Mode = AddrModeFlat;
sf.AddrFrame.Offset = pContext->Ebp;
sf.AddrFrame.Mode = AddrModeFlat;
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
赞赏
他的文章
看原图
赞赏
雪币:
留言: