如果能解释一下我在下面的例子中使用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();
}
答案 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位数字。