2012-03-12 47 views
0

如果能够解释我在下面的例子中使用printf编译nasm和gcc,我将不胜感激。 为什么仅在屏幕上打印“sud”?我也不明白为什么当我用“push'sudo'”交换“push'sud'”时,屏幕上印着“sudobor”? 有人可以解释为什么我需要推esp?它是否为null,需要在printf中的字符串末尾? 谢谢提前。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(); 

}

回答

6

首先,感谢吹我的脑海里。当我第一次看到你的代码时,我不相信它会起作用。然后我尝试了它并再现了你的结果。现在它对我来说很合理,尽管是扭曲的。 :-)我会尽力解释它。

首先,让我们看看更理智的方式来实现这一点。调用的printf前

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

然后在堆栈上推该字符串的地址:定义该ASM文件中的数据部分的字符串

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 ... 

所以,当打印被调用时,它使用第一参数作为字符串指针并开始打印字符,直到它看到NULL(0x00)。

这就是为什么这个例子只打印“123”(在你原来的“sud”)。

所以我们推“1234”而不是“123”。这意味着我们正在推送值0x34333231。当调用的printf堆栈现在看起来像:

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

现在有在栈上的2串之间没有空差距,这个例子将打印“1234567”(或在你原来的“sudobor”)。

含义:尝试推送“5678”而不是“567”。你可能会得到一个分段错误,因为printf会一直读取要打印的字符,直到它尝试读取没有权限读取的内存为止。另外,请尝试推送长度超过4个字符的字符串(例如“push'12345'”)。汇编器不会让你,因为它不能将它转换为32位数字。

+0

你需要null终止字符串吗? – Jeff 2012-03-13 15:15:21

+0

@Jeff:我想我必须明确地添加NULL。但是,上面的代码工作。也许在正确的地方有另一个NULL,纯粹是巧合。 – 2012-03-13 16:49:07

+0

作为一个测试,我会在第一个之后立即声明另一个字符串,看看它是否仍然按预期工作。如果需要或不需要,我会包含空终止符,并且永远不会知道。 – Jeff 2012-03-13 18:13:13