Backend Note: This article comes from the "Smashing C VPTRS" in Phrack56. As most foreign hackers, technical principles and applications are more detailed, but the source code provided seems to always have a problem. This may be because they feel that the readers should study and debug themselves to better master these technologies. Maybe I will do this later. ;)
test environment:
Operating system: Red Hat 6.1 (i386)
Nuclear version: kernel 2.2.14
Nuclear Patch: None Non-Executable Stack Patch (By Solar Design)
C compiler: GCC
--- [[言]] --------------------------------------
So far, the buffer overflow program I have mastered is for C programming languages. Although C language programming is almost ubiquitous in UNIX systems, more and more C programs have begun. For most cases, C-language overflow technology is also applicable for C languages, but the object-oriented characteristics of C also results in new buffer overflow technologies. The following is analyzed in the X86 Linux system and the C GNU compiler.
--- [[Basis-Simple C Program] ------------------------------------ -
I don't want to waste time here to explain too much C language foundation. If you don't know anything about C or object-oriented programming technology, please find this book to see this. Before you continue to look down, please confirm that you have mastered or understand the following C terms:
1, Class (class)
2, Object (object)
3, Method (method)
4, Virtual (virtual)
5, inherit (inherited)
6, DeriVATIVE (derived)
Then, after reading the following two programs, confirm that you understand the meaning and role of each statement:
// bo1.cpp
// C basic procedure
#include
#include
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 program with buffer overflow vulnerabilities #include
#include
Class Baseclass
{
Private:
Char buffer [32];
PUBLIC:
Void setBuffer (Char * String)
{
STRCPY (Buffer, String); // There is a 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 resulting result after Bo2.cpp:
[Backend @ isbase test]> ./bo2
Myclass1: string1
Myclass2: String2
[Backend @ isbase test]>
Once again, when you continue to look down, you are sure that you read the above program, especially the object virtual method PrintBuffer (). Unlike the setBuffer () method, the PrintBuffer method must declare and implement in the base class baseclass derived type myclass1 and myclass2. This makes the setBuffer and PrintBuffer methods vary in runtime.
--- [[[C virtual pointer (Virtual Pointer, VPTR)] --------------------------------- -----
We know that a virtual method and a non-virtual method are that the call to the non-virtual method is determined at compile time (commonly referred to as "static binding"), but the call of the virtual method is determined when the program is determined ( Usually referred to as "dynamic binding"). The baseclass base class and its derived class in the above example are some explanations for dynamic binding mechanisms.
The compiler first checks the declaration of the BaseClass base class when compiling. In this case, the compiler first retains 32 bytes for private variables, followed by non-virtual method setBuffer () and specifies the corresponding call address (static binding process), and finally check the virtual method. When PrintBuffer (), dynamic binding processing will be done, ie, 4 bytes assigned in the class to store pointers for the virtual method. The structure is as follows:
Bbbbbbbbbbbbbbbbbbbbbbbbbbbbbvvvv
Description: B variable buffer is occupied.
V Virtual method pointer is occupied.
This pointer is often referred to as "VPTR" (Virtual Pointer), which points to one of the function portions in a "vTable" structure. Every class has a vTable. As shown below: Object [0]: bbbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvv
= ==
|
----------------------------
|
-> vtable_myclass1: iiiiiiiiiiiiipppp
Object [1]: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbwwwww
= ==
|
----------------------------
|
-> vtable_myclass2: iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiqqqq
Description: B variable buffer is occupied.
V Point VPTR pointer to vtable_myclass1.
W point to VPTR pointer to vtable_myclass2.
I other use data
P myclass1 object instance's address pointer.
Q Myclass2 object instance's address pointer of the printbuffer () method.
We can find that the VPTR is behind the Buffer variable in the process memory. That is, it is possible to overwrite the content of VPTR when calling the dangerous strcpy () function!
According to RIX research test, for Visual C 6.0 on the Windows platform, VPTR is located in the starting position of the object, so the technique mentioned here is unable to generate. This is very different from GNU C .
--- [[Analyzed VPTR] ----------------------------------------------------------------------------------------------------
It is also possible to use GDB under Linux:
[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 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 assembler code for function main:
0x8049400
0x8049401
0x8049403
0x8049406
0x8049407
0x8049408
0x8049409
0x8049410
0x8049413
0x8049415
0x8049417
0x8049418
0x804941D
0x8049420
0x8049422
0x8049424
0x8049429
0x8049430
0x8049433
0x8049435
0x804943a
0x804943D
0x804943f
0x8049441
0x8049442
0x8049447
0x804944A
0x804944c
0x804944e
0x8049450
0x8049455
0x8049458
0x804945D
0x8049460
0x8049461
0x8049466
0x8049469
0x804946e
0x8049471
0x8049472
0x8049477
0x804947a
0x804947D
0x8049480
0x8049483
0x8049486
0x8049487
0x8049489
0x804948B
0x804948e
0x8049491
0x8049494
0x8049497
0x804949a
0x804949B
0x804949d
0x804949f
0x80494a2
0x80494A4
0x80494A6
0x80494A8
0x80494A9
0x80494ae
0x80494B1
0x80494B6
0x80494B7
0x80494BC
0x80494C1
0x80494c3
0x80494c8
0x80494cd
0x80494d0
0x80494d3
0x80494d4
0x80494d5
--- Type
0x80494d6
0x80494d7
0x80494d8
0x80494d9
0x80494DA
0x80494db
0x80494dc
0x80494dd
0x80494de
0x80494df
End of assembler dump.
(GDB)
The following is an explanation for assembly code for the program:
0x8049400
0x8049401
0x8049403
0x8049406
0x8049407
0x8049408
Build a stack. To the Object [] array (ie, two 4-byte pointer addresses), the pointer of Object [0] is stored in 0xfffffff8 (% EBP), the pointer of Object [1] is stored at 0FFFFFFFC (% EBP). The register is then saved.
0x8049409
0x804940B
0x8049410
First call __builtin_new, assign 0x24 (36 bytes) to Object [0] in the heap (HEAP), and save its first address to the EAX register. The 32-byte of this 36 bytes is buffer variable, and the last 4 bytes are occupied by VPTR. 0x8049413
0x8049415
0x8049417
0x8049418
0x804941D
Stack the first address of the object and call the __8myclass1 function. This is actually a constructor for MyClass1 objects.
(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
Register EBX now stores a 36-byte pointer to allocated (in the C language, called "this" pointer).
0x804c913 <__8myclass1 7>: push% EBX
0x804c914 <__8myclass1 8>: Call 0x804c958 <__ 9baseclass>
0x804c919 <__8myclass1 13>: add $ 0x4,% ESP
First call the constructor of base class 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
Register EDX now stores a 36-byte pointer to allocated ("this" pointer).
0x804c95e <__ 9baseclass 6>: MOVL $ 0x804e01c, 0x20 (% EDX)
Store 0x804e01c to EDX 0x20 (= EDX 32). Let's take a look at the 0x804E01C address memory data:
(GDB) x 0x804e01c
0x804e01c <__ vt_9baseclass>: 0x00000000
You can see that this address stored in EDX 0x20 (i.e., the VPTR position of the object) is the vTable address of the base class baseclass.
Now return to the constructor of myclass1 object:
0x804C91C <__ 8MYCLASS1 16>: MOVL $ 0x804e010, 0x20 (% EBX) stores 0x804e010 to EBX 0x20 (ie VPTR). Also let us see the 0x804e010 address memory data:
(GDB) x 0x804e010
0x804e010 <__ vt_8myclass1>: 0x00000000
Now, we know that VPTR has been rewritten, and then it is the VTABLE address of myclass1 object in its content. When returning to the main () function, the register EAX stores the pointer in the memory.
0x8049420
0x8049422
0x8049424
0x8049429
0x8049430
Get the resulting address pointer to Object [0]. Then the program performs the same processing on Object [1], but the return of the return is different. After the above object initialization is initialized, the following instructions are executed:
0x8049458
0x804945D
0x8049460
Stack 0x804cda2 and Object [0]. Observe the contents of 0x804cda2:
(GDB) x / s 0x804cda2
0x804cda2 <_io_stdin_used 30>: "String1"
It is understood that the address stores the setBuffer function that will pass the base class baseclass to the string "string1" "in the buffer.
0x8049461
0x8049466
Call the setBuffer () method of the base class 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].
In order to verify that these two objects are properly initialized, we will set the following breakpoints:
0x8049410: Get the address of the first object.
0x804943A: Get the address of the second object.
0x804947A: Whether the initialization of the test object is correct.
(GDB) BREAK * 0x8049410
Breakpoint 1 at 0x8049410
(GDB) BREAK * 0x804943A
Breakpoint 2 AT 0x804943A
(GDB) BREAK * 0x804947A
Breakpoint 3 AT 0x804947A
Run this program now:
ST