Как пропустить строку, выполняющую переполнение буфера в C
Я хочу пропустить строку в C, строку x=1;
в основном разделе, используя bufferoverflow
; однако я не знаю, почему я не могу пропустить адрес от 4002f4
до следующего адреса 4002fb
, несмотря на то, что я рассчитываю 7 байт с <main+35>
до <main+42>
.
Я также настроил параметры среды randomniZation и execstack в среде Debian и AMD, но я все еще получаю x=1;
. Что не так с этой процедурой?
Я использовал dba для отладки стека и адресов памяти:
0x00000000004002ef <main+30>: callq 0x4002a4 **<function>**
**0x00000000004002f4** <main+35>: movl $0x1,-0x4(%rbp)
**0x00000000004002fb** <main+42>: mov -0x4(%rbp),%esi
0x00000000004002fe <main+45>: mov $0x4629c4,%edi
void function(int a, int b, int c)
{
char buffer[5];
int *ret;
ret = buffer + 12;
(*ret) += 8;
}
int main()
{
int x = 0;
function(1, 2, 3);
x = 1;
printf("x = %i \n", x);
return 0;
}
Ответы
Ответ 1
Вы должны читать статью Smashing the Stack for Fun and Profit. Я читал ту же статью и нашел ту же проблему, что и не пропускал эту инструкцию. После нескольких часов отладки в IDA я изменил код, как показано ниже, и он печатает x = 0 и b = 5.
#include <stdio.h>
void function(int a, int b) {
int c=0;
int* pointer;
pointer =&c+2;
(*pointer)+=8;
}
void main() {
int x =0;
function(1,2);
x = 3;
int b =5;
printf("x=%d\n, b=%d\n",x,b);
getch();
}
Ответ 2
Чтобы изменить адрес возврата в function()
, чтобы пропустить x = 1
в main()
, вам нужны две части информации.
1. Расположение адреса возврата в кадре стека.
Я использовал gdb для определения этого значения. Я установил точку останова в function()
(break function
), выполнив код до точки останова (run
), извлеките местоположение в память текущего фрейма стека (p $rbp
или info reg
), а затем извлеките расположение в памяти buffer
(p &buffer
). Используя полученные значения, можно определить местоположение адреса возврата.
(скомпилированный флаг w/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
Если у вас возникли трудности с пониманием того, как работает стек вызовов, прочитав стек вызовов и пролог функции Статьи Wikipedia могут помочь. Это показывает сложность создания примеров переполнения буфера в 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]
, чтобы перезаписать обратный адрес. Однако в большинстве современных реализаций для защиты от этого используются методы, такие как стекалки канарей.
Ответ 3
То, что вы здесь делаете, похоже, не имеет особого отношения к классической атаке bufferoverflow. Вся идея атаки bufferoverflow заключается в изменении обратного адреса "функции". Разборка вашей программы покажет вам, откуда берется его адрес ret
(при условии x86). Это то, что вам нужно изменить, чтобы указать на main+42
.
Я предполагаю, что вы хотите явно спровоцировать здесь bufferoverflow, как правило, вы должны спровоцировать его, манипулируя входами "функции".
Просто объявив a buffer[5]
, вы перемещаете указатель стека в неправильном направлении (проверьте это, посмотрев на сгенерированную сборку), обратный адрес находится где-то глубже внутри стека (он был помещен туда командой вызова). В x86 стеки растут вниз, то есть к нижним адресам.
Я бы подошел к этому, объявив int*
и переместив его вверх, пока не нахожусь в указанном адресе, где был вставлен обратный адрес, затем измените это значение на пункт main+42
и позвольте функции ret
.
Ответ 4
Вы не можете этого сделать.
Здесь используется классический пример кода bufferoverflow. Посмотрите, что произойдет, как только вы будете кормить его 5, а затем 6 символов с клавиатуры. Если вы переходите на большее количество (16 символов должны делать), вы будете перезаписывать базовый указатель, затем возвращать адрес функции, и вы получите ошибку сегментации. Что вы хотите сделать, так это выяснить, какие 4 символа перезаписывают возвращаемый адр. и заставьте программу выполнить свой код. Google вокруг стека linux, структуры памяти.
void ff(){
int a=0; char b[5];
scanf("%s",b);
printf("b:%x a:%x\n" ,b ,&a);
printf("b:'%s' a:%d\n" ,b ,a);
}
int main() {
ff();
return 0;
}