Windbg查看调用堆栈(k*)

https://www.52pojie.cn/thread-664189-1-1.html     

 无论是分析程序崩溃原因,还是解决程序hang问题,我们最常查看的就是程序调用堆栈。学会windbg调用堆栈命令,以及理解堆栈中的各个参数的意义就显得至关重要。

上图就是一个典型的Windbg堆栈,如果不理解ChildEBP、RetAddr、Args to Child等参数意义,以及它们之间的来龙去脉,调试工作将很难进行下去。

1. 函数参数

        函数的参数传递有二种方式:堆栈方式、寄存器方式。如果是堆栈方式传递的,就需要定义参数在堆栈中的传递顺序,并约定函数被调用之后,由谁来平衡堆栈;如果是寄存器方式传递的,就需要确定参数存放在哪个寄存器中。每一种方式都有其优缺点,而且与使用的编程语言有关系,不存在哪种方式好与坏。

如Visual Studio中的C++工程,可以C++ --> 高级 --> 调用约定中进行设置:

        常用的调用约定类型有__cdecl、stdcall、PASCAL、fastcall。除了fastcall可以支持以寄存器的方式来传递函数参数外,其他的都是通过堆栈的方式来传递函数参数的。

利用堆栈传递参数

        堆栈是一种“后进先出”的数据结构,ESP寄存器始终指向栈顶。栈中数据地址从底部到顶部依次减小,也就是说,栈底对应高地址,栈顶对应低地址。
调用函数时,调用者依次把参数压栈,然后调用函数,函数被调用之后,在堆栈中取得参数数据。函数调用结束以后,堆栈需要恢复到函数调用之前的样子,具体由调用者来恢复还是由函数自身来恢复,根据不同的调用约定类型采用不同的方式。

约定类型 __cdecl stdcall PASCAL fastcall
参数传递顺序 从右到左 从右到左 从左到右 使用寄存器
平衡堆栈者 调用者 函数自身 函数自身 函数自身

cdcel是C/C++/MFC程序默认的调用约定。
stdcall是Win32中绝大多数 API函数的约定方式,也有少部分使用cdcel约定方式,如wsprintf等。

        在windows C/C++开发中常用的就是__cdecl和stdcall这2种调用约定。
假设调用函数int add(int a, int b), 按照不同的调用约定来调用它。从调用者的视角来看,其汇编代码分别表示如下:

__cdecl

push b     ;参数按从右到左传递
push a
call add
add esp, 8 ;调用者在函数外部平衡堆栈

stdcall

push b     ;参数按从右到左传递
push a
call add   ;函数自己内部平衡堆栈

在函数调用过程中,参数入栈的过程:

上图中,EBP和函数返回地址都是地址,在32位程序中地址占4个字节。在函数的一次调用过程中EBP是不会变化的,函数调用完之后会将EBP恢复为暂存在堆栈中的原EBP值。所以,通过EBP可以获取函数各个参数的值:
参数a = EBP + 0x8
参数b = EBP + 0xC

2. Windbg堆栈命令

2.1 显示堆栈信息k*

[~Thread] k[b|p|P|v] [c] [n] [f] [L] [M] [FrameCount]
[~Thread] k[b|p|P|v] [c] [n] [f] [L] [M] = BasePtr [FrameCount]
[~Thread] k[b|p|P|v] [c] [n] [f] [L] [M] = BasePtr StackPtr InstructionPtr
[~Thread] kd [WordCount]

参数:
Thread  指定显示哪个线程的调用堆栈。如果省略该参数,则显示当前线程的调用堆栈。*显示所有线程的调用堆栈。

b  显示每个函数的前3个参数。

p  显示每个函数的所有参数。参数列表包括每个参数的类型、名称、值。

        如上图,可以看到函数的每个参数的类型,名称,值。但是这个需要有对应的符号文件(pdb),没有应用程序的符号文件只能显示系统API的参数信息。

P  类似p。不同之处在于,每个参数显示在单独的行上面。

n  显示调用堆栈中每帧的序号(一般称栈帧,如栈帧3)。

FrameCount  指定显示调用堆栈的帧数,即调用堆栈的深度。默认为16进制格式。默认帧数为0x14=20

2.2 切换到指定帧信息.frame

        调用堆栈显示出来之后,如果想知道调用某帧时的相关信息,可以使用.frame 来切换指定帧,然后就可以使用如dv命令显示局部变量等。

.frame [/c] [/r] [FrameNumber] 

/c

/r  显示执行该帧时寄存器的值。

FrameNumber  指定要切换到的帧号。

3. 实例分析

kbn 显示堆栈信息:

栈帧12:
调用add函数,参数1=00000001,参数2=000000002,EBP=0015fc0c
根据图1得知,函数返回地址=EBP+4,我们使用dw命令来验证。

参考:
《软件调试》张银奎 著
《格蠹汇编》张银奎 著
《加密与解密》第三版 段刚编著

