Могу ли я получить доступ к закрытым членам вне класса без использования друзей?


отказ от ответственности

Да, я полностью осознаю, что то, о чем я спрашиваю, совершенно глупо и что любой, кто захочет попробовать такую вещь в производственном коде, должен быть уволен и/или застрелен. Я в основном ищу, чтобы увидеть, если можете быть сделано.

теперь, когда это не так, есть ли способ получить доступ к частным членам класса В C++ из-за пределов класса? Например, есть ли способ сделать это с смещением указателя?

(наивно и иначе непроизводственные готовые методы приветствуются)

обновление

во всяком случае, если кто-то заинтересован в том, как это вышло, вот он: враги тестовой разработки Часть I: инкапсуляция (я предлагаю прочитать его, прежде чем вы решите, что я сошла с ума).

24 58

24 ответа:

Если класс содержит какие-либо функции-члены шаблона, вы можете специализировать эту функцию-член в соответствии с вашими потребностями. Даже если первоначальный разработчик не думал об этом.

безопасная.h

class safe
{
    int money;

public:
    safe()
     : money(1000000)
    {
    }

    template <typename T>
    void backdoor()
    {
        // Do some stuff.
    }
};

главная.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;
}

The Rob шаблон класса определяется такой и должен быть определен только один раз, независимо от того, сколько членов вы планируете открыть

template<typename Tag, typename Tag::type M>
struct Rob { 
  friend typename Tag::type get(Tag) {
    return M;
  }
};
, это не показывает, что правила доступа c++не являются надежными. Языковые правила предназначены для защиты от случайных ошибок - при попытке ограбить данные объекта, язык по-дизайн не займет много времени, чтобы предотвратить вас.

следующее является скрытым, незаконным, зависящим от компилятора и может не работать в зависимости от различных деталей реализации.

#define private public
#define class struct

но это ответ на ваш OP, в котором вы явно приглашаете технику, которая, и я цитирую, "совершенно глупа и что любой, кто захочет попробовать такую вещь в производственном коде, должен быть уволен и/или застрелен".


другой метод заключается в доступе к закрытым данным членов, путем построения указателей с использованием жесткого / ручного кода смещения от начала объекта.

Хммм, не знаю, если это будет работать, но может быть стоит попробовать. Создайте другой класс с тем же макетом, что и объект с закрытыми членами, но с закрытым, измененным на public. Создайте переменную указателя на этот класс. Используйте простое приведение, чтобы указать это на свой объект с закрытыми членами и попробуйте вызвать закрытую функцию.

ожидайте искры и, возможно, аварии;)

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

union 
{ 
    A a; 
    B b; 
};

Это должно сделать это.

ETA: это будет работать для такого рода тривиального класса, но в целом это не будет.

TC++PL раздел C. 8. 3: "класс с конструктором, деструктором или операцией копирования не может быть типом члена объединения ... потому что компилятор не будет знать, какой член уничтожить."

таким образом, мы остаемся с лучшим выбором, чтобы объявить class B в матче Aмакет и взломать, чтобы посмотреть на класс частные.

Это определенно можно получить доступ к закрытым членам со смещением указателя в C++. Предположим, у меня было следующее определение типа, к которому я хотел получить доступ.

class Bar {
  SomeOtherType _m1;
  int _m2;
};

предполагая, что в баре нет виртуальных методов, простой случай-_m1. Члены В C++ хранятся как смещения расположения памяти объекта. Первый объект находится в смещении 0, второй объект-в смещении 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));
}

Если вы можете получить указатель на член класса, вы можете использовать указатель независимо от того, каковы спецификаторы доступа (даже методы).

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

предполагая, что создатель выше определил бэкдор для его нормального использования. Но вы хотите получить доступ к объекту и посмотреть на закрытых переменных-членов. Даже если в классе был составлен в статическую библиотеку вы можете добавить ваш собственный шаблон специализации для бэкдора и таким образом получить доступ к членам.

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

классный вопрос кстати... вот мой кусок:

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's 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, 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++ искажает имена, да.

Если, я полагаю, это не виртуальная функция. Но тогда, если вы знаете, как ваш компилятор C++ строит VTABLE ...

Edit: глядя на другие ответы, я понимаю, что неправильно понял вопрос и думал, что речь идет о функциях-членах, а не о данных-членах. Тем не менее, точка все еще стоит: если вы знаете, как ваш компилятор выкладывает данные, то вы можете получить доступ к этим данным.

в качестве альтернативы шаблонному методу backdoor вы можете использовать класс template backdoor. Разница в том, что вам не нужно помещать этот бэкдор-класс в публичную область класса, который вы собираетесь тестировать. Я использую тот факт, что многие компиляторы позволяют вложенным классам получать доступ к частной области заключающего класса (что не совсем стандарт 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();
}

