web-dev-qa-db-de.com

Kann ich auf private Mitglieder von außerhalb der Klasse zugreifen, ohne Freunde zu verwenden?

Haftungsausschluss

Ja, ich bin mir völlig bewusst, dass das, worüber ich frage, total dumm ist und dass jeder, der so etwas im Produktionscode versuchen möchte, gefeuert und/oder erschossen werden sollte. Ich schaue hauptsächlich nach, ob can fertig ist.

Gibt es eine Möglichkeit, auf private Klassenmitglieder in C++ von außerhalb der Klasse zuzugreifen? Gibt es beispielsweise eine Möglichkeit, dies mit Zeigerversätzen zu tun?

(Naive und ansonsten nicht produktionsfertige Techniken sind erwünscht)

Aktualisieren

Wie in den Kommentaren erwähnt, habe ich diese Frage gestellt, weil ich einen Blogbeitrag über die Überkapselung schreiben wollte (und deren Auswirkungen auf TDD). Ich wollte herausfinden, ob es eine Möglichkeit gibt zu sagen, dass "die Verwendung privater Variablen keine hundertprozentig zuverlässige Methode ist, um die Kapselung auch in C++ zu erzwingen." Am Ende beschloss ich, mich mehr mit der Lösung des Problems zu beschäftigen als mit dem Grund, warum es ein Problem ist. Daher habe ich einige der hier gezeigten Dinge nicht so prominent dargestellt, wie ich es geplant hatte, aber ich habe immer noch einen Link hinterlassen.

Jedenfalls, wenn jemand daran interessiert ist, wie es herauskam, hier ist es: Feinde der Testgetriebenen Entwicklung Teil I: Verkapselung (Ich schlage vor, es zu lesen, bevor Sie entscheiden, dass ich verrückt bin).

59
Jason Baker

Wenn die Klasse Template-Member-Funktionen enthält, können Sie diese Member-Funktion entsprechend Ihren Anforderungen spezialisieren. Auch wenn der ursprüngliche Entwickler nicht daran gedacht hat.

safe.h

class safe
{
    int money;

public:
    safe()
     : money(1000000)
    {
    }

    template <typename T>
    void backdoor()
    {
        // Do some stuff.
    }
};

main.cpp:

#include <safe.h>
#include <iostream>

class key;

template <>
void safe::backdoor<key>()
{
    // My specialization.
    money -= 100000;
    std::cout << money << "\n";
}

int main()
{
    safe s;
    s.backdoor<key>();
    s.backdoor<key>();
}

Ausgabe:

900000
800000
66
dalle

Ich habe meinem Blog einen Eintrag hinzugefügt (siehe unten), der zeigt, wie das gemacht werden kann. Hier ein Beispiel, wie Sie es für die folgende Klasse verwenden

struct A {
private:
  int member;
};

Deklarieren Sie einfach eine Struktur, in der Sie sie beschreiben, und instanziieren Sie die für den Diebstahl verwendete Implementierungsklasse

// tag used to access A::member
struct A_member { 
  typedef int A::*type;
  friend type get(A_member);
};

template struct Rob<A_member, &A::member>;

int main() {
  A a;
  a.*get(A_member()) = 42; // write 42 to it
  std::cout << "proof: " << a.*get(A_member()) << std::endl;
}

Die Rob-Klassenvorlage ist wie folgt definiert und muss nur einmal definiert werden, unabhängig davon, auf wie viele private Mitglieder Sie zugreifen möchten

template<typename Tag, typename Tag::type M>
struct Rob { 
  friend typename Tag::type get(Tag) {
    return M;
  }
};

Dies zeigt jedoch nicht, dass die Zugriffsregeln von C++ nicht zuverlässig sind. Die Sprachregeln dienen dem Schutz vor versehentlichen Fehlern. Wenn Sie versuchen, Daten eines Objekts zu rauben, dauert es nicht lange, bis die Sprache by-design Sie daran hindert. 

