76

免責事項

はい、私が尋ねていることは完全に愚かであり、製品コードでそのようなことを試みたい人は誰でも解雇および/または撃たれるべきであることを十分に認識しています. 私は主に、できるかどうかを見ています。

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

(ナイーブで本番環境に対応していないテクニックを歓迎します)

アップデート

コメントに記載されているように、この質問をしたのは、過剰カプセル化 (およびそれが TDD に与える影響) に関するブログ投稿を書きたかったからです。「プライベート変数の使用は、C++ であっても、カプセル化を強制する 100% 信頼できる方法ではない」と言う方法があるかどうかを確認したかったのです。最後に、問題がなぜ問題なのかよりも、問題を解決する方法にもっと焦点を当てることにしたので、ここで取り上げたもののいくつかは、計画していたほど目立つようには取り上げませんでしたが、それでもリンクを残しました.

いずれにせよ、それがどのように生まれたかに興味がある人は、次 のとおりです。

4

27 に答える 27

80

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

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
于 2009-01-08T16:43:42.970 に答える
65

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

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++ のアクセス ルールが信頼できないことを示しているわけではありません。言語規則は、偶発的なミスから保護するように設計されています。オブジェクトのデータを盗もうとしても、言語の設計により、阻止するのに長い道のりはかかりません。

于 2010-07-03T22:38:43.413 に答える
30

以下は卑劣で違法であり、コンパイラに依存しており、さまざまな実装の詳細によっては機能しない可能性があります。

#define private public
#define class struct

しかし、それはあなたのOPへの答えであり、引用すると、「完全に愚かであり、プロダクションコードでそのようなことを試みたい人は誰でも解雇および/または撃たれるべきである」というテクニックを明示的に招待します。


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

于 2009-01-08T12:53:43.700 に答える
24

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

火花とおそらくクラッシュを期待してください ;)

于 2009-01-08T13:04:52.930 に答える
12
class A 
{ 
   int a; 
}
class B
{
   public: 
   int b;
}

union 
{ 
    A a; 
    B b; 
};

それはそれをする必要があります。

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

TC ++ PLセクションC.8.3:「コンストラクタ、デストラクタ、またはコピー操作を含むクラスは、ユニオンメンバーのタイプにすることはできません...コンパイラは破棄するメンバーを認識しないためです。」

したがって、最善の策は、のレイアウトclass Bに一致することを宣言Aし、クラスのプライベートを調べるためにハックすることです。

于 2009-01-09T03:41:22.097 に答える
10

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

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());
}
于 2009-01-08T19:13:56.570 に答える
9

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

class Bar {
  SomeOtherType _m1;
  int _m2;
};

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

_m1 にアクセスする方法を次に示します。

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

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

int& GetM2(Bar* pBar) {
  char* p = reinterpret_cast<char*>(pBar);
  p += sizeof(SomeOtherType);
  return *(reinterpret_cast<int*>(p));
}
于 2009-01-08T15:34:39.383 に答える
5

この回答は、 @Johannes の answer/blogで示されている正確な概念に基づいています。これが唯一の「正当な」方法のようです。そのサンプル コードを便利なユーティリティに変換しました。C++03 と簡単に互換性があります (実装std::remove_referenceと置換によりnullptr)。

としょうかん

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

#define ALLOW_ACCESS(CLASS, MEMBER, ...) \
  template<typename Only, __VA_ARGS__ CLASS::*Member> \
  struct CONCATE(MEMBER, __LINE__) { friend __VA_ARGS__ CLASS::*Access(Only*) { return Member; } }; \
  template<typename> struct Only_##MEMBER; \
  template<> struct Only_##MEMBER<CLASS> { friend __VA_ARGS__ 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>, <member>, <type>);

使用法

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, member, int);

int main() {
  X x;
  ACCESS(x, member) = 42;
  std::cout << "proof: " << x.get_member() << std::endl;
}
于 2017-01-01T15:25:43.290 に答える
3

C++ コンパイラがどのように名前を変更するかを知っていれば、そうです。

それが仮想関数でない限り。しかし、C++ コンパイラが VTABLE を構築する方法を知っていれば ...

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

于 2009-01-08T12:50:19.213 に答える
3

ところでクールな質問...これが私の作品です:

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

お役に立てれば。

于 2011-03-29T09:03:22.457 に答える
1

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

class jail {
    int inmate;
public:
    int& escape() { return inmate; }
};
于 2009-01-08T16:22:48.847 に答える
0

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

于 2009-01-08T13:01:40.533 に答える
0

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

于 2009-01-08T13:30:34.827 に答える
0

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

クラスが const 参照を返すゲッターを提供する場合 (ただし、セッターはありません)、ゲッターの戻り値を const_cast し、それを左辺値として使用できます。

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;
于 2013-07-16T10:14:59.493 に答える
0

必要なクラスのオブジェクトがあるので、クラスの宣言があると思います。今できることは、同じメンバーを持つ別のクラスを宣言することですが、そこにあるすべてのアクセス指定子をパブリックとして保持します。

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

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

public:
    somefunctions
}

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

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

    somefunctions
};

Iamcompprivateあとは、クラスのポインターをクラスのポインターにキャストし、NowIampublicそれらを自由に使用するだけです。

例:

NowIampublic * changetopublic(Iamcompprivate *A)
{
    NowIampublic * B = (NowIampublic *)A;
    return B;
}
于 2010-06-30T09:56:53.113 に答える
0

研究目的のみ....これを試してください....役立つかもしれません.....このプログラムは、値を知るだけでプライベートデータにアクセスできます...

//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.....";
}
于 2016-09-18T18:11:30.710 に答える
0

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

#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).*/
于 2014-08-10T12:58:58.537 に答える
0

「 #define private public 」を提案しているすべての人へ:

このようなことは違法です。標準では、予約済みの言語キーワードと字句的に同等なマクロを定義/未定義にすることを禁止しています。あなたのコンパイラはおそらく文句を言わないでしょうが(私はまだそうするコンパイラを見たことがありません)、それは「良いこと」ではありません。

于 2009-01-08T16:16:12.917 に答える
0

私は別の便利なアプローチ (およびソリューション) を使用して、c++ のプライベート/保護されたメンバーにアクセスしました。
唯一の条件は、アクセスしたいクラスから継承できることです。
次に、すべての功績はreinterpret_cast<>()に行きます。

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

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 ライブラリがデバッグ モードでコンパイルされている場合にのみ機能し、もちろん d_ptr 保護されたメンバー (他の内部構造の中でも) にアクセスする必要があります。 私がしたことは、提案されたソリューションを使用して、自分のクラス ( QObjectWrapper ) のdumpObjectInfo2 () とdumpObjectTree2 () のメソッドを (コピー アンド ペーストで) 再実装し、これらのデバッグ プリプロセッサ ガードを削除することでした。

于 2014-04-08T15:31:07.930 に答える
0

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

  }
于 2018-07-22T21:36:46.490 に答える
0

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

于 2010-12-22T21:55:26.667 に答える
-1
class Test{
    int a;
    alignas(16) int b;
    int c;
};

Test t;

方法 A : 押し付けがましい気分。 ソースコードにアクセスして再コンパイルできるため、フレンドクラスなどの他の多くの方法を使用してプライベートメンバーにアクセスできます。これらはすべて合法的なバックドアです.

方法 B : 野蛮な気分。

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

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

方法 C : スーパーハッカー気分。 押し付けがましくなく野蛮でないムードはありますか?クラス Test のレイアウト情報はコンパイラによって隠蔽されているため、コンパイラの口からオフセット情報を取得することはできません。元。

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 コンパイラではコンパイルできません。

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

于 2014-09-11T02:25:31.173 に答える