惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cloudbric
Cloudbric
E
Exploit-DB.com RSS Feed
SecWiki News
SecWiki News
Forbes - Security
Forbes - Security
N
News | PayPal Newsroom
S
Security @ Cisco Blogs
Schneier on Security
Schneier on Security
V
V2EX - 技术
S
Secure Thoughts
W
WeLiveSecurity
Google DeepMind News
Google DeepMind News
C
CERT Recently Published Vulnerability Notes
NISL@THU
NISL@THU
S
Securelist
S
Security Archives - TechRepublic
Know Your Adversary
Know Your Adversary
V
Vulnerabilities – Threatpost
Security Latest
Security Latest
Recent Commits to openclaw:main
Recent Commits to openclaw:main
G
GRAHAM CLULEY
H
Hacker News: Front Page
Microsoft Azure Blog
Microsoft Azure Blog
I
Intezer
Google Online Security Blog
Google Online Security Blog
美团技术团队
阮一峰的网络日志
阮一峰的网络日志
T
The Exploit Database - CXSecurity.com
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Webroot Blog
Webroot Blog
Jina AI
Jina AI
Engineering at Meta
Engineering at Meta
P
Proofpoint News Feed
The Cloudflare Blog
I
InfoQ
L
LangChain Blog
U
Unit 42
P
Proofpoint News Feed
S
Schneier on Security
S
Security Affairs
Y
Y Combinator Blog
T
Tenable Blog
N
News and Events Feed by Topic
MyScale Blog
MyScale Blog
量子位
Google DeepMind News
Google DeepMind News
Cyberwarzone
Cyberwarzone
博客园 - 聂微东
D
Darknet – Hacking Tools, Hacker News & Cyber Security
GbyAI
GbyAI
AWS News Blog
AWS News Blog

博客园 - 高原

[转] 定义、注册和实现 GObject 类的子类 [转] GType 类型系统的功能 [转] Android FrameWork——Touch事件派发过程详解 [转] GObject对象系统 [转] GTYPE类型系统分析 VC里嵌汇编,获取寄存器的值 [转] 探索Win32系统之窗口类(Window Classes in Win32) [转] 编译器开关参数集 [转] Nano-X的详细介绍 [转] 为MicroWindows添加透明绘图函数 [转] microwindows位图解析 [转] Microwindows及其中文化方法 [转] Nano-X显示系统的代码分析 [转] Nano-X图形引擎分析及其优化 [转] 使用 pq magic 分区出现 Error 983 报错信息的处理办法 [转] IncrediBuild 试用时间推迟的算法 [原创] MicroWindows学习笔记之底层消息的读取 [原创] MicroWindows学习笔记之对底层设备的管理 [转载] Makefile详解
[转] 使用dbghelp获取调用堆栈--release下的调试方法学
高原 · 2011-04-20 · via 博客园 - 高原

当软件作为release模式被发布给用户时,当程序崩溃时我们很难去查找原因。常见的手法是输出LOG文件,根据LOG文件分析
程序崩溃时的运行情况。我们可以通过SEH来捕获程序错误,然后输出一些有用的信息作为我们分析错误的资料。一般我们需要
输出的信息包括:系统信息、CPU寄存器信息、堆栈信息、调用堆栈等。而调用堆栈则是最有用的部分,它可以直接帮我们定位
到程序崩溃时所处的位置(在何处崩溃)。(codeproject上关于这个专题的常见开场白 = =#)

要获取call stack(所谓的调用堆栈),就需要查看(unwind)stack的内容。We could conceivably attempt to unwind the
stack ourselves using inline assembly. But stack frames can be organized in different ways, depending on compiler
optimizations and calling conventions, so it could become complicated to do it that way.(摘自vld文档)要获取栈的
内容,我们可以自己使用内联汇编获取,但是考虑到兼容性,内联汇编并不是一个好的解决方案。我们可以使用微软的dbghelp
中的StackWalk64来获取栈的内容。

StackWalk64声明如下:
BOOL StackWalk64(
  DWORD MachineType,
  HANDLE hProcess,
  HANDLE hThread,
  LPSTACKFRAME64 StackFrame,
  PVOID ContextRecord,
  PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
  PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
  PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
  PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
);

具体每个参数的含义可以参见MSDN。这里说下ContextRecord参数,该参数指定了CPU各个寄存器的内容。StackFrame指定了stack
frame的内容。stack frame是什么,我也不知道。(= =) StackWalk64函数需要用户指定当前frame的地址,以及当前程序的指令
地址。这两个信息都被填充进ContextRecord,然后传进StackWalk64函数。

那么如何获取当前的stack frame地址和当前程序指令地址呢?如前所说,你可以使用内联汇编。(对于程序指令地址,因为要获取
EIP寄存器的内容,而该寄存器不能被软件访问)也可以使用GetThreadContext一次性获取当前线程当前运行情况下的CPU各个寄存器
内容。补充下,当前frame地址被放在EBP寄存器里,当前程序指令地址放在EIP寄存器里。但是,如同MSDN对GetThreadContext函数
的说明一样,该函数可能获取到错误的寄存器内容(You cannot get a valid context for a running thread)。

另一种获取Context(包含EBP and EIP)的方法就是使用SEH(结构化异常处理),在__except中使用GetExceptionInformation获取。

GetExceptionInformation 传回一个LPEXCEPTION_POINTERS指针,该指针指向一个EXCEPTION_POINTERS结构,该结构里包含一个
Context的指针,即达到目标,可以使用StackWalk函数。

补充一下,你可以直接使用StackWalk函数,StackWalk被define为StackWalk64(windows平台相关)。

unwind栈后,可以进一步获取一个stack frame的内容,例如函数名。这里涉及到SymFromAddr函数,该函数可以根据一个地址返回
符号名(函数名)。还有一个有意思的函数:SymGetLineFromAddr,可以获取函数对应的源代码的文件名和行号。

当然,这一切都依赖于VC产生的程序数据库文件(pdb),以及提供以上API函数的dbghelp.dll。

参考一段简单的代码:

///
///
///

#include <windows.h>
#include 
<stdio.h>
#include 
<dbghelp.h>

#pragma comment( lib, 
"dbghelp.lib" )

