Мотивация и введение
Недавно я поговорил с парой разработчиков 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 ++?
Удачного взлома!
Если вы узнали что-то новое, поделитесь этим и порекомендуйте это, чтобы другие тоже узнали!