Раздел: Документация
0 ... 39 40 41 42 43 44 45 ... 102 После возврата из дочерней функции все принадлежащие ей локальные пеор автоматически освобождаются, поэтому использовать дочерний буфер для хНь** материнских переменных нельзя (точнее, не рекомендуется, но если Действоват ***** рожно — то можно). Обратитесь к куче, статической памяти или автоматической параллельного потока, воздействуя на нее косвенным образом.памяГ1 Выше кадра стека располагаются сохраненные значения регистров, восстанав ливаемые при выходе из функции. Если материнская функция хранит в однощ или нескольких таких регистрах критические переменные (например, указате ли, в которые что-то записывается), мы можем свободно воздействовать на них по своему усмотрению. Дальше начинается область, «оккупированная» локальными переменными (и переполняющимся буфером в том числе). В зависимости от прихоти компилятора последний может быть расположен как наверху кадра стека, так и в гуще локальных переменных — это уже как повезет (или не повезет - сточки зрения жертвы). Переменные, находящиеся «ниже» переполняющегося буфера, могут быть затерты при последовательном переполнении — самом распространенном типе переполнения. Переменные, находящиеся «выше» переполняющегося буфера, затираются лишь индексным переполнением, которое мало распространено. Наконец, выше кадра стека находится только небо и звезды, пардон — свободное стековое пространство. Затирать тут особенно нечего, и эта область памяти используется в основном для служебных нужд shell-кода. При этом следует учитывать следующее: •объем стека не безграничен и упирается в определенный лимит, так что вы делять гигабайты памяти все-таки не стоит; •если один из спящих объектов процесса-жертвы неожиданно проснется.** держимое свободной стековой памяти окажется искажено, и, чтобы зТе. случилось, shell-код должен подтянуть регистр ESP к верхнему уровню, зервируя необходимое количество байт памяти; •поскольку стековая память, принадлежащая потоку, выделяется д"наев0й ски но мере его распухания, всякая попытка выхода за пределы сТ00за11ра-страницы {page guard) завершается исключением, поэтому либо № шивайте более 4 Кбайт, либо прочитайте хотя бы по одной яченкс . дой резервируемой страницы, двигаясь снизу вверх. Подробнее оо э но прочитать у Рихтера. д зП В зависимости от ограничений, наложенных на предельно допустимыми-переполняющегося буфера, могут затираться те или иные локальные 8о< ные или служебные структуры данных. Очень может статься, что до аакТ, •С0 врата просто не удастся «дотянуться», а даже если и удастся — не ч< обычно выполняется до вызова дочерних функций, а корректность ;№т ческих переменных после их инициализации проверяют только цат, "Мат11- ВНИМАНИЕ--— не грохнется задолго до своего завершения. Допустим, за концом стро-фуикиия [ая11Я10Щегося буфера располагается указатель, из которого после кнВоГ0.К.ция что-то читается (записывается). Поскольку переполнение буфе-lu-peiio- затирает указатель, всякая попытка чтения оттуда вызывает не-;IIC1.шое исключение и — как следствие — аварийное завершение нрограм-"\причем затереть адрес возврата, подсунув указателю корректный адрес, е всего, не удастся, так как в операционных системах семейства Windows е гарантированно доступные адреса лежат значительно ниже OlOlOlOlh — наименьшего адреса, который только можно внедрить в середину строкового буфера (подробнее см. раздел «Запрещенные символы»). Так что буферы, рас-пюженные внизу кадра стека, для переполнения все же предпочтительнее. Ча конном адреса возврата начинается область памяти, принадлежащая материнским функциям и содержащая: аргументы дочерней функции, автоматические неременные материнской функции, сохраненные регистры, кадр стека нра-матерннской функции, адрес возврата в праматеринскую функции и т. д. Теоретически переполняющийся буфер может все это затереть (ну бывают же такие буйные буферы), практически же — это либо ненужно, либо неосуществимо. Если мы можем навязать программе корректный адрес возврата (то есть адрес возврата, указывающий на shell-код или любую точку «родного» кода программы), то в материнскую функцию она уже не вернется и все махинации с материнскими переменными останутся незамеченными. Если же навязать корректный адрес возврата по тем или иным причинам невозможно, то материнская функция тем более не сможет получить управления. Большую информацию несет чтение материнской области памяти (см. ранее радел «Указатели и индексы») — здесь действительно можно встретить много чего интересного: конфиденциальные данные (типа паролей и номеров кредитных карт), дескрипторы секретных файлов, которые невозможно открыть обычным образом, сокеты установленных TCP-соединений (почему бы их не использовать для обхода брандмаузеров?) и т. д. Модификация аргументов дочерней функции менее перспективна, хотя време-"*ш и бывает полезной. Среди аргументов Си/Си++-нрограмм традиционно 1И ° УКазателей. Обычно это указатели на данные, но встречаются и указате-у Накод- Последние более перспективны, поскольку позволяют захватывать хоп Ление ПР°граммой до ее обрушения. Указатели на данные, конечно, тоже иавя™1" Осоенно те из них, что позволяют записывать но навязанным адресам ЛотяГ1НЫеДаННЫетоесть работают как Бейсик-функция РОКЕ), однако, чтобы МотоёЛо своих аргументов при последовательном переполнении уязви-ft Уферы, необходимо пересечь ячейки памяти, занятые адресом возврата... :>тоаб*а,1ИИ адРеса возврата есть одна интересная тонкость: адрес возврата — Ка еам ЛЮтныи адрес, п, если мы хотим передать управление непосредственно ! УяЗВи"еРеполняющийся буфер, нам либо приходится надеяться на то, что ; »Го неИ пР°гРамме переполняющийся буфер окажется по такому-то адресу , е Факт), либо искать механизм передачи управления на вершину стека. нвь Love с, ""Ниц • Решает проблему путем подмены адреса возврата на адрес 0,1 инструкции JMP ESP, расположенной во владениях операционной 136Глава 4. Ошибки переполнения буфера извне --—-- - -—— еииз„ системы. Недостатки такой методики очевидны: во-первых, она не т-,г в тех случаях, когда переполняюшпися буфер расположен ниже вепщ 8fi ка, а во-вторых, местоположение инструкции JMP ESP тесно связано (- ЬСТ операционной системы, и получается как в той поговорке: «За что бороч то и напоролись». К сожалению, более прогрессивных методик перед;,,,,, СЬ"" лення пока не придумано..."Р* в куче... Буферы, расположенные в динамической памяти, также подвержены нереПо нению. Многие программисты, ленивые от природы, сначала выделяютfiv*, фиксированного размера, а затем определяют, сколько памяти им реадьно обходимо, причем ситуацию недостатка памяти обработать традиционно заб вают. В куче чаще всего встречаются переполняющиеся буферы двух типов-ад. менты структур и динамически выделяемые блоки памяти. Допустим, в программе имеется структура demo, содержащая в том числе и б\ фер фиксированного размера (листинг 4.10). Листинг 4.10. Пример структуры с переполняющимся буфером внутри (он выделен полужирным) strict demo ( int а: char but" [8]: int b: Неосторожное обращение с обрабатываемыми данными (например, otcvtctw нужных проверок в нужном месте) может привести к возможности перепада чия буферы buf и как следствие - затиранию расположенных за ним пере* ных. В первую очередь это переменные-члены самой структуры (в данномм1 чае - переменная Ь), стратегия модификации которых вполне тили1" и подчиняется тем же правилам - общим для всех переполняющихся буфер Менее очевидна возможность затирания ячеек памяти, лежащих за ире**" выделенного блока памяти. Кстати, для буферов, монопольно владеющих во выделенным блоком памяти, это единственно возможная стратегия перс""1" ния вообще. Взгляните на следующий код (листинг 4.11). Как вы думаете < здесь можно переполнить? Листинг 4.11. Пример динамического блока памяти, подверженного переполнению #def-;ne MAX BUF S:ZE 8 define MAX STR SI/E 256 char *p; P = manoc(MAX BbF SIZE): StrncpyCp. MAXSTRSIZE. Str); 0 ... 39 40 41 42 43 44 45 ... 102
|