Printf в поведении Nasm - PullRequest
       20

Printf в поведении Nasm

0 голосов
/ 12 марта 2012

Буду благодарен, если сможете объяснить мне, что происходит в следующем примере, используя printf, компилируя с nasm и gcc.Почему «sud» печатается только на экране?Я также не понимаю, почему «судобор» печатается на экране, когда я заменяю «push 'sud" на «push' sudo '»?Может кто-то, также объяснить, почему мне нужно нажать esp?Это нуль, который должен быть в конце строки в printf?Заранее спасибо.

Это файл string.s:

section .data

section .text
  global start
  extern printf

start:
     push    ebp           
     mov     ebp, esp  
     push 'bor'
     push 'sud'
     push esp
     call printf
     mov     esp, ebp
     pop     dword ebp

  ret

это файл c:

#include <stdio.h>
#include <stdlib.h>
extern void start();
int main(void) {
  start();

}

1 Ответ

7 голосов
/ 12 марта 2012

Прежде всего, спасибо, что взорвал мой разум.Когда я впервые посмотрел на ваш код, я не верил, что он вообще сработает.Тогда я попробовал и воспроизвел ваши результаты.Теперь это имеет смысл для меня, хотя и в искаженном виде.:-) Я попытаюсь объяснить это.

Во-первых, давайте посмотрим на более разумный способ добиться этого.Определите строку в части данных файла ASM:

section .data
string: db "Hey, is this thing on?", 0

Затем поместите адрес этой строки в стек перед вызовом printf:

push string
call printf

Итак, этот первый параметр дляprintf (последний параметр, помещаемый в стек перед вызовом) - указатель на строку формата.То, что сделал ваш код, это поместил строку в стек, а затем указатель стека, который затем указал на строку.

Далее я собираюсь заменить ваши строки, чтобы их было легче отслеживать при разборке:

push '567'
push '123'
push esp
call printf

Соберите с помощью nasm, а затем разберите с помощью objdump:

nasm string.s -f elf32 -o string.o
objdump -d -Mintel string.o

Когда вы нажимаете, например, «123», это преобразуется в 32-разрядную шестнадцатеричную цифру:0x333231 в этом случае.Обратите внимание, что полные 32 бита равны 0x00333231.

3:  68 35 36 37 00          push   0x373635
8:  68 31 32 33 00          push   0x333231
d:  54                      push   esp

Вставка в стек уменьшает указатель стека.Предполагая начальный указатель стека 0x70 (придуманный для простоты), это состояние стека перед вызовом printf:

64:         68:         6c:         70:
68 00 00 00 31 32 33 00 35 36 37 00 ...

Итак, когда вызывается print, он использует первый параметр в качестве указателя строки иначинает печатать символы до тех пор, пока не увидит NULL (0x00).

Вот почему в этом примере выводится только «123» («sud» в вашем оригинале).

Так что давайте нажимаем «1234» вместо"123".Это означает, что мы выдвигаем значение 0x34333231.При вызове printf стек теперь выглядит следующим образом:

64:         68:         6c:         70:
68 00 00 00 31 32 33 34 35 36 37 00 ...

Теперь между этими двумя строками в стеке нет пропуска NULL, и в этом примере будет напечатано «1234567» (или «sudobor» в оригинале).1030 *

Последствия: попробуйте нажать «5678» вместо «567».Вы, вероятно, получите ошибку сегментации, потому что printf будет просто читать символы для печати, пока не попытается прочитать память, у которой нет разрешения на чтение.Также попробуйте нажать строку длиной более 4 символов (например, «push '12345'»).Ассемблер не позволит вам, потому что он не может преобразовать это в 32-битное число.

...