突破C++的虚拟指针--C++程序的缓冲区溢出攻击
2007-10-27 07:46
393 查看
2000年12月21日 15:43:00
作者:rix (rix@securiweb.net)
backend注:本文来自Phrack56期的《SMASHING C++ VPTRS》。正如大多数国外黑客的文章,技术原理及应用都讲得比较详细,但所提供的源代码似乎总是会存在不大不小的问题。这也许是因为他们觉得应该让读者自己去研究和调试,以更好地掌握这些技术。或许以后我也会这样做。;)
测试环境:
操作系统:Red Hat 6.1 (i386)
内核版本:Kernel 2.2.14
内核补丁:None Non-executable stack patch (by Solar Design)
C++编译器:gcc
---[[ 前言 ]]--------------------------------------
到目前为止,我所掌握的缓冲区溢出程序都是针对C编程语言的。虽然C语言编程在UNIX系统中几乎无处不在,但越来越多的C++程序也开始出现了。对于大多数情况,C语言的溢出技术对于C++语言也是适用的,但C++的面向对象的特性也导致了新的缓冲区溢出技术。下面以x86 Linux系统和C++ GNU编译器为平台进行分析。
---[[ 基础--简单的C++程序 ]]--------------------------------------
我不愿在这里浪费时间讲解太多的C++语言基础。如果你对C++或面向对象编程技术一无所知,请先找本这方面的书籍看看。在继续往下看之前,请确认你已经掌握或了解以下C++术语:
1、Class(类)
2、Object(对象)
3、Method(方法)
4、Virtual(虚拟)
5、Inherit(继承)
6、Derivative(派生)
接着,把下面的两个程序看完,确认你了解每条语句的含义和作用:
// bo1.cpp
// C++基础程序
#include >stdio.h<
#include >string.h<
class MyClass
{
private:
char Buffer[32];
public:
void SetBuffer(char *String)
{
strcpy(Buffer, String);
}
void PrintBuffer()
{
printf("%s/n", Buffer);
}
};
void main()
{
MyClass Object;
Object.SetBuffer("string");
Object.PrintBuffer();
}
===========================================================
// bo2.cpp
// 有缓冲区溢出漏洞的常见C++程序
#include >stdio.h<
#include >string.h<
class BaseClass
{
private:
char Buffer[32];
public:
void SetBuffer(char *String)
{
strcpy(Buffer,String); // 存在缓冲区溢出漏洞
}
virtual void PrintBuffer()
{
printf("%s/n",Buffer);
}
};
class MyClass1:public BaseClass
{
public:
void PrintBuffer()
{
printf("MyClass1: ");
BaseClass::PrintBuffer();
}
};
class MyClass2:public BaseClass
{
public:
void PrintBuffer()
{
printf("MyClass2: ");
BaseClass::PrintBuffer();
}
};
void main()
{
BaseClass *Object[2];
Object[0] = new MyClass1;
Object[1] = new MyClass2;
Object[0]-main<: push %ebp
0x8049401 >main+1<: mov %esp,%ebp
0x8049403 >main+3<: sub $0x8,%esp
0x8049406 >main+6<: push %edi
0x8049407 >main+7<: push %esi
0x8049408 >main+8<: push %ebx
0x8049409 >main+9<: push $0x24
0x804940b >main+11<: call 0x804b580 >__builtin_new<
0x8049410 >main+16<: add $0x4,%esp
0x8049413 >main+19<: mov %eax,%eax
0x8049415 >main+21<: mov %eax,%ebx
0x8049417 >main+23<: push %ebx
0x8049418 >main+24<: call 0x804c90c >__8MyClass1<
0x804941d >main+29<: add $0x4,%esp
0x8049420 >main+32<: mov %eax,%esi
0x8049422 >main+34<: jmp 0x8049430 >main+48<
0x8049424 >main+36<: call 0x8049c3c >__throw<
0x8049429 >main+41<: lea 0x0(%esi,1),%esi
0x8049430 >main+48<: mov %esi,0xfffffff8(%ebp)
0x8049433 >main+51<: push $0x24
0x8049435 >main+53<: call 0x804b580 >__builtin_new<
0x804943a >main+58<: add $0x4,%esp
0x804943d >main+61<: mov %eax,%eax
0x804943f >main+63<: mov %eax,%esi
0x8049441 >main+65<: push %esi
0x8049442 >main+66<: call 0x804c8ec >__8MyClass2<
0x8049447 >main+71<: add $0x4,%esp
0x804944a >main+74<: mov %eax,%edi
0x804944c >main+76<: jmp 0x8049455 >main+85<
0x804944e >main+78<: mov %esi,%esi
0x8049450 >main+80<: call 0x8049c3c >__throw<
0x8049455 >main+85<: mov %edi,0xfffffffc(%ebp)
0x8049458 >main+88<: push $0x804cda2
0x804945d >main+93<: mov 0xfffffff8(%ebp),%eax
0x8049460 >main+96<: push %eax
0x8049461 >main+97<: call 0x804c930 >SetBuffer__9BaseClassPc<
0x8049466 >main+102<: add $0x8,%esp
0x8049469 >main+105<: push $0x804cdaa
---Type >return< to continue, or q >return< to quit---
0x804946e >main+110<: mov 0xfffffffc(%ebp),%eax
0x8049471 >main+113<: push %eax
0x8049472 >main+114<: call 0x804c930 >SetBuffer__9BaseClassPc<
0x8049477 >main+119<: add $0x8,%esp
0x804947a >main+122<: mov 0xfffffff8(%ebp),%edx
0x804947d >main+125<: mov 0x20(%edx),%eax
0x8049480 >main+128<: add $0x8,%eax
0x8049483 >main+131<: mov 0xfffffff8(%ebp),%edx
0x8049486 >main+134<: push %edx
0x8049487 >main+135<: mov (%eax),%edi
0x8049489 >main+137<: call *%edi
0x804948b >main+139<: add $0x4,%esp
0x804948e >main+142<: mov 0xfffffffc(%ebp),%edx
0x8049491 >main+145<: mov 0x20(%edx),%eax
0x8049494 >main+148<: add $0x8,%eax
0x8049497 >main+151<: mov 0xfffffffc(%ebp),%edx
0x804949a >main+154<: push %edx
0x804949b >main+155<: mov (%eax),%edi
0x804949d >main+157<: call *%edi
0x804949f >main+159<: add $0x4,%esp
0x80494a2 >main+162<: xor %eax,%eax
0x80494a4 >main+164<: jmp 0x80494d0 >main+208<
0x80494a6 >main+166<: jmp 0x80494d0 >main+208<
0x80494a8 >main+168<: push %ebx
0x80494a9 >main+169<: call 0x804b4f0 >__builtin_delete<
0x80494ae >main+174<: add $0x4,%esp
0x80494b1 >main+177<: jmp 0x8049424 >main+36<
0x80494b6 >main+182<: push %esi
0x80494b7 >main+183<: call 0x804b4f0 >__builtin_delete<
0x80494bc >main+188<: add $0x4,%esp
0x80494bf >main+191<: jmp 0x8049450 >main+80<
0x80494c1 >main+193<: jmp 0x80494c8 >main+200<
0x80494c3 >main+195<: call 0x8049c3c >__throw<
0x80494c8 >main+200<: call 0x8049fc0 >terminate__Fv<
0x80494cd >main+205<: lea 0x0(%esi),%esi
0x80494d0 >main+208<: lea 0xffffffec(%ebp),%esp
0x80494d3 >main+211<: pop %ebx
0x80494d4 >main+212<: pop %esi
0x80494d5 >main+213<: pop %edi
---Type >return< to continue, or q >return< to quit---
0x80494d6 >main+214<: leave
0x80494d7 >main+215<: ret
0x80494d8 >main+216<: nop
0x80494d9 >main+217<: nop
0x80494da >main+218<: nop
0x80494db >main+219<: nop
0x80494dc >main+220<: nop
0x80494dd >main+221<: nop
0x80494de >main+222<: nop
0x80494df >main+223<: nop
End of assembler dump.
(gdb)
以下是对该程序汇编代码的解释:
0x8049400 >main<: push %ebp
0x8049401 >main+1<: mov %esp,%ebp
0x8049403 >main+3<: sub $0x8,%esp
0x8049406 >main+6<: push %edi
0x8049407 >main+7<: push %esi
0x8049408 >main+8<: push %ebx
构建堆栈。为Object[]数组保留8个字节(即两个4字节指针地址),则Object[0]的指针存放在0xfffffff8(%ebp),Object[1]的指针存放在0fffffffc(%ebp)。接着保存寄存器。
0x8049409 >main+9<: push $0x24
0x804940b >main+11<: call 0x804b580 >__builtin_new<
0x8049410 >main+16<: add $0x4,%esp
首先调用__builtin_new,在堆(heap)中分配0x24(36字节)给Object[0],并将其首地址保存到EAX寄存器中。这36字节中前32字节是Buffer变量的,后4字节由VPTR占用。
0x8049413 >main+19<: mov %eax,%eax
0x8049415 >main+21<: mov %eax,%ebx
0x8049417 >main+23<: push %ebx
0x8049418 >main+24<: call 0x804c90c >__8MyClass1<
0x804941d >main+29<: add $0x4,%esp
将对象的首地址压栈,然后调用__8MyClass1函数。这其实是MyClass1对象的构造函数(constructor)。
(gdb) disassemble __8MyClass1
Dump of assembler code for function __8MyClass1:
0x804c90c >__8MyClass1<: push %ebp
0x804c90d >__8MyClass1+1<: mov %esp,%ebp
0x804c90f >__8MyClass1+3<: push %ebx
0x804c910 >__8MyClass1+4<: mov 0x8(%ebp),%ebx
寄存器EBX现在存放着指向分配的36个字节的指针(在C++语言中,称之为"This"指针)。
0x804c913 >__8MyClass1+7<: push %ebx
0x804c914 >__8MyClass1+8<: call 0x804c958 >__9BaseClass<
0x804c919 >__8MyClass1+13<: add $0x4,%esp
首先调用基类BaseClass的构造函数。
(gdb) disassemble __9BaseClass
Dump of assembler code for function __9BaseClass:
0x804c958 >__9BaseClass<: push %ebp
0x804c959 >__9BaseClass+1<: mov %esp,%ebp
0x804c95b >__9BaseClass+3<: mov 0x8(%ebp),%edx
寄存器EDX现在存放着指向分配的36个字节的指针("This"指针)。
0x804c95e >__9BaseClass+6<: movl $0x804e01c,0x20(%edx)
将0x804e01c存放到EDX+0x20(=EDX+32)。让我们看看该0x804e01c地址内存数据:
(gdb) x 0x804e01c
0x804e01c >__vt_9BaseClass<: 0x00000000
可以看到这个存放到EDX+0x20(即该对象的VPTR位置)的地址是基类BaseClass的VTABLE地址。
现在回到MyClass1对象的构造函数:
0x804c91c >__8MyClass1+16<: movl $0x804e010,0x20(%ebx)
将0x804e010存放到EBX+0x20(即VPTR)。同样让我们看看该0x804e010地址内存数据:
(gdb) x 0x804e010
0x804e010 >__vt_8MyClass1<: 0x00000000
现在,我们知道VPTR被改写了,再在它的内容是MyClass1对象的VTABLE地址。当返回到main()函数时寄存器EAX中存放着该对象在内存中的指针。
0x8049420 >main+32<: mov %eax,%esi
0x8049422 >main+34<: jmp 0x8049430 >main+48<
0x8049424 >main+36<: call 0x8049c3c >__throw<
0x8049429 >main+41<: lea 0x0(%esi,1),%esi
0x8049430 >main+48<: mov %esi,0xfffffff8(%ebp)
将得到的地址指针赋予Object[0]。然后程序对Object[1]进行同样的处理,只不过返回的地址不同罢了。在经过以上对象初始化处理后,将执行以下指令:
0x8049458 >main+88<: push $0x804cda2
0x804945d >main+93<: mov 0xfffffff8(%ebp),%eax
0x8049460 >main+96<: push %eax
将0x804cda2和Object[0]的值压栈。观察一下0x804cda2的内容:
(gdb) x/s 0x804cda2
0x804cda2 >_IO_stdin_used+30<: "string1"
可知该地址存放了将要通过基类BaseClass的SetBuffer函数拷贝到Buffer中的字符串"string1"。
0x8049461 >main+97<: call 0x804c930 >SetBuffer__9BaseClassPc<
0x8049466 >main+102<: add $0x8,%esp
调用基类BaseClass的SetBuffer()方法。注意到这种SetBuffer方法的调用是“静态绑定”(因为它不是虚拟方法)。对Object[1]的处理也是一样的。
为了验证这两个对象在运行时都被正确地初始化,我们将要设置如下断点:
0x8049410: 获得第一个对象的地址。
0x804943a: 获得第二个对象的地址。
0x804947a: 检验对象的初始化是否正确。
(gdb) break *0x8049410
Breakpoint 1 at 0x8049410
(gdb) break *0x804943a
Breakpoint 2 at 0x804943a
(gdb) break *0x804947a
Breakpoint 3 at 0x804947a
现在运行这个程序:
St
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=3278
作者:rix (rix@securiweb.net)
backend注:本文来自Phrack56期的《SMASHING C++ VPTRS》。正如大多数国外黑客的文章,技术原理及应用都讲得比较详细,但所提供的源代码似乎总是会存在不大不小的问题。这也许是因为他们觉得应该让读者自己去研究和调试,以更好地掌握这些技术。或许以后我也会这样做。;)
测试环境:
操作系统:Red Hat 6.1 (i386)
内核版本:Kernel 2.2.14
内核补丁:None Non-executable stack patch (by Solar Design)
C++编译器:gcc
---[[ 前言 ]]--------------------------------------
到目前为止,我所掌握的缓冲区溢出程序都是针对C编程语言的。虽然C语言编程在UNIX系统中几乎无处不在,但越来越多的C++程序也开始出现了。对于大多数情况,C语言的溢出技术对于C++语言也是适用的,但C++的面向对象的特性也导致了新的缓冲区溢出技术。下面以x86 Linux系统和C++ GNU编译器为平台进行分析。
---[[ 基础--简单的C++程序 ]]--------------------------------------
我不愿在这里浪费时间讲解太多的C++语言基础。如果你对C++或面向对象编程技术一无所知,请先找本这方面的书籍看看。在继续往下看之前,请确认你已经掌握或了解以下C++术语:
1、Class(类)
2、Object(对象)
3、Method(方法)
4、Virtual(虚拟)
5、Inherit(继承)
6、Derivative(派生)
接着,把下面的两个程序看完,确认你了解每条语句的含义和作用:
// bo1.cpp
// C++基础程序
#include >stdio.h<
#include >string.h<
class MyClass
{
private:
char Buffer[32];
public:
void SetBuffer(char *String)
{
strcpy(Buffer, String);
}
void PrintBuffer()
{
printf("%s/n", Buffer);
}
};
void main()
{
MyClass Object;
Object.SetBuffer("string");
Object.PrintBuffer();
}
===========================================================
// bo2.cpp
// 有缓冲区溢出漏洞的常见C++程序
#include >stdio.h<
#include >string.h<
class BaseClass
{
private:
char Buffer[32];
public:
void SetBuffer(char *String)
{
strcpy(Buffer,String); // 存在缓冲区溢出漏洞
}
virtual void PrintBuffer()
{
printf("%s/n",Buffer);
}
};
class MyClass1:public BaseClass
{
public:
void PrintBuffer()
{
printf("MyClass1: ");
BaseClass::PrintBuffer();
}
};
class MyClass2:public BaseClass
{
public:
void PrintBuffer()
{
printf("MyClass2: ");
BaseClass::PrintBuffer();
}
};
void main()
{
BaseClass *Object[2];
Object[0] = new MyClass1;
Object[1] = new MyClass2;
Object[0]-main<: push %ebp
0x8049401 >main+1<: mov %esp,%ebp
0x8049403 >main+3<: sub $0x8,%esp
0x8049406 >main+6<: push %edi
0x8049407 >main+7<: push %esi
0x8049408 >main+8<: push %ebx
0x8049409 >main+9<: push $0x24
0x804940b >main+11<: call 0x804b580 >__builtin_new<
0x8049410 >main+16<: add $0x4,%esp
0x8049413 >main+19<: mov %eax,%eax
0x8049415 >main+21<: mov %eax,%ebx
0x8049417 >main+23<: push %ebx
0x8049418 >main+24<: call 0x804c90c >__8MyClass1<
0x804941d >main+29<: add $0x4,%esp
0x8049420 >main+32<: mov %eax,%esi
0x8049422 >main+34<: jmp 0x8049430 >main+48<
0x8049424 >main+36<: call 0x8049c3c >__throw<
0x8049429 >main+41<: lea 0x0(%esi,1),%esi
0x8049430 >main+48<: mov %esi,0xfffffff8(%ebp)
0x8049433 >main+51<: push $0x24
0x8049435 >main+53<: call 0x804b580 >__builtin_new<
0x804943a >main+58<: add $0x4,%esp
0x804943d >main+61<: mov %eax,%eax
0x804943f >main+63<: mov %eax,%esi
0x8049441 >main+65<: push %esi
0x8049442 >main+66<: call 0x804c8ec >__8MyClass2<
0x8049447 >main+71<: add $0x4,%esp
0x804944a >main+74<: mov %eax,%edi
0x804944c >main+76<: jmp 0x8049455 >main+85<
0x804944e >main+78<: mov %esi,%esi
0x8049450 >main+80<: call 0x8049c3c >__throw<
0x8049455 >main+85<: mov %edi,0xfffffffc(%ebp)
0x8049458 >main+88<: push $0x804cda2
0x804945d >main+93<: mov 0xfffffff8(%ebp),%eax
0x8049460 >main+96<: push %eax
0x8049461 >main+97<: call 0x804c930 >SetBuffer__9BaseClassPc<
0x8049466 >main+102<: add $0x8,%esp
0x8049469 >main+105<: push $0x804cdaa
---Type >return< to continue, or q >return< to quit---
0x804946e >main+110<: mov 0xfffffffc(%ebp),%eax
0x8049471 >main+113<: push %eax
0x8049472 >main+114<: call 0x804c930 >SetBuffer__9BaseClassPc<
0x8049477 >main+119<: add $0x8,%esp
0x804947a >main+122<: mov 0xfffffff8(%ebp),%edx
0x804947d >main+125<: mov 0x20(%edx),%eax
0x8049480 >main+128<: add $0x8,%eax
0x8049483 >main+131<: mov 0xfffffff8(%ebp),%edx
0x8049486 >main+134<: push %edx
0x8049487 >main+135<: mov (%eax),%edi
0x8049489 >main+137<: call *%edi
0x804948b >main+139<: add $0x4,%esp
0x804948e >main+142<: mov 0xfffffffc(%ebp),%edx
0x8049491 >main+145<: mov 0x20(%edx),%eax
0x8049494 >main+148<: add $0x8,%eax
0x8049497 >main+151<: mov 0xfffffffc(%ebp),%edx
0x804949a >main+154<: push %edx
0x804949b >main+155<: mov (%eax),%edi
0x804949d >main+157<: call *%edi
0x804949f >main+159<: add $0x4,%esp
0x80494a2 >main+162<: xor %eax,%eax
0x80494a4 >main+164<: jmp 0x80494d0 >main+208<
0x80494a6 >main+166<: jmp 0x80494d0 >main+208<
0x80494a8 >main+168<: push %ebx
0x80494a9 >main+169<: call 0x804b4f0 >__builtin_delete<
0x80494ae >main+174<: add $0x4,%esp
0x80494b1 >main+177<: jmp 0x8049424 >main+36<
0x80494b6 >main+182<: push %esi
0x80494b7 >main+183<: call 0x804b4f0 >__builtin_delete<
0x80494bc >main+188<: add $0x4,%esp
0x80494bf >main+191<: jmp 0x8049450 >main+80<
0x80494c1 >main+193<: jmp 0x80494c8 >main+200<
0x80494c3 >main+195<: call 0x8049c3c >__throw<
0x80494c8 >main+200<: call 0x8049fc0 >terminate__Fv<
0x80494cd >main+205<: lea 0x0(%esi),%esi
0x80494d0 >main+208<: lea 0xffffffec(%ebp),%esp
0x80494d3 >main+211<: pop %ebx
0x80494d4 >main+212<: pop %esi
0x80494d5 >main+213<: pop %edi
---Type >return< to continue, or q >return< to quit---
0x80494d6 >main+214<: leave
0x80494d7 >main+215<: ret
0x80494d8 >main+216<: nop
0x80494d9 >main+217<: nop
0x80494da >main+218<: nop
0x80494db >main+219<: nop
0x80494dc >main+220<: nop
0x80494dd >main+221<: nop
0x80494de >main+222<: nop
0x80494df >main+223<: nop
End of assembler dump.
(gdb)
以下是对该程序汇编代码的解释:
0x8049400 >main<: push %ebp
0x8049401 >main+1<: mov %esp,%ebp
0x8049403 >main+3<: sub $0x8,%esp
0x8049406 >main+6<: push %edi
0x8049407 >main+7<: push %esi
0x8049408 >main+8<: push %ebx
构建堆栈。为Object[]数组保留8个字节(即两个4字节指针地址),则Object[0]的指针存放在0xfffffff8(%ebp),Object[1]的指针存放在0fffffffc(%ebp)。接着保存寄存器。
0x8049409 >main+9<: push $0x24
0x804940b >main+11<: call 0x804b580 >__builtin_new<
0x8049410 >main+16<: add $0x4,%esp
首先调用__builtin_new,在堆(heap)中分配0x24(36字节)给Object[0],并将其首地址保存到EAX寄存器中。这36字节中前32字节是Buffer变量的,后4字节由VPTR占用。
0x8049413 >main+19<: mov %eax,%eax
0x8049415 >main+21<: mov %eax,%ebx
0x8049417 >main+23<: push %ebx
0x8049418 >main+24<: call 0x804c90c >__8MyClass1<
0x804941d >main+29<: add $0x4,%esp
将对象的首地址压栈,然后调用__8MyClass1函数。这其实是MyClass1对象的构造函数(constructor)。
(gdb) disassemble __8MyClass1
Dump of assembler code for function __8MyClass1:
0x804c90c >__8MyClass1<: push %ebp
0x804c90d >__8MyClass1+1<: mov %esp,%ebp
0x804c90f >__8MyClass1+3<: push %ebx
0x804c910 >__8MyClass1+4<: mov 0x8(%ebp),%ebx
寄存器EBX现在存放着指向分配的36个字节的指针(在C++语言中,称之为"This"指针)。
0x804c913 >__8MyClass1+7<: push %ebx
0x804c914 >__8MyClass1+8<: call 0x804c958 >__9BaseClass<
0x804c919 >__8MyClass1+13<: add $0x4,%esp
首先调用基类BaseClass的构造函数。
(gdb) disassemble __9BaseClass
Dump of assembler code for function __9BaseClass:
0x804c958 >__9BaseClass<: push %ebp
0x804c959 >__9BaseClass+1<: mov %esp,%ebp
0x804c95b >__9BaseClass+3<: mov 0x8(%ebp),%edx
寄存器EDX现在存放着指向分配的36个字节的指针("This"指针)。
0x804c95e >__9BaseClass+6<: movl $0x804e01c,0x20(%edx)
将0x804e01c存放到EDX+0x20(=EDX+32)。让我们看看该0x804e01c地址内存数据:
(gdb) x 0x804e01c
0x804e01c >__vt_9BaseClass<: 0x00000000
可以看到这个存放到EDX+0x20(即该对象的VPTR位置)的地址是基类BaseClass的VTABLE地址。
现在回到MyClass1对象的构造函数:
0x804c91c >__8MyClass1+16<: movl $0x804e010,0x20(%ebx)
将0x804e010存放到EBX+0x20(即VPTR)。同样让我们看看该0x804e010地址内存数据:
(gdb) x 0x804e010
0x804e010 >__vt_8MyClass1<: 0x00000000
现在,我们知道VPTR被改写了,再在它的内容是MyClass1对象的VTABLE地址。当返回到main()函数时寄存器EAX中存放着该对象在内存中的指针。
0x8049420 >main+32<: mov %eax,%esi
0x8049422 >main+34<: jmp 0x8049430 >main+48<
0x8049424 >main+36<: call 0x8049c3c >__throw<
0x8049429 >main+41<: lea 0x0(%esi,1),%esi
0x8049430 >main+48<: mov %esi,0xfffffff8(%ebp)
将得到的地址指针赋予Object[0]。然后程序对Object[1]进行同样的处理,只不过返回的地址不同罢了。在经过以上对象初始化处理后,将执行以下指令:
0x8049458 >main+88<: push $0x804cda2
0x804945d >main+93<: mov 0xfffffff8(%ebp),%eax
0x8049460 >main+96<: push %eax
将0x804cda2和Object[0]的值压栈。观察一下0x804cda2的内容:
(gdb) x/s 0x804cda2
0x804cda2 >_IO_stdin_used+30<: "string1"
可知该地址存放了将要通过基类BaseClass的SetBuffer函数拷贝到Buffer中的字符串"string1"。
0x8049461 >main+97<: call 0x804c930 >SetBuffer__9BaseClassPc<
0x8049466 >main+102<: add $0x8,%esp
调用基类BaseClass的SetBuffer()方法。注意到这种SetBuffer方法的调用是“静态绑定”(因为它不是虚拟方法)。对Object[1]的处理也是一样的。
为了验证这两个对象在运行时都被正确地初始化,我们将要设置如下断点:
0x8049410: 获得第一个对象的地址。
0x804943a: 获得第二个对象的地址。
0x804947a: 检验对象的初始化是否正确。
(gdb) break *0x8049410
Breakpoint 1 at 0x8049410
(gdb) break *0x804943a
Breakpoint 2 at 0x804943a
(gdb) break *0x804947a
Breakpoint 3 at 0x804947a
现在运行这个程序:
St
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=3278
相关文章推荐
- 突破C++的虚拟指针--C++程序的缓冲区溢出攻击
- 突破C++的虚拟指针--C++程序的缓冲区溢出攻击
- 突破C++的虚拟指针--C++程序的缓冲区溢出攻击
- 突破C++的虚拟指针--C++程序的缓冲区溢出攻击
- C++虚拟继承中_对象内存的分布_虚继承会多余分配虚表v-tab的指针vptr_图1-1清楚的描述了虚继承类对象内存的分布_转载淘宝共享数据平台
- C++析构函数的自动调用(析构函数必须是虚拟的,这样删除父类指针指向的子类对象,才能同时调用两者的析构函数,否则就没有机会调用子类析构函数)
- C++利用指针突破私有成员访问限制
- C++智能指针使用详解
- C++指针作为函数参数的问题
- C++指针详解
- C++返回引用类型 指针的引用
- C++中关于虚拟继承的一些总结
- C++中智能指针的设计和使用
- 对C++中this指针的认识
- 有关对c++指针的思考
- C++智能指针与重载操作符
- C\C++中结构体变量与结构体指针内存分配问题
- 【C++】指针解析
- c++之this指针做参数实现对象间的交互实例
- (转)c++ typedef 函数指针详细说明