首先,感谢吹我的脑海里。当我第一次看到你的代码时,我不相信它会起作用。然后我尝试了它并再现了你的结果。现在它对我来说很合理,尽管是扭曲的。 :-)我会尽力解释它。
首先,让我们看看更理智的方式来实现这一点。调用的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位数字。
你需要null终止字符串吗? – Jeff 2012-03-13 15:15:21
@Jeff:我想我必须明确地添加NULL。但是,上面的代码工作。也许在正确的地方有另一个NULL,纯粹是巧合。 – 2012-03-13 16:49:07
作为一个测试,我会在第一个之后立即声明另一个字符串,看看它是否仍然按预期工作。如果需要或不需要,我会包含空终止符,并且永远不会知道。 – Jeff 2012-03-13 18:13:13