首页
论坛
课程
招聘
[原创]Windbg调试栈溢出
2022-7-27 20:37 4626

[原创]Windbg调试栈溢出

2022-7-27 20:37
4626

背景:windows开发的老表们,你是否遇到过这类问题,程序崩溃了,也抓到了dmp,windbg打开dmp文件,执行!analyze -v指令,傻眼了,显示的调用堆栈被破坏了,执行kb,看下调用堆栈,也是显示的被破坏的调用堆栈。怎么办老表,你往下看.......

一、函数调用时的栈帧结构

    如果你知道这些废话,直接跳过这段,往下看,不要浪费时间, 调查windows栈溢出此类的问题,首先要明白函数的调用原理,实际上,函数的调用是在栈上进行的,栈的生长方向是由高地址向低地址,即栈底为高地址,栈顶为低地址。每个函数在被调用时都对应着各自一个栈帧,用来记录函数自身的一些信息(返回地址、局部变量…),因此栈帧也叫“过程活动记录”,为了衡量栈帧的范围,就需要用到两个寄存器:ESP(Extend Stack Pointer)和EBP(Extend Base Pointer)。ESP就是,也叫栈指针,ESP中始终存放着指向当前栈帧顶部的指针,时刻指向栈帧顶部,即当压入数据时,ESP-=4;弹出数据时ESP+=4。EBP时刻指向当前栈帧的底部(并非栈底),局部变量,参数,返回地址、的访问都是以EBP寄存器为参考点。

表哥,明白下面三个寄存器的作用,你就可以阅读了,以下说的都是x86架构。



寄存器名字英文缩写功能备注
EBPExtend Base Pointer指向栈最上面一个栈帧的底部,用它可直接存取栈中的数据,包括,参数,局部变量,返回地址。EBP和ESP是一对
ESPExtend Stack Pointer

始终指向栈顶

压栈:ESP-4

出栈:ESP_4

EBP和ESP是一对
EIP

Instruction Pointer

EIP寄存器保存CPU将要执行的下一句指令地址。每次CPU执行完相应的汇编指令之后,EIP寄存器的值就会增加。EIP指向即将执行的代码地址。



二、函数调用堆栈被破坏bug分析

1、遍历所有线程,找到出问题的线程。

拿到dump文件,输入命令~*kbn查看所有线程信息,发现异常出现在14号线程,输入命令~14s切换到出问题的线程,下面是14号线程的调用栈。一般来说,对付“kernel32!UnhandledExceptionFilter+0x1af”的方法是输入命令“.cxr poi(0xXXXXXXXX+4)”,(其中0xXXXXXXXX为第一个参数,即红色标出位置的值)即可看到发生异常前的调用栈。但是在本实例中这个值为0x00000000,是个无效地址,我们已经不能用常规方法看到调用栈了



2、恢复调用栈

首先要观察下,栈溢出到什么程度,我给他大致分四种情况。

    a)栈中保存的父函数的ebp被覆盖。

            当前栈帧的ebp指向父函数的ebp(看下图)

            dd ebp,当前帧的ebp指向父函数的ebp,可执行dd ebp指令查看父函数的ebp的值,如果不正常,那就是被覆盖了。观察下父函数ebp寄存器的值和esp寄存器的值相差大不大,如果两者相差不大,如果明显相差很大,完全不在一个区间,证明父函数ebp寄存器被覆盖  了。父函数的ebp被覆盖了,会导致返回父函数时,定位局部变量,参数时发生错误。另外还可以输入命令!teb查看线程的栈空间范围,看下esp和ebp是不是在[StackLimit   StackBase]范围之内。

    b)栈中保存的返回地址(调用子函数时push eip)被覆盖

            返回地址:当前栈帧ebp+4(看下图)

            观察下栈中保存的返回地址,是不是在正常的代码段地址返回内,如果明显不合法的代码地址,那么就是返回地址被覆盖了,在当前函数执行完毕后,返回到父函数时,会把返回地址弹出栈到EIP寄存器中,如果栈中保存的返回地址是错误的,那么pop到EIP寄存器的返回地址也就是错误的。会执行到不可访问或者非法的代码地址处。

    c)当前执行函数的参数被覆盖

            当前执行的函数的第一个参数位置处:ebp + 8(看下图)

            当前执行的函数的第二个参数位置处:ebp + 12(看下图)

    d)覆盖到父函数的栈帧空间(局部变量)



恢复调用栈方法,

第一种方法:输入命令dps esp L1000,查看调用栈,下面是其中的一些关键部分。

第二种方法如果esp寄存器被破坏,可以可以输入命令!teb查看线程的栈空间范围,

                   然后执行命令dps  StackLimit   StackBase查看堆栈上的符号。


通过查找ebp便可以恢复调用栈了(从0fadff94(从函数__RtlUserThreadStart的ebp往上递推) 向上找到0fadff88 再向上找到0fadd724,依次类推) 而最后的0fadd674 再也找不到存储它的地方了,说明这里已经是调用栈的尽头。那么破坏栈的地方也就是发生在这里了

VFPrintOpr!CVFPrintMgrWnd::Refresh+0x25 [E:\Daily_Build\code\src\Set_Nuclear\EngStationFunctions\VFPrintMgr\VFPrintMgrWnd.cpp @ 165]

通过进一步分析代码查看变量值便可以找到问题的所在


    (通过ebp追踪被破坏的调用堆栈)


      (堆栈布局)


一般的追踪思路,因为每个线程的创建函数为ntdll!__RtlUserThreadStart,可以通过ntdll!__RtlUserThreadStart函数的ebp往上追踪,具体的追踪思路

如下:




[2022夏季班]《安卓高级研修班(网课)》月薪三万班招生中~

最后于 2022-7-31 01:29 被sanganlei编辑 ,原因:
收藏
点赞5
打赏
分享
最新回复 (3)
雪    币: 3
活跃值: 活跃值 (1454)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
咖啡_741298 活跃值 2022-7-27 22:13
2
0
思路不错
雪    币: 2800
活跃值: 活跃值 (1991)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
caolinkai 活跃值 2022-7-29 09:05
3
0
学习了
雪    币: 3209
活跃值: 活跃值 (1309)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
木志本柯 活跃值 2022-8-1 18:04
4
0
先收藏 以后遇到 在看看
游客
登录 | 注册 方可回帖
返回