📄 csdn_文档中心_突破c++的虚拟指针--c++程序的缓冲区溢出攻击.htm
字号:
<SetBuffer__9BaseClassPc><BR>0x8049466
<main+102>: add $0x8,%esp<BR>0x8049469
<main+105>: push $0x804cdaa<BR>---Type
<return> to continue, or q <return> to
quit---<BR>0x804946e
<main+110>: mov 0xfffffffc(%ebp),%eax<BR>0x8049471
<main+113>: push %eax<BR>0x8049472
<main+114>: call 0x804c930
<SetBuffer__9BaseClassPc><BR>0x8049477
<main+119>: add $0x8,%esp<BR>0x804947a
<main+122>: mov 0xfffffff8(%ebp),%edx<BR>0x804947d
<main+125>: mov 0x20(%edx),%eax<BR>0x8049480
<main+128>: add $0x8,%eax<BR>0x8049483
<main+131>: mov 0xfffffff8(%ebp),%edx<BR>0x8049486
<main+134>: push %edx<BR>0x8049487
<main+135>: mov (%eax),%edi<BR>0x8049489
<main+137>: call *%edi<BR>0x804948b
<main+139>: add $0x4,%esp<BR>0x804948e
<main+142>: mov 0xfffffffc(%ebp),%edx<BR>0x8049491
<main+145>: mov 0x20(%edx),%eax<BR>0x8049494
<main+148>: add $0x8,%eax<BR>0x8049497
<main+151>: mov 0xfffffffc(%ebp),%edx<BR>0x804949a
<main+154>: push %edx<BR>0x804949b
<main+155>: mov (%eax),%edi<BR>0x804949d
<main+157>: call *%edi<BR>0x804949f
<main+159>: add $0x4,%esp<BR>0x80494a2
<main+162>: xor %eax,%eax<BR>0x80494a4
<main+164>: jmp 0x80494d0 <main+208><BR>0x80494a6
<main+166>: jmp 0x80494d0 <main+208><BR>0x80494a8
<main+168>: push %ebx<BR>0x80494a9
<main+169>: call 0x804b4f0
<__builtin_delete><BR>0x80494ae
<main+174>: add $0x4,%esp<BR>0x80494b1
<main+177>: jmp 0x8049424 <main+36><BR>0x80494b6
<main+182>: push %esi<BR>0x80494b7
<main+183>: call 0x804b4f0
<__builtin_delete><BR>0x80494bc
<main+188>: add $0x4,%esp<BR>0x80494bf
<main+191>: jmp 0x8049450 <main+80><BR>0x80494c1
<main+193>: jmp 0x80494c8 <main+200><BR>0x80494c3
<main+195>: call 0x8049c3c
<__throw><BR>0x80494c8
<main+200>: call 0x8049fc0
<terminate__Fv><BR>0x80494cd
<main+205>: lea 0x0(%esi),%esi<BR>0x80494d0
<main+208>: lea 0xffffffec(%ebp),%esp<BR>0x80494d3
<main+211>: pop %ebx<BR>0x80494d4
<main+212>: pop %esi<BR>0x80494d5
<main+213>: pop %edi<BR>---Type <return> to
continue, or q <return> to quit---<BR>0x80494d6
<main+214>: leave <BR>0x80494d7
<main+215>: ret <BR>0x80494d8
<main+216>: nop <BR>0x80494d9
<main+217>: nop <BR>0x80494da
<main+218>: nop <BR>0x80494db
<main+219>: nop <BR>0x80494dc
<main+220>: nop <BR>0x80494dd
<main+221>: nop <BR>0x80494de
<main+222>: nop <BR>0x80494df
<main+223>: nop <BR>End of assembler
dump.<BR>(gdb)<BR><BR> 以下是对该程序汇编代码的解释:<BR><BR>0x8049400
<main>: push %ebp<BR>0x8049401
<main+1>: mov %esp,%ebp<BR>0x8049403
<main+3>: sub $0x8,%esp<BR>0x8049406
<main+6>: push %edi<BR>0x8049407
<main+7>: push %esi<BR>0x8049408
<main+8>: push %ebx<BR><BR> 构建堆栈。为Object[]数组保留8个字节(即两个4字节指针地址),则Object[0]的指针存放在0xfffffff8(%ebp),Object[1]的指针存放在0fffffffc(%ebp)。接着保存寄存器。<BR><BR>0x8049409
<main+9>: push $0x24<BR>0x804940b
<main+11>: call 0x804b580
<__builtin_new><BR>0x8049410
<main+16>: add $0x4,%esp<BR><BR> 首先调用__builtin_new,在堆(heap)中分配0x24(36字节)给Object[0],并将其首地址保存到EAX寄存器中。这36字节中前32字节是Buffer变量的,后4字节由VPTR占用。<BR><BR>0x8049413
<main+19>: mov %eax,%eax<BR>0x8049415
<main+21>: mov %eax,%ebx<BR>0x8049417
<main+23>: push %ebx<BR>0x8049418
<main+24>: call 0x804c90c
<__8MyClass1><BR>0x804941d
<main+29>: add $0x4,%esp<BR><BR> 将对象的首地址压栈,然后调用__8MyClass1函数。这其实是MyClass1对象的构造函数(constructor)。<BR><BR>(gdb)
disassemble __8MyClass1<BR>Dump of assembler code for function
__8MyClass1:<BR>0x804c90c
<__8MyClass1>: push %ebp<BR>0x804c90d
<__8MyClass1+1>: mov %esp,%ebp<BR>0x804c90f
<__8MyClass1+3>: push %ebx<BR>0x804c910
<__8MyClass1+4>: mov 0x8(%ebp),%ebx<BR><BR> 寄存器EBX现在存放着指向分配的36个字节的指针(在C++语言中,称之为"This"指针)。<BR><BR>0x804c913
<__8MyClass1+7>: push %ebx<BR>0x804c914
<__8MyClass1+8>: call 0x804c958
<__9BaseClass><BR>0x804c919
<__8MyClass1+13>: add $0x4,%esp<BR><BR> 首先调用基类BaseClass的构造函数。<BR><BR>(gdb)
disassemble __9BaseClass<BR>Dump of assembler code for function
__9BaseClass:<BR>0x804c958
<__9BaseClass>: push %ebp<BR>0x804c959
<__9BaseClass+1>: mov %esp,%ebp<BR>0x804c95b
<__9BaseClass+3>: mov 0x8(%ebp),%edx<BR><BR> 寄存器EDX现在存放着指向分配的36个字节的指针("This"指针)。<BR><BR>0x804c95e
<__9BaseClass+6>: movl $0x804e01c,0x20(%edx)<BR><BR> 将0x804e01c存放到EDX+0x20(=EDX+32)。让我们看看该0x804e01c地址内存数据:<BR><BR>(gdb)
x 0x804e01c<BR>0x804e01c
<__vt_9BaseClass>: 0x00000000<BR><BR> 可以看到这个存放到EDX+0x20(即该对象的VPTR位置)的地址是基类BaseClass的VTABLE地址。<BR> 现在回到MyClass1对象的构造函数:<BR><BR>0x804c91c
<__8MyClass1+16>: movl $0x804e010,0x20(%ebx)<BR><BR> 将0x804e010存放到EBX+0x20(即VPTR)。同样让我们看看该0x804e010地址内存数据:<BR><BR>(gdb)
x 0x804e010<BR>0x804e010
<__vt_8MyClass1>: 0x00000000<BR><BR> 现在,我们知道VPTR被改写了,再在它的内容是MyClass1对象的VTABLE地址。当返回到main()函数时寄存器EAX中存放着该对象在内存中的指针。<BR><BR>0x8049420
<main+32>: mov %eax,%esi<BR>0x8049422
<main+34>: jmp 0x8049430 <main+48><BR>0x8049424
<main+36>: call 0x8049c3c <__throw><BR>0x8049429
<main+41>: lea 0x0(%esi,1),%esi<BR>0x8049430
<main+48>: mov %esi,0xfffffff8(%ebp)<BR><BR> 将得到的地址指针赋予Object[0]。然后程序对Object[1]进行同样的处理,只不过返回的地址不同罢了。在经过以上对象初始化处理后,将执行以下指令:<BR><BR>0x8049458
<main+88>: push $0x804cda2<BR>0x804945d
<main+93>: mov 0xfffffff8(%ebp),%eax<BR>0x8049460
<main+96>: push %eax<BR><BR> 将0x804cda2和Object[0]的值压栈。观察一下0x804cda2的内容:<BR><BR>(gdb)
x/s 0x804cda2<BR>0x804cda2
<_IO_stdin_used+30>: "string1"<BR><BR> 可知该地址存放了将要通过基类BaseClass的SetBuffer函数拷贝到Buffer中的字符串"string1"。<BR><BR>0x8049461
<main+97>: call 0x804c930
<SetBuffer__9BaseClassPc><BR>0x8049466
<main+102>: add $0x8,%esp<BR><BR> 调用基类BaseClass的SetBuffer()方法。注意到这种SetBuffer方法的调用是“静态绑定”(因为它不是虚拟方法)。对Object[1]的处理也是一样的。<BR><BR> 为了验证这两个对象在运行时都被正确地初始化,我们将要设置如下断点:<BR><BR>0x8049410:
获得第一个对象的地址。<BR>0x804943a: 获得第二个对象的地址。<BR>0x804947a:
检验对象的初始化是否正确。<BR><BR>(gdb) break *0x8049410<BR>Breakpoint 1 at
0x8049410<BR>(gdb) break *0x804943a<BR>Breakpoint 2 at
0x804943a<BR>(gdb) break *0x804947a<BR>Breakpoint 3 at
0x804947a<BR><BR> 现在运行这个程序:<BR><BR>St<BR></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR>
<TABLE align=center bgColor=#006699 border=0 cellPadding=0 cellSpacing=0
width=770>
<TBODY>
<TR bgColor=#006699>
<TD align=middle bgColor=#006699 id=white><FONT
color=#ffffff>对该文的评论</FONT></TD>
<TD align=middle>
<SCRIPT
src="CSDN_文档中心_突破C++的虚拟指针--C++程序的缓冲区溢出攻击.files/readnum.htm"></SCRIPT>
</TD></TR></TBODY></TABLE>
<TABLE align=center bgColor=#666666 border=0 cellPadding=2 cellSpacing=1
width=770>
<TBODY>
<TR>
<TD bgColor=#cccccc colSpan=3><SPAN style="COLOR: #cccccc"><IMG height=16
hspace=1 src="CSDN_文档中心_突破C++的虚拟指针--C++程序的缓冲区溢出攻击.files/ico_pencil.gif"
width=16> </SPAN> ghj1976 <I>(2000-12-21
15:47:57)</I> </TD></TR>
<TR>
<TD bgColor=#ffffff colSpan=3 width=532><BR>现在运行这个程序: Starting program:
/home/backend/test/bo2 Breakpoint 1, 0x8049410 in main ()
查看EAX寄存器,将得到第一个对象的地址: (gdb) info reg eax eax 0x804f870 134543472
接着运行到下一个断点: (gdb) cont Continuing. Breakpoint 2, 0x804943a in main ()
查看第二个对象的地址: (gdb) info reg eax eax 0x804f898 134543512
继续运行,直到对象的构造函数和SetBuffer()方法均已执行。 (gdb) cont Continuing. Breakpoint 3,
0x804947a in main () 这两个对象实例的内存地址分别为0x804f870和0x804f898。然而,0x804f898 -
0x804f870 = 0x28(40),这表明在第一个对象与第二个对象之间多了4个字节的内容。让我们看看是什么: (gdb) x/aw
0x804f898-4 0x804f894: 0x29 原来是0x29。第二个对象的结尾也有这种特殊的数据: (gdb) x/xb
0x804f898+32+4 0x804f8bc: 0x49 下面我们来看一下初始化后这两个对象的结构内容: (gdb) x/s
0x804f870 0x804f870: “string1“ (gdb) x/a 0x804f870+32
0x804f890: 0x8048948 <__vt_8MyClass1> (gdb) x/s 0x804f898
0x804f898: “string2“ (gdb) x/a 0x804f898+32 0x804f8b8: 0x804e004
<force_to_data> 再看一下这两个对象实例的VTABLE中的内容: (gdb) x/a 0x804e010
0x804e010 <__vt_8MyClass1>: 0x0 (gdb) x/a 0x804e010+4 0x804e014
<__vt_8MyClass1+4>: 0x804ca10 <__tf8MyClass1> (gdb) x/a
0x804e010+8 0x804e018 <__vt_8MyClass1+8>: 0x804c9e0
<PrintBuffer__8MyClass1> (gdb) x/a 0x804e004 0x804e004
<force_to_data>: 0x0 (gdb) x/a 0x804e004+4 0x804e008
<force_to_data+4>: 0x804c9a0 <__tf8MyClass2> (gdb) x/a
0x804e004+8 0x804e00c <force_to_data+8>: 0x804c970
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -