友達を使わずにクラス外からプライベートメンバーにアクセスできますか?

StackOverflow https://stackoverflow.com/questions/424104

質問

免責事項

はい、私が質問していることは完全に愚かであり、本番コードでそのようなことを試してみたい人は解雇および/または射撃されるべきであることを完全に認識しています。主に、できるできるかどうかを確認しています。

これで問題はなくなりました。クラス外からC ++のプライベートクラスメンバーにアクセスする方法はありますか?たとえば、ポインターオフセットを使用してこれを行う方法はありますか?

(素朴で非生産的な手法は歓迎)

更新

コメントで述べたように、オーバーカプセル化に関するブログ投稿(およびTDDへの影響)を書きたかったので、この質問をしました。 <!> quot; C ++であっても、プライベート変数を使用することは、カプセル化を強制する100%信頼できる方法ではない、と言う方法があるかどうかを見たかったのです。<!> quot;最後に、なぜそれが問題なのかというよりも、問題を解決する方法にもっと集中することに決めたので、ここで取り上げたもののいくつかを計画したほど目立たせませんでしたが、私はまだリンクを残しました。 / p>

とにかく、誰かがそれがどのように出てきたかに興味があるなら、ここにあります:テスト駆動開発の敵パートI:カプセル化(私がおかしいと判断する前に読むことをお勧めします)。

役に立ちましたか?

解決

クラスにテンプレートメンバー関数が含まれている場合、そのメンバー関数をニーズに合わせて特殊化できます。元の開発者が考えていなかったとしても。

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

出力:

900000
800000

他のヒント

エントリをブログに追加しました(以下を参照)は、その方法を示しています。以下のクラスでどのように使用するかの例を示します

struct A {
private:
  int member;
};

説明する場所に構造体を宣言し、強盗に使用される実装クラスをインスタンス化します

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

Robクラステンプレートはこのように定義され、アクセスするプライベートメンバーの数に関係なく、一度だけ定義する必要があります

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

ただし、これは、c ++のアクセス規則が信頼できないことを示していません。言語規則は、偶発的な間違いから保護するように設計されています。オブジェクトのデータを奪おうとすると、言語は設計によってあなたを防ぐのに長い時間をかけません。

以下は不正で、違法で、コンパイラに依存しており、さまざまな実装の詳細によっては動作しない場合があります。

#define private public
#define class struct

しかし、それはあなたのOPに対する答えであり、あなたは明示的に私が引用するテクニックを招待します。および/またはショット<!> quot;。


もう1つの手法は、オブジェクトの先頭からハードコーディング/ハンドコーディングされたオフセットを使用してポインターを構築することにより、プライベートメンバーデータにアクセスすることです。

うーん、これが機能するかどうかはわかりませんが、試してみる価値はあります。プライベートメンバーを持つオブジェクトと同じレイアウトで、プライベートをパブリックに変更した別のクラスを作成します。このクラスへのポインターの変数を作成します。単純なキャストを使用して、これをプライベートメンバーを持つオブジェクトにポイントし、プライベート関数を呼び出してみてください。

スパークが発生し、クラッシュする可能性があります;)

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

union 
{ 
    A a; 
    B b; 
};

それを行う必要があります。

ETA:この種の些細なクラスでは機能しますが、一般的には機能しません。

  

TC ++ PLセクションC.8.3:<!> quot;コンストラクタ、デストラクタ、またはコピー操作を含むクラスは、ユニオンメンバーのタイプにはなりません...コンパイラは破棄するメンバーを知らないためです。 <!>引用;

そのため、class Bのレイアウトに一致するようにAを宣言し、クラスのプライベートを見るためにハックするのが最善策です。

クラスのメンバーへのポインターを取得できる場合、アクセス指定子が何であっても(メソッドであっても)ポインターを使用できます。

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

もちろん、私のお気に入りの小さなハックはフレンドテンプレートのバックドアです。

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

上記の作成者が通常の使用のためにbackDoorを定義していると仮定します。ただし、オブジェクトにアクセスして、プライベートメンバー変数を確認する必要があります。上記のクラスが静的ライブラリにコンパイルされている場合でも、backDoorに独自のテンプレート特化を追加して、メンバーにアクセスできます。

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

C ++では、ポインターオフセットを使用してプライベートメンバーにアクセスすることは間違いなく可能です。私がアクセスしたい次の型定義を持っていると仮定しましょう。

class Bar {
  SomeOtherType _m1;
  int _m2;
};

Barには仮想メソッドがないと仮定すると、簡単なケースは_m1です。 C ++のメンバーは、オブジェクトのメモリ位置のオフセットとして保存されます。最初のオブジェクトはオフセット0にあり、2番目のオブジェクトはsizeof(first member)のオフセットにあります...

つまり、_m1にアクセスする方法です。

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

Now _m2はもう少し難しいです。元のポインターsizeof(SomeOtherType)バイトを元のポインターから移動する必要があります。 charへのキャストは、バイトオフセットをインクリメントすることを保証することです

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

かっこいい質問です...ここに私の作品があります:

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

これがお役に立てば幸いです。

この回答は、 @Johannesの回答/ブログで示されている正確な概念に基づいています。 <!> quot;正当な<!> quot;方法。このサンプルコードを便利なユーティリティに変換しました。 C ++ 03と簡単に互換性があります(std::remove_reference <!> ampを実装し、nullptrを置き換えます)。

ライブラリ

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

使用法

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

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

C ++コンパイラが名前をマングルする方法を知っているなら、はい。

そうでない場合、仮想関数だと思います。ただし、C ++コンパイラがVTABLEをビルドする方法を知っている場合は...

編集:他の回答を見ると、質問を読み違えて、メンバーデータではなくメンバー関数に関するものだと思いました。ただし、ポイントはまだあります。コンパイラーがデータをどのようにレイアウトするかを知っていれば、そのデータにアクセスできます。

実際には非常に簡単です:

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

テンプレートバックドアメソッドの代替として、テンプレートバックドアクラスを使用できます。違いは、このバックドアクラスを、テストするクラスのパブリックエリアに配置する必要がないことです。私は、多くのコンパイラーがネストされたクラスが囲んでいるクラスのプライベートエリアにアクセスすることを許可するという事実を使用します(これは1998年の標準ではありませんが、<!> quot; right <!> quot;動作と見なされます)。そしてもちろん、C ++ 11ではこれは法的挙動になりました。

この例を参照してください:

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

#define private public のほかに、 #define private protected を使用して、fooクラスを(現在保護されている)メソッドにアクセスするために必要なクラスの子孫として定義することもできます型キャスト経由。

クラスを拡張するには、独自のアクセスメンバー関数を作成します。

<!> quot; #define private public <!> quot;を提案しているすべての人々へ:

この種のものは違法です。標準では、予約語キーワードと字句的に同等なマクロの定義/定義解除は禁止されています。あなたのコンパイラはおそらく文句を言わないでしょう(私はまだコンパイラを見ていません)が、それは<!> quot; Good Thing <!> quot;であるものではありません。

<!> quot;プライベート変数の使用は、C ++であってもカプセル化を強制する100%信頼できる方法ではありません。<!> quot; 本当に?必要なライブラリを分解し、必要なすべてのオフセットを見つけて使用できます。 それはあなたが好きなプライベートメンバーを変更する能力を与えるでしょう...しかし! いくつかの汚いハッキングなしでプライベートメンバーにアクセスすることはできません。 const を書いても、定数が本当に一定になるわけではない、と言うことができます。 const をキャストするか、そのアドレスを使用して無効にします。 MSVC ++を使用しており、<!> quot; -merge:.rdata = .data <!> quot;を指定した場合リンカにとって、このトリックはメモリアクセス障害なしで機能します。 C ++でアプリを書くことは、プログラムを書くための信頼できる方法ではないと言うことさえできます。なぜなら、結果の低レベルのコードは、アプリの実行中に外部からパッチされる可能性があるからです。 次に、カプセル化を実施するための信頼できる文書化された方法は何ですか? RAMのどこかにデータを隠して、コード以外のデータにアクセスできないようにすることはできますか?私が持っている唯一のアイデアは、プライベートメンバーを暗号化してバックアップすることです。 私の答えがあまりにも失礼な場合は申し訳ありませんが、誰かを怒らせるつもりはありませんでしたが、私はその文が賢明だとは本当に思いません。

