如果可以解释一下我在下面的例子中使用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
Run Code Online (Sandbox Code Playgroud)
这是c文件:
#include <stdio.h>
#include <stdlib.h>
extern void start();
int main(void) {
start();
Run Code Online (Sandbox Code Playgroud)
}
首先,谢谢你的想法.当我第一次看到你的代码时,我不相信它会起作用.然后我尝试了并重现了你的结果.现在它对我来说非常有意义,尽管是扭曲的.:-)我会试着解释一下.
首先,让我们看看实现这一目标的更合理的方法.在ASM文件的数据部分中定义一个字符串:
section .data
string: db "Hey, is this thing on?", 0
Run Code Online (Sandbox Code Playgroud)
然后在调用printf之前将该字符串的地址压入堆栈:
push string
call printf
Run Code Online (Sandbox Code Playgroud)
因此,printf的第一个参数(在调用之前在堆栈上推送的最后一个参数)是指向格式字符串的指针.你的代码所做的是将字符串推入堆栈,然后是堆栈指针,然后指向字符串.
接下来,我将替换您的字符串,以便在反汇编中更容易跟踪:
push '567'
push '123'
push esp
call printf
Run Code Online (Sandbox Code Playgroud)
用nasm组装,然后用objdump反汇编:
nasm string.s -f elf32 -o string.o
objdump -d -Mintel string.o
Run Code Online (Sandbox Code Playgroud)
推送时,例如'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
Run Code Online (Sandbox Code Playgroud)
推入堆栈会减少堆栈指针.假设初始堆栈指针为0x70(为简单起见而设计),这是调用printf之前的堆栈状态:
64: 68: 6c: 70:
68 00 00 00 31 32 33 00 35 36 37 00 ...
Run Code Online (Sandbox Code Playgroud)
因此,当调用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 ...
Run Code Online (Sandbox Code Playgroud)
现在堆栈上的这两个字符串之间没有空隙,这个例子将打印"1234567"(或原始版本中的"sudobor").
含义:尝试按"5678"而不是"567".您可能会遇到分段错误,因为printf将继续读取要打印的字符,直到它尝试读取它没有读取权限的内存.另外,尝试推送长度超过4个字符的字符串(例如,"push'12345'").汇编程序不会让你,因为它无法将其转换为32位数字.