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


72

免责声明

是的,我完全意识到我的要求完全是愚蠢的,任何想在生产代码中尝试这种事情的人都应该被开除和/或开枪。我主要是看是否可以完成。

既然这已经成为现实,有什么办法可以从类外部访问C ++中的私有类成员?例如,有没有办法用指针偏移量做到这一点?

(欢迎使用天真的和其他非生产就绪技术)

更新资料

如评论中所述,我之所以问这个问题,是因为我想写一篇有关过度封装(以及它如何影响TDD)的博客文章。我想看看是否有一种方法可以说“即使在C ++中,使用私有变量也不是100%可靠的强制封装方法”。最后,我决定将重点更多地放在解决问题的方法上,而不是解决问题的原因,因此,我没有像计划中那样突出此处提出的一些内容,但我仍然留下了联系。

无论如何,如果有人对它的发布方式感兴趣,那就是: 测试驱动开发的敌人:第一部分:封装(建议您在决定疯了之前先阅读它)。


出于兴趣,为什么要问这个问题。我能想到的唯一用途是侵入他人的API造成破坏。
SmacL

我正在写有关过度封装的博客文章。我只是想看看是否有可能说“即使在C ++中,通过私有方法进行的保护也不是完美的!” 链接写好后,我将发布该链接。
杰森·贝克

Answers:


76

如果该类包含任何模板成员函数,则可以对该成员函数进行特殊化以满足您的需求。即使原始开发人员没有想到这一点。

安全

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

14
密钥可能会发生冲突。将其放在匿名名称空间中。
马丁·约克

62

我已经在博客中添加了一个条目(如下所示),该条目显示了如何完成此操作。这是有关如何在下课中使用它的示例

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 ++的访问规则不可靠。语言规则旨在防止意外错误-如果您尝试窃取对象的数据,则语言设计不会花费很长时间来阻止您。


难道不是“语言规则不是..”吗?

这似乎不起作用。它可以与您博客上的私有函数示例一起使用,但是可以通过int得到:错误:类型为'int A :: *'的非类型模板参数必须具有整数或枚举类型(对于模板类rob <Amem,&A: :member>;)
克里斯

2
如果答案看起来很棘手,请注意两个要点:1)template struct Rob<A_member, &A::member>;这是一个显式的模板类实例化。&A::member在这里工作的原因是:“显式实例化定义忽略成员访问说明符:参数类型和返回类型可能是私有的”
bartolo-otrit

2
2)a.*get(A_member())调用get(A_member)返回的非成员好友函数&A::member。这是因为“朋友注入”而起作用的。尝试删除get()函数的“无用”参数,您应该得到“在此范围内未声明get”(我希望看到对此行为的简要说明)。
bartolo-otrit

1
如果A::member不是int,但enum Foo名称在其中Foo是私有的,是否可以进行修改A
MM

30

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

#define private public
#define class struct

但这是对OP的一种回答,其中您明确邀请了一种技术,我引用此技术是“完全愚蠢的,任何希望在生产代码中尝试此类操作的人都应被解雇和/或开枪”。


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


6
您还需要#define类结构,否则默认的private可能会妨碍您。
汤姆(Tom)” 2009年

1
与指针解引用相比,预处理器黑客对私有成员的访问不再更多。
cletus

rq在该机制上是错误的(由于发生访问的时刻,因此宽松的访问检查永远不会使有效程序无效)。骇客违反了ODR,并可能由于名称修改而破坏了程序。
MSalters

1
对于使用Xcode 6(LLVM)的TDD也很有效,此答案是解决我的问题的最佳解决方案,而无需完全修改原始代码。
Kaa 2014年

1
这不起作用在某些情况下,因为class可以在模板参数一起使用(相同typename的,但struct不能。
user202729

24

嗯,不知道这样是否行得通,但值得一试。使用具有私有成员但将private更改为public的对象创建具有与对象相同布局的另一个类。创建一个指向此类的指针的变量。使用简单的转换将其指向带有私有成员的对象,然后尝试调用私有函数。

期待火花,甚至可能崩溃;)


2
“具有相同的布局”。这是困难的部分。编译器在如何布局非POD中的成员方面有很大的自由度。但实际上,它可能会起作用。
理查德·科登

@Richard Corden-那也是我的感觉。再次说明“可能有用”和“应该在发行后完成”是天壤之别。
SmacL

1
请记住,编译器可以根据访问控制完全自由地对成员进行重新排序(实际上,如果有任何访问控制,则可以完全自由地对成员进行重新排序)。我不知道有多少真正做到这一点....
大卫·索恩利

大多数编译器不会对成员进行重新排序-不允许他们对POD结构进行重新排序,否则它们将破坏与C结构的兼容性(例如struct sockaddr)。确实,提供自动会员重新订购的动机很少。
汤姆(Tom)

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

union 
{ 
    A a; 
    B b; 
};

那应该做。

预计到达时间:适用于这种琐碎的课程,但一般来说不会。

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

因此,我们最好的选择是声明class B以匹配A的布局并破解以查看类的私有对象。


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

@ formerlyknownas_463035818仅来自具有访问权限的方法。如果某个方法可以访问该方法,则它可以获取该函数的指针并将其返回。一旦有了指针,就可以传递和使用该指针,因为该指针没有权限。
马丁·约克

@ formerlyknownas_463035818 1:test()是,private并且getMethod()(是public)返回的值可以是任何值。2:不,您不能从外部获得指针。
马丁·约克

感谢您的意见。我花了一段时间才意识到,我每天使用的几乎所有课程确实都有后门。我以前知道最好不要开门,现在我也知道在哪里可以找到它。如果您不介意,我将删除我的评论,我想您想删除您的评论
maximum_prime_is_463035818 '19

9

在C ++中,绝对可以使用指针偏移量访问私有成员。假设我有以下想要访问的类型定义。

class Bar {
  SomeOtherType _m1;
  int _m2;
};

假设Bar中没有虚拟方法,最简单的情况是_m1。C ++中的成员存储为对象内存位置的偏移量。第一个对象的偏移量为0,第二个对象的偏移量为sizeof(first member),依此类推...

因此,这是访问_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));
}

注意内存填充和对齐问题。但是+1肯定可行!
Ree先生

4

此答案基于@Johannes的答案/博客展示的确切概念,因为这似乎是唯一的“合法”方法。我已经将示例代码转换为方便的实用程序。它很容易与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;
}

我无法用类型的私有成员使用这个enum Foo地方Foo也是在私人部分,因为Class::Foo不能在使用ALLOW_ACCESS宏观由于是保密的!有什么建议?
MM

@MM不知道如何进行这项工作。尝试了几种替代方法,但无济于事。如果找到解决方案,将进行更新。
iammilind

3

如果您知道C ++编译器如何处理名称,则可以。

我想除非它是一个虚函数。但是然后,如果您知道您的C ++编译器如何构建VTABLE ...

编辑:查看其他答复,我意识到我误解了问题,并认为这是关于成员函数,而不是成员数据。但是,重点仍然是:如果您知道编译器如何布置数据,则可以访问该数据。


1
我觉得这个答案很有趣,因为反对使用Python的私有成员的最大论据是它实际上只是名称修饰(尽管是更标准化的名称修饰)。有趣的是,C ++的情况差不多。
贾森·贝克

2
Jason-区别在于,在Python中,您可能会因为打入私人物品而手腕被打光。在C ++中,您的同行开发人员会竭尽全力破坏您的封装。
汤姆

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

希望这可以帮助。


1

实际上很简单:

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

1
我假设OP希望访问而不修改类定义本身
j_random_hacker 2009年

对于测试驱动开发,这样的后门很有意义。使用#define可以轻松删除它们。与公共/私人混淆的#define可能会影响程序语义,例如POD-ness。
MSalters 2009年

1

作为模板后门方法的替代方法,可以使用模板后门类。所不同的是,您无需将此后门课程放在要测试的课程的公共区域中。我使用了这样一个事实,即许多编译器允许嵌套类访问封闭类的私有区域(这并不是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();
}

0

除了#define private public之外,您还可以#define private protected,然后将某些foo类定义为所需类的后代,以通过类型转换来访问它的(现在受保护的)方法。


0

只需创建您自己的访问成员函数即可扩展该类。


0

对于所有建议“ #define私人公众”的人们:

这种事情是非法的。该标准禁止定义/取消定义在词汇上等同于保留语言关键字的宏。尽管您的编译器可能不会抱怨(我还没有看到可以这样做的编译器),但这并不是一件“好事”。


但是您会注意到,该问题并不要求这样做是一件“好事”。实际上,执行此操作的好方法可能不存在。
杰森·贝克

1
好吧,这个问题确实要求该方法有效,对吗?由于标准禁止这种事情,其他人概述的方法被定义简单地打破了。之所以有效,是因为大多数编译器都不执行这个规则
Tritium

杰森(Jason),这个问题要求做到这一点。至少当您包含标准标头时,您会遇到未定义的行为,而这不能解决这个问题。将内容转换为未定义的行为,使编译器可以执行所需的任何操作。(格式化HD)
Johannes Schaub-litb

(并禁止您访问私人用户-具有讽刺意味的是,提问者希望这样做)。据我所知,当同一翻译单元中不包含标准标题时,就可以#define关键字。但正如我所说,那不可能是解决方案!
Johannes Schaub-litb

小伙子,你的逻辑不太正确。明确地禁止定义一个宏名称,该宏名称在词法上与ANY保留关键字相同,无论您在翻译单元中包括什么内容。就像这样简单:“不要这样做,这是非法的”。
Tritium

0

