Russian Qt Forum

Qt => Уроки и статьи => Тема начата: __Heaven__ от Сентябрь 21, 2015, 10:35



Название: Что плохого в записи за пределы массива
Отправлено: __Heaven__ от Сентябрь 21, 2015, 10:35
Привет, друзья!
Наткнулся на интересный пример в сети, решил, что будет хорошо его добавить сюда.
Имеется код:
Код
C++ (Qt)
#include <iostream>
 
int foo(){
   std::cout << "Hi! You are out of range :^)";
   return 1;
}
 
void bar(){
   int arr[500];
   arr[501] = reinterpret_cast<int> (&foo);
}
 
int main(){
   bar();
 
   return 0;
}
 

Видно, что в функции bar мы производим запись в ячейку с номером на 2 больше максимально допустимого.
Данный код я скомпилировал используя компилятор MinGW 4.92. Далее приводится результат работы (аттач 1).

Видно, что в коде происходит присвоение элементу массива arr с номером 501 адреса функции foo(). Никакого вызова функции foo() в коде нет. Однако, при исполнении программы, происходит вызов функции foo(), о чем свидетельствует появление в консоли строчки "Hi! You are out of range :^)".

Почему это происходит? Если мы посмотрим на устройство стека данной программы (аттач 2), то несложно заметить, что при попытке обращения к ячейке a[501] мы обращаемся в ячейку, которая отвечает за адрес возврата в место вызова функции bar. Переписав это значение адресом функции foo возврат состоится именно в неё! Важно отметить, что на разных системах с разными компиляторами стек может быть устроен по разному, соответственно, для воспроизведения данного примера может потребоваться заменить 501 на другое число чуть больше 499.


Название: Re: Что плохого в записи за пределы массива
Отправлено: gil9red от Сентябрь 21, 2015, 19:14
Так вот как переполнение буфера может помочь получить доступ к системе :)


Название: Re: Что плохого в записи за пределы массива
Отправлено: Bepec от Сентябрь 21, 2015, 20:26
Это всем давно известно. Более того, такой хак может применяется в случае необходимости доступа к приватным членам класса :)


Название: Re: Что плохого в записи за пределы массива
Отправлено: Igors от Сентябрь 22, 2015, 09:59
Это всем давно известно. Более того, такой хак может применяется в случае необходимости доступа к приватным членам класса :)
Прошу привести пример такого доступа


Название: Re: Что плохого в записи за пределы массива
Отправлено: __Heaven__ от Сентябрь 22, 2015, 10:04
Прошу привести пример такого доступа
Код
C++ (Qt)
#include <iostream>
 
class MyClass{
   int a;
   int b;
public:
   MyClass() : a(5), b(8) {}
};
 
int main(){
   MyClass my;
   int a[1];
   std::cout << a[1] << a[2] << '\n';
   return 0;
}
 


Название: Re: Что плохого в записи за пределы массива
Отправлено: Igors от Сентябрь 22, 2015, 10:29
Прошу привести пример такого доступа
Код
C++ (Qt)
#include <iostream>
 
class MyClass{
   int a;
   int b;
public:
   MyClass() : a(5), b(8) {}
};
 
int main(){
   MyClass my;
   int a[1];
   std::cout << a[1] << a[2] << '\n';
   return 0;
}
 
Это не работает напр на MSVC 2012, причем без разницы в каком порядке объявлены "my" и "a", тамошний компилятор первым на стеке помещает my - ну ему виднее. Вообще для таких трюков стек не нужен, просто
Код
C++ (Qt)
int * a = (int *) &my;
 
Обычно доступ (о котором говорил Верес которому удалось увильнуть от ответа) сводится к нахождению смещения нужного члена в отладчике и потом что-то типа
Код:
int * dst = (int *)((char *) &my + MAGIC_OFFSET);
Конечно это работает до первой перекомпиляции либы, тогда придется уточнять MAGIC_OFFSET.

Конечно никто не спорит что в любом случае это плохая практика, но можно ли сделать это легальным (с точки зрения языка) и независимым от платформы/компилятора?


Название: Re: Что плохого в записи за пределы массива
Отправлено: Bepec от Сентябрь 22, 2015, 16:29
Я не ускользал, я всё ж на форуме не живу)
Да, для этого нужно знать смещение, да, для разных компиляторов смещение будет разным и возможно порядок хаотичным, но на уже скомпилированной либе смещения статичны.

Средствами языка, без изменения исходников либы, невозможно. Хотя что считать средствами языка :D