Question

Clause de non-responsabilité

Oui, je suis pleinement conscient du fait que ce que je demande est totalement stupide et que toute personne souhaitant essayer une telle chose dans le code de production devrait être renvoyée et / ou fusillée. Je cherche surtout à savoir si peut être fait.

Maintenant que ce n'est plus possible, existe-t-il un moyen d'accéder aux membres de classes privées en C ++ de l'extérieur de la classe? Par exemple, existe-t-il un moyen de le faire avec des décalages de pointeur?

(Les techniques naïves et les techniques non prêtes à la production sont les bienvenues)

Mettre à jour

Comme indiqué dans les commentaires, j’ai posé cette question parce que je souhaitais écrire un article de blog sur la surencapsulation (et son incidence sur le TDD). Je voulais voir s'il existait un moyen de dire que & "L'utilisation de variables privées n'est pas un moyen fiable d'appliquer l'encapsulation, même en C ++. &"; À la fin, j'ai décidé de me concentrer davantage sur la façon de résoudre le problème plutôt que sur la raison pour laquelle il s'agissait d'un problème. Je n'ai donc pas présenté certains des éléments évoqués ici aussi clairement que prévu, mais j'ai tout de même laissé un lien.

Quoi qu'il en soit, si quelqu'un est intéressé par le résultat, le voici: Ennemis du développement piloté par les tests, partie I: encapsulation (je suggère de le lire avant de décider que je suis fou).

Était-ce utile?

La solution

Si la classe contient des fonctions membres de modèle, vous pouvez les personnaliser en fonction de vos besoins. Même si le développeur d'origine n'y a pas pensé.

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>();
}

Sortie:

900000
800000

Autres conseils

J'ai ajouté une une entrée sur mon blog (voir ci-dessous) qui montre comment procéder. Voici un exemple d'utilisation de cette classe pour la classe suivante

struct A {
private:
  int member;
};

Déclarez simplement une structure pour celle-ci dans laquelle vous la décrivez et instanciez la classe d'implémentation utilisée pour le vol qualifié

// 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;
}

Le modèle de classe Rob est défini comme ceci et ne doit être défini qu'une fois, quel que soit le nombre de membres privés auxquels vous prévoyez d'accéder

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

Cependant, cela ne montre pas que les règles d'accès de c ++ ne sont pas fiables. Les règles de langage sont conçues pour protéger contre les erreurs accidentelles - si vous essayez de voler des données à un objet, le langage by-design ne vous empêchera pas longtemps.

Ce qui suit est sournois, illégal, dépendant du compilateur et peut ne pas fonctionner en fonction de divers détails d’implémentation.

#define private public
#define class struct

Mais c’est une réponse à votre OP, dans laquelle vous invitez explicitement une technique qui, et je cite, est & "totalement stupide et que quiconque voudrait essayer une telle chose dans le code de production devrait être congédié" et / ou tiré & ";

Une autre technique consiste à accéder aux données des membres privés en construisant des pointeurs à l'aide de décalages codés en dur / codés à la main depuis le début de l'objet.

Hmmm, je ne sais pas si cela fonctionnerait, mais cela pourrait valoir la peine d'essayer. Créez une autre classe avec la même présentation que l'objet avec des membres privés, mais avec une classe privée modifiée en public. Créez une variable de pointeur sur cette classe. Utilisez un casting simple pour le faire pointer sur votre objet avec des membres privés et essayez d’appeler une fonction privée.

Attendez-vous à des étincelles et peut-être à un crash;)

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

union 
{ 
    A a; 
    B b; 
};

Cela devrait le faire.

ETA: Cela fonctionnera pour ce genre de classe triviale, mais en général cela ne fonctionnera pas.

  

TC ++ PL Section C.8.3: & "Une classe avec un constructeur, un destructeur ou une opération de copie ne peut pas être du type d'un membre d'union ... car le compilateur ne sait pas quel membre détruire. "

Nous nous retrouvons donc avec le meilleur choix de déclarer class B pour correspondre à la présentation de A et bidouiller pour regarder les membres privés d'une classe.

Si vous pouvez obtenir un pointeur sur un membre d'une classe, vous pouvez l'utiliser, quels que soient les spécificateurs d'accès (même les méthodes).

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);
}

Bien sûr, mon petit hack préféré est la porte arrière du modèle d’ami.

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

En supposant que le créateur de ce qui précède ait défini backDoor pour ses utilisations normales. Mais vous voulez accéder à l'objet et consulter les variables de membre privé. Même si la classe ci-dessus a été compilée dans une bibliothèque statique, vous pouvez ajouter votre propre spécialisation de modèles pour backDoor et accéder ainsi aux membres.

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());
}

Il est vraiment possible d’accéder à des membres privés avec un pointeur décalé en C ++. Supposons que j’avais la définition de type suivante à laquelle je voulais avoir accès.

class Bar {
  SomeOtherType _m1;
  int _m2;
};

En supposant qu'il n'y ait pas de méthodes virtuelles dans Bar, le cas le plus simple est _m1. Les membres en C ++ sont stockés en tant que décalages de l'emplacement mémoire de l'objet. Le premier objet est à l'offset 0, le second à l'offset de sizeof (premier membre), etc ...

Voici donc un moyen d'accéder à _m1.

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

Maintenant, _m2 est un peu plus difficile. Nous devons déplacer les octets sizeof (SomeOtherType) du pointeur d'origine de l'original. Le transtypage vers caractère est destiné à garantir que j'incrémente un décalage d'octet

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

question cool btw ... voici ma pièce:

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;
}

J'espère que cela vous aidera.

Cette réponse est basée sur le concept exact présenté par blog / réponse de Johannes @ , car il semble que ce soit le seul " légitime " façon. J'ai converti cet exemple de code en un utilitaire pratique. Il est facilement compatible avec C ++ 03 (en implémentant std::remove_reference & Et remplace nullptr).

Bibliothèque

#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>);

Utilisation

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

Exemple

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;
}

Si vous savez comment votre compilateur C ++ modifie les noms, oui.

Sauf si, je suppose, c'est une fonction virtuelle. Mais ensuite, si vous savez comment votre compilateur C ++ construit le VTABLE ...

Edit: en regardant les autres réponses, je me rends compte que j’ai mal interprété la question et je pensais que c’était à propos des fonctions des membres, pas des données des membres. Cependant, le problème demeure: si vous savez comment votre compilateur dispose les données, vous pouvez y accéder.

C'est en fait assez facile:

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

Comme alternative à la méthode de porte dérobée par modèle, vous pouvez utiliser la classe template backdoor. La différence est qu'il n'est pas nécessaire de placer cette classe de porte dérobée dans la zone publique de la classe que vous allez tester. J'utilise le fait que de nombreux compilateurs autorisent les classes imbriquées à accéder à la zone privée de la classe englobante (ce qui n'est pas exactement le standard de 1998 mais est considéré comme un comportement & "Droit &"). Et bien sûr, en C ++ 11, cela est devenu un comportement légal.

Voir cet exemple:

#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();
}

Outre #define private public , vous pouvez également #define private protected , puis définir une classe foo en tant que descendant de la classe recherchée pour avoir accès à ses méthodes (désormais protégées). via le casting de type.

créez simplement votre propre fonction membre d'accès pour étendre la classe.

À toutes les personnes suggérant & "; #define private public &" ;:

Ce genre de chose est illégal . La norme interdit de définir / exécuter des macros équivalentes lexicalement à des mots-clés de langue réservés. Bien que votre compilateur ne se plaindra probablement pas (je n'ai pas encore vu de compilateur le faire), ce n'est pas quelque chose qui est un & "Good Thing &"; à faire.

& "Utiliser des variables privées n'est pas une méthode fiable à 100% pour appliquer l'encapsulation, même en C ++. &"; Vraiment? Vous pouvez désassembler la bibliothèque dont vous avez besoin, trouver tous les décalages nécessaires et les utiliser. Cela vous donnera la possibilité de changer n'importe quel membre privé que vous aimez ... MAIS! Vous ne pouvez pas accéder à des membres privés sans un certain piratage. Disons qu'écrire const ne rendra pas votre constante vraiment constante, car vous pouvez limitez const ou utilisez simplement son adresse pour l'invalider. Si vous utilisez MSVC ++ et que vous avez spécifié & Quot; -merge: .rdata = .data & Quot; Pour un éditeur de liens, l'astuce fonctionnera sans aucune erreur d'accès à la mémoire. Nous pouvons même dire que l'écriture d'applications en C ++ n'est pas un moyen fiable d'écrire des programmes, car le code de bas niveau obtenu peut être corrigé de l'extérieur lorsque votre application est en cours d'exécution. Alors, quel est le moyen documenté fiable pour appliquer l’encapsulation? Pouvons-nous cacher les données quelque part dans la RAM et empêcher quoi que ce soit d'y accéder, à l'exception de notre code? La seule idée que j'ai est de chiffrer les membres privés et de les sauvegarder, car quelque chose peut corrompre ces membres. Désolé si ma réponse est trop impolie, je ne voulais offenser personne, mais je ne pense vraiment pas que cette affirmation soit sage.

puisque vous avez un objet de classe requise, je suppose que vous avez une déclaration de classe. Maintenant, ce que vous pouvez faire est de déclarer une autre classe avec les mêmes membres, mais de garder tous les spécificateurs d’accès comme publics.

Par exemple, la classe précédente est:

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

public:
    somefunctions
}

