Printf在Nasm的行为

时间:2012-03-12 14:25:14

标签: printf nasm

如果能解释一下我在下面的例子中使用printf,用nasm和gcc编译会发生什么,我将不胜感激。 为什么“sud”只打印在屏幕上?我也不明白为什么当我用“推'sudo''交换”push'dud'“时,屏幕上会印上”sudobor“? 可以有人,也解释为什么我需要推esp?它是null,需要在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 个答案:

答案 0 :(得分:7)

首先,谢谢你的想法。当我第一次看到你的代码时,我不相信它会起作用。然后我尝试了并重现了你的结果。现在它对我来说非常有意义,尽管是扭曲的。 :-)我会试着解释一下。

首先,让我们看看实现这一目标的更合理的方法。在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 ...

现在堆叠中的这两个字符串之间没有空隙,这个例子将打印“1234567”(或原始版本中的“sudobor”)。

含义:尝试按“5678”而不是“567”。您可能会遇到分段错误,因为printf将继续读取要打印的字符,直到它尝试读取它没有读取权限的内存。另外,尝试按下长度超过4个字符的字符串(例如,“push'12345'”)。汇编程序不会让你,因为它无法将其转换为32位数字。