我正在学习高级汇编语言,并且正在玩堆栈以更好地理解所有内容。
我注意到在下面的程序中,我可以弹出堆栈的内容,而不会在程序崩溃之前将任何东西压到它上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位。
答案 0 :(得分:3)
在执行main()
之前,操作系统需要完成一系列其他操作,以便在对应用程序执行控制执行之前正确设置环境。因此,此时堆栈上的大部分内容都是以前操作中留下的垃圾。
在main()
执行之前,您可以在堆栈上找到argc
和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
我在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)。访问未分配的内存通常是未定义的行为,这可以解释您获得的随机数据。