Переполнение буфера в heap. 1 серия
kas1e
Xakep, номер #057, стр. 057-074-2
5. Initialized data - сегмент инициализированных данных. Если мы объявим переменную как глобальную и присвоим ей значение в исходном тексте программы, например, так: char hahah = "im was here";, то строка hahah разместится в этом сегменте инициализированных данных.
6. Text segment (сегмент текста). Это read-only сегмент, включающий в себя все инструкции программы.
7. Разделяемые библиотеки. Функции общего и не очень общего ;) назначения.
0xBFFFFFFF |-------------------|
| |
| . |
| . |
| . |
| . |
| и т.д. |
1 |env/argv указатели |
| argc |
|-------------------|
| |
2 | стек |
| |
| | |
| | |
| V |
/ /
\ \
| |
| ^ |
3 | | |
| | |
| |
| heap |
|-------------------|
4 | bss (data segment)|
|-------------------|
5 | initialized data |
|-------------------|
6 | text |
|-------------------|
7 | shared libraries |
| etc. |
0x8000000 |-------------------|
Примерно так выглядит образ запущенной ELF программы в памяти. Непосредственно из самой программы я буду брать некоторые данные, например, значение Global Offset Table. Об этом позже, а пока рассмотрим более подробно heap область.
Подробно о heap
Существуют разные способы выделения памяти (всего 4). Нас будет интересовать способ, когда выделяется дополнительная память, т.к. она берется именно из heap области. Heap - это некая виртуальная память, расположенная рядом с сегментом данных. Ее размер меняется в зависимости от самих запросов на выделение памяти. Например, при использовании библиотечной функции malloc(), происходит запрос на выделение дополнительной памяти, которая в дальнейшем и будет использоваться для динамического размещения данных. Или, скажем, функция ctime() - она тоже требует выделения дополнительной памяти. Вообще, чуть ниже мы поговорим более подробно об этих функциях, работающих с heap, а пока рассмотрим интересную зависимость между heap и bss. Дело в том, что heap сливается с bss не просто так: данные, данные и вдруг бац - пошел heap. Нет. Между ними есть 4 байта некого адреса, который называется "break address". Изменение размера сегмента данных по существу заключается в изменении break адреса. Для изменения его значения есть 2 системных вызова: brk и sbrk. Первый вызов (brk) позволяет непосредственно установить значение break адреса, второй вызов (sbrk) позволяет увеличить значение break адреса на определенную величину (тут есть несколько нюансов, например, размеры порций выделяемой памяти; это оставим самым пытливым). Сразу резонный вопрос: зачем же нам нужно вообще изменять этот break адрес? Затем, что именно это и есть граница между heap и bss, и, используя именно эти вызовы, мы выделяем память из heap. Так что функции malloc и другие - это не что иное, как простые "обертки" для brk и sbrk. Рассмотрим рисунок: