Private members in C++ cannot be accessed from outside the class directly. In this post I will tell an interesting fact, that in C++ however there are means with which you can access/modify the values of the private members of a class from outside of the class, when the structure of the class is known.
The stuff
In my previous university I noticed there was a misconception in some students that the private (and other access specifier) keyword in C++ and in other language is used for “security”, but this is false. This is a construct to support the object-oriented design of a program. This simply forbids the programmer to directly access a data member outside its class, which is not designed to be done.
This is simply a demonstration that the private data members can be accessed during runtime directly from outside of its class. The private data members and such access specifier only enforce the access permissions on compile time, and not on run time. In run time the memory address space is not protected by these constructs, and if you get the address location right, then you can access it directly (by the same process).
To access the private member of a class directly from outside of the class in C++, we need to know the definition of the class. The idea is to get the address of the private data member location and directly access the memory location through pointers. I am describing the idea with an example.
The code
#include <iostream> using namespace std; class foo { private: int x; char y; float z; public: foo (int xx, char yy, float zz) : x (xx), y (yy), z (zz) { } void show_x () { cout << "x = " << x << endl; } void show_y () { cout << "y = " << y << endl; } void show_z () { cout << "z = " << z << endl; } }; int main (void) { foo obj (10, 'a', 22/7.0); cout << "\nInitial values: " << endl; obj.show_x (); obj.show_y (); obj.show_z (); cout << "\nValues after direct access: " << endl; /* The direct access process through pointers */ *((int *)(&obj)) = 12345; *(char *)(((int *)(&obj)) + 1) = 'X'; *(((float *)(&obj + 1)) - 1) = 123.4567; obj.show_x (); obj.show_y (); obj.show_z (); cout << endl; return 0; }
What’s going on
Above the class foo contains three private data members x, y and z each of different types. Three public function members are introduced which will simply display the values of these three components. An object obj of the class foo is created and initialized with some values, and the initial values are showed.
The memory image of the object obj can be visualized as below:
+-----------------------+ | int x | +-----------------------+ | char y | +-----------------------+ | float z | +-----------------------+ | . | + . + | . |
Note: Above, each cell location is considered to have the corresponding type width. Padding is not taken into consideration.
Now we come to the pointer access part. We know that the location of the integer element x is the first. Therefore to get the address of the variable x we get the base address of the object with &obj. As this is the first element of the class, the integer x will be stored in this location, so the address is typecast into an int * and a new value is assigned into this address value, thus changing the content.
obj the object of class foo &obj the starting address of the object obj (int *)(&obj) typecast the starting address of obj to int * *((int *)(&obj)) the value at the above location interpreted as an integer type *((int *)(&obj)) = 12345 store something in it +-----------------------+ <---------- &obj | int x | +-----------------------+ | char y | +-----------------------+ | float z | +-----------------------+ | . | + . + | . |
In the case of the char type data, it is located right after the int. Therefore we need to first offset to the char data before doing any modification. As before first the base address of the class is typecast into int * and then 1 is added to it. By pointer arithmetic (((int *)(&obj)) + 1) will add sizeof (int) and point to the memory location right after the int element, which is the char element. Thus we get the address of the char field. Now we typecast this address as a char * type and assign some value.
obj the object of class foo &obj the starting address of the object obj (int *)(&obj) typecast the starting address of obj to int * ((int *)(&obj)) + 1 point to the next location by skipping one sizeof (int) bytes (char *)(((int *)(&obj)) + 1) we know this location is a char, so typecast this location as a char * *(char *)(((int *)(&obj)) + 1) access value at the above location *(char *)(((int *)(&obj)) + 1) = 'X' assign whatever +-----------------------+ +--------- (&obj + sizeof (int)) ie. | int x | | (((int *)(&obj)) + 1) +-----------------------+ <---+ | char y | +-----------------------+ | float z | +-----------------------+ | . | + . + | . |
In the case of the float element we get the address by calculating the offset backward. First we get the base address of the obj like before and then directly add 1 to it. By pointer arithmetic it will skip sizeof (foo) bytes and point to the location which is immediately after the end of obj. Now, as we know that z is the last element, we need to get back, and also because we know that the last element is a float it is first typecast to float * and then 1 is subtracted, which decrements the pointer value to sizeof (float) bytes, and positions it to the starting of the element z. Thus we get the base address of the float type element and typecast it as float * and access it directly.
obj the object of class foo &obj the starting address of the object object (&obj + 1) increment the address by sizeof (foo), this places the pointer right after the end of object (float *)(&obj + 1) typecast this location as float (((float *)(&obj + 1)) - 1) decrement the value of the previous pointer by sizeof (float). This sets the pointer at the base of the variable `z' *(((float *)(&obj + 1)) - 1) access the above location +-----------------------+ +--------- (((float *)(&obj + 1)) - 1) ie. | int x | | (&obj + sizeof (foo) - sizeof (float)) +-----------------------+ | | char y | | +-----------------------+ <---+ | float z | +-----------------------+ <------------- (&obj + 1) | . | + . + | . |
When the above code is run we get the output as expected.
Initial values: x = 10 y = a z = 3.14286 Values after direct access: x = 12345 y = X z = 123.457
Pitfalls
Although this can be done, this is definitely not the way the thing to be done. The above process totally ignores padding which may be included by the compiler. For exmaple in this example, if you print out the the number of bytes the object obj takes by printing sizeof (obj) then you might get an output 12 bytes, which is true for my case, because there is padding included after the char data. Padding is included to make the memory operations efficient.
For example if we seek the base address of the float element in forward direction (as in the int element) instead of backward direction, by doing *((float *)((((char *)(((int *)(&obj)) + 1))) + 1)) then we would land one byte right after the first byte of the char data, and take that memory location as the float elements address, which would fail, as this location just calculated falls/might fall in the padding area.
In the other hand we need to have prior knowledge of the class structure inorder to make the above possible.
At last i would like to say that although this is fun, but do not think even remotely to use such a process in your code, it would be like eating your meal passing your arms between your legs. Follow object oriented design.
Amazing !!
great!!!!!!!
very nice
how can I access to char* in private and change it ?
class sample{
private:
char x;
char* name;
In that case you need to know what is the padding between the first character char x and therefore know the precise address of char *name . Therefore it is not fully deterministic, as padding can vary depending on compiler and compiler options.
Please note that this post attempts to make a point that the keywords private, public, protected are not security features, but tools to assist implementation of an Object Oriented Design, and are explicit enforcement for the programmer.
hack private data by just knowing the values ….study purpose only…
//GEEK MODE….;)
try this
https://github.com/zjx20/stealer