一个Buffer Overwritten case分析笔记

最近QA测试的时候发现在导入一个数据文件的时候会出现access violation。导入其他数据文件则没有问题。初步判断是heap corruption。

由于QA使用的是release baseline,于是本地换成了debug baseline,这样对于debug会有帮助。

在debug baseline上导入同样的数据文件,同样会出现问题。只不过由于是debug baseline,它会报出下面的消息:

Heap missing last entry in committed range near 0x2fba26b8

(你必须用windbg attach上进程,然后上述信息才会显示)

于是,看一下heap block信息。

0:008> !heap -x 0x2fba26b8
HEAP 073a0000 (Seg 25400000) At 2fba29e0 Error: invalid block size

Entry     User      Heap      Segment       Size  PrevSize  Unused    Flags
-----------------------------------------------------------------------------
2fba26b8  2fba26c0  073a0000  25400000       328        30         c  busy

这个上面说一个heap block 2fba29e0的block size记录不对。

如果你注意的话,block 2fba29e0 其实就是在2fba26b8 后面的那个block。

2fba26b8 + 0x328 = 2fba29e0

于是看一下2fba29e0的meta data。

0:008> dt _HEAP_ENTRY 2fba29e0
ntdll!_HEAP_ENTRY
   +0x000 Size             : 0
   +0x002 PreviousSize     : 0
   +0x000 SubSegmentCode   : (null)
   +0x004 SmallTagIndex    : 0xcd ''
   +0x005 Flags            : 0xcd ''
   +0x006 UnusedBytes      : 0xcd ''
   +0x007 SegmentIndex     : 0xcd ''

heap block的meta data显然被破坏了,因为不可能分配一个0 字节的block啊

难道是2fba26b8 的block给破坏了?

由于是debug baseline, debug crt会使用32字节来存一些bookkeeping的信息。于是看一下。

0:008> dt _CrtMemBlockHeader 2fba26b8+0x8 
msvcr90d!_CrtMemBlockHeader
   +0x000 pBlockHeaderNext : 0x2fba1408 _CrtMemBlockHeader
   +0x004 pBlockHeaderPrev : 0x2fba2778 _CrtMemBlockHeader
   +0x008 szFileName       : (null)
   +0x00c nLine            : 0
   +0x010 nDataSize        : 0x88
   +0x014 nBlockUse        : 1
   +0x018 lRequest         : 13136747
   +0x01c gap              : [4]  "???"

注意nDataSize这一行。0x88?? 0x88表示用于想分配0x88个字节的空间。

还记得heap block meta data吗?再看一眼。


Entry     User      Heap      Segment       Size  PrevSize  Unused    Flags
-----------------------------------------------------------------------------
2fba26b8  2fba26c0  073a0000  25400000       328        30         c  busy

heap block meta data里记录了当前这个block是0x328个字节。

heap manager和CRT 记录的不一样啊。。。。。。

顿时陷入了无穷的困惑中。。。。。。

这块内存究竟是多大了?

幸好,我们还有另外一个手段。DEBUG CRT会在用户空间的前后加上一些保护字段(fdfdfdfd)标志。来看一下吧

0:008>  dc 2fba26b8 2fba2788
2fba26b8  00060065 0a0c0191 2fba1408 2fba2778  e........../x'./
2fba26c8  00000000 00000000 00000088 00000001  ................
2fba26d8  00c8736b fdfdfdfd 39c69d34 ffffffff  ks......4..9....
2fba26e8  00000000 00000000 2f8cc200 00000000  .........../....
2fba26f8  00000000 cdcdcdcd cdcdcdcd 00000000  ................
2fba2708  39c66190 00000005 00000005 2fba2798  .a.9.........'./
2fba2718  00000001 00000004 00000001 00000000  ................
2fba2728  00000002 2fbe22a2 0000007b 00000001  ....."./{.......
2fba2738  00000000 00000002 00000000 00000004  ................
2fba2748  00000001 00000000 00000000 00000000  ................
2fba2758  00000000 00000001 00000000 00000000  ................
2fba2768  fdfdfdfd 00000000 00170008 0a0801a8  ................
2fba2778  2fba26c0 2fba27b8 00000000 00000000  .&./.'./........
2fba2788  00000014

注意,从2fba26e0是用户空间起点。我们注意到,在2fba2768 有结束标志fdfdfdfd。而2fba2768-2fba26e0=0x88。

这说明CRT的记录是正确的。那为什么heap block meta data会记录错误了?

如果你再注意一下,在2fba2770处,应该是下一个Heap block的起点。从上面可以看出,当前这个block size应该是0x17*8 = 0xb8。而 2fba26b8+0xb8 = 2fba2770。该地址正是下一个heap block的起点。

于是,我们可以肯定,block 2fba26b8 的meta data被别人改写了。

一般而言,都是由于前面的block越界写了。于是,我们决定看一下前面的block是啥东东。

0:008> dc 2fba26b8-0x30 2fba26b8
2fba2688  00080006 0a0b0197 2fbbd3a8 30054a80  .........../.J.0
2fba2698  00000000 00000000 00000001 00000001  ................
2fba26a8  00c96597 fdfdfdfd 656c6554 706f6353     .e......TeleScop
2fba26b8  00060065                                                 e...

记住,0x30是前一个block size。所以我们减去0x30,就得到了前一个block 的起始地址。

我们发现,前一个heap block记录了一串字符, telescope。

但是我们发现,用户空间的前面有fdfdfdfd保护,而后面的fdfdfdfd消失了。。。

我们再看一下crt的bookkeeping信息。

0:008> dt _CrtMemBlockHeader 2fba2688+0x8 
msvcr90d!_CrtMemBlockHeader
   +0x000 pBlockHeaderNext : 0x2fbbd3a8 _CrtMemBlockHeader
   +0x004 pBlockHeaderPrev : 0x30054a80 _CrtMemBlockHeader
   +0x008 szFileName       : (null)
   +0x00c nLine            : 0
   +0x010 nDataSize        : 1
   +0x014 nBlockUse        : 1
   +0x018 lRequest         : 13198743
   +0x01c gap              : [4]  "???"

只申请了1个字节?但是却存了10个字符!!

于是,真相大白。该处只申请了1个字节,却存了10个字符。10个字符不仅把fdfdfdfd给覆盖了,而且覆盖了下一个block的meta data的前两个字节,改写了下一个block的“block size”。

接下来的工作就轻松许多了。我们只需要看一下数据文件里哪些地方存储了TeleScope这个字符,然后看看是怎么解析它的了。

原文地址:https://www.cnblogs.com/xiaxi/p/2059590.html