“即使在C ++中,使用私有变量也不是强制执行封装的100%可靠方法。” 真?您可以反汇编所需的库,找到所有需要的偏移量并使用它们。这样您就可以更改自己喜欢的任何私人成员...但是!没有一些恶意的黑客攻击,您将无法访问私人成员。让我们说写const不会使您的常量真正成为常量,因为您可以强制转换const离开或只是使用它的地址使其无效。如果您使用的是MSVC ++,并且为链接程序指定了“ -merge:.rdata = .data”,则该技巧将起作用而不会出现任何内存访问错误。我们甚至可以说用C ++编写应用程序不是编写程序的可靠方法,因为当您的应用程序运行时,可能会从外部某个地方修补产生的底层代码。那么,什么是可靠的文献记载的实施封装的方法呢?我们可以将数据隐藏在RAM中的某处,并阻止除我们的代码以外的任何其他东西访问它们吗?我唯一的想法是加密私有成员并备份它们,因为某些事情可能会破坏这些成员。抱歉,如果我的回答太不礼貌,我并不是要冒犯任何人,但我真的不认为该说法是明智的。


问题是关于语言本身的问题,而不是关于您可以或不能做到的平台特定问题。
罗斯兰

0

因为您有必需类的对象,所以我猜您有类的声明。现在,您可以做的是声明另一个具有相同成员的类,但是将所有访问说明保持为公共。

例如,上一类是:

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

未定义的行为未定义
Barry

0

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

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

0

通常,一个类为私有数据(getter和setter)提供mutator方法。

如果一个类确实提供了一个返回const引用的getter(但没有setter),那么您可以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;

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中
有2种方法,dumpObjectInfo()和dumpObjectTree(),这些方法仅在QT库是在调试模式下编译时才起作用的,并且它们当然需要访问d_ptr受保护的成员(以及其他内部结构)。 我所做的是使用提议的解决方案在我自己的类(QObjectWrapper)中的dumpObjectInfo2()和dumpObjectTree2()中重新实现(复制和粘贴)那些方法,从而删除了这些调试前处理器保护。


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

这是您确实知道类实例变量布局的“理想”情况。但是,如果班级的祖先具有ivars或具有虚拟成员,这是不正确的。不过,您是基于特定实现的知识来解决的。因此,基本上,您不会回答原始问题“我可以在不使用朋友的情况下从类外部访问私有成员吗?”,而是说明如何在特定实现中访问类私有成员的内存。
Petro Korienev 2014年

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

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;

  }

0

好吧,有了指针偏移量,这很容易。困难的部分是找到偏移量:

other.hpp

class Foo
{
  public:
    int pub = 35;

  private:
    int foo = 5;
    const char * secret = "private :)";
};

main.cpp

#include <iostream>
#include <fstream>
#include <string>
#include <regex>

#include "other.hpp"

unsigned long long getPrivOffset(
  const char * klass,
  const char * priv,
  const char * srcfile
){

  std::ifstream read(srcfile);
  std::ofstream write("fork.hpp");
  std::regex r ("private:");
  std::string line;
  while(getline(read, line))
    // make all of the members public
    write << std::regex_replace(line, r, "public:") << '\n';
  write.close();
  read.close();
  // find the offset, using the clone object
  std::ofstream phony("phony.cpp");
  phony << 
  "#include <iostream>\n"
  "#include <fstream>\n"
  "#include \"fork.hpp\"\n"
  "int main() {\n";
  phony << klass << " obj;\n";
  // subtract to find the offset, the write it to a file
  phony << 
  "std::ofstream out(\"out.txt\");\n out <<   (((unsigned char *) &(obj." 
<< priv << ")) -((unsigned char *)   &obj)) << '\\n';\nout.close();";
  phony << "return 0;\n}";
  phony.close();
  system(
    "clang++-7 -o phony phony.cpp\n"
    "./phony\n"
    "rm phony phony.cpp fork.hpp");
  std::ifstream out("out.txt");
  // read the file containing the offset
  getline(out, line);
  out.close();
  system("rm out.txt");
  unsigned long long offset = strtoull(line.c_str(), NULL, 10);
  return offset;
}


template <typename OutputType, typename Object>
OutputType hack(
  Object obj, 
  const char * objectname,
  const char * priv_method_name,
  const char * srcfile
  ) {
  unsigned long long o = getPrivOffset(
    objectname, 
    priv_method_name, 
    srcfile
  );
  return *(OutputType *)(((unsigned char *) (&obj)+o));
}
#define HACK($output, $object, $inst, $priv, $src)\
hack <$output, $object> (\
  $inst,\
  #$object,\
  $priv,\
  $src)

int main() {
  Foo bar;
  std::cout << HACK(
    // output type
    const char *, 
    // type of the object to be "hacked"
    Foo,
    // the object being hacked
    bar,
    // the desired private member name
    "secret",
    // the source file of the object's type's definition
    "other.hpp"
    ) << '\n';
  return 0;
}
clang++ -o main main.cpp
./main

输出:

private :)

您也可以使用reinterpret_cast


-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类的布局更改时,魔术数字是很大的bug来源。

方法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编译器上编译。

这样的结论可能是: 房东建造了所有后门。小偷总是有粗暴和坏方法闯入。黑客意外事件具有优雅而自动化的破解方法。


这听起来像是对其他答案的怒,而不是答案。您具有足够的声誉,可以在其他答案上留下清晰的评论。
Ben Voigt 2014年

抱歉,我没有学习正确的讨论规则。但是结论是我的思想结果是真实的。
托马斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.