2

I'm trying to access and modify the private data member of a class by offset.
AFAIK, first thing is to calculate the offset, then access the member through the offset.
Here is my code.

class Test {  
public:
    int a;  
    int b;
private:  
    int c;
};  
Test test;
cout << *(&test + &Test::b + 1);  // access c

I got an error : "+" operator invalid, the right operand contains "int Test::*" type.

There is a similar post, it printf the offset of abas 1,4, but when try like this:

cout << &Test::a << '\t' << &Test::b;  

I got 1 1.

My question:
1. Why cout got the wrong result?
2. What does Test::*p point to?
3. How to access and modify the Test::* pointer?(Or how to access the private member when doesn't know the offset?)

Community
  • 1
  • 1
Nickolas
  • 780
  • 8
  • 27
  • Does this answer your question? [Can I access private members from outside the class without using friends?](https://stackoverflow.com/questions/424104/can-i-access-private-members-from-outside-the-class-without-using-friends) – wonko realtime Apr 01 '22 at 08:08

5 Answers5

4

If you must, the #define private public trick should do it.

Otávio Décio
  • 73,752
  • 17
  • 161
  • 228
4

Test::p is a member pointer. Member pointers are not the same a regular pointers. You can't just do pointer arithmetic to them. They are not references to a location in memory; they're a special kind of object.

If you want a regular pointer to an object (ie: the member of an existing class), you have to get the pointer to that particular piece of memory.

&test.b

BTW, whatever private data you're trying to access was made private for a reason. So unless you have a very, very good reason to basically break memory protection (as well as portability, since this is completely undefined behavior), you should not do this.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Using tricks to access private data is not appropriate, I just want to understand the memory distribution fully. Thanks for your interpretation. – Nickolas Sep 03 '11 at 06:22
4

This works:

#include <iostream>
using namespace std;
class test {
  public:
      int a;
      int b;
      test () : c(100) {}
      void show () {cout << "c = " << c << endl;}
  private:
      int c;
};


int main (void)
{
  test x;
  test *ptr;
  ptr = &x;
  ptr++;
  ptr = (test *) ((int *)ptr - 1);
  cout << "c = " << *((int *)ptr) << endl;
  *((int *)ptr) = 200;
  x.show ();
  return 0;
}

But the offset should be known. In the above ptr is type of test and therefore ptr++ will increment the pointer value to sizeof (test). Because the location of the private member c is at the end and is a type of integer so one integer step is decreased by casting ptr into int * first and then decrementing 1, which now points to the address of c. It is printed first, and next the value of c is modified by first casting ptr to int * and then assigning a value, and then printed.

It is not guaranteed that it will always get the value of c when you know the position of c as the padding might be different in other cases. Also there is no point accessing a private data member because at the time of the object design approach it was made to be accessed by the member functions and is a tool for organization by providing abstraction. When implementing such a concept in other languages like C, which does not have object oriented features, you can implement such a private - public environment by personally following a convention, but the compiler will not enforce anything if you accessed a "private" where you actually shouldn't. But in C++ the compiler provides the restriction and stops you from diverting and breaking the object orientated design which you made. At run time all's in memory and you can access them without any restriction from your code at that time nothings private or public. If you know how to interpret the bytes in the executable you can change anything you like, even the code itself. I do not think this is a good idea to be implemented, as it will make the code unpredictable and unportable and definitely violate the OOD approach.

phoxis
  • 60,131
  • 14
  • 81
  • 117
  • Thanks a lot, I'm wondering how the access section affect the object memory. It is not hard to understand them now. – Nickolas Sep 03 '11 at 07:42
  • Compiler can sometimes add padding in class `test` after member `c` so it will not work always. – Lisur Nov 19 '20 at 13:27
  • @Lisur That is what I already have mentioned in the last paragraph.This results in type of stuffs are implementation defined behaviour, so definitely should never be relied on. Just a demo on it actually can be accessed. – phoxis Nov 19 '20 at 15:02
1

You really cannot test for the memory layout of the objects. The compiler is free to do it as it sees fit.

When the members are separated by different access specifiers, the language doesn't say anything about their order. In your case, a must be followed by b but in theory c can be placed wherever the compiler sees fit.

In a larger example like

class Test {  
public:
    int a;  
    int b;
private:  
    int c;
protected:
    double d;
};

the compiler is allowed to place d before c if that would improve alignment, for example.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
  • Thanks to point it out. It seems that [Otávio Décio's idea](http://stackoverflow.com/questions/7291058/how-to-access-private-data-private-member-through-its-offset/7291071#7291071) is the best choice if I must do that. – Nickolas Sep 03 '11 at 07:50
-1
*((int*) &test + 2);    

With this, you should be able to access the private member c. Try it in gdb online compiler or add header cstdlib.

Now let me show you how to access any members of a class using pointer arithmetic other than the member functions.

Consider the following code:

    #include <iostream>        
#include <cstring>        

using namespace std;        

class test        
{        
    public:        
    int a;        
    char name[50];        
    double m;        
};        

int main()        
{        
    test *t = new test;        

    void *ptr = (char*)t + offsetof(test, m);         
    void *ptr1 = (char*)t + offsetof(test, name);        

    //printf("%p\n%p\n", &t->name, ptr1);        
    *((double*)ptr) = 98.999006;        
    strcpy((char*)ptr1,"Pointer Arithmetic");        

    cout<<t->m<<endl;        
    cout<<t->name<<endl;       

    cout<<(*(double*)ptr)<<endl<<((char*)ptr1)<<endl;    
    cout<<*((double*)((char*)t + offsetof(test, m)))<<endl;    
    cout<<(char*)t + offsetof(test, name);    
 }        

Output: 98.999
Pointer Arithmetic
98.999
Pointer Arithmetic
98.999
Pointer Arithmetic