Domanda

Disclaimer

Sì, sono pienamente consapevole che quello che sto chiedendo è totalmente stupido e che tutti coloro che desiderano provare una cosa del genere nel codice di produzione dovrebbe essere licenziato e/o colpo.Io sono principalmente cercando di vedere se può essere fatto.

Ora che questo è fuori strada, c'è un modo per accesso privato ai membri della classe in C++ da fuori la classe?Per esempio, c'è un modo per fare questo con il puntatore offset?

(Ingenuo e non altrimenti pronto per la produzione e tecniche di benvenuto)

Aggiornamento

Come indicato nei commenti, ho fatto questa domanda perché volevo scrivere un post sul blog su-incapsulamento (e come influisce TDD).Volevo vedere se c'era un modo per dire "che utilizza variabili private non è affidabile al 100% per far applicare l'incapsulamento, anche in C++." Alla fine, ho deciso di concentrarsi di più su come risolvere il problema, piuttosto che perché è un problema, quindi non ho un po ' di roba portato qui prominente come avevo previsto, ma mi ha lasciato comunque un link.

In ogni caso, se qualcuno è interessato a come è venuto fuori, qui è: I nemici di Test Driven Development parte I:incapsulamento (Vi consiglio di leggerlo prima di decidere che io sono pazzo).

È stato utile?

Soluzione

Se la classe contiene qualsiasi modello funzioni di membro specializzati membro in funzione alle proprie esigenze.Anche se lo sviluppatore originale non penso.

cassetta di sicurezza.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>();
}

Output:

900000
800000

Altri suggerimenti

Ho aggiunto un voce al mio blog (vedi sotto) che mostra come può essere fatto.Qui è un esempio di come si usa per la seguente classe

struct A {
private:
  int member;
};

Basta dichiarare una struttura per cui si descrivono e creare un'istanza della classe di implementazione utilizzata per la rapina

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

Il Rob modello di classe è definita come questo, e deve essere definita solo una volta, indipendentemente dal numero di soci privati si prevede di accesso

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

Tuttavia, questo non mostra che c++, le regole di accesso non sono affidabili.Il linguaggio regole sono progettati per proteggere contro accidentali errori - se si tenta di rapinare i dati di un oggetto, la lingua by-design non ci vuole molto modi per prevenire voi.

Il seguente è subdolo, illegale, compilatore-dipendente, e potrebbe non funzionare su vari dettagli di implementazione.

#define private public
#define class struct

Ma è una risposta alla tua OP, in cui è esplicitamente invitare una tecnica che, e cito, è "totalmente stupido e per tutti coloro che desiderano provare una cosa del genere nel codice di produzione dovrebbe essere licenziato e/o di ripresa".


Un'altra tecnica è quella di membro privato di accesso dati, da contructing puntatori utilizzando hard-coded/manualmente il codice di offset dall'inizio dell'oggetto.

Hmmm, non so se questo potrebbe funzionare, ma potrebbe essere la pena di provare.Creare un'altra classe con lo stesso layout come l'oggetto con soci privati, ma con il proprio cambiato il pubblico.Creare una variabile di un puntatore a questa classe.Utilizzare un semplice cast a questo punto al tuo oggetto con soci privati e provare a chiamare una funzione privata.

Si aspettano scintille e forse un crash ;)

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

union 
{ 
    A a; 
    B b; 
};

Che dovrebbe fare.

ETA:Si lavorerà per questo tipo di classe banale, ma come una cosa generale non.

TC++PL Sezione C. 8.3:"Una classe con un costruttore, distruttore, l'operazione di copia o non può essere il tipo di membro dell'unione ...perché il compilatore non sa quale membro di distruggere."

Così siamo lasciati con la scommessa migliore di essere a dichiarare class B a partita A's layout e hack a guardare una classe di privati.

Se è possibile ottenere un puntatore a un membro di una classe, è possibile utilizzare il puntatore del mouse non importa ciò che il specificatori di accesso sono (anche i metodi).

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

Ovviamente la mia preferita po ' di hack è il modello friend porta sul retro.

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