vous pouvez déclarer une classe comme

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

    somefunctions
};

Il ne vous reste plus qu'à convertir le pointeur de la classe Iamcompprivate en un pointeur de la classe NowIampublic et à l'utiliser comme bon vous semble.

Exemple:

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

En faisant référence à * this , vous activez une porte dérobée pour toutes les données privées d'un objet.

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

Très souvent, une classe fournit des méthodes de mutation à des données privées (getters et setters).

Si une classe fournit un getter qui retourne une référence const (mais pas un séparateur), vous pouvez simplement const_cast la valeur renvoyée par le getter et l'utiliser comme une valeur l:

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;

J'ai utilisé une autre approche (et solution) utile pour accéder à un membre c ++ privé / protégé.
La seule condition est que vous puissiez hériter de la classe à laquelle vous souhaitez accéder.
Ensuite, tout le mérite revient à reinterpret_cast & Lt; & Gt; () .

Un problème possible est que cela ne fonctionnera pas si vous insérez une fonction virtuelle, qui modifiera la table virtuelle, et donc la taille / l'alignement de l'objet.

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();
};

Ensuite, il vous suffit d'utiliser la classe comme suit:

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

Mon problème initial était le suivant: j'avais besoin d'une solution qui n'implique pas une recompilation des bibliothèques QT.
Il existe 2 méthodes dans QObject , dumpObjectInfo () et dumpObjectTree (), qui Il suffit de travailler si les bibliothèques QT sont compilées en mode débogage et qu’elles ont bien sûr besoin d’un accès au membre d_ptr proteted (parmi d’autres structures internes).
Ce que j'ai fait est d'utiliser la solution proposée pour réimplémenter (avec copier-coller) ces méthodes dans dumpObjectInfo2 () et dumpObjectTree2 () dans ma propre classe ( QObjectWrapper ) supprimer ces gardes de préprocesseur de débogage.

Le code suivant accède et modifie un membre privé de la classe en utilisant un pointeur sur cette classe.

#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).*/

but d'étude seulement .... essayez ceci .... peut être utile je suppose ..... ce programme peut accéder aux données privées simplement en connaissant les valeurs ...

//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.....";
}

Inspiré de @Johannes Schaub - litb, le code suivant peut être un peu plus facile à digérer.

    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;

  }
class Test{
    int a;
    alignas(16) int b;
    int c;
};

Test t;

méthode A: humeur intrusive. puisque nous pouvons accéder au code source et le compléter, nous pouvons utiliser de nombreuses autres manières, comme la classe friend d’accéder à un membre privé, elles sont toutes des portes dérobées légales.

méthode B: humeur brute.

int* ptr_of_member_c = reinterpret_cast<int*>(reinterpret_cast<char*>(&t) + 20);

nous utilisons un nombre magique (20), et ce n’est pas toujours correct. Lorsque la présentation de la classe Test a changé, le nombre magique est une source importante de bogues.

méthode C: humeur du super pirate. Y a-t-il une humeur non intrusive et non brute? étant donné que la mise en page de la classe Test est masquée par le compliant, nous ne pouvons pas obtenir d'informations décalées de la bouche du complie. ex.

offsetof(Test,c); //complie error. they said can not access private member.

nous ne pouvons pas non plus obtenir le pointeur membre de la classe Test. ex.

&Test::c ;  //complie error. they said can not access private member.

@Johannes Schaub - litb a un blog, il a trouvé un moyen de voler le pointeur d’un membre privé. mais je pensais que cela devait être le bogue ou le piège linguistique du complier. Je peux le respecter sur gcc4.8, mais pas sur vc8.

donc la conclusion peut être: le propriétaire construit toutes les portes dérobées. le voleur a toujours brute et mauvaise façon de s'introduire. le pirate accidentel a une manière élégante et automatisée de s'introduire.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top