Das Folgende ist hinterlistig, illegal, vom Compiler abhängig und funktioniert je nach verschiedenen Implementierungsdetails möglicherweise nicht.

#define private public
#define class struct

Aber es ist eine Antwort auf Ihr OP, in der Sie explizit eine Technik einladen, die, und ich zitiere, "völlig dumm ist und dass jeder, der so etwas im Produktionscode versuchen möchte, gefeuert und/oder erschossen werden sollte".


Eine andere Technik besteht darin, auf private Mitgliedsdaten zuzugreifen, indem Zeiger mit hartcodierten/handcodierten Offsets vom Objektanfang aus zusammengesetzt werden.

29
ChrisW

Hmmm, ich weiß nicht, ob das funktioniert, aber es wäre einen Versuch wert. Erstellen Sie eine andere Klasse mit demselben Layout wie das Objekt mit privaten Mitgliedern, wobei private jedoch in public geändert wurde. Erstellen Sie eine Variable des Zeigers auf diese Klasse. Verwenden Sie einen einfachen Cast, um dies auf Ihr Objekt mit privaten Mitgliedern zu verweisen, und versuchen Sie, eine private Funktion aufzurufen.

Erwarte Funken und vielleicht einen Sturz;)

24
SmacL
class A 
{ 
   int a; 
}
class B
{
   public: 
   int b;
}

union 
{ 
    A a; 
    B b; 
};

Das sollte es tun.

ETA: Es wird für diese Art von trivialer Klasse funktionieren, aber allgemein nicht. 

TC++ PL Abschnitt C.8.3: "Eine Klasse mit Konstruktor, Destruktor oder Kopiervorgang kann nicht der Typ eines Unionsmitglieds sein ... da der Compiler nicht weiß, welches Member zu zerstören ist."

Die beste Wette besteht also darin, class B das Layout von A zuzuordnen und die Privaten einer Klasse zu hacken.

12
Rob K

Wenn Sie einen Zeiger auf ein Member einer Klasse erhalten können, können Sie den Zeiger verwenden, unabhängig von den Zugriffsmerkmalen (sogar Methoden).

class X;
typedef void (X::*METHOD)(int);

class X
{
    private:
       void test(int) {}
    public:
       METHOD getMethod() { return &X::test;}
};

int main()
{
     X      x;
     METHOD m = x.getMethod();

     X     y;
     (y.*m)(5);
}

Natürlich ist mein liebster kleiner Hack die Hintertür des Freundes.

class Z
{
    public:
        template<typename X>
        void backDoor(X const& p);
    private:
        int x;
        int y;
};

Angenommen, der Schöpfer des obigen hat backDoor für seine normalen Zwecke definiert. Sie möchten jedoch auf das Objekt zugreifen und die privaten Mitgliedsvariablen betrachten. Auch wenn die obige Klasse in eine statische Bibliothek kompiliert wurde, können Sie Ihre eigene Vorlagenspezialisierung für backDoor hinzufügen und somit auf die Member zugreifen.

namespace
{
    // Make this inside an anonymous namespace so
    // that it does not clash with any real types.
    class Y{};
}
// Now do a template specialization for the method.
template<>
void Z::backDoor<Y>(Y const& p)
{
     // I now have access to the private members of Z
}

int main()
{
    Z  z;   // Your object Z

    // Use the Y object to carry the payload into the method.
    z.backDoor(Y());
}
8
Martin York

Es ist definitiv möglich, auf private Mitglieder mit einem Zeigerversatz in C++ zuzugreifen. Nehmen wir an, ich hatte die folgende Typdefinition, auf die ich zugreifen wollte.

class Bar {
  SomeOtherType _m1;
  int _m2;
};

Angenommen, es gibt keine virtuellen Methoden in Bar. Der einfache Fall ist _m1. Member in C++ werden als Offsets des Speicherorts des Objekts gespeichert. Das erste Objekt befindet sich am Offset 0, das zweite Objekt am Offset der Größe von (erster Member) usw.

Hier ist also eine Möglichkeit, auf _m1 zuzugreifen.

SomeOtherType& GetM1(Bar* pBar) {
  return*(reinterpret_cast<SomeOtherType*>(pBar)); 
}

Jetzt ist _m2 etwas schwieriger. Wir müssen die ursprüngliche Zeigergröße (SomeOtherType) Bytes vom Original verschieben. Die Umwandlung in Char soll sicherstellen, dass ich in einem Byte-Offset inkrementiere

int& GetM2(Bar* pBar) {
  char* p = reinterpret_cast<char*>(pBar);
  p += sizeof(SomeOtherType);
  return *(reinterpret_cast<int*>(p));
}
8
JaredPar

Diese Antwort basiert auf dem genauen Konzept von @ Johannes's Antwort/blog , da dies der einzige "legitime" Weg zu sein scheint. Ich habe diesen Beispielcode in ein praktisches Hilfsprogramm umgewandelt. Es ist leicht mit C++ 03 kompatibel (durch Implementierung von std::remove_reference und Ersetzen von nullptr).

Bibliothek

#define CONCATE_(X, Y) X##Y
#define CONCATE(X, Y) CONCATE_(X, Y)

#define ALLOW_ACCESS(CLASS, TYPE, MEMBER) \
  template<typename Only, TYPE CLASS::*Member> \
  struct CONCATE(MEMBER, __LINE__) { friend TYPE (CLASS::*Access(Only*)) { return Member; } }; \
  template<typename> struct Only_##MEMBER; \
  template<> struct Only_##MEMBER<CLASS> { friend TYPE (CLASS::*Access(Only_##MEMBER<CLASS>*)); }; \
  template struct CONCATE(MEMBER, __LINE__)<Only_##MEMBER<CLASS>, &CLASS::MEMBER>

