Уязвимость классов C++ или Как получить доступ к запрещенным элементам

от автора

Привет Хабр!

В процессе написания очередного файлового менеджера, задался вопросом: «Каким же образом происходит деление на частные, защищенные и публичные объекты в классах?». Как оказалось, никакого деления нет — это чистая абстракция.

Ниже описано строение и функционирование классов на языке C++, а так же способы обходов запретов частных полей доступа.

Классы

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

Класс — разновидность абстрактного типа данных в объектно-ориентированном программировании, характеризуемый способом своего построения.

Класс, на самом деле, – это простая структура, не хранящая в себе никаких методов, полей доступа и конструкторов – вот здесь и проявляется, та самая абстракция.

Рассмотрим некий класс:

class _Class { private: 	int Value; public: 	_Class() { 	} }; 

Внутри данный класс представляет собой структуру данных, которая не делится, ни на частные, ни на публичные поля доступа, а структура, в свою очередь, является «массивом» элементов разной величины. Т.е. ключевое слово struct просто говорит компилятору о том, что необходимо зарезервировать N байт памяти.

struct _Class { 	int Value; }; 

Значит, что адрес объекта класса или структуры – есть адрес первого атрибута. Получается, что для получения значения любого атрибута класса, необходимо иметь его адрес, который с легкость можно получить сдвигом указателя объекта класса на N >= 0 байт.
Например, поменяем значение частного атрибута класса _Class:

class _Class { private: 	int Value; public: 	_Class() { 	} }; _Class Object; __asm { 	lea eax, Object 	mov ebx, 123 	mov [eax + 0], ebx } 

lea eax, Object 

– создаем указатель на объект класса и помещаем в регистр EAX.

mov [eax + 0], ebx 

– помещаем в атрибут Value, значение регистра EBX = 123. (0 – байтовый сдвиг, соответственно для второго элемента он будет равен 4 т.к. sizeof(Value) = 4).
Вот таким вот, достаточно простым и понятным способом можно получать и менять значения всех атрибутов, не зависимо от принадлежности к полю доступа.

Получается, что все «запреты» в языке C++ происходят исключительно на уровне компилятора. Аналогичным образом, используя Inline Assembler, можно менять значения константных переменных, например:

const int Value = 7; __asm { 	mov ebx, 123 	mov Value, ebx } 

Методы

Но как же тогда устроены методы класса, если структура не хранит в себе указатели на функции?
Функции в классах работаю почти так же как и обычные функции. Именуются методы классов следующим образом _Class::_Class. Но основное отличие, разумеется, не в этом – это просто имя, а в 2 ключевых строках дизассемблированного кода.
1. Создается указатель на объект класса и помещается в регистр ECX
2. При вызове метода класса регистр ECX копируется в локальный указатель, именуемый this.

Пример полноценной реализации вызова метода класса на языке C++:

class _Class { private: 	int Value; public: 	_Class() { 	} public: 	void Function() { 		this->Value = 7; 	} };  _Class Object; Object.Function(); 

А вот как это выглядит на самом деле:

struct _Class { 	int Value; };  void _Class::_Class() { 	_Class* this; 	__asm { 		mov this, ecx 	} }  void _Class::Function() { 	_Class* this; 	__asm { 		mov this, ecx 	} 	this->Value = 7; }  _Class Object; __asm { 	lea ecx, Object 	call _Class::Function } 

И о чем нам это говорит – независимо от поля, метод класса – это просто функция, которая при запуске инициализирует указатель на наш элемент, а класс — это просто структура с публичными атрибутами. Получается, что даже частные методы вызываются с помощью Inline Assembler, без каких либо запретов.

Вот и все, что я хотел рассказать. Спасибо за внимание!

ссылка на оригинал статьи http://habrahabr.ru/post/172237/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *