Чтобы изменить обратный адрес в function()
и пропустить x = 1
в main()
, вам нужны две части информации.
1. Расположение адреса возврата в кадре стека.
Я использовал gdb для определения этого значения. Я устанавливаю точку останова на function()
(break function
), выполняю код до точки останова (run
), получаю местоположение в памяти текущего кадра стека (p $rbp
или info reg
), а затем получить местоположение в памяти buffer
(p &buffer
). Используя извлеченные значения, можно определить местоположение обратного адреса.
(скомпилировано с флагом GCC -g
для включения символов отладки и выполнено в 64-битной среде)
(gdb) break function
...
(gdb) run
...
(gdb) p $rbp
$1 = (void *) 0x7fffffffe270
(gdb) p &buffer
$2 = (char (*)[5]) 0x7fffffffe260
(gdb) quit
(адрес указателя кадра + размер слова) - адрес буфера = количество байтов от локальной переменной буфера до адреса возврата
(0x7fffffffe270
+ 8) - 0x7fffffffe260
= 24
Если у вас возникли трудности с пониманием того, как работает стек вызовов, прочтите стек вызовов и пролог функции могут помочь статьи Википедии. Это показывает сложность создания примеров "переполнения буфера" в C. Смещение 24 от buffer
предполагает определенный стиль заполнения и параметры компиляции. GCC с радостью вставит стековые канарейки в настоящее время, если вы не скажете ему не делать этого.
2. Количество байтов, которое нужно добавить к адресу возврата, чтобы пропустить x = 1
.
В вашем случае указатель сохраненной инструкции будет указывать на 0x00000000004002f4
(<main+35>
), первую инструкцию после возврата функции. Чтобы пропустить задание, нужно сделать так, чтобы указатель сохраненной инструкции указывал на 0x00000000004002fb
(<main+42>
).
Ваш расчет, что это 7 байт, верен (0x4002fb
- 0x4002fb
= 7).
Я использовал gdb для дизассемблирования приложения (disas main
) и также проверил вычисления для своего случая. Это значение лучше всего решить вручную, проверив разборку.
Обратите внимание, что я использовал 64-битную среду Ubuntu 10.10 для тестирования следующего кода.
#include <stdio.h>
void function(int a, int b, int c)
{
char buffer[5];
int *ret;
ret = (int *)(buffer + 24);
(*ret) += 7;
}
int main()
{
int x = 0;
function(1, 2, 3);
x = 1;
printf("x = %i \n", x);
return 0;
}
вывод
x = 0
На самом деле это просто изменение адреса возврата function()
, а не фактическое переполнение буфера. При фактическом переполнении буфера вы бы переполнили buffer[5]
, чтобы перезаписать адрес возврата. Однако в большинстве современных реализаций для защиты от этого используются такие методы, как стековые канарейки.
person
jschmier
schedule
06.04.2011
x/i10
стек$esp
и добавить это к вашему объяснению. Также покажите, что происходит, когда вы переходите к инструкциям с самого началаfunction
. - person Alex Brown   schedule 12.03.2011