Нужны подробные объяснения о BOF - PullRequest
0 голосов
/ 01 июня 2019

Итак, я следовал учебнику о переполнении буфера со следующим кодом:

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc, char **argv)
{
  volatile int modified;
  char buffer[64];

 modified = 0;
  gets(buffer);

  if(modified != 0) {
      printf("you have changed the 'modified' variable\n");
  } else {
      printf("Try again?\n");
  }
}

Затем я компилирую его с помощью gcc и дополнительно запускаю заранее sudo sysctl -w kernel.randomize_va_space=0, чтобы предотвратить случайную память и разрешить использование стека (переполнение буфера)

gcc protostar.c -g -z execstack -fno-stack-protector -o protostar

-g - разрешить отладку в gdb ('list main')
-z execstack -fno-stack-protector - удалить защиту стека

и затем выполните его:

python -c 'print "A"*76' | ./protostar

Попробовать еще раз?

python -c 'print "A"*77' | ./protostar

вы изменили «измененную» переменную

Таким образом, я не понимаю, почему переполнение буфера происходит с 77, в то время как это должно было быть 65, таким образом это имеет значение 12 битов (3 байта). Интересно, почему, если у кого-то есть четкое объяснение?

Так же остается с 77 до 87:

python -c 'print "A"*87' | ./protostar
you have changed the 'modified' variable

А из 88 добавлен сегмент:

python -c 'print "A"*88' | ./protostar
you have changed the 'modified' variable
Segmentation fault (core dumped)

Привет

1 Ответ

1 голос
/ 01 июня 2019

Чтобы полностью понять, что происходит, в первую очередь важно отметить, как ваша программа распределяет память.

Из вашего комментария у вас есть то, что для этого конкретного прогона память для buffer начинается с 0x7fffffffdf10, а затем modified начинается с 0x7fffffffdf5c (хотя randomize_va_space может поддерживать это согласованным для всех прогонов,но я не совсем уверен).

Итак, у вас есть что-то вроде этого:

0x7fffffffdf10            0x7fffffffdf50      0x7fffffffdf5c
↓                         ↓                   ↓
(64 byte buffer)..........(some 12 bytes).....(modified)....

По сути, у вас есть буфер из 64 символов, затем, когда это заканчивается, есть 12 байтов, которыеиспользуется для некоторой другой переменной стека (вероятно, 4 байта argc и 8 байтов для argv), а затем модифицируется после, точно начиная с 64 + 12 = 76 байтов после запуска буфера.

Поэтому, когда вы записываете от 65 до 76 символов в 64-байтовый буфер, он проходит мимо и начинает запись в те 12 байтов, которые находятся между буфером и modified.Когда вы начинаете писать 77-й символ, он начинает перезаписывать то, что в modified, что заставляет вас видеть сообщение "you have changed the 'modified' variable".

Вы также спросили "почему это работает, если я подхожу до 87 итогда на 88 есть segfault? Ответ в том, что, поскольку это неопределенное поведение, как только вы начнете запись в недействительную память и ядро ​​распознает это, это немедленно убьет ваш процесс, потому что вы пытаетесь читать / записывать память, которую вы не 'у вас нет доступа к.

Обратите внимание, что на практике вы почти никогда не должны использовать gets, и это серьезная причина, поскольку вы точно не знаете, сколько байтов вы будете читать, поэтому есть шанс перезаписатьТакже обратите внимание, что поведение, которое вы видите, не является тем же поведением, которое я наблюдаю на своей машине, когда я запускаю его. Это нормально, и это потому, что это неопределенное поведение. Нет никаких гарантий того, что произойдет, когда вы запустите его.На моей машине modified на самом деле предшествует buffer в памяти, поэтому я никогда не вижу, чтобы переменная modified перезаписывалась,Я думаю, что это хороший учебный пример, чтобы понять, почему неопределенное поведение, подобное этому, просто непредсказуемо.

...