Break through the Virtual pointer of C ++ -- Buffer Overflow Attack of C ++ Program

Source: Internet
Author: User
Backend Note: This article is from "smashing C ++ vptrs" in phrack56. As in most foreign hacker articles, the technical principles and applications are described in detail, but the source code provided always seems to be a small problem. This may be because they feel that they should let the readers study and debug the technology to better master the technology. Maybe I will do the same in the future .;)

Test environment:

Operating System: Red Hat 6.1 (i386)
Kernel version: Kernel 2.2.14
Kernel Patch: None non-executable stack patch (by solar design)
C ++ Compiler: gcc
  

--- [[Preface] ----------------------------------------

So far, all the buffer overflow programs I have mastered are for the C programming language. Although C language programming is almost ubiquitous in Unix systems, more and more C ++ programs are emerging. In most cases, the overflow Technology of C language is also applicable to C ++, but the object-oriented feature of C ++ also leads to a new buffer overflow technology. The following uses the x86 Linux System and the C ++ GNU Compiler as the platform for analysis.

--- [[Basic -- simple C ++ program] ------------------------------------------

I don't want to waste time explaining the basics of C ++. If you have no idea about C ++ or object-oriented programming technology, read this book first. Before proceeding, check whether you have mastered or understood the following C ++ terms:
  
1. Class)
2. Object)
3. Method)
4. Virtual)
5. inherit (inheritance)
6. Derivative (derived)

Next, read the following two programs and confirm that you understand the meaning and function of each statement:
  
// Bo1.cpp
// C ++ BASIC Program

# 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
// Common C ++ programs with Buffer Overflow Vulnerabilities

# Include <stdio. h>
# Include <string. h>

Class baseclass
{
PRIVATE:
Char buffer [32];
Public:
Void setbuffer (char * string)
{
Strcpy (buffer, string); // Buffer Overflow Vulnerability
}
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]-> setbuffer ("string1 ");
Object [1]-> setbuffer ("string2 ");
Object [0]-> printbuffer ();
Object [1]-> printbuffer ();
}

The following is the running result of bo2.cpp Compilation:

[Backend @ isbase test]>./bo2
Myclass1: string1
Myclass2: string2
[Backend @ isbase test]>

Another reminder is that you are sure you have read the above program, especially the virtual (virtual) method printbuffer (). Unlike the setbuffer () method, the printbuffer method must be declared and implemented in the derived classes myclass1 and myclass2 of the baseclass base class. This makes the processing of setbuffer and printbuffer methods different at runtime.

