程序启动前堆栈上有什么?

Jas*_*ill 6 assembly stack

我正在学习高级汇编语言,并正在玩堆栈以更好地理解一切.

我注意到在下面的程序中,我可以弹出堆栈的内容,而不会在程序崩溃之前将任何东西压到它上37次.

ike1: uns32 := 1;

begin test1;

while (ike1 < 38) do
pop(eax);
stdout.put(ike1, nl);
stdout.put("ESP: ", esp, nl);
stdout.put("EAX:", eax, nl, nl);
add(1, ike1);
endwhile;
end test1;
Run Code Online (Sandbox Code Playgroud)

每次将堆栈弹出到EAX中,并且EAX的输出每次都显示随机数据.

我首先不明白这是怎么可能的,因为我认为每个程序都被分成了自己的私有内存空间?

无论如何,我正在弹出堆栈的数据......这会是什么,它会影响任何其他正在运行的程序吗?

我的操作系统是Windows 7 64位.

kar*_*lip 4

在执行main()一系列其他操作之前,操作系统需要完成正确设置环境,然后再将执行控制交给您的应用程序。因此,此时堆栈上的大部分内容都是先前操作留下的垃圾。

在执行之前,您还可以在堆栈中main()找到argcand 。argv

编辑:

来自用户的评论有点挑战我在gdb中调试汇编应用程序并检查堆栈以备份我在原始答案中所做的声明的过程。

因此,请考虑以下用nasm编写的汇编代码:

section .data

  mymsg db "hello, world", 0xa  ; string with a carriage-return
  mylen equ $-mymsg             ; string length in bytes

section .text
global mystart                ; make the main function externally visible

mystart:
    ; prepare the arguments for syscall write()
    push dword mylen          ; msg length                           
    push dword mymsg          ; msg to write
    push dword 1              ; file descriptor number

    ; call write()
    mov eax, 0x4              ; 0x4 identifies syscall write()
    sub esp, 4                ; OS X (and BSD) syscalls needs "extra space" on stack
    int 0x80                  ; trigger the call

    ; clean up the stack
    add esp, 16               ; 3 args * 4 bytes/arg + 4 bytes extra space = 16 bytes

    ; prepare argument for syscall exit()
    push dword 0              ; exit status returned to the operating system

    ; call exit()
    mov eax, 0x1              ; 0x1 identifies syscall exit()
    sub esp, 4                ; OS X (and BSD) system calls needs "extra space" on stack
    int 0x80                  ; trigger the call
Run Code Online (Sandbox Code Playgroud)

我在 Mac OS X 上编译了这个:

nasm -f macho -o hello.o hello.nasm
ld -o hello -e mystart hello.o 
Run Code Online (Sandbox Code Playgroud)

正如您可能从源代码中看出的那样,应用程序的启动是由 定义的mystart,并且它不带任何参数。

现在,让我们通过在gdb中打开这个程序来让这个调查变得更令人兴奋:

gdb ./hello
Run Code Online (Sandbox Code Playgroud)

加载gdb后,出于教育目的,为此应用程序设置命令行参数非常重要,即使它并未编写为接受任何参数。

set args deadbeef
Run Code Online (Sandbox Code Playgroud)

此时应用程序仍未运行。我们需要在函数的开头设置一个断点,以便在我们的应用程序开始执行它自己的代码之前检查堆栈以查看发生了什么:

break mystart
Run Code Online (Sandbox Code Playgroud)

在gdb上执行命令run来启动应用程序并中断执行。现在我们可以使用以下命令检查堆栈:

x/20xw $esp
Run Code Online (Sandbox Code Playgroud)

输出:

(gdb) x/20xw $esp
0xbffff8cc: 0x00002000  0x00000000  0x00000002  0xbffff96c
0xbffff8dc: 0xbffff98b  0x00000000  0xbffff994  0xbffff9b0
0xbffff8ec: 0xbffff9c1  0xbffff9d1  0xbffffa0b  0xbffffa40
0xbffff8fc: 0xbffffa5b  0xbffffa86  0xbffffa97  0xbffffaad
0xbffff90c: 0xbffffad8  0xbffffafa  0xbffffb06  0xbffffb28
Run Code Online (Sandbox Code Playgroud)

是的,先生,这个命令打印堆栈的内容。它告诉gdb以十六进制格式显示从寄存器存储的地址开始的 20 个字$esp

让我们看看,$esp实际上指向0xbffff8cc,好吧,但是检查这个内存地址存储的内容会发现另一个地址:0x00002000。它指向什么???

(gdb) x/20sw 0x00002000
0x2000 <mymsg>:  "hello, world\n"
Run Code Online (Sandbox Code Playgroud)

不令人震惊,对吧?那么让我们看看该表的其他一些地址都指向什么:

(gdb) x/1sw 0xbffff96c
0xbffff96c:  "/Developer/workspace/asm/hello"
Run Code Online (Sandbox Code Playgroud)

哇。这实际上是存储在堆栈上的原始应用程序的名称和路径!太棒了,让我们继续表中下一个有趣的地址:

(gdb) x/1sw 0xbffff98b
0xbffff98b:  "deadbeef"
Run Code Online (Sandbox Code Playgroud)

大奖!我们在执行应用程序时传递的命令行参数也存储在堆栈中。正如我之前所说,在应用程序执行之前存储在堆栈中的垃圾中,您还可以找到用于执行应用程序的命令行参数,即使应用main()程序的函数带有void任何参数并且不带有任何参数。