Pergunta

Aviso

Sim, estou plenamente consciente de que o que estou perguntando é totalmente estúpido e que qualquer um que gostaria de tentar uma coisa dessas no código de produção deve ser demitido e / ou tiro. Eu estou olhando principalmente para ver se pode ser feito.

Agora que isso está fora do caminho, há alguma maneira de membros de acesso de classe privada em C ++ de fora da sala de aula? Por exemplo, existe alguma maneira de fazer isso com offsets ponteiro?

(técnicas ingênuo e de outra forma não-produção pronto bem-vindos)

Atualização

Como observado nos comentários, eu fiz esta pergunta porque eu queria escrever um post sobre o excesso de encapsulamento (e como isso afeta TDD). Eu queria ver se havia uma maneira de dizer "utilizando variáveis ??privadas não é uma maneira 100% confiável para impor encapsulamento, mesmo em C ++". No final, eu decidi concentrar mais em como resolver o problema em vez de por que é um problema, então eu não apresentam algumas das coisas trouxe aqui tão destacada como eu tinha planejado, mas eu ainda deixou um link.

De qualquer forma, se alguém está interessado em saber como ele saiu, aqui está: inimigos do Test Driven Development parte I:. encapsulamento (sugiro lê-lo antes de decidir que eu sou louca)

Foi útil?

Solução

Se a classe contém quaisquer funções membro modelo que você pode especializar-se que a função de membro para atender às suas necessidades. Mesmo se o desenvolvedor original não pensar sobre isso.

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

Output:

900000
800000

Outras dicas

Eu adicionei uma entrada ao meu blog (veja abaixo) que mostra como isso pode ser feito. Aqui está um exemplo de como você usá-lo para a seguinte classe

struct A {
private:
  int member;
};

Apenas declarar uma estrutura para ele onde você descrevê-lo e instanciar a classe de implementação usado para roubo

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

O modelo de classe Rob é definida como esta, e só precisa ser definido uma vez, independentemente quantos membros privadas você pretende acesso

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

No entanto, isso não mostra que c ++ 's regras de acesso não são confiáveis. As regras de linguagem são projetados para proteger contra erros acidentais - se você tentar dados roubar de um objeto, a linguagem por-design não leva muito tempo maneiras de impedi-lo.

O seguinte é sorrateira, ilegal, dependente do compilador, e pode não funcionar, dependendo de vários detalhes de implementação.

#define private public
#define class struct

Mas é uma resposta à sua OP, em que você convida explicitamente uma técnica que, e cito, é "totalmente estúpido e que qualquer um que gostaria de tentar uma coisa dessas no código de produção deve ser demitido e / ou tiro ".


Outra técnica é a de dados de acesso de membros privado, por contructing ponteiros usando deslocamentos hard-coded / mão-codificado desde o início do objeto.

Hmmm, não sei se isso iria funcionar, mas pode ser uma tentativa vale a pena. Crie outra classe com o mesmo layout como o objeto com membros privados, mas com mudado privada para pública. Crie uma variável de ponteiro para esta classe. Use um elenco simples de apontar isso para o seu objeto com membros privados e tentar chamar uma função privado.

Esperar faíscas e talvez um acidente;)

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

union 
{ 
    A a; 
    B b; 
};

Isso deve fazê-lo.

ETA: Ele vai trabalhar para este tipo de classe trivial, mas como uma coisa geral não vai.

TC ++ PL Seção C.8.3: "Uma classe com um construtor, destruidor, ou copiar operação não pode ser o tipo de um membro do sindicato ... porque o compilador não saberia qual membro de destruir"

Assim, ficamos com a melhor aposta sendo a class B declarar para corresponder ao layout do A e cortar a olhar para privates de uma classe.

Se você pode obter um ponteiro para um membro de uma classe que você pode usar o ponteiro não importa o que são os especificadores de acesso (mesmo os métodos).

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

É claro que meu corte pequeno favorito é a porta modelo amigo de volta.

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

