让我们假设这个功能框架:



如何计算和检查第3行减去的堆栈空间是否为58h ?: sub esp, 58h

是代码,编译器也是Dev C ++:

#include <stdio.h>

char *the_good_one = "gb_master";

void cookie()
{
    printf("Get a cookie!\n");
}

char check_password()
{
    char password[64];

    printf("Enter password: ");
    scanf("%s", password);

    return (!strcmp(password, the_good_one));
}

int main(void)
{
    if(check_password())
    {
        printf("GOOOOOOOOOOD!\n");
        cookie();
    }
    else
    {
        printf("Wrong password\n");
    }

    return 0;
}


check_password()函数反汇编已添加:



评论

我添加了源代码

您能否给出完全反汇编的“ check_password”代码?由于编译器可能会插入一些金丝雀来进行堆栈保护。

@tathanhdinh添加了

谢谢弗拉德。很抱歉再次打扰您,能否请您提供使用过的编译器选项?

坦白说,我觉得这很奇怪。大小为72的堆栈足以用于“ check_password”,因为我们需要64个字节的密码和8个字节的printf,scanf和strcmp作为参数。我找不到任何数据对齐的证据。当我尝试使用gcc 4.9.0,编译器会生成堆栈大小为84的“ check_password” !!!!

#1 楼

较早的编译器通过将它们压入栈并在函数调用后从栈中弹出来为栈中的函数参数留出空间。较新的编译器对此进行了优化。例如,执行一个函数时,堆栈会像这样更改:

 start        calling       after         before scanf   after scanf 
              printf        printf                                   
+-----------+ +-----------+ +-----------+ +-----------+ +-----------+
|return addr| |return addr| |return addr| |return addr| |return addr|
+-----------+ +-----------+ +-----------+ +-----------+ +-----------+
|           | |           | |           | |           | |           |
| local     | | local     | | local     | | local     | | local     |
| variables | | variables | | variables | | variables | | variables |
|           | |           | |           | |           | |           |
|           | |           | |           | |           | |           |
|           | |           | |           | |           | |           |
|           | |           | |           | |           | |           |
|           | |           | |           | |           | |           |
|           | |           | |           | |           | |           |
+-----------+ +-----------+ +-----------+ +-----------+ +-----------+
              | "Enter.." |               | password  |              
              +-----------+               +-----------+              
                                          | "%s"      |              
                                          +-----------+              


您会看到sp(堆栈的底部)如何随每个函数调用而变化。< br较新版本的gcc改变了这一点;它们从一开始就在堆栈上留出足够的空间(用于局部变量和所有可能的函数参数),只需将参数移至相对于堆栈指针的地址即可:

 start        calling       after         before scanf   after scanf 
              printf        printf                                   
+-----------+ +-----------+ +-----------+ +-----------+ +-----------+
|return addr| |return addr| |return addr| |return addr| |return addr|
+-----------+ +-----------+ +-----------+ +-----------+ +-----------+
|           | |           | |           | |           | |           |
| local     | | local     | | local     | | local     | | local     |
| variables | | variables | | variables | | variables | | variables |
|           | |           | |           | |           | |           |
|           | |           | |           | |           | |           |
|           | |           | |           | |           | |           |
|           | |           | |           | |           | |           |
|           | |           | |           | |           | |           |
|           | |           | |           | |           | |           |
+-----------+ +-----------+ +-----------+ +-----------+ +-----------+
|           | |           | |           | | password  | |           |
+-----------+ +-----------+ +-----------+ +-----------+ +-----------+
|           | | "Enter.." | |           | | "%s"      | |           |
+-----------+ +-----------+ +-----------+ +-----------+ +-----------+


请注意,从一开始,堆栈是如何具有第4步所需的大小的(在scanf之前),以及如何将“ Enter ..”字符串直接移动到堆栈指针所在的位置(因此它是堆栈上的第一个参数)堆栈),而不是直接放在局部变量下面的空间。

因此要从源代码计算堆栈大小,您需要知道


多少个字节您的返回帧需要
为堆栈金丝雀保留多少字节,如果有的话
局部变量需要多少字节
为函数参数保留多少字节?这可能包括对不遵循标准约定的结构或双精度结构的特殊对待。
由于在寄存器中传递了函数参数,因此要从函数参数中减去多少个字节,尤其适用于64位代码。

每个编译器版本也可能会更改。看来您的编译器为堆栈帧/金丝雀保留了8个字节;密码数组需要64个(0x40)字节,而函数参数需要另外16个字节,其中8个就足够了(也许是出于对齐的原因)?

我不会依赖任何公式作为数字所需的字节数;相反,请检查我使用的特定编译器,并准备在使用其他编译器时更改此数字。

评论


我想你是对的。实际上,我仍然不明白为什么函数需要88个字节的堆栈,而64(局部变量)+ 8(参数)= 72应该足够。

– Ta Thanh Dinh
2015年6月7日在22:38