我正在学习汇编套接字编程,有人告诉我要写这段代码:
section .text
global _start
_start:
push byte 6
push byte 1
push byte 2
mov ecx, esp
mov ebx, 1
mov eax, 102
int 80h
;confusing part
mov edi, eax ;why is this (and pushing it) necessary?
push dword 0x00000000
push word 0x2923
push word 2
mov ecx, esp
push byte 16
push ecx
push edi ;why not push eax directly? Doesn't sys_socketcall put the file descriptor back into eax?
mov ecx, esp
mov ebx, 2
mov eax, 102
int 80h
我在我不理解的部分添加了注解。可以在这里找到不相关部分的完整代码。
为什么他们要把寄存器的值移到另一个寄存器呢?难道他们不能把原来的值推进去吗?
1条答案
按热度按时间ctzwtxfj1#
mov edi,eax
是保持fd
在第二个int 80h
* 之后 * 可用的一种方法。**如果您决定从现在开始将套接字fd
保留在EDI中,作为它的永久宿主,那么它对push edi
**是有意义的。但是,push eax
是可以工作的,因为此时在EAX中仍有它的副本。在第一次介绍这一点的“第30课”中,这似乎是毫无意义的;但是如果你看后面的“课程”,他们是在这段代码的基础上构建的,使用它没有改变,并在后面添加了更多的系统调用(比如
listen
,它也使用push edi
)。在后面的代码中,你仍然需要从socket()
推送fd
,而不是从bind()
推送返回值。这段代码有点笨拙,比如不移除一直压入的结构体,所以如果你在循环中这样做,最终会耗尽堆栈空间。通常你会在系统调用后使用
add esp, whatever
来撤销压入。或者使用
mov
存储到堆栈,用新的args结构替换现有的args结构。如果文件描述符在同一个位置,你可以把它留在那里。(或者在当前代码中,push dword [esp+24]
或下一个代码块中的东西,但是如果你要再次存储它,把fd放在寄存器中会更有意义。)写
push byte 1
也很奇怪,它仍然将ESP调整4并存储0x00000001
,而push word 2
只将ESP调整2并存储0x0002
。(How many bytes does the push instruction push onto the stack when I don't specify the operand size?)(x86仅支持将字和双字操作数大小的操作数用于push,但支持imm 8和全角立即数。如果要强制使用1字节立即数,语法为
push strict byte 1
,但NASM已经这样做了,因为NASM中默认启用优化至少已有十年。)顺便说一句,从Linux 4.3开始,对于套接字API中的每个系统调用,i386都有单独的系统调用号,如
bind
和listen
,因此您不需要将结构体推到堆栈上并调用通用的socketcall
入口点。The man page表示x86-64和ARM根本没有socketcall
系统调用,始终只为每个系统调用提供单独的syscall入口点。