#define ACCESS(OBJECT, MEMBER) \  
(OBJECT).*Access((Only_##MEMBER<std::remove_reference<decltype(OBJECT)>::type>*)nullptr)

API

ALLOW_ACCESS(<class>, <type>, <member>);

Verwendungszweck

ACCESS(<object>, <member>) = <value>;   // 1
auto& ref = ACCESS(<object>, <member>); // 2

Beispiel

struct X {
  int get_member () const { return member; };
private:
  int member = 0;
};

ALLOW_ACCESS(X, int, member);

int main() {
  X x;
  ACCESS(x, member) = 42;
  std::cout << "proof: " << x.get_member() << std::endl;
}
4
iammilind

coole frage übrigens ... hier ist mein stück:

using namespace std;

class Test
{

private:

  int accessInt;
  string accessString;

public:

  Test(int accessInt,string accessString)
  {
    Test::accessInt=accessInt;
    Test::accessString=accessString;
  }
};

int main(int argnum,char **args)
{
  int x;
  string xyz;
  Test obj(1,"Shit... This works!");

  x=((int *)(&obj))[0];
  xyz=((string *)(&obj))[1];

  cout<<x<<endl<<xyz<<endl;
  return 0;
}

Hoffe das hilft.

4
Sushant Mahajan

Wenn Sie wissen, wie Ihr C++ - Compiler Namen benennt, ja.

Es sei denn, ich nehme an, es ist eine virtuelle Funktion. Aber wenn Sie wissen, wie Ihr C++ - Compiler das VTABLE erstellt ...

Edit: Wenn ich mir die anderen Antworten anschaue, stelle ich fest, dass ich die Frage falsch verstanden habe und dachte, es handele sich um Mitgliedsfunktionen und nicht um Mitgliederdaten. Der Punkt bleibt jedoch bestehen: Wenn Sie wissen, wie Ihr Compiler Daten anlegt, können Sie auf diese Daten zugreifen.

3
kdgregory

Als Alternative zur Vorlagen-Backdoor-Methode können Sie die Vorlagen-Backdoor-Klasse verwenden. Der Unterschied ist, dass Sie diese Backdoor-Klasse nicht in den öffentlichen Bereich der Klasse einfügen müssen, die Sie testen möchten. Ich nutze die Tatsache, dass viele Compiler verschachtelten Klassen den Zugriff auf den privaten Bereich der umschließenden Klasse erlauben (der nicht genau 1998 Standard ist, aber als "richtiges" Verhalten betrachtet wird). Und natürlich wurde dies in C++ 11 zu rechtlichem Verhalten.

Siehe dieses Beispiel:

#include <vector>
#include <cassert>
#include <iostream>
using std::cout;
using std::endl;


///////// SystemUnderTest.hpp
class SystemUnderTest
{
   //...put this 'Tested' declaration into private area of a class that you are going to test
   template<typename T> class Tested;
public:
   SystemUnderTest(int a): a_(a) {}
private:
   friend std::ostream& operator<<(std::ostream& os, const SystemUnderTest& sut)
   {
      return os << sut.a_;
   }
   int a_;
};

/////////TestFramework.hpp
class BaseTest
{
public:
   virtual void run() = 0;
   const char* name() const { return name_; }
protected:
   BaseTest(const char* name): name_(name) {}
   virtual ~BaseTest() {}
private:
   BaseTest(const BaseTest&);
   BaseTest& operator=(const BaseTest&);
   const char* name_;
};

class TestSuite
{
   typedef std::vector<BaseTest*> Tests;
   typedef Tests::iterator TIter;
public:
   static TestSuite& instance()
   {
      static TestSuite TestSuite;
      return TestSuite;
   }
   void run()
   {
      for(TIter iter = tests_.begin(); tests_.end() != iter; ++iter)
      {
         BaseTest* test = *iter;
         cout << "Run test: " << test->name() << endl;
         test->run();
      }
   }
   void addTest(BaseTest* test)
   {
      assert(test);
      cout << "Add test: " << test->name() << endl;
      tests_.Push_back(test);
   }
private:
   std::vector<BaseTest*> tests_;
};

#define TEST_CASE(SYSTEM_UNDER_TEST, TEST_NAME) \
class TEST_NAME {}; \
template<> \
class SYSTEM_UNDER_TEST::Tested<TEST_NAME>: public BaseTest \
{ \
   Tested(): BaseTest(#SYSTEM_UNDER_TEST "::" #TEST_NAME) \
   { \
      TestSuite::instance().addTest(this); \
   } \
   void run(); \
   static Tested instance_; \
}; \
SYSTEM_UNDER_TEST::Tested<TEST_NAME> SYSTEM_UNDER_TEST::Tested<TEST_NAME>::instance_; \
void SYSTEM_UNDER_TEST::Tested<TEST_NAME>::run()


//...TestSuiteForSystemUnderTest.hpp
TEST_CASE(SystemUnderTest, AccessPrivateValueTest)
{
   SystemUnderTest sut(23);
   cout << "Changed private data member from " << sut << " to ";
   sut.a_ = 12;
   cout << sut << endl;
}

//...TestRunner.cpp
int main()
{
   TestSuite::instance().run();
}
1
AlexT

Es ist eigentlich ganz einfach:

class jail {
    int inmate;
public:
    int& escape() { return inmate; }
};
1
MSalters

erstellen Sie einfach Ihre eigene Access-Member-Funktion, um die Klasse zu erweitern.

0
RichieHH

Der folgende Code greift auf einen privaten Member der Klasse zu und ändert ihn mit einem Zeiger auf diese Klasse. 

#include <iostream>
using namespace std;
class A
{
    int private_var;
    public:
    A(){private_var = 0;}//initialized to zero.
    void print(){cout<<private_var<<endl;}
};

int main()
{
    A ob;
    int *ptr = (int*)&ob; // the pointer to the class is typecast to a integer pointer.  
    (*ptr)++; //private variable now changed to 1.
    ob.print();
    return 0;
}
/*prints 1. subsequent members can also be accessed by incrementing the pointer (and
  type casting if necessary).*/
0
tanujrastogi

"Die Verwendung privater Variablen ist selbst in C++ keine hundertprozentig zuverlässige Möglichkeit, die Kapselung zu erzwingen."Ja wirklich? Sie können die benötigte Bibliothek zerlegen, alle benötigten Offsets finden und verwenden. Dies gibt Ihnen die Möglichkeit, jedes private Mitglied, das Sie mögen, zu wechseln. ABER! Sie können ohne Mitglieder nicht auf private Mitglieder zugreifen dirty hacking . Nehmen wir an, dass das Schreiben von const Ihre Konstante nicht wirklich konstant machen wird, weil Sie es können const weg oder verwenden Sie einfach ihre Adresse, um sie zu entwerten. Wenn Sie MSVC++ verwenden und "-merge: .rdata = .data" für einen Linker angegeben haben, funktioniert der Trick ohne Speicherzugriffsfehler. Wir können sogar sagen, dass das Schreiben von Apps in C++ kein zuverlässiger Weg ist Programme schreiben, da der resultierende Low-Level-Code möglicherweise von irgendwo außerhalb gepatcht wird, wenn Ihre App ausgeführt wird. Was ist dann der zuverlässig dokumentierte Weg, um die Kapselung zu erzwingen? Können wir die Daten irgendwo in RAM verbergen und verhindern, dass irgendetwas außer auf unseren Code darauf zugreift? Die einzige Idee, die ich habe, ist, private Mitglieder zu verschlüsseln und sie zu sichern, weil etwas diese Mitglieder verderben kann. Tut mir leid, wenn meine Antwort zu unhöflich ist, wollte ich niemanden beleidigen Aussage ist klug.

0

nur studienzweck .... versuchen sie dies .... kann hilfreich sein, ich denke .....

//GEEK MODE....;)
#include<iostream.h>
#include<conio.h>

    class A
    {
    private :int iData,x;
    public: void get()             //enter the values
        {cout<<"Enter iData : ";
            cin>>iData;cout<<"Enter x : ";cin>>x;}

        void put()                               //displaying values
    {cout<<endl<<"sum = "<<iData+x;}
};

void hack();        //hacking function

void main()
{A obj;clrscr();
obj.get();obj.put();hack();obj.put();getch();
}

void hack()         //hack begins
{int hck,*ptr=&hck;
cout<<endl<<"Enter value of private data (iData or x) : ";
cin>>hck;     //enter the value assigned for iData or x
for(int i=0;i<5;i++)
{ptr++;
if(*ptr==hck)
{cout<<"Private data hacked...!!!\nChange the value : ";
cin>>*ptr;cout<<hck<<" Is chaged to : "<<*ptr;
return;}
}cout<<"Sorry value not found.....";
}
0
NEURON ENIX

Ich habe eine andere nützliche Methode (und Lösung) verwendet, um auf ein privates/geschütztes C++ - Mitglied zuzugreifen.
Die einzige Bedingung ist, dass Sie von der Klasse erben können, auf die Sie zugreifen möchten.
Dann geht das gesamte Guthaben nach reinterpret_cast <> () .

Ein mögliches Problem ist, dass es nicht funktioniert, wenn Sie eine virtuelle Funktion einfügen, die die virtuelle Tabelle und damit die Objektgröße/-ausrichtung ändert.

class QObject
{
    Q_OBJECT
    Q_DECLARE_PRIVATE(QObject)
    void dumpObjectInfo();
    void dumpObjectTree();
...
protected:
    QScopedPointer<QObjectData> d_ptr;
...
}

class QObjectWrapper : public QObject
{
public:
    void dumpObjectInfo2();
    void dumpObjectTree2();
};

Dann müssen Sie die Klasse nur wie folgt verwenden:

QObject* Origin;
QObjectWrapper * testAccesor = reinterpret_cast<QObjectWrapper *>(Origin);
testAccesor->dumpObjectInfo2();
testAccesor->dumpObjectTree2();

Mein ursprüngliches Problem war wie folgt: Ich brauchte eine Lösung, die keine Neukompilierung von QT-Bibliotheken impliziert.
Es gibt zwei Methoden in QObject, dumpObjectInfo () und dumpObjectTree (). Diese Funktion funktioniert nur, wenn QT-Bibliotheken im Debug-Modus kompiliert werden , und sie benötigen natürlich Zugriff auf d_ptr-Mitglied (unter anderen internen Strukturen).
Was ich tat, war, die vorgeschlagene Lösung zu verwenden, um diese Methoden in dumpObjectInfo2 () und dumpObjectTree2 () in meiner eigenen Klasse (QObjectWrapper) Entfernen dieser Debug-Präprozessor-Wachen.

0
kikeenrique

da Sie über ein Objekt der erforderlichen Klasse verfügen, vermute ich, dass Sie eine Deklaration der Klasse ..__ haben. Jetzt können Sie eine andere Klasse mit denselben Mitgliedern deklarieren, aber alle dort angegebenen Zugriffsbezeichner als öffentlich deklarieren.

Zum Beispiel ist die vorherige Klasse:

class Iamcompprivate
{
private:
    Type1 privateelement1;
    Typ2 privateelement2;
    ...

public:
    somefunctions
}

sie können eine Klasse als deklarieren

class NowIampublic
{
**public:**
    Type1 privateelement1;
    Type2 privateelement2;
    ...

    somefunctions
};

Jetzt brauchen Sie nur noch den Zeiger der Klasse Iamcompprivate in einen Zeiger der Klasse NowIampublic umzuwandeln und als U-Wunsch zu verwenden.

Beispiel:

NowIampublic * changetopublic(Iamcompprivate *A)
{
    NowIampublic * B = (NowIampublic *)A;
    return B;
}
0
Alok

Oft stellt eine Klasse Mutator-Methoden für private Daten (Getter und Setter) zur Verfügung.

Wenn eine Klasse einen Getter bereitstellt, der eine const-Referenz (aber keinen Setter) zurückgibt, können Sie einfach den Rückgabewert des Getters const_cast eingeben und diesen als l-Wert verwenden:

class A {
  private:
    double _money;
  public:
    A(money) :
      _money(money)
    {}

    const double &getMoney() const
    {
      return _money;
    }
};

A a(1000.0);
const_cast<double &>(a.getMoney()) = 2000.0;
0
Lanting

Inspiriert von @Johannes Schaub - litb kann der folgende Code etwas leichter zu verstehen sein.

    struct A {
    A(): member(10){}
    private:
    int get_member() { return member;}
    int member;
   };

   typedef int (A::*A_fm_ptr)();
   A_fm_ptr  get_fm();

  template<   A_fm_ptr p> 
  struct Rob{ 
     friend A_fm_ptr  get_fm() {
   return p;
  }
};

 template struct Rob<  &A::get_member>;

 int main() {
   A a;
  A_fm_ptr p = get_fm();

    std::cout << (a.*p)() << std::endl;

  }
0
Yunzhou Wu

An alle, die " #define private public " vorschlagen:

Diese Art von Sachen ist illegal . Der Standard verbietet die Definition/Undefinging-Makros, die lexikalisch den Schlüsselwörtern für reservierte Sprachen entsprechen. Ihr Compiler wird sich wahrscheinlich nicht beschweren (ich habe noch keinen Compiler gesehen, der das tut), aber es ist nichts, was ein "gutes Ding" ist.

0
Tritium

Neben #define private public können Sie auch #define private protected definieren und dann eine foo-Klasse als Nachkomme der gewünschten Klasse definieren, um Zugriff auf ihre (jetzt geschützten) Methoden über Typumwandlung zu erhalten. 

0
dmajkic

Durch die Bezugnahme auf * this aktivieren Sie eine Hintertür für alle privaten Daten in einem Objekt.

class DumbClass
{
private:
    int my_private_int;
public:
    DumbClass& backdoor()
    {
        return *this;
    }
}
0
mage_hat