必要なクラスのオブジェクトがあるので、クラスの宣言があると推測しています。 ここでできることは、同じメンバーで別のクラスを宣言することですが、そこにあるすべてのアクセス指定子をpublicのままにしておきます。

たとえば、前のクラスは次のとおりです。

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

public:
    somefunctions
}

クラスを次のように宣言できます

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

    somefunctions
};

今やるべきことは、クラスIamcompprivateのポインターをクラスNowIampublicのポインターにキャストし、それらをUの希望通りに使用することです。

例:

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

* this を参照することにより、オブジェクト内のすべてのプライベートデータへのバックドアを有効にします。

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

多くの場合、クラスはプライベートデータ(ゲッターとセッター)にミューテーターメソッドを提供します。

クラスがconst参照を返すゲッターを提供する場合(ただし、セッターは提供しない場合)、ゲッターの戻り値をconst_castし、それを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;

別の便利なアプローチ(およびソリューション)を使用して、c ++プライベート/保護メンバーにアクセスしました。
唯一の条件は、アクセスしたいクラスから継承できることです。
その後、すべてのクレジットは reinterpret_cast <!> lt; <!> gt;()に割り当てられます。

考えられる問題は、仮想テーブルを変更する仮想関数を挿入すると機能しないことです。そのため、オブジェクトのサイズ/配置を調整します。

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

次に、次のようにクラスを使用する必要があります。

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

元の問題は次のとおりでした。QTライブラリの再コンパイルを意味しないソリューションが必要でした。
QObject には、 dumpObjectInfo ()と dumpObjectTree ()の2つのメソッドがあります。 QT libsがデバッグモードでコンパイルされ、もちろんd_ptr protetedメンバー(他の内部構造)にアクセスする必要がある場合にのみ機能します。
私がやったのは、提案されたソリューションを使用して、自分のクラス( QObjectWrapper )これらのデバッグプリプロセッサガードを削除します。

次のコードは、そのクラスへのポインターを使用して、クラスのプライベートメンバーにアクセスして変更します。

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

学習目的のみ。... これを試してみてください。 このプログラムは、値を知るだけでプライベートデータにアクセスできます...

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

@Johannes Schaub-litbに触発されて、次のコードは少し簡単に消化できるかもしれません。

    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;

方法A:邪魔な気分。 ソースコードにアクセスして再コンパイルできるため、使用できます 友人クラスのようにプライベートメンバーにアクセスする他の多くの方法は、すべて合法的なバックドアです。

方法B:野moodなムード。

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

マジック番号(20)を使用しますが、常に正しいとは限りません。クラスTestのレイアウトが変更されたとき、マジックナンバーは大きなバグの原因になります。

方法C:スーパーハッカーの気分。 邪魔にならず、無作法なムードはありますか? クラスTestのレイアウト情報はコンパイラーによって非表示になっているため、 complieの口からオフセット情報を取得することはできません。 例

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

クラスTestからメンバーポインターを取得することもできません。 例

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

@Johannes Schaub-litbにはブログがあり、プライベートメンバーポインターを奪う方法を見つけました。 しかし、これはコンパイラーのバグまたは言語の落とし穴だと思いました。 私はgcc4.8ではそれを遵守できますが、vc8コンパイラではそれができません。

したがって、結論は次のようになります。 家主はすべてのバックドアを構築します。 泥棒は常に野bruで悪い侵入方法を持っています。 ハッカーは偶発的に侵入するエレガントで自動化された方法を持っています。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top