За да промените адреса за връщане в рамките на 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 с радост ще вмъкне stack canary днес, освен ако не му кажете да не го прави.
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]
, за да презапишете адреса за връщане. Повечето съвременни реализации обаче използват техники като stack canary за защита срещу това.
person
jschmier
schedule
06.04.2011
x/i10
стека$esp
и да добавите това към обяснението си. Също така покажете какво се случва, докато пристъпвате към инструкциите от началото наfunction
- person Alex Brown   schedule 12.03.2011