--- [[Virtual pointer, vptr] --------------------------------------------------

We know that the difference between a virtual method and a non-virtual method is that the call of a non-virtual method is determined during compilation (usually called "static binding "), the call of the virtual method is determined during the program (usually called "dynamic binding "). In the following example, the baseclass base class and its derived class explain the dynamic binding mechanism.

The compiler first checks the declaration of the baseclass base class during compilation. In this example, the compiler first retains 32 bytes for the private variable buffer (string type), then calculates the non-virtual method setbuffer () and specifies the corresponding call address (static binding ), finally, when the virtual method printbuffer () is checked, dynamic binding is performed, that is, four bytes are allocated in the class to store the pointer of the virtual method. The structure is as follows:

Bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbvvvv

Note: Buffer usage of variable B.
V virtual method pointer usage.

This pointer is usually referred to as "virtual Pointer" and points to one of the function portals in a "vtable" structure. Each class has a vtable. As shown in:

Object [0]: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbvvvv
= + =
|
+ ------------------------------ +
|
+ --> Vtable_myclass1: iiiiiiiiiiiipppp

Object [1]: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbwwww
= + =
|
+ ------------------------------ +
|
+ --> Vtable_myclass2: iiiiiiiiiiiiqqqq

Note: Buffer usage of variable B.
The vptr pointer of V pointing to vtable_myclass1 occupies.
W occupies the vptr pointer pointing to vtable_myclass2.
I Data for other purposes
The address pointer of the printbuffer () method of the P myclass1 object instance.
Q: The address pointer of the printbuffer () method of the myclass2 object instance.

We can find that the vptr is located after the buffer variable in the process memory. That is, when calling a dangerous strcpy () function, it is possible to overwrite vptr content!
  
According to Rix's research and testing, for Visual C ++ 6.0 on Windows, vptr is located at the starting position of the object, so the technology mentioned here cannot work. This is quite different from gnu c ++.

--- [[Analyze vptr] ----------------------------------------

In Linux, GDB is used for analysis:

[Backend @ isbase test]> gcc-O bo2 bo2.cpp
[Backend @ isbase test]> GDB bo2
Gnu gdb 4.18
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
Welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux "...
(GDB) disassemble main
Dump of worker er code for function main:
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
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, 0 xfffffffc (% EBP)
0x8049458 <main + 88>: Push $0x804cda2
0x804945d <main + 93>: mov 0xfffffff8 (% EBP), % eax
0x8049460 <main + 96>: Push % eax
0x8049461 <main + 97>: Call 0x804c930 <setbuffer _ 9 baseclasspc>
0x8049466 <main + 102>: add $0x8, % ESP
0x8049469 <main + 105>: Push $ 0x804cdaa
--- Type <return> to continue, or q <return> to quit ---
0x804946e <main + 110>: mov 0 xfffffffc (% EBP), % eax
0x8049471 <main + 113>: Push % eax
0x8049472 <main + 114>: Call 0x804c930 <setbuffer _ 9 baseclasspc>
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 0 xfffffffc (% EBP), % edX
0x8049491 <main + 145>: mov 0x20 (% EDX), % eax
0x8049494 <main + 148>: add $0x8, % eax
0x8049497 <main + 151>: mov 0 xfffffffc (% 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 0 xffffffec (% 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 worker er dump.
(GDB)

The following is an explanation of the assembly code of the program:

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

Build a stack. Keep 8 bytes for the object [] array (that is, two 4-byte pointer addresses), then the object [0] pointer is stored in 0xfffffff8 (% EBP ), the pointer of object [1] is stored in 0 fffffffc (% EBP ). Then save the register.

0x8049409 <main + 9>: Push $0x24
0x804940b <main + 11>: Call 0x804b580 <__builtin_new>
0x8049410 <main + 16>: add $0x4, % ESP

Call _ builtin_new to allocate 0x24 (36 bytes) to object [0] In heap, and save the first address to the eax register. The first 32 bytes of the 36 bytes are buffer variables, and the last 4 bytes are occupied by 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

Press the first address of the object to stack, and then call the _ 8myclass1 function. This is actually the constructor of the myclass1 object ).

(GDB) disassemble _ 8myclass1
Dump of Cycler 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

The register EBX now stores pointers pointing to the allocated 36 bytes (in C ++, called the "this" pointer ).

0x804c913 <__ 8myclass1 + 7>: Push % EBX
0x804c914 <__ 8myclass1 + 8>: Call 0x804c958 <__ 9 baseclass>
0x804c919 <__ 8myclass1 + 13>: add $0x4, % ESP

First, call the base class baseclass constructor.

(GDB) disassemble _ 9 baseclass
Dump of worker er code for function _ 9 baseclass:
0x804c958 <__ 9 baseclass>: Push % EBP
0x804c959 <__ 9 baseclass + 1>: mov % ESP, % EBP
0x804c95b <__ 9 baseclass + 3>: mov 0x8 (% EBP), % edX

Register edX now stores the pointer ("this" pointer) pointing to the allocated 36 bytes ).

0x804c95e <__ 9 baseclass + 6>: movl $ 0x804e01c, 0x20 (% EDX)

Store 0x804e01c to edX + 0x20 (= edX + 32 ). Let's take a look at the memory data of the 0x804e01c address:

(GDB) x 0x804e01c
0x804e01c <__vt_9baseclass>: 0x00000000

We can see that the address stored in edX + 0x20 (that is, the vptr location of the object) is the vtable address of the base class baseclass.
Return to the constructor of the myclass1 object:

0x804c91c <__8myclass1 + 16>: movl $0x804e010, 0x20 (% EBX)

Store 0x804e010 to EBX + 0x20 (vptr ). Let's also look at the memory data of the 0x804e010 address:

(GDB) x 0x804e010
0x804e010 <__vt_8myclass1>: 0x00000000

Now we know that the vptr has been rewritten, and its content is the vtable address of the myclass1 object. When returned to the main () function, the eax register stores the pointer of this object in the memory.

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)

Assign the obtained address pointer to object [0]. The program then processes the same object [1], but the returned address is different. After initialization, the following command is executed:

0x8049458 <main + 88>: Push $0x804cda2
0x804945d <main + 93>: mov 0xfffffff8 (% EBP), % eax
0x8049460 <main + 96>: Push % eax

Press the values of 0x804cda2 and object [0] on the stack. Observe the content of 0x804cda2:

(GDB) x/s 0x804cda2
0x804cda2 <_ io_stdin_used + 30>: "string1"

The address stores the string "string1" to be copied to the buffer using the setbuffer function of baseclass ".

0x8049461 <main + 97>: Call 0x804c930 <setbuffer _ 9 baseclasspc>
0x8049466 <main + 102>: add $0x8, % ESP

Call the setbuffer () method of baseclass. Note that the call to this setbuffer method is "static binding" (because it is not a virtual method ). The same is true for object [1.

To verify that these two objects are correctly initialized at runtime, we will set the following breakpoint:

0x8049410: Get the address of the first object.
0x804943a: Get the address of the second object.
0x804947a: Check whether object initialization is correct.

(GDB) Break x 0x8049410
Breakpoint 1 at 0x8049410
(GDB) break * 0x804943a
Breakpoint 2 at 0x804943a
(GDB) break * 0x804947a
Breakpoint 3 at 0x804947a

Now run this program:

St

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.