Можно ли перезаписать %eax с помощью переполнения буфера?

Я знаю, что программный стек выглядит примерно так (от высокого к низкому):

   EIP   |   EBP   |   local variables

Но где мне найти %eax и другие общие регистры? Можно ли перезаписать их с помощью переполнения буфера?

Обновление: В итоге мне даже не пришлось перезаписывать %eax, потому что оказалось, что программа в какой-то момент указала %eax на пользовательский ввод.


person LonelyWebCrawler    schedule 03.11.2014    source источник
comment
Это зависит от используемого соглашения о вызовах и, возможно, от решений компилятора по оптимизации.   -  person Mark    schedule 03.11.2014
comment
Лучшее название для вашего вопроса может быть Возможно ли перезаписать регистр с помощью переполнения буфера? потому что, как сказано, это предполагает ложную предпосылку, строго (хотя, кстати, это оказывается правдой по крайней мере в одном контексте)   -  person codenheim    schedule 03.11.2014
comment
Вы правы, я изменил его.   -  person LonelyWebCrawler    schedule 04.11.2014


Ответы (3)


Регистр по определению не находится в оперативной памяти. Регистры находятся в ЦП и не имеют адресов, поэтому перезаписать их при переполнении буфера нельзя. Однако регистров очень мало, поэтому компилятор действительно использует их как своего рода кэш для наиболее часто используемых элементов стека. Это означает, что хотя вы не можете переполнить регистры в строгом смысле, значения, перезаписанные в ОЗУ, рано или поздно будут загружены в регистры.

(В ЦП Sparc политика «регистры как стек-кеш» даже жестко запрограммирована.)

В вашей схеме EIP и EBP нет в стеке; соответствующие слоты в стеке являются областями, из которых эти два регистра будут перезагружены (при выходе из функции). EAX, с другой стороны, является регистром общего назначения, который код будет использовать здесь и там без строгого соглашения.

person Thomas Pornin    schedule 03.11.2014

EAX, вероятно, никогда не появится в стеке. Для большинства компиляторов x86 EAX является 32-битным регистром возвращаемого значения и никогда не сохраняется в стеке и не восстанавливается из стека (RAX в 64-битных системах).

Это не означает, что переполнение буфера нельзя использовать для изменения содержимого EAX путем помещения исполняемого кода в стек; если выполнение кода в стеке не было отключено операционной системой, это можно сделать, или вы можете принудительно ввести в стек фиктивный адрес возврата, который передает управление кодовой последовательности, которая загружает значение в EAX, но это довольно сложно выдернуть. Точно так же, если известно, что значение, возвращаемое функцией, хранится в локальной переменной, сбой стека, изменивший эту переменную, изменит значение, скопированное в EAX, но оптимизирующие компиляторы могут изменить расположение стека по прихоти, поэтому эксплойт, который работает в одной версии может полностью выйти из строя в новом выпуске или исправлении.

person flounder    schedule 03.11.2014
comment
Спасибо за подробности. Все это имеет смысл, за исключением одного: для большинства компиляторов x86 EAX является 32-битным регистром возвращаемого значения. Я думал, что EAX — это просто общий регистр. Разве возвращаемые значения не хранятся в другом месте? - person LonelyWebCrawler; 03.11.2014
comment
Различные операционные системы имеют разные соглашения о вызовах, но я никогда не видел ни одной, которая не использовала бы EAX (или AX или RAX) для записи возвращаемого значения из функции. После выполнения CALL вы обычно увидите, что значение в EAX проверяется или сохраняется. - person Jason Goemaat; 04.11.2014

См. ответы Томаса Порнина (+1) и Флаудера (+1), они хороши. Я хочу добавить дополнительный ответ, который, возможно, упоминался, но не был конкретно указан, и это разлив регистра.

Хотя «где» исходного вопроса (по крайней мере, в такой формулировке), похоже, основано на ложном предположении, что %eax находится в стеке и, будучи регистром, не является частью стек на x86 (хотя вы можете эмулировать любой аппаратный регистр, установленный в стеке, и некоторые архитектуры действительно делают это, но это не имеет значения), кстати, регистры часто сбрасываются/заполняются из стека. Таким образом, можно разбить значение регистра с переполнением стека, если регистр был перенесен в стек. Это потребует от вас знания механизма сброса конкретного компилятора, и для этого вызова функции вам нужно будет знать, что %eax был удален, куда он был удален, и топать это местоположение стека, и когда он будет следующим. заполненный из своей копии в памяти, он получает новое значение. Как бы маловероятно это ни казалось, эти атаки обычно инспирированы чтением исходного кода и знанием чего-то о рассматриваемом компиляторе, так что на самом деле это не так уж надуманно.

См. это, чтобы узнать больше о разливе регистров.

перенос регистра аргументов gcc на x86-64

https://software.intel.com/en-us/articles/dont-spill-that-register-ensuring-optimal-performance-from-intrinsics

person codenheim    schedule 03.11.2014