我可以在不使用朋友的情况下从课外访问私人成员吗?

放弃

是的,我完全知道我所问的是完全愚蠢的,任何想要在生产代码中尝试这样的东西的人都应该被解雇和/或开枪。 我主要看看是否可以做到。

现在,这是没有办法,有没有办法从课堂以外的C ++访问私人类成员? 例如,有没有办法做到这一点与指针偏移量?

(天真和否则生产准备技术欢迎)

更新

正如在评论中指出的那样,我问这个问题是因为我想写一个关于封装(以及它如何影响TDD)的博客文章。 我想看看是否有办法说“使用私有variables不是100%可靠的方法来强制封装,即使在C ++中”。 最后,我决定把更多的精力放在如何解决问题上,而不是为什么这是一个问题,所以我没有像我计划的那样突出显示一些东西,但是我仍然留下了一个链接。

无论如何,如果有人对它是如何感兴趣的话,这里是: testing驱动开发的敌人第一部分:封装 (我build议在你决定疯了之前阅读它)。

如果类包含任何模板成员函数,您可以专门化该成员函数以满足您的需求。 即使原来的开发者没有想到它。

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类模板是这样定义的,并且只需定义一次,而不pipe您打算访问多less个私有成员

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

但是,这并不表明c ++的访问规则是不可靠的。 语言规则旨在防止意外错误 – 如果您尝试抢夺对象的数据,则语言devise不会妨碍您的工作。

以下是偷偷摸摸的,非法的,依赖于编译器的,根据各种实现细节可能无法正常工作。

 #define private public #define class struct 

但是这是对你的OP的一个回答,你在OP中明确地邀请了一个我引用的技术是“完全愚蠢的,任何想在生产代码中尝试这样的东西的人都应该被解雇和/或开枪”。


另一种技术是通过从对象开始使用硬编码/手工编码的偏移构造指针来访问私有成员数据。

嗯,不知道这是否会奏效,但可能值得一试。 创build另一个与私人成员相同的布局的类,但私人更改为公共。 创build一个指向这个类的指针的variables。 使用一个简单的强制转换将它指向私有成员的对象,然后尝试调用一个私有函数。

期待火花,也许是一个崩溃;)

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

这应该做到这一点。

ETA:它会适用于这种微不足道的课程,但作为一般的东西,它不会。

TC ++ PL C.8.3节:“带有构造函数,析构函数或复制操作的类不能是联合成员的types……因为编译器不知道要销毁哪个成员。

所以我们留下了最好的select,就是宣布class BA的布局相匹配,并且破解一个class级的私人空间。

用C ++中的指针偏移量访问私有成员是完全可能的。 让我们假设我有以下types的定义,我想访问。

 class Bar { SomeOtherType _m1; int _m2; }; 

假设Bar中没有虚方法,简单的情况是_m1。 C ++中的成员被存储为对象内存位置的偏移量。 第一个对象是在偏移0,第二个对象在sizeof(第一个成员)的偏移等…

所以这里是访问_m1的一种方式。

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

现在_m2有点困难。 我们需要移动原始指针的sizeof(SomeOtherType)字节。 转换为字符是为了确保我在一个字节偏移增量

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

如果你能得到一个指向类的成员的指针,你可以使用指针,而不pipe访问说明符是什么(甚至是方法)。

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

假设上面的创build者已经为他的正常使用定义了backDoor。 但是你想访问对象并查看私有成员variables。 即使上面的类已经被编译成静态库,你也可以为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()); } 

很酷的问题btw …这是我的作品:

 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的回答/博客所展示的确切概念,因为这似乎是唯一的“合法”方式。 我已经将该示例代码转换为便利的实用程序。 它很容易与C ++ 03兼容(通过实现std::remove_reference并replacenullptr )。

图书馆

 #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 ++编译器如何改变名称,是的。

除非我想,这是一个虚拟function。 但是,如果你知道你的C ++编译器如何构buildVTABLE …

编辑:看着其他的答复,我意识到我误解了这个问题,认为这是关于成员函数,而不是成员数据。 然而,问题依然存在:如果您知道您的编译器如何布置数据,那么您可以访问这些数据。

作为模板后门方法的替代方法,您可以使用模板后门类。 不同的是,你不需要把这个后门类放到你要testing的类的公共区域。 我使用的事实是,许多编译器允许嵌套类访问封闭类的私有区域(这不完全是1998年的标准,但被认为是“正确”的行为)。 当然,在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(); } 

以下代码使用指向该类的指针访问和修改该类的私有成员。

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

除了#define private public外,你还可以#define private protected ,然后定义一些foo类作为想要的类的后代,以通过types转换来访问它的(现在受保护的)方法。

只需创build自己的访问成员函数来扩展该类。

对所有build议“定义私人公众 ”的人:

这种事情是非法的 。 标准禁止定义/定义与保留语言关键字在词汇上相同的macros。 虽然你的编译器可能不会抱怨(我还没有看到编译器这样做),但这不是一件好事。

其实很简单:

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

“使用私有variables并不是100%可靠的方法来强制封装,即使在C ++中也是如此。” 真? 您可以拆卸所需的库,find所需的所有偏移量并使用它们。 这将让你有能力改变任何你喜欢的私人会员…但! 你不能访问私人成员没有一些肮脏的黑客攻击。 让我们说写const是不会使你的常量真的是不变的,因为你可以把const放在外面,或者只是使用它的地址来使它失效。 如果您使用的是MSVC ++,并且您将“-merge:.rdata = .data”指定给链接器,则该技巧将无任何内存访问错误。 我们甚至可以说用C ++编写应用程序并不是编写程序的可靠方法,因为低级代码可能会在应用程序运行时从外部打补丁。 那么什么是可靠的logging方式来强制封装? 我们可以隐藏RAM中的数据,并防止任何东西访问它们,除了我们的代码? 我唯一的想法是encryption私人成员并对其进行备份,因为有些东西可能会腐蚀那些成员。 对不起,如果我的回答太不礼貌,我不是故意冒犯任何人,但我真的不认为这个说法是明智的。

因为你有一个需要的类的对象,我猜你有类的声明。 现在你可以做的是用相同的成员声明另一个类,但是将所有的访问说明符都保存为public。

例如,以前的类是:

 class Iamcompprivate { private: Type1 privateelement1; Typ2 privateelement2; ... public: somefunctions } 

你可以声明一个类

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

现在,您只需将Iamcompprivate类的指针转换为NowIampublic类的指针,并将其用作U wish。

例:

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

通过引用*,您可以为对象内的所有私有数据启用后门。

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

很多时候,一个类为私有数据(getter和setter)提供了mutator方法。

如果一个类提供了一个返回一个const引用(但不包含setter)的getter,那么你可以const_cast getter的返回值,并用它作为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; 

我用另一个有用的方法(和解决scheme)来访问一个c + +私有/受保护的成员。
唯一的条件是你可以inheritance你想访问的类。
然后所有的功劳都去reinterpret_cast <>()

一个可能的问题是,如果你插入一个虚拟函数,它将修改虚拟表,所以,对象大小/alignment,它将无法正常工作。

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

我原来的问题如下:我需要一个解决scheme,并不意味着重新编译QT库。
QObjectdumpObjectInfo ()和dumpObjectTree ()中有两个方法,如果QT库是在debugging模式下编译的,它们当然需要访问d_ptr proteted成员(以及其他内部结构)。
我所做的就是使用build议的解决scheme,在我自己的类( QObjectWrapper )的dumpObjectInfo2 ()和dumpObjectTree2 ()中重新实现(复制和粘贴)这些方法,除去这些debugging预处理器或守护程序。

学习目的只….试试这个….可能是有用的,我猜…..这个程序可以访问私人数据,只知道值…

 //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....."; } 
 class Test{ int a; alignas(16) int b; int c; }; Test t; 

方法一:侵入心情。 由于我们可以访问源代码并重新编译,所以我们可以使用许多其他方式访问私有成员,这些都是合法的后门程序。

方法B:暴躁的心情。

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

我们使用一个幻数(20),并不总是正确的。 当类Test的布局改变时,幻数是一个很大的bug源。

方法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有一个博客,他find了一个方法来抢私人成员指针。 但我认为这应该是编译器的错误或语言陷阱。 我可以编译它在gcc4.8,但不是在vc8编译器。

所以结论可能是:地主build立所有的后门。 小偷总是有蛮横的坏方法闯入。 黑客意外有着优雅而自动化的方式闯入。