всем людям, предлагающим" #define private public":

такого рода вещи незаконно. Стандарт запрещает определять / отменять макросы, которые лексически эквивалентны зарезервированным ключевым словам языка. Хотя ваш компилятор, вероятно, не будет жаловаться (я еще не видел компилятора, который это делает), это не то, что "хорошо" делать.

Это на самом деле довольно проста:

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

следующий код получает и изменяет собственный член класса, используя указатель на этот класс.

#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 как потомок желаемого класса, чтобы иметь доступ к его (теперь защищенным) методам с помощью приведения типов.

просто создайте свою собственную функцию-член access, чтобы расширить класс.

" использование частных переменных не является 100% надежным способом обеспечения инкапсуляции, даже в C++." Правда? Вы можете разобрать нужную вам библиотеку, найти все необходимые смещения и использовать их. Это даст вам возможность изменить любой частный член вам нравится... Но! Вы не можете получить доступ к частным членам без некоторых грязных взломов. Допустим, что написание const не сделает вашу константу действительно постоянной, потому что вы можете бросок const или просто использовать это адрес, чтобы признать его недействительным. Если вы используете MSVC++ и вы указали " - merge:.rdata=.данные " для компоновщика, трюк будет работать без каких-либо ошибок доступа к памяти. Мы даже можем сказать, что написание приложений на C++ не является надежным способом написания программ, потому что полученный код низкого уровня может быть исправлен откуда-то извне, когда ваше приложение работает. Тогда каков надежный документированный способ обеспечения инкапсуляции? Можем ли мы скрыть данные где-нибудь в оперативной памяти и предотвратить доступ к ним, кроме нашего кода? Единственная идея, которая у меня есть, - это шифрование частных членов и резервное копирование их, потому что что-то может повредить этим членам. Извините, если мой ответ слишком груб, я не хотел никого обидеть, но я действительно не думаю, что это утверждение мудро.

поскольку у вас есть объект требуемого класса, я предполагаю, что у вас есть объявление класса. Теперь то, что вы можете сделать, это объявить другой класс с теми же членами, но сохранить все там спецификаторы доступа как общедоступные.

например предыдущий класс:

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

по ссылке на *этой вы включаете бэкдор для всех личных данных внутри объекта.

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

довольно часто класс предоставляет методы мутатора для частных данных (геттеры и сеттеры).

Если класс предоставляет геттер, который возвращает ссылку const (но не сеттер), то вы можете просто const_cast возвращаемое значение геттера и использовать его как 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;

я использовал другой полезный подход (и решение) для доступа к закрытому/защищенному члену 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.
Есть 2 метода в QObject,dumpObjectInfo() и dumpObjectTree(), что просто работайте, если библиотеки QT компилируются в режиме отладки, и им, конечно же, нужен доступ к члену d_ptr proteted (среди других внутренних структур).
То, что я сделал, это использовать предлагаемое решение для переопределения (с копией и вставить) эти методы в dumpObjectInfo2() и dumpObjectTree2() в моем классе (QObjectWrapper) удаление этих предохранителей препроцессора отладки.

единственная цель исследования.... попробовать это. ...может быть полезно, я думаю..... эта программа может получить доступ к личным данным, просто зная значения...

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

вдохновленный @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;

  }
class Test{
    int a;
    alignas(16) int b;
    int c;
};

Test t;

метод а: навязчивое настроение. поскольку мы можем получить доступ к исходному коду и перекомпилировать его, мы можем использовать многие другие способы, такие как класс друзей для доступа к частному члену, все они являются законным бэкдором.

метод Б: грубое настроение.

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

мы используем магическое число (20) , и это не всегда правильно. Когда макет теста класса изменился, магическое число является источником большой ошибки.

метод C: супер хакер настроение. есть есть какое-то ненавязчивое и не грубое настроение ? поскольку детали макета тест-класса скрывать от компилятора, мы не можем получить информацию о смещении из уст компли. бывший.

offsetof(Test,c); //complie error. they said can not access private member.

мы также не можем получить указатель члена от теста класса. бывший.

&Test::c ;  //complie error. they said can not access private member.

@Johannes Schaub-litb имеет блог, он нашел способ ограбить частный указатель члена. но я думал, что это должно быть ошибка компилятора или языка ловушка. я могу составить его на gcc4. 8, но не на vc8 - нет.

таким образом, вывод может быть: домовладелец построить все бэкдор. вор всегда имеет грубый и плохой способ проникнуть. хакер случайно имеет элегантный и автоматизированный способ взлома.