Supponendo che il creatore di sopra ha definito backDoor per il suo normale utilizzo.Ma si desidera accedere all'oggetto e guardare le variabili membro private.Anche se la classe è stato compilato in una libreria statica è possibile aggiungere il proprio modello di specializzazione per backDoor e quindi l'accesso membri.

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

È sicuramente possibile accedere ai membri privati con un offset puntatore in C++.Supponiamo che ho avuto la seguente definizione del tipo che ho voluto accesso.

class Bar {
  SomeOtherType _m1;
  int _m2;
};

Supponendo che non ci siano i metodi virtuali nella Barra, Il caso facile è _m1.Soci in C++ sono memorizzati come offset della locazione di memoria dell'oggetto.Il primo oggetto è all'offset 0, il secondo oggetto offset di sizeof(primo membro), ecc ...

Ecco quindi un modo per accedere _m1.

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

Ora _m2 è un po ' più difficile.Abbiamo bisogno di spostare l'originale puntatore sizeof(SomeOtherType) byte dall'originale.Il cast a char è che mi permettano di incrementare in un offset di byte

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

fresco domanda btw...ecco il mio pezzo:

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

Spero che questo aiuta.

Questa risposta è basata su la nozione esatta dimostrato da @Johannes risposta/blog, come che sembra essere l'unico "legittimo" modo.Ho convertito il codice di esempio in un programma di utilità pratico.È facilmente compatibile con il C++03 (mediante l'attuazione di std::remove_reference e sostituzione nullptr).

Libreria

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

Utilizzo

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

Esempio

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

Se si sa come il vostro compilatore C++ manipola nomi, sì.

A meno che, suppongo, è una funzione virtuale.Ma poi, se sai il C++ compilatore genera il VTABLE ...

Edit:guardando le altre risposte, mi rendo conto che ho letto male la domanda e ho pensato che era circa le funzioni membro, non sono stati dati.Tuttavia, il punto rimane lo stesso:se si sa come il vostro compilatore stabilisce dati, quindi si può accedere ai dati.

In realtà è abbastanza semplice:

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

Come alternativa al modello di backdoor metodo che è possibile utilizzare il modello backdoor classe.La differenza è che non c'è bisogno di mettere questo backdoor classe in area pubblica della classe la vostra intenzione di test.Io uso il fatto che molti compilatori consentono di classi nidificate per accesso area privata di racchiudere classe (che non è esattamente 1998 standard, ma considerato "giusto" comportamento).E, naturalmente, in C++11 questo è diventato legale il comportamento.

Vedi questo esempio:

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

Accanto #define pubblico e privato si può anche #define privato protetto e poi definire alcune classe pippo, come discendente di voluto classe di avere accesso ad esso (ora protetta) metodi tramite il tipo di fusione.

basta creare il proprio accesso funzione membro di estendere la classe.

A tutte le persone suggerendo "#define pubblico e privato":

Questo tipo di cosa è illegale.La norma vieta la definizione/undef-ing macro che sono lessicalmente equivalente riservati le parole chiave del linguaggio.Mentre il compilatore probabilmente non si lamentano (non ho ancora visto un compilatore che fa), non è qualcosa che è una "Buona Cosa" fare.

"utilizzo di variabili private non è affidabile al 100% per far applicare l'incapsulamento, anche in C++." Davvero?È possibile smontare la biblioteca è necessario, trovare tutti gli spostamenti necessari e il loro utilizzo.Che vi darà una possibilità di cambiare qualsiasi membro privato che ti piace...MA!Non è possibile accedere ai membri privati senza qualche sporco di hacking.Diciamo che la scrittura const non rendere il vostro costante di essere molto costante, perche 'lo si può cast const di distanza, o semplicemente usare l'indirizzo che compromettono la validità.Se si utilizza MSVC++ e specificato "-merge:.rdata=.dati" per un linker, il trucco funzionerà senza alcun accesso alla memoria guasti.Possiamo anche dire che la scrittura di applicazioni in C++ non è un modo affidabile per scrivere programmi, perche 'con conseguente basso livello di codice può essere patchato da qualche parte al di fuori quando l'applicazione è in esecuzione.Allora che cosa è affidabile documentato modo per forzare l'incapsulamento?Possiamo nascondere i dati da qualche parte in RAM e prevenire qualsiasi accesso a esse, a meno che il nostro codice?L'unica idea che ho è quello di crittografare i soci privati e di backup, perche 'qualcosa potrebbe corrompere i membri.Scusa se la mia risposta è troppo rude, non intendevo offendere nessuno, ma io davvero non credo che la dichiarazione è saggio.

