адрес возврата функции отличается от его предполагаемого значения, переполнение буфера, ПОМОГИТЕ ПОЖАЛУЙСТА - PullRequest
0 голосов
/ 18 июня 2010

Добрый день всем!

Я пытаюсь понять, как работает переполнение буфера. Сейчас я нахожусь в процессе определения адреса обратного адреса функции, который я должен изменить, чтобы выполнить атаку переполнения буфера. Я написал простую программу, основанную на примере, который я прочитал в Интернете. Эта программа создает целочисленный указатель для хранения адреса адреса возврата функции в стеке. Для этого (если я понимаю, как переменные функции / программы организованы в стеке), я добавляю 8 к адресу переменной буфера и устанавливаю его в качестве значения ret. Я не делаю здесь ничего, что изменило бы адрес, указанный в адресе возврата func.

ОБНОВЛЕНИЕ: Я немного изменил программу, поэтому она печатает адрес стека параметра func a. Как вы можете видеть, расстояние между a и буфером составляет около 8 байтов, так что это, вероятно, будет означать, исходя из схемы стека, что сохраненный FP и старый EIP (адрес возврата func) находится между ними. Я прав?

Вот программа:

void func( int a){
    char buffer[3];

    int *ret;

    ret = buffer + 11; // this is the configuratio which made the whole program works... This now points to the address containing func's return address

    printf (" address of a is %d\n", &a);

    printf ("address of buffer is %x\n", buffer);

    printf ("address of ret is %x\n", ret);

    printf ("value of ret is %x\n", (*ret));

}

void main(){
    int num;

    num = 0;

    func(num);

    num = 1;

    printf("Num now is %d", num);
}

Вывод программы при получении:

альтернативный текст http://img20.imageshack.us/img20/2034/72783404.png

Как видите, я печатаю адрес буфера переменных и ret. Я добавил дополнительный оператор, печатающий значение переменной ret (предполагаемое расположение обратного адреса func, поэтому следует напечатать адрес следующей инструкции, которая будет выполнена после возврата func из выполнения).

Вот дамп, который показывает предполагаемый адрес инструкции, которая будет выполнена после возврата func. (Подчеркнуто зеленым) Как видите, это значение сильно отличается от напечатанного значения, содержащегося в переменной ret.

альтернативный текст http://img717.imageshack.us/img717/8273/assemblycodecopy.png

Мой вопрос: почему они разные? (конечно в предположении, что все, что я сделал, все правильно). Иначе, что я сделал не так? Мое понимание стека выполнения программы неверно? Пожалуйста, помогите мне понять это. Мой проект должен выйти через неделю, и я его почти не трогал. Прошу прощения, если я требую, мне очень нужна ваша помощь.

Ответы [ 3 ]

1 голос
/ 18 июня 2010

Для следующей программы

int main(int argc, char **argv) {
   int v[2];

   return 0;
}

Структура стека в основном следующая:

 
       -------------   
           arg n 
       ------------- 
         .........
       -------------   
0x1010     arg 0 
       ------------- 
0x100C  ret address
       =============
0x1008     old fp 
       -------------
0x1004     v[1]
       -------------
0x1000     v[0]
       -------------

Вы можете узнать обратный адрес main, используя v + 3.

Если предположить, что адреса расположены в левой части стека, v имеет адрес 0x1000, обратный адрес имеет адрес (v + 3 => 0x1000 + 4 * 3 = 0x100C)

1 голос
/ 18 июня 2010

Прежде всего, обратите внимание, что адрес буфера является нечетным числом 0xbffffd51, а затем вы добавляете к нему 8, чтобы получить 0xbffffd59. Я был бы весьма удивлен, если бы адрес возврата в стеке не был выровнен с четырехбайтовым адресом.

В зависимости от компилятора, точное расположение кадра стека может варьироваться (например, даже если buffer является первым в исходном коде, компилятор может поместить ret выше в стеке), так что вы можете нужно экспериментировать с вашими ценностями. Я бы сделал пару вещей:

  1. Изменить буфер на 4 байта.
  2. Эксперимент с различными смещениями. У меня такое ощущение, что вам, возможно, придется поискать 12 или даже 16 байтов, чтобы найти свой обратный адрес.
0 голосов
/ 18 июня 2010

Конечно, вы не можете изменить исходный номер, если не передадите на него указатель;поэтому в основном num сначала равен 0, а затем равен 1, и он никогда не модифицируется функцией.Адрес (&a) в func - это адрес локальной копии (по значению) аргумента, скорее всего, адрес в стеке в большинстве случаев.И на что бы указывал рет?У вас есть 3-х символьный буфер, и вы получаете адрес за его пределами;теперь вы должны рассматривать это как указатель на мусор, хотя, скорее всего, вы указываете на что-то «интересное» в зависимости от того, как локальные переменные «организованы» в памяти.Таким образом, вы не можете быть на 100% уверены, что он действительно указывает на обратный адрес.Вы предполагаете следующее:

0  4 bytes (for char, assuming 4bytes alignment)
4  4 bytes (for whatever, maybe argument)
8  4 bytes (return address)

И это зависит.Это зависит от архитектуры;это зависит от того, как компилятор «переводит» код функции.Давайте представим себе x86.Ниже приведен разумный способ выполнения func

func:
  push ebp  ; save some regs...
  push eax  ; or with pusha?
  mov ebp, esp
  push 0   ; for char a[3]
  mov eax, ebp
  add eax, 4  ; -4 + 8
  push eax ; for int *ret
  ; -4(ebp) gives a
  ; -8(ebp) gives int *ret
  ; so ebp-4 is the pointer to a, we
  ; add 8, to obtain ebp+4, which points
  ; to saved ebp... missing the ret ptr
  ; (other code...)
  mov esp, ebp
  pop eax ; or with popa?
  pop ebp
  ret

, а что если сохраненных регистров больше?что если поменять порядок char a [4] и int * ret?Как ты можешь знать?Вы не можете ничего предполагать, если только вы не пишете код непосредственно в asm, в этом случае вы можете точно контролировать то, что происходит.В противном случае работающий код C, который делает то, что вы хотите, будет работать случайно ...

...