Ubuntu 通过堆栈传递变量的程序集

Ubuntu 通过堆栈传递变量的程序集,ubuntu,assembly,nasm,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, S

所以,在为了好玩而涉猎了一点组装之后,我现在被困在调用过程中

...
_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出现故障,而如果我用打印函数的内容替换“调用打印”,它工作正常

下面的代码是您应该使用的编写方式:

_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
问题是堆栈没有指向它应该指向的位置。你必须记住栈的最后一个先出的性质,并且还要考虑<代码>调用< /COD>和<代码> RET 指令影响堆栈指针。当
调用
函数时,返回地址会被推到堆栈上,因此当您在
打印
内部执行
弹出
操作时,实际上是从堆栈中弹出返回值,这不仅给您提供了错误的值,而且还破坏了您稍后执行
返回
urn的能力


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

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

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

这是因为指令pop ecx接收的是
eax SYS_EXIT
的地址,而不是ret指令,您是否知道调用也会将值推入堆栈?修复代码时,为什么不在
call print
之后清理堆栈?我在那里看到一条评论,
;和堆栈
,但我不知道这意味着什么,我怀疑询问者也不会。@CodyGray:下面的SYS\u出口也会“清理”堆栈。所以不管它是否被清理干净。我不知道为什么OP现在还没有清理堆栈。也许它是为了一个不在这里发布的目的而需要的。如果我弹出回信地址并存储它,然后弹出其余的,我能把回信地址推回去然后再返回吗?@Jackywathy:是的,但这不是好的风格。你为什么要这么做?