程序集通过堆栈传递变量

时间:2017-04-23 06:54:30

标签: ubuntu assembly nasm

所以,在考虑了一些装配以获得乐趣之后,我现在停留在调用程序。

...
_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

它打印出程序的所有内容然后seg故障,而如果我用打印功能的内容替换“call print”,它工作正常。

2 个答案:

答案 0 :(得分:2)

以下代码是你应该写的方式:

_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

问题是堆栈没有指向应该的位置。您必须记住堆栈的后进先出性质,并且还要考虑callret指令会影响堆栈指针。当你call一个函数时,返回地址被压入堆栈,所以当你在pop内执行print时,你实际上是从堆栈中弹出返回值,这不仅会给你错误的价值,而且还会使你ret以后的能力变得混乱。

检索传递给堆栈上的函数的参数的正确方法是通过堆栈指针(ESP)的偏移量。第一个参数将在ESP + 4处找到(在call推入堆栈的4字节返回地址之后)。有关其他信息,您可以查找C代码常用的STDCALL和CDECL调用约定。

答案 1 :(得分:1)

第一个POP弹出返回地址,第二个POP弹出地址msg。如果你没有弄乱int 80h调用,当函数试图返回时,你至少会遇到一个分段错误。

相关值可以在返回地址后面找到,这里是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