Мотивация и введение

Недавно я поговорил с парой разработчиков C ++, которые работают в банках и имеют солидный опыт. В основном мы обсуждали C ++ (что еще, как не C ++?), И я заметил, что некоторые из них не знали об интересном приеме / уловке C ++. Итак, я решил написать об этом.
Пойдем посмотрим, что это за хак.

В 1979 году Бьярн Страуструп начал работу над языком программирования «C с классами» в лаборатории AT&T Bell Labs. В 1983 году «C с классами» был переименован в «C ++» (++ - оператор приращения в C).

C ++ унаследовал большую часть синтаксиса C и функций Simula, но C ++ предоставляет возможности объектно-ориентированного программирования (ООП) для C.
В целом ООП обеспечивает абстракцию, инкапсуляцию, наследование и полиморфизм. Также будет правильным сказать, что C ++ предлагает классы, которые обеспечивают упомянутые выше четыре общие особенности ООП.
В этой статье я кратко объясню, что такое инкапсуляция и продемонстрируем, как его можно взломать.

Инкапсуляция в C ++

Не существует однозначного определения инкапсуляции, это концепция / механизм ООП, который связывает вместе данные (элементы данных), чтобы предотвратить доступ к данным, если они определены как частные. Это своего рода концепция / механизм сокрытия данных. В C ++ доступ к скрытым данным предоставляется через функции класса (функции-члены).
Например,

class MyClass {
private:
  int data;
public:
  MyClass(int value): data(value) {}
  ~MyClass() {}
  void print() { std::cout << a << “\n”; }
};

Возникает вопрос, действительно ли (член данных data) конфиденциальный? Могу ли я получить доступ к данным вне интерфейса класса, например my_object.data?
Ответ: Теоретически вы не можете, потому что это частный, но на практике вы можете, потому что C ++ наследует некоторые действительно интересные функции от C, например, предоставление доступа к значению переменной через адрес памяти и изменение значения.

Давайте взломаем

Итак, как можно изменить значение частного члена данных без функции члена? Вот как это сделать.

MyClass my_obj(5);
my_obj.print(); // Here it should print 5
int* obj_ptr = (int*)(&my_obj);
(*obj_ptr)++;
my_obj.print(); // Here it should print 6

Как это работает ? Чтобы ответить на этот вопрос, давайте перейдем к уровню памяти и посмотрим, как объект хранится в памяти. Когда вы объявляете объект
MyClass my_obj(5); вот так (см. Рис. 1.) он будет выглядеть в памяти (при условии, что система 64-битная, а int - 4 байта).

Итак, когда программа вызывает строку int* obj_ptr = (int*)(&my_obj); , тогда значение obj_ptr будет 0x7ffd2d0caf6c, и мы знаем, что можно изменить значение переменной с помощью указателя в C / C ++.

Заключение

Текущий хакерский прием был протестирован на Ubuntu 18.04 (4.15.0–39-generic, x86_64), а исходный код был скомпилирован компилятором g++ (версия 7.3.0).
Итак, инкапсуляция работать или имеет смысл для C ++?

Удачного взлома!

Если вы узнали что-то новое, поделитесь этим и порекомендуйте это, чтобы другие тоже узнали!