怎么删除?

时间: 2024-08-01 04:51:37

Windbg查看调用堆栈(k*)的相关文章

Android调用堆栈跟踪实例分析_Android

本文实例讲述了Android调用堆栈跟踪的方法.分享给大家供大家参考.具体如下: Android开发中,我们也会经常遇到段错误,也就是SIGSEGV(11),这个时候libc的backtrace会打印出对应的堆栈信 息,而你看到的仅仅是一对数字,好像无从查起. 如下面这一从串断错误: ActivityManager( 1105): Displayed activity com.android.browser/.BrowserActivity: 2460 ms (total 2460 ms) I/

小览CallStack(调用栈)(三)-用调试器脚本查看调用栈信息

在这一系列之前的两篇文章中,我介绍了如何在windbg中查看调用栈的相关 信息(详见小览call stack(调用栈)(一)),以及调用约定(详见小览call stack(调用栈) (二)--调用约定).今天的这篇博客在二者的基础 之上,介绍如何使用调式器脚本程序来观察调用栈.对CallStack感兴趣的朋友 可以在此基础上开发更加详尽的脚本来观察CallStack的信息:对调试感兴趣的 朋友则可以看一下DScript的用处. 我们先来看一个例子,下面的程序并不是一个优美的程序片段,但是它能够

调试技巧之调用堆栈

简单介绍 调试是程序开发者必备技巧.如果不会调试,自己写的程序一旦出问题,往往无从下手.本人总结10年使用VC经验,对调试技巧做一个粗浅的介绍.希望对大家有所帮助. 今天简单的介绍介绍调用堆栈.调用堆栈在我的专栏的文章VC调试入门提了一下,但是没有详细介绍. 首先介绍一下什么叫调用堆栈:假设我们有几个函数,分别是function1,function2,function3,funtion4,且function1调用function2,function2调用function3,function3调用

Python记录详细调用堆栈日志的方法

  本文实例讲述了Python记录详细调用堆栈日志的方法.分享给大家供大家参考.具体实现方法如下: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import sys import os def detailtrace(info): retStr = "" curindex=0 f = sys._getframe() f = f.f_back # first frame is detailtrace, ignore

C/C++中手动获取调用堆栈

C/C++中手动获取调用堆栈 当我们的程序core掉之后,如果能获取到core时的函数调用堆栈将非常有利于定位问题.在Windows下可以使用SEH机制:在Linux下通过gdb使用coredump文件即可. 但有时候由于某些错误导致堆栈被破坏,发生拿不到调用堆栈的情况. 一些基础预备知识本文不再详述,可以参考以下文章: 函数调用栈的获取原理分析 寄存器.函数调用与栈帧 需要知道的信息: 函数调用对应的call指令本质上是先压入下一条指令的地址到堆栈,然后跳转到目标函数地址 函数返回指令ret则

调用堆栈-C#程序调用C++的exe,exe出现错误

问题描述 C#程序调用C++的exe,exe出现错误 exe的调用堆栈里提示: KernelBase.dll!74aac42d() [下面的框架可能不正确和/或缺失,没有为 KernelBase.dll 加载符号] 解决方案 exe本身能不能命令行等执行. 是不是权限,或者exe需要其他依赖文件. 解决方案二: 但是exe自己运行的时候,是没有问题的..另外,我如何加日志分析.可以加我QQ吗?请教一下.. 解决方案三: CoInitialize(NULL); // 初始化COM接口 MSXML2

xml添加节点后,运行出现“调用堆栈”,不知如何解决?

问题描述 在xml中添加节点后,然后让所有节点遍历显示出来,程序编写完成后,一运行,并没有出现错误提示,却出现了"调用堆栈"的信息,但是程序不能正常运行,这是什么问题,如何解决?本人初学者,求各位大侠帮忙解决,不胜感激··· 解决方案 解决方案二:手动添加的节点吗?在检查一下文件.解决方案三:把代码贴出来...

JavaScript调用堆栈及setTimeout使用方法深入剖析_javascript技巧

Javascript中会经常用到setTimeout来推迟一个函数的执行,如: 复制代码 代码如下: setTimeout(function(){alert("Hello World");},1000); 会在执行到这句话后延迟1秒钟来弹出alert窗口.那么再看这一段: 复制代码 代码如下: function a(){ setTimeout(function() {alert(1)}, 0); alert(2); } a(); 注意这段代码中的setTimeout延迟设为了0,就是延

asp.net-Response.End() 由于代码已经过优化或者本机框架位于调用堆栈之上,无法计算表达式的值。

问题描述 Response.End() 由于代码已经过优化或者本机框架位于调用堆栈之上,无法计算表达式的值. public void Write(string obj){ Response.Clear(); Response.Write(obj); HttpContext.Current.ApplicationInstance.CompleteRequest(); Response.End();} AJax请求自己的后台页面后台输出参数Write(""1"");报错