程序集通过堆栈传递变量
Assembly passing variables through the stack
所以,在为了好玩而涉足了一些汇编之后,我现在被困在调用过程上。
...
_start:
push dword len
push dword msg
call print
mov eax, SYS_EXIT
mov ebx, 0
int 80h
print: ; *char (message), int (len) -> push len, then message
mov eax, SYS_WRITE
mov ebx, STDOUT
pop ecx
pop edx
int 80h
ret
当我运行这块组装
nasm -f elf program.asm && ld -m elf_i386 -o program program.o && ./program
它打印出程序的所有内容然后出现段错误,而如果我用打印功能的内容替换 "call print",它工作正常。
下面的代码应该是这样写的:
_start:
push dword len
push dword msg
call print
add esp, 8 ; equivalent to 2 32-bit POP instructions
; (effectively "undoes" the above PUSH instructions
; to restore the stack to its original state)
mov eax, SYS_EXIT
mov ebx, 0
int 80h
print: ; *char (message), int (len) -> push len, then message
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, [esp+4] ; load "msg" from stack using an offset from ESP
mov edx, [esp+8] ; load "length" from stack using an offset from ESP
int 80h
ret
问题是堆栈没有指向它应该指向的位置。您必须记住堆栈的后进先出性质,还要考虑 call
和 ret
指令影响堆栈指针。当你 call
函数时,return 地址被压入堆栈,所以当你在 print
中执行 pop
时,你实际上是弹出 [=26] =] value off the stack,这不仅会给你错误的值,而且还会打乱你以后 ret
urn 的能力。
在堆栈上检索传递给函数的参数的正确方法是通过堆栈指针的偏移量 (ESP
)。第一个参数位于 ESP + 4
(在 call
压入堆栈的 4 字节 return 地址之后)。更多信息,您可以查看C代码常用的STDCALL和CDECL调用约定。
第一个 POP
弹出 return 地址,第二个 POP
弹出 msg
地址。如果你不搞乱 int 80h
调用,当函数尝试 return.
时,你至少会遇到一个分段错误
相关值可以在return地址后面找到,这里是esp+4和esp+8。您可以直接使用 ESP+xx
访问此地址。当你构建更复杂的过程时,你可能想避开 EBP
但暂时用 ESP
来做:
SYS_EXIT equ 1
SYS_WRITE equ 4
STDOUT equ 1
segment .data
msg db `Hello world!\n` ; Backspaces for C-like escape-sequences ("\n")
len equ $- msg
section .text
global _start
_start:
push dword len
push dword msg
call print
; and the stack?
mov eax, SYS_EXIT
mov ebx, 0
int 80h
print: ; *char (message), int (len) -> push len, then message
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, [esp+4]
mov edx, [esp+8]
int 80h
ret
所以,在为了好玩而涉足了一些汇编之后,我现在被困在调用过程上。
...
_start:
push dword len
push dword msg
call print
mov eax, SYS_EXIT
mov ebx, 0
int 80h
print: ; *char (message), int (len) -> push len, then message
mov eax, SYS_WRITE
mov ebx, STDOUT
pop ecx
pop edx
int 80h
ret
当我运行这块组装
nasm -f elf program.asm && ld -m elf_i386 -o program program.o && ./program
它打印出程序的所有内容然后出现段错误,而如果我用打印功能的内容替换 "call print",它工作正常。
下面的代码应该是这样写的:
_start:
push dword len
push dword msg
call print
add esp, 8 ; equivalent to 2 32-bit POP instructions
; (effectively "undoes" the above PUSH instructions
; to restore the stack to its original state)
mov eax, SYS_EXIT
mov ebx, 0
int 80h
print: ; *char (message), int (len) -> push len, then message
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, [esp+4] ; load "msg" from stack using an offset from ESP
mov edx, [esp+8] ; load "length" from stack using an offset from ESP
int 80h
ret
问题是堆栈没有指向它应该指向的位置。您必须记住堆栈的后进先出性质,还要考虑 call
和 ret
指令影响堆栈指针。当你 call
函数时,return 地址被压入堆栈,所以当你在 print
中执行 pop
时,你实际上是弹出 [=26] =] value off the stack,这不仅会给你错误的值,而且还会打乱你以后 ret
urn 的能力。
在堆栈上检索传递给函数的参数的正确方法是通过堆栈指针的偏移量 (ESP
)。第一个参数位于 ESP + 4
(在 call
压入堆栈的 4 字节 return 地址之后)。更多信息,您可以查看C代码常用的STDCALL和CDECL调用约定。
第一个 POP
弹出 return 地址,第二个 POP
弹出 msg
地址。如果你不搞乱 int 80h
调用,当函数尝试 return.
相关值可以在return地址后面找到,这里是esp+4和esp+8。您可以直接使用 ESP+xx
访问此地址。当你构建更复杂的过程时,你可能想避开 EBP
但暂时用 ESP
来做:
SYS_EXIT equ 1
SYS_WRITE equ 4
STDOUT equ 1
segment .data
msg db `Hello world!\n` ; Backspaces for C-like escape-sequences ("\n")
len equ $- msg
section .text
global _start
_start:
push dword len
push dword msg
call print
; and the stack?
mov eax, SYS_EXIT
mov ebx, 0
int 80h
print: ; *char (message), int (len) -> push len, then message
mov eax, SYS_WRITE
mov ebx, STDOUT
mov ecx, [esp+4]
mov edx, [esp+8]
int 80h
ret