栈底指针可以被用来判断传递的参数的值,还有函数的局部变量的值。
让我们继续用上一个例子来说明一下吧。
ChildEBP RetAddr
0012ff34 00401121 Simple!Foo2+0x64 [C:\Labfiles\Module04\Simple\Simple.cpp @ 82]
0012ff4c 004010da Simple!Foo1+0x31 [C:\Labfiles\Module04\Simple\Simple.cpp @ 65]
0012ff64 00401096 Simple!Foo+0x2d [C:\Labfiles\Module04\Simple\Simple.cpp @ 49]
0012ff80 00404739 Simple!main+0x36 [C:\Labfiles\Module04\Simple\Simple.cpp @ 33]
0012ffc0 7c817077 Simple!mainCRTStartup+0xe9 [crt0.c @ 206]
栈中的内容如下:
dd esp
0012ff14 cccccccc cccccccc cccccccc cccccccc
0012ff24 cccccccc cccccccc cccccccc cccccccc
0012ff34 0012ff4c 00401121 00000001 00000002
0012ff44 00000002 00000001 0012ff64 004010da
0012ff54 0012ff60 0012ff5c 00000002 00000001
0012ff64 0012ff80 00401096 00000001 00000002
0012ff74 00000002 00000001 cccccccc 0012ffc0
0012ff84 00404739 00000001 00420e90 00420de0dd
0012ff94 43010000 00000000 7ffde000 00000001
0012ffa4 00000006 0012ff94 80621a58 0012ffe0
0012ffb4 00408084 00417218 00000000 0012fff0
0012ffc4 7c817077 43010000 00000000 7ffde000
0012ffd4 8054c6ed 0012ffc8 8918f3e8 ffffffff
0012ffe4 7c839ad8 7c817080 00000000 00000000
0012fff4 00000000 00404650 00000000 78746341
00130004 00000020 00000001 000024b8 000000c4
利用EBP确定参数
==============
函数Foo2的栈底位置为0012ff34 ,而函数返回值的位置就在栈底的下方,即ebp+4. 在这里为00401121。
第一个参数在参数表的最左边,是最后一个压栈的参数,所以第一个参数的起始地址为ebp+8,根据前面我们的程序Simple.cpp,可以看出第一个参数的值为1, 第二个参数的值为2.
运行如下的命令来验证。可以看到ebp+8所指向的内存中,值为1。第二个参数的位置为ebp+12,在下面的内存dump中,可以看到该位置的值确实为2.
dd ebp+8
0012ff3c 00000001 00000002 00000002 00000001
0012ff4c 0012ff64 004010da 0012ff60 0012ff5c
0012ff5c 00000002 00000001 0012ff80 00401096
0012ff6c 00000001 00000002 00000002 00000001
0012ff7c cccccccc 0012ffc0 00404739 00000001
0012ff8c 00420e90 00420de0 43010000 00000000
0012ff9c 7ffde000 00000001 00000006 0012ff94
0012ffac 80621a58 0012ffe0 00408084 00417218
Windbg有个查看函数ebp,返回值和前三个参数的命令,很方便。
ChildEBP RetAddr Args to Child
0012ff34 00401121 00000001 00000002 00000002 Simple!Foo2+0x64 [C:\Labfiles\Module04\Simple\Simple.cpp @ 82]
0012ff4c 004010da 0012ff60 0012ff5c 00000002 Simple!Foo1+0x31 [C:\Labfiles\Module04\Simple\Simple.cpp @ 65]
0012ff64 00401096 00000001 00000002 00000002 Simple!Foo+0x2d [C:\Labfiles\Module04\Simple\Simple.cpp @ 49]
0012ff80 00404739 00000001 00420e90 00420de0 Simple!main+0x36 [C:\Labfiles\Module04\Simple\Simple.cpp @ 33]
0012ffc0 7c817077 43010000 00000000 7ffde000 Simple!mainCRTStartup+0xe9 [crt0.c @ 206]
WARNING: Stack unwind information not available. Following frames may be wrong.
0012fff0 00000000 00404650 00000000 78746341 kernel32!RegisterWaitForInputIdle+0x49
利用EBP确定局部变量
==============
局部变量的地址是按照与EBP的距离由近到远,从第一个往后分配地址的。
所以,第一个局部变量的位置是ebp-4,第二个是ebp-8.
由于有些局部变量在函数中非常的常用,所以有时局部变量不一定会被压入栈中,而在寄存器中。所以,确定局部变量有可能需要去查看汇编代码,来看变量时从哪里来的,如何移动的,以及如何改变的。
总结一下
项目 | 根据EBP的计算公式 |
当前函数栈底的位置 | EBP |
上一个函数栈底位置 | [EBP] |
当前函数的返回地址 | EBP+4 |
第一个局部变量 | EBP-4 |
第二个局部变量 | EBP-0x8 |
第三个局部变量 | EBP-0xc |
第一个参数 | EBP+8 |
第二个参数 | EBP+0xc |