Assumindo que o criador do acima definiu backdoor para seus usos normais. Mas você quer acessar o objeto e olhar para as variáveis ??de membro privadas. Mesmo que a classe acima foi compilado em uma biblioteca estática que você pode adicionar sua própria especialização de modelo para Backdoor e, assim, acessar os membros.

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

É definitivamente possível acessar membros privados com um ponteiro compensados ??em C ++. Vamos supor que eu tinha a seguinte definição de tipo que eu queria acesso.

class Bar {
  SomeOtherType _m1;
  int _m2;
};

Assumindo que não há métodos virtuais em Bar, O caso mais fácil é _M1. Membros em C ++ são armazenados como offsets do local de memória do objeto. O primeiro objeto é no deslocamento 0, o segundo objeto no deslocamento de sizeof (primeiro membro), etc ...

Então aqui está uma forma de acesso _M1.

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

Agora _m2 é um pouco mais difícil. Precisamos mover o ponteiro originais sizeof (SomeOtherType) bytes a partir do original. O elenco para char é para garantir que estou incrementando em um deslocamento de byte

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

questão legal btw ... aqui está o meu pedaço:

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

Espero que isso ajude.

Esta resposta é baseada no conceito exato demonstrado por @ de Johannes resposta / blog , como que parece ser a única forma "legítima". Eu ter convertido aquele código exemplo em um utilitário prático. É facilmente compatível com C ++ 03 (através da implementação de std::remove_reference & substituindo nullptr).

Biblioteca

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

Uso

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

Exemplo

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 você sabe como seu compilador C ++ Mangles nomes, sim.

A menos que, suponho, é uma função virtual. Mas então, se você sabe como seu compilador C ++ constrói o VTABLE ...

Edit: olhando para as outras respostas, eu percebo que eu descaracterizou a pergunta e pensei que era sobre as funções de membro, e não dados dos membros. No entanto, o ponto ainda permanece:. Se você sabe como seu compilador estabelece dados, então você pode acessar esses dados

É realmente muito fácil:

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

Como uma alternativa ao método de modelo backdoor você pode usar classe backdoor modelo. A diferença é que você não precisa colocar essa classe backdoor para a área pública da classe você está indo para teste. Eu uso o fato de que muitos compiladores permitem classes aninhadas para acessar a área privada de encerrar classe (o que não é exatamente o padrão de 1998, mas considerado um comportamento "direito"). E, claro, em C ++ 11 este tornou-se um comportamento legal.

Veja este exemplo:

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

Ao lado de pública privada #define Você também pode #define privada protegida e, em seguida, definir alguma classe foo como descendente de classe queria ter acesso a ele de métodos (agora protegidas) através do tipo de fundição.

basta criar sua própria função de membro do acesso a estender a classe.

Para todas as pessoas sugerindo " pública privada #define ":

Este tipo de coisa é ilegal . Os proíbe padrão definindo / macros que são lexically equivalente ao idioma palavras-chave reservadas undef-ing. Enquanto seu compilador provavelmente não vai reclamar (eu ainda tenho que ver um compilador que faz), não é algo que é uma "boa coisa" a fazer.

"usando variáveis ??privadas não é uma maneira 100% confiável para impor encapsulamento, mesmo em C ++." Realmente? Você pode desmontar a biblioteca que você precisa, encontrar todas as compensações necessárias e usá-los. Isso lhe dará a capacidade de alterar qualquer membro particular você gosta ... MAS! Você não pode acessar membros privados sem algum sujo hacking. Digamos que a escrita const não vai fazer seu constante ser muito constante, porque você pode cast const afastado ou apenas usá-lo de endereço para invalidá-la. Se você estiver usando MSVC ++ e você especificado "-merge: .rdata = .data" a um ligador, o truque irá funcionar sem quaisquer falhas de acesso à memória. Podemos até dizer que escrever aplicativos em C ++ não é maneira confiável de programas de gravação, porque resultando código de baixo nível pode ser corrigido de fora em algum lugar quando o seu aplicativo está sendo executado. Então o que é possível documentada confiável para impor encapsulamento? podemos esconder a algum lugar de dados na memória RAM e evitar qualquer coisa de acessá-los, exceto o nosso código? A única ideia que tenho é criptografar os membros privados e de backup-los, porque algo pode corromper os membros. Desculpe se minha resposta é muito rude, eu não tive a intenção de ofender ninguém, mas eu realmente não acho que essa afirmação é sábio.

