2017-01-06 1 views
0

Я пытаюсь понять какой-то разобщенный код и математику в нем. По моим расчетам 256 dec = 0x100 hex. Когда я лукавлю char buff[256], я вижу, что выделенные sub $0x108,%esp

Вот код С

void vuln(char* arg) { 
    char buf[256]; 
    strcpy(buf, arg); 
} 

Вот в разобранном инструкциями

push %ebp 
mov %esp, %ebp 
sub $0x108,%esp 
sub $0x8,%esp 
pushl 0x8(%ebp) 
lea -0x108(%ebp),%eax 
push %eax 
call 8048300 <[email protected]> 
add $0x10,%esp 
leave 
ret 

Я также не понимаю add $0x10,%esp в конце , Я предполагаю, что мы удаляем возвращаемое значение из strcpy, но почему $0x10? strcpy возвращает char *, не должно быть $0x8?

+1

Вы забыли скомпилировать с оптимизацией включен. В противном случае компилятор будет генерировать код мусора. Обратите внимание, что вы все равно можете выделить больше выделенной памяти, которая обычно используется для выравнивания. – Jester

ответ

3

Как указано в этом Agner Fog document on calling conventions, Глава 5:

В Компилятор Gnu версии 3.x и более поздних версий для 32-разрядных Linux и Mac OS X делает указатель стека , выровненный по 16 в каждой команде вызова функции.

Так что, как объяснил @Anty в своем ответе, значение по умолчанию -mpreferred-stack-boundary 4, чтобы получить выравнивание 2 .

При проверке выравнивания стека полезно принять соглашение, которое явно показывает, насколько указатель стека не равен.
я придумал сам, что принимает форму @ т + п (например, @ 16 + 4) и означает, что указатель стека находится и адрес п байт меньше чем кратное м (например, 4 байта меньше, чем кратное 16, например 0x000c, 0x001c, 0x002c и т. Д.).
Это полезно, потому что стек растет вниз, поэтому, если указатель стека равен, скажем, @ 16 + 4, то после push ebp он находится на @ 16 + 8 и т. Д. (Учитывая модульный характер выравнивания).

До vuln называется стеком в @ 16 + 0 (как это предусмотрено компилятором).
Сразу после вызова, из-за неявного нажатия обратного адреса, это как @ 16 + 4.

Пролог является:

;Every annotation shows the alignment of the stack 
; AFTER the annotated instruction has executed 

push %ebp   ;@16+8 
mov %esp, %ebp 

Теперь компилятор должен выделить 0x100 байт, но он хочет, чтобы они были распределены на предпочтительном выравнивании, просто делает sub $0x108,%esp приведет указатель стека на @ 16 + 8 + 0x100 = @ 16 + 0x108 === @ 16 + 8.
Чтобы перейти к следующей границе 16 байтов, потребуется еще восемь байтов, таким образом размер 0x108.

sub $0x108,%esp  ;@16+0, Aligned 

Далее необходимо выполнить вызов, при выполнении команды вызова необходимо снова выровнять стопку.
Пока это так, но после нажатия параметров его больше не будет.
Поскольку strcpy имеет два 32-битных параметров, восемь байтов в общей сложности, компилятор перемещает указатель стека еще восемь байт вниз (8 + 8 = 16) соблюдать выравнивание ограничения

sub $0x8,%esp    ;@16+8 

pushl 0x8(%ebp)    ;@16+12 
lea -0x108(%ebp),%eax 
push %eax     ;@16+0, Aligned 
call 8048300 <[email protected]> 

Теперь, C calling convention мандаты что он является вызывающим, который очищает стек.
Два параметра были сдвинуты, поэтому требуется add $0x8,%esp.
Плюс другой add $0x8,%esp, чтобы сбалансировать sub $0x8,%esp выше.
Эти две комбинации объединены в одну инструкцию.

add $0x10,%esp    ;@16+0, Aligned 

Обратите внимание, что это не имеет никакого отношения к возвращаемому значению и типу функции.
Это аргументы толкнули, что считать.

Наконец пролог

leave       ;@16+8 
ret       ;@16+4 
+0

Спасибо @ Маргарет Блум, я подозревал, что он должен был с выравниванием стека, но не полностью понял, пока вы не сломали его шаг за шагом. Отлично! –

3

Что вы видите, это эффект выравнивания стека, в частности вариант -mpreferred-stack-boundary. В вашем случае

-mpreferred-stack-boundary=4 

что означает выравнивать к 2^4 = 16

поэтому код компилируется

vuln(char*): 
     push ebp 
     mov  ebp, esp 
     sub  esp, 264 
     sub  esp, 8 
     push DWORD PTR [ebp+8] 
     lea  eax, [ebp-264] 
     push eax 
     call strcpy 
     add  esp, 16 
     nop 
     leave 
     ret 

Если вы измените его на

-mpreferred-stack-boundary=2 

будет выравнивать до 4 байтов (2^2) и код, который вы получите, это

vuln(char*): 
     push ebp 
     mov  ebp, esp 
     sub  esp, 256 
     push DWORD PTR [ebp+8] 
     lea  eax, [ebp-256] 
     push eax 
     call strcpy 
     add  esp, 8 
     nop 
     leave 
     ret 

Как вы можете видеть в первом случае это sub esp, 8 и add esp, 16 (потреблять дополнительные 8 байт), тогда как во втором случае просто add esp, 8

Смежные вопросы