Подтвердить что ты не робот

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

Отказ

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

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

(приветствуются наивные и другие технологии, не готовые к производству)

Update

Как отмечалось в комментариях, я задал этот вопрос, потому что я хотел написать сообщение в блоге о чрезмерной инкапсуляции (и как это повлияло на TDD). Я хотел посмотреть, есть ли способ сказать: "Использование частных переменных - это не 100% надежный способ принудительного инкапсуляции, даже на С++". В конце я решил больше сосредоточиться на том, как решить проблему, а не на том, почему это проблема, поэтому я не использовал некоторые вещи, которые были здесь подняты, так же, как я планировал, но я все еще оставил ссылку.

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

4b9b3361

Ответ 1

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

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

Ответ 2

Я добавил запись в мой блог (см. ниже), которая показывает, как это можно сделать. Ниже приведен пример того, как вы используете его для следующего класса

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

Однако это не показывает, что правила доступа С++ ненадежны. Языковые правила предназначены для защиты от случайных ошибок - если вы пытаетесь ограбить данные объекта, язык по дизайну не займет много времени, чтобы предотвратить вас.

Ответ 3

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

#define private public
#define class struct

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


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

Ответ 4

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

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

Ответ 5

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

union 
{ 
    A a; 
    B b; 
};

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

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

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

Итак, у нас остается лучшая ставка: объявить class B в соответствии с макетом A и взломать взгляд на класс privates.

Ответ 6

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

class Bar {
  SomeOtherType _m1;
  int _m2;
};

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

Ответ 7

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

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

Ответ 8

крутой вопрос 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;
}

Надеюсь, что это поможет.

Ответ 9

Этот ответ основан на точной концепции, продемонстрированной @Johannes answer/blog, поскольку это кажется единственным "законным" способом. Я превратил этот пример кода в удобную утилиту. Он легко совместим с С++ 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;
}

Ответ 10

Если вы знаете, как ваш компилятор С++ управляет именами, да.

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

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

Ответ 11

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

Ответ 12

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

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

Ответ 13

Рядом с #define private public вы также можете #define private protected, а затем определите некоторый класс foo как потомок желаемого класса, чтобы иметь доступ к нему (теперь защищенным) методам через литье типа.

Ответ 14

просто создайте свою собственную функцию доступа для расширения класса.

Ответ 15

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

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

Ответ 16

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

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

Ответ 17

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

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

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

Ответ 18

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

Ответ 19

Относясь к *, вы включаете бэкдор ко всем личным данным внутри объекта.

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

Ответ 20

Довольно часто класс предоставляет методы mutator для частных данных (getters и seters).

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

Ответ 21

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

Ответ 22

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

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

Ответ 23

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 изменился, магическое число является большим источником ошибок.

метод 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 complier.

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