在 C++ 中调用私有方法

Calling private method in C++

提问人:Luchian Grigore 提问时间:7/29/2011 最后编辑:CactusLuchian Grigore 更新时间:2/18/2023 访问量:46126

问:

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

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

C++语言

评论

5赞 ascanio 7/29/2011
从同一类的公共函数调用私有方法
0赞 BЈовић 7/29/2011
您是如何做到不出现编译错误的?
0赞 Luchian Grigore 7/29/2011
有地址...您可以通过知道实例的地址来获取虚拟表和成员的地址。
1赞 7/29/2011
顺便说一句,并不是说你不应该叫它,只是你一定不能。
0赞 Lightness Races in Orbit 7/29/2011
@Luchian:C++对虚拟表一无所知。如果你想在这里弄乱指针算术,你就处于高度特定于实现的领域。

答:

11赞 Pete 7/29/2011 #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;
}

评论

10赞 Nawaz 7/29/2011
如果我有呢?class A { void f() {} };
2赞 Pete 7/29/2011
谁 -1 我?- OP说这纯粹是理论上的!
11赞 Antonio Pérez 7/29/2011
这太脏了,我的眼睛在流血XD
7赞 Lightness Races in Orbit 7/29/2011
这些重新定义了渲染整个程序 UB,严格来说,您已经更改了类型声明。写作几乎不是一种黑客。friend
5赞 Nawaz 7/29/2011
重新定义关键字会调用未定义的行为
1赞 ascanio 7/29/2011 #2

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

0赞 T.E.D. 7/29/2011 #3

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

如果你坚持要找一个邪恶的方法来做这件事......井。。。对于某些编译器,它可能会创建您自己的头文件版本,其中一种方法是 而不是 .不过,邪恶有一种令人讨厌的方式反弹到你身上(这就是为什么我们称之为“邪恶”)。publicprivate

评论

0赞 Luchian Grigore 7/29/2011
我正在寻找“邪恶”的方式。
6赞 Nawaz 7/29/2011 #4

如果函数返回函数的地址,则可以调用它,然后任何人都可以使用该地址调用私有函数。publicprivate

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

评论

1赞 Luchian Grigore 7/29/2011
我想在不更改类声明的情况下做到这一点。
2赞 Luchian Grigore 7/29/2011
如果我可以声明函数get(),为什么不从中调用f呢?
0赞 Antonio Pérez 7/29/2011
好点子,但是,与仅仅调用私有函数相比,这不是太复杂了吗
2赞 Antonio Pérez 7/29/2011 #5

你有朋友类和函数。

我知道,如果有人将某个方法声明为私有,您可能会 不应该叫它。

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

评论

0赞 Luchian Grigore 7/29/2011
我只是想调用私有方法......就是这样。在不更改类定义的情况下。
0赞 Antonio Pérez 7/29/2011
好吧,我认为友谊是你正在寻找的工具,尽管你确实需要为此更改类声明。你不能使用继承来做到这一点。
0赞 Luchian Grigore 7/29/2011
我正在寻找的是黑客。我设法通过获取 vftable 的地址并在该地址调用函数来调用私有虚拟。我本可以用友谊来做到这一点,但这就是我正在寻找的东西。
3赞 legendlee 7/29/2011 #6

最简单的方法:

#define private public
#define protected public

评论

3赞 Lightness Races in Orbit 7/29/2011
UB(虽然,我想,我无法想象一个不会的“黑客”,这里..)。至少,这是作弊。
3赞 David Hammen 7/29/2011 #7

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

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

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

0赞 Lightness Races in Orbit 7/29/2011 #8

我认为你最接近黑客攻击的是这个,但它不仅是不明智的,而且是未定义的行为,所以它没有语义。如果它碰巧按照您想要的方式运行任何单个程序调用,那么这纯粹是偶然的。

评论

