2012-03-12 3 views
0

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

Это 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 

это с файл:

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

}

ответ

6

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

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

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

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

push string 
call printf 

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

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

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

Assemble с NASM, а затем разобрать с objdump:

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

Когда ты push, например, '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 ... 

Так, при печати называется, он использует первый параметр как указатель строки и начинает печать до тех пор, пока не увидит 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 между этими 2 строками в стеке и в этом примере будет печатать «1234567» (или «sudobor» в оригинале).

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

+0

Нужно ли исковерять строку? – Jeff

+0

@Jeff: Я думал, что мне нужно будет явно добавить NULL. Однако приведенный выше код работал. Возможно, в правильном месте был другой НУЛЛ, по совпадению. –

+0

В качестве теста я объявлял бы другую строку сразу после первой и посмотрел бы, работает ли она так, как ожидалось. Я бы включил нулевой терминатор и никогда не знал, требуется ли это или нет. – Jeff