在 C++ 中调用私有方法

Posted

技术标签:

【中文标题】在 C++ 中调用私有方法【英文标题】:Calling private method in C++ 【发布时间】:2011-10-15 22:23:32 【问题描述】:

这纯粹是一个理论问题,我知道如果有人将方法声明为私有,您可能不应该调用它。我设法调用私有虚拟方法并更改实例的私有成员,但我不知道如何调用私有非虚拟方法(不使用__asm)。有没有办法获得指向方法的指针?还有其他方法吗?

编辑:我不想更改类定义!我只想要一个黑客/解决方法。 :)

【问题讨论】:

从同一个类的公共函数中调用私有方法 你是如何做到没有编译错误的? 有了地址...你可以通过知道你的实例的地址来获取虚拟表和成员的地址。 顺便说一句,不是你不应该叫它,只是你一定不能。 @Luchian:C++ 对虚拟表一无所知。如果你想在这里搞乱指针运算,那么你就处于高度特定于实现的领域。 【参考方案1】:

#include头文件,但是:

#define private public
#define class struct

显然,您需要绕过各种包含保护等,并在一个独立的编译单元中执行此操作。

编辑: 仍然很老套,但不那么老套了:

#include <iostream>

#define private friend class Hack; private

class Foo

public:
    Foo(int v) : test_(v) 
private:
    void bar();
    int test_;
;
#undef private
void Foo::bar()  std::cout << "hello: " << test_ << std::endl; 

class Hack

public:
    static void bar(Foo& f) 
        f.bar();
    
;

int _tmain(int argc, _TCHAR* argv[])

    Foo f(42);
    Hack::bar(f);
    system("pause");
    return 0;

【讨论】:

如果我有class A void f() ;怎么办? 谁-1'd我? - OP 说这纯粹是理论上的! 这太脏了我的眼睛在流血XD 这些重新定义了渲染你整个程序的UB,严格来说你改变了类型声明。写friend 几乎不是什么黑客攻击。 重新定义关键字会调用未定义的行为【参考方案2】:

从同一类的公共函数调用私有方法。

【讨论】:

【参考方案3】:

嗯,显而易见的方法是编辑代码,使其不再是私有的。

如果您坚持要找到一种 evil 方法来做到这一点...嗯...使用某些编译器,它可能会创建您自己的头文件版本,其中一种方法是 public而不是private。不过,邪恶有一种令人讨厌的反弹方式(这就是我们称之为“邪恶”的原因)。

【讨论】:

我在寻找“邪恶”的方式。【参考方案4】:

如果public 函数返回private 函数的地址,则可以调用它,那么任何人都可以使用该地址调用私有函数。

例子,

class A

   void f()  cout << "private function gets called" << endl; 
 public:
     typedef void (A::*pF)();
     pF get()  return &A::f; 
;

int main() 

        A a;
        void (A::*pF)() = a.get();
        (a.*pF)(); //it invokes the private function!

输出:

private function gets called

ideone 演示:http://www.ideone.com/zkAw3

【讨论】:

我想在不更改类声明的情况下这样做。 如果我可以声明函数get(),为什么不直接调用f呢? 好点,但是与只调用私有函数相比,这不是太复杂【参考方案5】:

您有friend 类和函数。

我知道如果有人声明一个私有方法,你可能 不应该这样称呼它。

重点不是“你不应该调用它”,而是“你不能调用它”。你到底想做什么?

【讨论】:

我只是想调用私有方法...就是这样。无需更改类定义。 好吧,我认为友谊是您正在寻找的工具,尽管您确实需要为此更改类声明。你只是不能使用继承来做到这一点。 我正在寻找的是一个黑客。我设法通过获取 vftable 的地址并在该地址调用函数来调用私有虚拟。我本可以通过友谊做到这一点,但这是我正在寻找的东西。【参考方案6】:

最简单的方法:

#define private public
#define protected public

【讨论】:

UB(不过,我想,我无法想象一个不会出现的“黑客”,在这里..)。至少,这是作弊。【参考方案7】:

跟进 T.E.D. 的回答:不要编辑标题。而是创建您自己的标头私有副本,并在该标头的伪造副本中插入一些 friend 声明。在您的来源中,#include 这个虚假标题而不是真实标题。瞧!

将 private 更改为 public 可能会更改由内联方法产生的弱符号,这反过来可能会导致链接器抱怨。如果所做的只是添加一些友元声明,则内联方法产生的弱符号将具有与虚假和真实标头相同的签名。通过这些朋友声明,您现在可以对类做各种邪恶的事情,例如访问私有数据和调用私有成员。

附录 如果有问题的标头使用#pragma once 而不是#include 保护来确保标头是幂等的,则此方法将不起作用。