void dump_callstack( CONTEXT *context )
{
 STACKFRAME sf;
 memset( 
&sf, 0sizeof( STACKFRAME ) );

 sf.AddrPC.Offset 
= context->Eip;
 sf.AddrPC.Mode 
= AddrModeFlat;
 sf.AddrStack.Offset 
= context->Esp;
 sf.AddrStack.Mode 
= AddrModeFlat;
 sf.AddrFrame.Offset 
= context->Ebp;
 sf.AddrFrame.Mode 
= AddrModeFlat;

 DWORD machineType 
= IMAGE_FILE_MACHINE_I386;

 HANDLE hProcess 
= GetCurrentProcess();
 HANDLE hThread 
= GetCurrentThread();

 
for( ; ; )
 
{
  
if!StackWalk(machineType, hProcess, hThread, &sf, context, 0, SymFunctionTableAccess, SymGetModuleBase, 0 ) )
  
{
   
break;
  }


  
if( sf.AddrFrame.Offset == 0 )
  
{
   
break;
  }

  BYTE symbolBuffer[ 
sizeof( SYMBOL_INFO ) + 1024 ];
  PSYMBOL_INFO pSymbol 
= ( PSYMBOL_INFO ) symbolBuffer;
 
  pSymbol
->SizeOfStruct = sizeof( symbolBuffer );
  pSymbol
->MaxNameLen = 1024;

  DWORD64 symDisplacement 
= 0;
  
if( SymFromAddr( hProcess, sf.AddrPC.Offset, 0, pSymbol ) )
  
{
   printf( 
"Function : %s\n", pSymbol->Name );
  }

  
else
  
{
   printf( 
"SymFromAdd failed!\n" );
  }


  IMAGEHLP_LINE lineInfo 
= sizeof(IMAGEHLP_LINE) };
  DWORD dwLineDisplacement;

  
if( SymGetLineFromAddr( hProcess, sf.AddrPC.Offset, &dwLineDisplacement, &lineInfo ) )
  
{
   printf( 
"[Source File : %s]\n", lineInfo.FileName ); 
   printf( 
"[Source Line : %u]\n", lineInfo.LineNumber ); 
  }

  
else
  
{
   printf( 
"SymGetLineFromAddr failed!\n" );
  }

 }

}


DWORD excep_filter( LPEXCEPTION_POINTERS lpEP )
{
 
/// init dbghelp.dll
 if( SymInitialize( GetCurrentProcess(), NULL, TRUE ) )
 
{
  printf( 
"Init dbghelp ok.\n" );
 }


 dump_callstack( lpEP
->ContextRecord );

 
if( SymCleanup( GetCurrentProcess() ) )
 
{
  printf( 
"Cleanup dbghelp ok.\n" );
 }


 
return EXCEPTION_EXECUTE_HANDLER;
}


void func1( int i )
{
 
int *= 0;
 
*= i;
}


void func2( int i )
{
 func1( i 
- 1 );
}


void func3( int i )
{
 func2( i 
- 1 );
}


void test( int i )
{
 func3( i 
- 1 );
}


int main()
{
 __try
 
{
  test( 
10 );
 }

 __except( excep_filter( GetExceptionInformation() ) )
 
{
  printf( 
"Some exception occures.\n" );
 }


 
return 0;
}



以上代码在release模式下需要关掉优化,否则调用堆栈显示不正确(某些函数被去掉了?),同时需要pdb文件。

参考资料:
http://www.codeproject.com/KB/threads/StackWalker.aspx
http://www.cnblogs.com/protalfox/articles/84723.html
http://www.codeproject.com/KB/debug/XCrashReportPt1.aspx
http://www.codeproject.com/KB/applications/visualleakdetector.aspx

ps,本文技术浅尝辄止,部分内容是否完全准确(正确)我个人都持保留态度,仅供参考。:D