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

时间:2011-08-24 16:55:23

标签: 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;

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

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

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

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

2 个答案:

答案 0 :(得分:3)

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

main()执行之前,您可以在堆栈上找到argcargv

修改

来自用户的评论有点挑战我在 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

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

nasm -f macho -o hello.o hello.nasm
ld -o hello -e mystart hello.o 

正如您可以通过源代码所知,应用程序的开始由mystart定义,并且它不接受任何参数。

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

gdb ./hello

加载 gdb 后,为教育目的设置此应用程序的cmd行参数非常重要,即使它没有写入接受任何参数。

set args deadbeef

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

break mystart

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

x/20xw $esp

输出:

(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

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

我们看到,$esp实际上指向0xbffff8cc,但是检查此内存地址存储的内容会显示另一个地址:0x00002000。它指向什么???

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

不是一个震惊,对吧?!那么让我们看一下表中其他一些地址指向的内容:

(gdb) x/1sw 0xbffff96c
0xbffff96c:  "/Developer/workspace/asm/hello"

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

(gdb) x/1sw 0xbffff98b
0xbffff98b:  "deadbeef"

累积奖金!我们在执行应用程序时传递的cmd行参数也存储在堆栈中。正如我之前所说的,在应用程序执行之前存储在堆栈中的垃圾中,您还可以找到用于执行应用程序的cmd行参数,即使应用程序的main()函数为{{1并且不接受任何参数。

答案 1 :(得分:1)

不保证分配给程序的专用内存空间(即0x0000)。访问未分配的内存通常是未定义的行为,这可以解释您获得的随机数据。