dal momento che si dispone di un oggetto di classe sto indovinando che abbiate dichiarazione di classe.Ora cosa si può fare è dichiarare in un'altra classe, con gli stessi membri, ma mantenere tutti ci specificatori di accesso pubblici.

Per esempio precedente classe è:

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

public:
    somefunctions
}

è possibile dichiarare una classe come

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

    somefunctions
};

Ora tutto quello che dovete fare è gettato puntatore di classe Iamcompprivate in un puntatore di classe NowIampublic e li usa come U wish.

Esempio:

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

Facendo riferimento al *questo si attiva una backdoor per tutti i dati privati all'interno di un oggetto.

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

Molto spesso una classe mutatore metodi per dati privati (getter e setter).

Se una classe non fornire una lettura che restituisce un const reference (ma non setter), allora si può solo const_cast il valore di ritorno del getter, e utilizzare che come un l-value:

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;

Ho usato un altro approccio utile (e la soluzione) per accedere a un c++ private/protected membro.
L'unica condizione è che siete in grado di ereditare dalla classe a cui si desidera accedere.
Quindi tutto il merito va a reinterpret_cast<>().

Un possibile problema è che non funziona se si inserisce una funzione virtuale, che permetterà di modificare la tabella virtuale, e così, la dimensione dell'oggetto/allineamento.

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

Poi basta usare la classe come segue:

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

Il mio problema è il seguente:Ho bisogno di una soluzione che non implica ricompilare le librerie QT.
Ci sono 2 metodi QObject, dumpObjectInfo() e dumpObjectTree(), che solo lavoro se le librerie QT sono compilate in modalità di debug, e, naturalmente, hanno bisogno di accedere a d_ptr proteted membri (tra le altre strutture interne).
Cosa che ho fatto è stato utilizzare la soluzione proposta reimplementare (con copia e incolla) questi metodi in dumpObjectInfo2() e dumpObjectTree2() nella mia classe (QObjectWrapper) la rimozione di quelli di debug preprocesor guardie.

Il codice riportato di seguito accede e modifica di un membro privato della classe utilizzando un puntatore ad una 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).*/

fini di studio solo....prova questo ....può essere utile, credo.....questo programma può accedere ai dati solo conoscendo i valori...

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

Ispirato @Johannes Schaub - litb, il codice riportato di seguito può essere un po ' più facile da digerire.

    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;

metodo A :invadente umore. dal momento che siamo in grado di accedere al codice sorgente e recomplie, possiamo utilizzare molti altri, come amico di classe a membro privato di accesso, sono tutti legali backdoor.

metodo B :bruta umore.

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

usiamo un numero magico (20) , e non È sempre giusto.Quando il formato di Test della classe cambiato, il numero magico è un grosso bug di origine.

metodo C :super hacker umore. c'è qualche non-intrusive e non brute umore ?dal momento che la classe di Test del layout di informazioni nascondi dal compilatore, non siamo in grado di ottenere i dati di offset dal complie bocca.ex.

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

anche noi non possiamo ottenere membro puntatore dalla classe di Test.ex.

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

@Johannes Schaub - litb ha un blog, ha trovato un modo per rob membro privato puntatore.ma ho pensato che questo dovrebbe essere compilatore bug di linguaggio o di insidia.posso complie su gcc4.8, ma non vc8 compilatore.

quindi, la conclusione potrebbe essere : il padrone di casa di creare tutti i backdoor.il ladro ha sempre bruto e cattivo modo per rompere.l'hacker accidentale dispone di camere eleganti e un modo automatico per rompere.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top