【讨论】:

【参考方案8】:

我认为你最接近 hack is this,但这不仅是不明智的,而且是未定义的行为,因此它没有语义。如果它恰好按照您对任何单个程序调用的方式运行,那么这纯粹是机会。

【讨论】:

这是更改私有成员的方法之一,但我不明白您如何调用私有方法。我正在寻找这种想法。 公平点。不过,它与您将得到的一样接近(无需使用#defines 重写代码,这绝对是作弊)。 怎么样?私有成员驻留在内存中,位于相对于对象地址的特定偏移处。私有非虚拟方法在其他地方(至少汇编程序是这么说的)。如果你知道怎么做,请用一些代码发布答案,我很乐意接受。 :) @Luchian:成员函数在哪里并不重要。当你调用它时,你(隐式地)向它传递一个指向它应该处理的对象的指针。通过黑客攻击,您可以将 A 类型的对象发送到期望在 B 类型上工作的成员函数中。在这种情况下,这是有争议的好处,这就是我说“公平点”的原因。 :) 我知道,您实际上将 this 的地址放在注册表中,然后该函数对其进行处理......这就像将 this 传递给函数一样。但这并不能解决问题......【参考方案9】:

定义一个类似的类,除了函数是公共的之外是相同的。

然后将具有私有函数的对象类型转换为具有公共函数的对象,然后您可以调用公共函数。

【讨论】:

【参考方案10】:

见my blog post。我在这里重新发布代码

template<typename Tag>
struct result 
  /* export it ... */
  typedef typename Tag::type type;
  static type ptr;
;

template<typename Tag>
typename result<Tag>::type result<Tag>::ptr;

template<typename Tag, typename Tag::type p>
struct rob : result<Tag> 
  /* fill it ... */
  struct filler 
    filler()  result<Tag>::ptr = p; 
  ;
  static filler filler_obj;
;

template<typename Tag, typename Tag::type p>
typename rob<Tag, p>::filler rob<Tag, p>::filler_obj;

一些有私有成员的类

struct A 
private:
  void f() 
    std::cout << "proof!" << std::endl;
  
;

以及如何访问它们

struct Af  typedef void(A::*type)(); ;
template class rob<Af, &A::f>;

int main() 
  A a;
  (a.*result<Af>::ptr)();

【讨论】:

很有趣,但我得到一个错误:错误 C2248: 'A::f' : cannot access private member declared in class 'A' on line template class rob ; @Luchian MSVC 不符合标准。 您可以发布标准的链接吗?我觉得编译器不让我访问 &A::f 是对的。 @Johannes Schaub:干得好。有没有更优雅的方法来实现同样的目标?也许通过使用您向私人成员透露的技巧?我真的很感激能把它缩短。 @mrA 查看我的博客。我在那里发布了一个更好的版本【参考方案11】:

如果我们说的是 MSVC,我认为除了调用私有方法本身之外没有其他危害的最简单方法是伟大的 __asm:

class A

private:
    void TestA () ;
;

A a;
__asm

    // MSVC assumes (this) to be in the ecx.
    // We cannot use mov since (a) is located on the stack
    // (i.e. [ebp + ...] or [esp - ...])
    lea     ecx, [a]
    call    A::TestA

【讨论】:

这仅适用于 x86 程序,我认为您不能在 MSVC 的 x64 代码中嵌入汇编程序。【参考方案12】:

对于 GCC,它可以通过使用函数的重命名来完成。

#include <stdio.h>

class A 
public:
    A() 
        f(); //the function should be used somewhere to force gcc to generate it
    
private:
    void f()  printf("\nf"); 
;

typedef void(A::*TF)();

union U 
    TF f;
    size_t i;
;

int main(/*int argc, char *argv[]*/) 
    A a;
    //a.f(); //error
    U u;
    //u.f = &A::f; //error

    //load effective address of the function
    asm("lea %0, _ZN1A1fEv"
    : "=r" (u.i));
    (a.*u.f)();
    return 0;

Mangled names 可以通过 nm *.o 文件找到。

添加 -masm=intel 编译器选项

来源:GCC error: Cannot apply offsetof to member function MyClass::MyFunction https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html

【讨论】:

以上是关于在 C++ 中调用私有方法的主要内容,如果未能解决你的问题,请参考以下文章

伪私有属性的意义

c++ - 使用私有参数调用公共函数

C++ 为啥我可以从派生类调用基类的私有虚函数?

C++ 设计模式:可能有继承的方法私有到派生类?

我可以在 C++ 中转换对象并访问私有数据成员吗?

C++:在派生类构造函数中调用基类赋值运算符的错误形式?