Как пропустить строку, выполняющую переполнение буфера в C

Я хочу пропустить строку в C, строку x=1; в основном разделе с использованием bufferoverflow ; однако я не знаю, почему я не могу пропустить адрес от 4002f4 до следующего адреса 4002fb несмотря на то, что я рассчитываю 7 байт в форме to .

Я также настроил параметры среды randomniZation и execstack в среде Debian и AMD, но я все еще получаю x=1; , Что случилось с этой процедурой?

Я использовал dba для отладки стека и адресов памяти:

 0x00000000004002ef : callq 0x4002a4 **** **0x00000000004002f4** : movl $0x1,-0x4(%rbp) **0x00000000004002fb** : mov -0x4(%rbp),%esi 0x00000000004002fe : 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; } 

Вы должны читать статью Smashing the Stack for Fun and Profit . Я читал ту же статью и нашел ту же проблему, что и не пропускал эту инструкцию. После нескольких часов отладки в IDA я изменил код, как показано ниже, и он печатает x = 0 и b = 5.

 #include  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(); } 

Чтобы изменить обратный адрес в 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

Если у вас возникли трудности с пониманием того, как работает стек вызовов, чтение стека вызовов и функций prologа статей Wikipedia может помочь. Это показывает трудность создания примеров переполнения буфера в C. Смещение 24 из buffer предполагает определенный стиль заполнения и компиляцию. GCC будет счастливо вставлять стекаторы в настоящее время, если вы не скажете об этом.

2. Количество байтов для добавления к обратному адресу для перехода по x = 1 .

В вашем случае указатель сохраненной инструкции будет указывать на 0x00000000004002f4 ( ), первая инструкция после возврата функции. Чтобы пропустить задание, вы должны сделать указатель указателя сохраненной инструкции равным 0x00000000004002fb ( ).

Ваш расчет, что это 7 байт, является правильным ( 0x4002fb0x4002fb = 7 ).

Я использовал gdb, чтобы разобрать приложение ( disas main ) и проверить расчет для моего дела. Это значение лучше всего разрешать вручную, проверяя parsingку.


Обратите внимание, что я использовал 64-битную среду Ubuntu 10.10 для тестирования следующего кода.

 #include  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] чтобы перезаписать обратный адрес. Однако в большинстве современных реализаций используются такие методы, как стекальные канарейки для защиты от этого.

То, что вы здесь делаете, похоже, не имеет большого количества проблем с classической атакой bufferoverflow. Вся идея атаки bufferoverflow заключается в изменении обратного адреса «функции». Разборка вашей программы покажет вам, откуда берет адрес инструкции ret (при условии x86). Это то, что вам нужно изменить, чтобы указать на main+42 .

Я предполагаю, что вы хотите явно спровоцировать здесь bufferoverflow, как правило, вы должны спровоцировать его, манипулируя входами «функции».

Просто объявив buffer[5] вы перемещаете указатель стека в неправильном направлении (проверьте это, посмотрев на сгенерированную сборку), обратный адрес находится где-то глубже внутри стека (он был помещен туда командой вызова). В x86 стеки растут вниз, то есть к нижним адресам.

Я бы подошел к этому, объявив int* и двигая его вверх, пока не нахожусь в указанном адресе, где был отодвинут адрес возврата, затем измените это значение, чтобы указать на main+42 и пусть функция ret .

Вы не можете так поступать. Вот classический пример кода bufferoverflow. Посмотрите, что произойдет, как только вы будете кормить его 5, а затем 6 символов с клавиатуры. Если вы переходите на большее количество (16 символов должны делать), вы будете перезаписывать базовый указатель, затем возвращать адрес функции, и вы получите ошибку сегментации. Что вы хотите сделать, так это выяснить, какие 4 символа перезаписывают возвращаемый адр. и заставьте программу выполнить свой код. Google вокруг стека Linux, структуры памяти.

  void ff(){ int a=0; char b[5]; scanf("%s",b); printf("b:%xa:%x\n" ,b ,&a); printf("b:'%s' a:%d\n" ,b ,a); } int main() { ff(); return 0; } и  void ff(){ int a=0; char b[5]; scanf("%s",b); printf("b:%xa:%x\n" ,b ,&a); printf("b:'%s' a:%d\n" ,b ,a); } int main() { ff(); return 0; }