前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >栈溢出场景的分析(2)

栈溢出场景的分析(2)

作者头像
河边一枝柳
发布2021-08-06 15:14:13
5070
发布2021-08-06 15:14:13
举报

之前一篇文章<<一种栈溢出的场景分析和建议>>中,本人分享了如何查找程序Crash的函数调用栈,然后通过代码审查找到栈溢出的原因。但是却有一些场景通过代码审查不易找到问题,比如如下两点:

  1. 函数的调用逻辑复杂,且触发逻辑依赖于输入样例。这样通过代码审查是很难看出问题所在的。
  2. 当触发的栈溢出问题在非自己公司开发的第三方库中,无法获取源代码,也不易看出问题。

那么针对上面这两点,都需要一个东西去做辅助分析,那就是触发栈溢出的输入内容(这的所谓输入内容不是指用户在交互界面输入,而是指触发这个栈溢出的数据),无论是自己用这个输入内容来调试栈溢出的触发逻辑,或者是交给第三方库的支持方,都是不可或缺的。

由于这种场景分析距今时间较长,本该在上一篇介绍的内容,便忘记介绍,好记性不如烂笔头。而今日正好又碰到了这种场景,遂记录于此,也与大家一起分享。

程序样例

为了将故事完整性,我重新编写了一段样例代码。记住我们的目标,是根据Crash后收集的dump内容,找到触发这个栈溢出的输入数据。 这个程序是如何触发栈溢出的:

  • 调用的函数是TriggerStackOverFlow
  • 导致栈溢出的递归调用的函数是Func
  • 这里特意设置的触发条件是当输入的数据为Data Trigger StackOverFlow ,只有这个输入的时候才会触发递归调用。实际真实的工程代码也是类似,并不是栈溢出问题必现,而是在特定的情况下才会触发,这也是为什么本文强调的是如何获取触发栈溢出的输入数据如此重要,因为调试问题离不开它。
#include <iostream>
int a = 1;
void Func(const char* pcsPara)
{
  std::cout << pcsPara << " " << ++a << std::endl;
  if (0 == strcmp(pcsPara, "Data Trigger StackOverFlow"))
    Func(pcsPara);
}

void TriggerStackOverFlow()
{
  char csTmpStr[1000] = "Data Trigger StackOverFlow";
  Func(csTmpStr);
}

int main()
{
  TriggerStackOverFlow();
  return 0;
}

找出函数调用栈

一般碰到Crash的Dump,习惯性的都是先用Windbgk命令看下函数调用栈。但是对于这个命令默认只能显示0xff个栈帧,那么我们其实还可以调用k [FrameCount], 这里的FrameCount就是设置为你想显示的栈帧数量,但是查看了最新的Windbg Preview版本最多也只能显示0xffff个栈帧,那么对于超过0xffff的栈帧无法显示。那么本人的就刚好碰到了这种场景(那也是因为我们把默认的栈空间调整到了更大),这个时候就要用到上一篇文章讲解的方法<<一种栈溢出的场景分析和建议>>, 把整个函数调用栈的空间用dps打印出来。不麻烦大家再去看原先的文章,我把这个步骤再做一次,此文不再重新说明。直接说结果,对于上述案例打印出来如下所示。0000004ea14010000000004ea1500000根据!teb得来的栈空间地址范围。

0:000> dps 0000004ea1401000 0000004ea1500000
......
0000004e`a14059a0  00007fff`d1059570 ucrtbase!_argc
0000004e`a14059a8  00007ff7`190d1cd6 StackOverflowFindData!Func+0x46 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 5]
0000004e`a14059b0  00007fff`c98c5080 MSVCP140!std::cout
0000004e`a14059b8  00007ff7`190d7bb0 StackOverflowFindData!__xt_z+0x110
0000004e`a14059c0  0000004e`00000000
0000004e`a14059c8  00007ff7`190d7bf3 StackOverflowFindData!__xt_z+0x153
0000004e`a14059d0  00007fff`d1059570 ucrtbase!_argc
0000004e`a14059d8  00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
0000004e`a14059e0  0000004e`a14ff8d0
......
0000004e`a14ff3b8  00007ff7`190d7bf3 StackOverflowFindData!__xt_z+0x153
0000004e`a14ff3c0  00007fff`d1059570 ucrtbase!_argc
0000004e`a14ff3c8  00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
0000004e`a14ff3d0  0000004e`a14ff8d0
0000004e`a14ff3d8  00007ff7`190d7bb0 StackOverflowFindData!__xt_z+0x110
0000004e`a14ff3e0  0000004e`00000000
0000004e`a14ff3e8  00007ff7`190d7bf3 StackOverflowFindData!__xt_z+0x153
0000004e`a14ff3f0  00007fff`d1059570 ucrtbase!_argc
0000004e`a14ff3f8  00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
0000004e`a14ff400  0000004e`a14ff8d0
0000004e`a14ff408  00007ff7`190d7bb0 StackOverflowFindData!__xt_z+0x110
0000004e`a14ff410  0000004e`00000000
0000004e`a14ff418  00007ff7`190d7bf3 StackOverflowFindData!__xt_z+0x153
0000004e`a14ff420  00007fff`d1059570 ucrtbase!_argc
0000004e`a14ff428  00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
0000004e`a14ff430  0000004e`a14ff8d0
0000004e`a14ff438  00007ff7`190d7bb0 StackOverflowFindData!__xt_z+0x110
......
0000004e`a14ff860  0000004e`00000000
0000004e`a14ff868  00007ff7`190d7bf3 StackOverflowFindData!__xt_z+0x153
0000004e`a14ff870  00007fff`d1059570 ucrtbase!_argc
0000004e`a14ff878  00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
0000004e`a14ff880  0000004e`a14ff8d0
0000004e`a14ff888  00007ff7`190d7bb0 StackOverflowFindData!__xt_z+0x110
0000004e`a14ff890  00000000`00000000
0000004e`a14ff898  00000000`4000006a
0000004e`a14ff8a0  00000000`00000001
0000004e`a14ff8a8  00007ff7`190d1d80 StackOverflowFindData!TriggerStackOverFlow+0x50 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 14]
......
0000004e`a14ffcc0  00002372`af0f3fac
0000004e`a14ffcc8  00000000`00000000
0000004e`a14ffcd0  00000000`00000044
0000004e`a14ffcd8  0000015e`fd10d100
0000004e`a14ffce0  00000000`00000000
0000004e`a14ffce8  00007ff7`190d1e49 StackOverflowFindData!main+0x9 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 18]
0000004e`a14ffcf0  00000000`0000001f
......

因为是样例程序,可能看不出问题的重点。这里不妨假设一下Func是一个第三方库的函数,如果通过k [FrameCount] 最多打印的函数调用栈也只能看到Func调用,却看不到是自己编写的程序哪里触发调用了Func,尤其是多处调用了第三方库的Func,甚至是调用了第三方库接口后,间接再调用了第三方库里的Func。那么通过上述方法就能够确定到,原来是我们自己编写的代码TriggerStackOverFlow调用了这个函数。

找到了触发的函数,那么这个时候没有栈帧编号,也无法用.frame [FrameNumber]去切换到TriggerStackOverFlow去直接查看函数调用中的一些参数或者局部变量的值了。那么我们接着下一章,讲一讲如何获取。

查找触发栈溢出的输入数据

在上一章的提到的情况下,可以设置BasePtr来设置栈帧: .frame /c /r = BasePtr

这个笔者测试过,并不是所有的地址都可以获取到正确的函数调用栈,在不确定的时候,可以在栈底附近的多个函数调用栈帧的地址都做尝试。比如笔者测试的地址位置如下:

0:000> .frame /c /r = 0000004e`a14ff400
00 0000004e`a14ff400 00007ff7`190d7bb0 KERNELBASE!WriteFile+0x73
rax=0000004ea1404040 rbx=0000004ea1404090 rcx=0000000000000058
rdx=0000000000000000 rsi=0000000000000058 rdi=0000000000000000
rip=00007fffd10a2573 rsp=0000004ea14ff400 rbp=0000004ea14055c1
 r8=0000000000000000  r9=0000000000000000 r10=0000000000000000
