Как пропустить строку, выполняющую переполнение буфера в 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;
 }