0赞 Luchian Grigore 7/29/2011
这是更改私有成员的方法之一,但我不明白如何调用私有方法。这种想法就是我想要的。
0赞 Lightness Races in Orbit 7/29/2011
公平点。不过,它已经非常接近了(没有用 s 重写代码,这绝对是作弊)。#define
0赞 Luchian Grigore 7/29/2011
如何?私有成员驻留在内存中,相对于对象地址的特定偏移量。私有的非虚拟方法在别的地方(至少汇编器是这么说的)。如果您知道如何操作,请发布带有一些代码的答案,我很乐意接受。:)
0赞 Lightness Races in Orbit 7/29/2011
@Luchian:成员函数在哪里并不重要。调用它时,会(隐式地)向它传递一个指向它应该处理的对象的指针。通过黑客攻击,您可以将 A 类型的对象发送到期望在 B 类型上工作的成员函数中。在这种情况下,这是有争议的好处,这就是为什么我说“公平点”。:)
0赞 Luchian Grigore 7/29/2011
我知道,您实际上将该地址放在注册表中,然后该函数就可以处理该地址了......这就像将 this 传递给函数一样。不过,这并不能解决问题......
0赞 QuentinUK 7/29/2011 #9

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

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

18赞 Johannes Schaub - litb 7/31/2011 #10

请参阅我的博客文章。我在这里重新发布代码

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

评论

1赞 Luchian Grigore 7/31/2011
有趣,但我收到一个错误:错误 C2248:“A::f”:无法访问在行模板类 rob<Af、&A::f>; 的类“A”中声明的私有成员
1赞 Johannes Schaub - litb 7/31/2011
@Luchian MSVC 不符合标准。
0赞 Luchian Grigore 7/31/2011
你能发布一个标准的链接吗?我觉得编译器不让我访问 &A::f 是对的。
0赞 mrAtari 9/23/2016
@Johannes Schaub:干得不错。有没有更优雅的方法可以实现同样的目标?也许通过使用您与私人成员透露的技巧?我真的很感激能把它缩短一点。
1赞 Johannes Schaub - litb 9/23/2016
@mrA看看我的博客。我在那里发布了一个更好的版本
0赞 Kde 1/30/2015 #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
}

评论

1赞 Fernando Gonzalez Sanchez 4/5/2016
我认为这仅适用于 x86 程序,我认为您不能在 MSVC 的 x64 代码中嵌入汇编程序。
0赞 bartolo-otrit 8/14/2015 #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;
}

损坏的名称可以通过 nm *.o 文件找到。

添加 -masm=intel 编译器选项

来源: GCC 错误: 无法将 offsetof 应用于成员函数 MyClass::MyFunction https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html

1赞 Gleb Bezborodov 3/23/2022 #13

调用私有方法的最简单方法(基于之前的答案,但更简单一些):

// Your class
class sample_class{
    void private_method(){
        std::cout << "Private method called" << std::endl;
    }
};

// declare method's type
template<typename TClass>
using method_t = void (TClass::*)();

// helper structure to inject call() code
template<typename TClass, method_t<TClass> func>
struct caller{
    friend void call(){
        TClass obj;
        (obj.*func)();
    }
};

// even instantiation of the helper
template struct caller<sample_class,&sample_class::private_method>;

// declare caller
void call();

int main(){
    call(); // and call!
    return 0;
}

评论

0赞 HolyBlackCat 3/23/2022
这仅限于一种方法。:(
0赞 Gleb Bezborodov 3/23/2022
它可以通过宏扩展到一些剩余的方法,请参阅:github.com/glensand/misc/blob/main/...
0赞 HolyBlackCat 3/23/2022
另一种选择是引入字符串名称作为另一个模板参数,并在实例化和调用方法时指定相同的字符串。
0赞 Hsu Pu 2/18/2023 #14

在阅读了 寻找一种优雅且非侵入性的方式来访问类的私有方法之后,我想总结一种理想的方法,因为这里没有其他人粘贴过它:

// magic
//

template <typename Tag, typename Tag::pfn_t pfn>
struct tag_bind_pfn
{
    // KEY: "friend" defines a "pfn_of" out of this template. And it's AMAZING constexpr!
    friend constexpr typename Tag::pfn_t pfn_of(Tag) { return pfn; }
};

// usage
//

class A
{
    int foo(int a) { return a; }
};

struct tag_A_foo
{
    using pfn_t = int (A::*)(int);
    // KEY: make compiler happy?
    friend constexpr typename pfn_t pfn_of(tag_A_foo);
};
// KEY: It's legal to access private method pointer on explicit template instantiation
template struct tag_bind_pfn<tag_A_foo, &A::foo>;

inline static constexpr const auto c_pfn_A_foo = pfn_of(tag_A_foo{});

#include <cstdio>

int main()
{
    A p;
    auto ret = (p.*(c_pfn_A_foo))(1);
    printf("%d\n", ret);
    return 0;
}