r11=0000004ea14040a0 r12=0000000000000040 r13=0000000000000000
r14=0000000000000058 r15=0000000000000001
iopl=0         nv up ei pl zr na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
KERNELBASE!WriteFile+0x73:
00007fff`d10a2573 ff152f0a1500    call    qword ptr [KERNELBASE!_imp_NtWriteFile (00007fff`d11f2fa8)] ds:00007fff`d11f2fa8={ntdll!NtWriteFile (00007fff`d4a5abc0)}

紧接着查看函数调用栈,查看新的栈帧。栈帧0x1b对应了函数调用TriggerStackOverFlow

0:000> k
  *** Stack trace for last set context - .thread/.cxr resets it
 # Child-SP          RetAddr           Call Site
00 0000004e`a14ff400 00007ff7`190d7bb0 KERNELBASE!WriteFile+0x73
01 0000004e`a14ff470 0000004e`00000000 StackOverflowFindData!__xt_z+0x110
02 0000004e`a14ff478 00007ff7`190d7bf3 0x0000004e`00000000
03 0000004e`a14ff480 00007fff`d1059570 StackOverflowFindData!__xt_z+0x153
04 0000004e`a14ff488 00007ff7`190d1d05 ucrtbase!_argc
05 0000004e`a14ff490 00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
06 0000004e`a14ff4c0 00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
07 0000004e`a14ff4f0 00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
08 0000004e`a14ff520 00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
09 0000004e`a14ff550 00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
0a 0000004e`a14ff580 00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
0b 0000004e`a14ff5b0 00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
0c 0000004e`a14ff5e0 00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
0d 0000004e`a14ff610 00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
0e 0000004e`a14ff640 00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
0f 0000004e`a14ff670 00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
10 0000004e`a14ff6a0 00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
11 0000004e`a14ff6d0 00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
12 0000004e`a14ff700 00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
13 0000004e`a14ff730 00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
14 0000004e`a14ff760 00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
15 0000004e`a14ff790 00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
16 0000004e`a14ff7c0 00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
17 0000004e`a14ff7f0 00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
18 0000004e`a14ff820 00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
19 0000004e`a14ff850 00007ff7`190d1d05 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
1a 0000004e`a14ff880 00007ff7`190d1d80 StackOverflowFindData!Func+0x75 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 8]
1b 0000004e`a14ff8b0 00007ff7`190d1e49 StackOverflowFindData!TriggerStackOverFlow+0x50 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 14]
1c 0000004e`a14ffcf0 00007ff7`190d227c StackOverflowFindData!main+0x9 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 18]
1d (Inline Function) --------`-------- StackOverflowFindData!invoke_main+0x22 [d:\agent\_work\2\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 78]
1e 0000004e`a14ffd20 00007fff`d41c4034 StackOverflowFindData!__scrt_common_main_seh+0x10c [d:\agent\_work\2\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288]
1f 0000004e`a14ffd60 00007fff`d4a33691 KERNEL32!BaseThreadInitThunk+0x14
20 0000004e`a14ffd90 00000000`00000000 ntdll!RtlUserThreadStart+0x21

然后我们切换到栈帧TriggerStackOverFlow,并且查看局部变量的信息:

0:000> .frame 1b; dv /v /t
1b 0000004e`a14ff8b0 00007ff7`190d1e49 StackOverflowFindData!TriggerStackOverFlow+0x50 [c:\personal\sync\beyourbest\cpp\windbgsample\stackoverflowfinddata\source.cpp @ 14]
0000004e`a14ff8d0 char [1000] csTmpStr = char [1000] "Data Trigger StackOverFlow"

此时我们终于查找到了触发栈溢出的输入数据。这个例子是一个字符串,但是也有可能你的输入数据是一段二进制,比如一个读入到内存的文件。那么这个时候并不会像字符串一样直观,需要导出来。这个时候可以利用.writemem这个命令,以这个字符串为例,我们可以操作如下,便把这1000个字节的内容全部导出到文件sample.txt中了。

0:000> .writemem c:\\test\\sample.txt 0000004e`a14ff8d0 L?0n1000
Writing 3e8 bytes.

既然触发栈溢出的数据已经导出,那么根据这个数据重现、调试,便比较容易找出逻辑bug所在了。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-05-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一个程序员的修炼之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 程序样例
  • 找出函数调用栈
  • 查找触发栈溢出的输入数据
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档