desde que você tem um objeto da classe requerida eu estou supondo que você tem declaração de classe. Agora, o que você pode fazer é declarar outra classe com os mesmos membros, mas manter todos lá especificadores de acesso como público.

Por exemplo classe anterior é:

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

public:
    somefunctions
}

Você pode declarar uma classe como

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

    somefunctions
};

Agora tudo que você precisa fazer é ponteiro elenco de Iamcompprivate classe em um ponteiro de NowIampublic classe e usá-los como U desejo.

Exemplo:

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

Ao fazer referência a * este ativar um backdoor a todos os dados confidenciais dentro de um objeto.

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

Muitas vezes uma classe fornece modificadores métodos para dados privados (getters e setters).

Se uma classe fornece um getter que retorna uma referência const (mas sem setter), então você pode apenas const_cast o valor de retorno do getter, e usar isso como um 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;

Eu usei uma outra abordagem útil (e solução) para acessar um c ++ membro privado / protegido.
A única condição é que você é capaz de herdar da classe que você deseja acessar.
Em seguida, todo o crédito vai para reinterpret_cast <> () .

Um possível problema é que ele não vai funcionar se você inserir uma função virtual, que irá modificar a tabela virtual, e assim, o tamanho do objeto / alinhamento.

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

Então você só precisa usar a classe da seguinte forma:

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

O meu problema original foi a seguinte: eu precisava de uma solução que não implicará recompilar bibliotecas Qt
. Existem 2 métodos em QObject , dumpObjectInfo () e dumpObjectTree (), que apenas trabalho se libs QT são compilados no modo de depuração, e, claro, acesso necessidade de d_ptr membro proteted (entre outras estruturas internas).
O que fiz foi usar a solução proposta reimplementar (com copiar e colar) esses métodos em dumpObjectInfo2 () e dumpObjectTree2 () em minha própria classe ( QObjectWrapper ) remover os guardas de depuração preprocesor.

Os seguintes acessos de código e modifica um membro particular da classe usando um ponteiro para essa 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).*/
único propósito

estudo .... tente este .... pode ser útil eu acho ..... este programa pode acessar os dados privados apenas por saber os valores ...

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

Inspirado pela @Johannes Schaub -. Litb, o seguinte código pode ser um pouco mais fácil de digerir

    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étodo A:. Humor intrusiva uma vez que pode acessar o código fonte e recomplie isso, podemos usar muitos outra maneira como o amigo de classe para acesso membro privado, todos eles são backdoor jurídica.

Método B:. Humor bruta

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

nós usamos um número mágico (20), e não é sempre certo. Quando o layout de classe de teste alterado, o número mágico é uma fonte de erro grande.

Método C:. Super-humor hackers há qualquer modo não-invasivo e não-bruta? desde infomation disposição da classe de teste é hide pelo compilador, não podemos obter a informação da boca do complie offset. ex.

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

nós também não pode obter ponteiro membro de teste de classe. ex.

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

@Johannes Schaub - litb tem um blog, ele encontrou uma maneira de roubar ponteiro membro privado. mas eu pensei que este deve ser bug ou linguagem armadilha do compilador. i pode complie-lo em gcc4.8, mas não em complier vc8.

Portanto, a conclusão pode ser: o proprietário construir tudo backdoor. o ladrão tem sempre forma bruta e ruim para invadir. acidental hacker tem maneira elegante e automatizado para invadir.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top