微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在scanf之后使用printf时的异常行为

如何解决在scanf之后使用printf时的异常行为

我正在使用scanf()用户那里获取密码,并将其存储在大小为20个字节的password变量中。我对照correctPassword检查输入的密码,如果它们匹配,则布尔变量pass将更改为true

因此,当我输入长度超过20个字符的密码时,将发生缓冲区溢出,并且pass的值将变为非零(即true)。但是,当我使用printf()打印变量pass的地址时,即使我使用的密码长度超过20个字符,也不会发生缓冲区溢出。

这是导致溢出的代码

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>

int main (int argc,char *argv[]) {
    char password[20];
    char correctPassword[] = "random";
    bool pass = false;
    
    printf("enter your password: ");
    scanf("%s",password);

    if (strcmp(password,correctPassword) == 0) {
        // compare the two strings,strcmp() returns 0 if two strings values are the same.
        pass = true;
    }
    if (pass) {
        printf("Connecting you to the central system...\n");
    } else {
        printf("Password is wrong! entry denied\n");
    }
    
    printf("%d\n",pass);

    return 0;
}

在这种情况下,输入的密码长度为40个字符(a的ASCII值为97)时,pass的值将更改为97(true)。

enter your password: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Connecting you to the central system...
97

这是相同的代码,但在末尾多了一行以显示变量pass的地址:

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>

int main (int argc,char *argv[]) {
    char password[20];
    char correctPassword[] = "random";
    bool pass = false;
    printf("enter your password: ");
    scanf("%s",strcmp() returns 0 if two strings values are the same.
        pass = true;
    }
    if (pass) {
        printf("Connecting you to the central system...\n");
    } else {
        printf("Password is wrong! entry denied\n");
    }

    printf("%d\n",pass);
    printf("%x\n",&pass);

    return 0;
}

在这种情况下,输入的密码长40个字符,但是pass仍然是0(假)。

enter your password: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Password is wrong! entry denied
0
61fec4

所以,我的问题是怎么只添加

printf("%x\n",&pass);

导致没有缓冲区溢出?

解决方法

scanf("%s",password)是一个安全漏洞:长度超过19个字节的任何输入字都将导致scanf()超出数组末尾写入,从而触发未定义的行为。

未定义的行为可能会带来无法预料的副作用,无论是否可见。 97的输出是一个轻微的副作用,但是足够长的输入将破坏返回地址并在从main()返回时导致分段错误,并且巧妙构造的输入可能使攻击者可以执行任意代码。更改程序可能会改变副作用,因为局部变量的分配可能会有所不同,例如当您使用&pass的地址时。这些都不是可预见的。

还请注意,printf("%x\n",&pass);也具有未定义的行为,因为%x期望使用unsigned int而不是bool *,因此您应该写printf("%p\n",(void *)&pass);

有一种简单的方法可以通过将长度字段传递给scanf()来防止这种情况:

    scanf("%19s",password);

但是请注意,您还应该检查返回值以检测文件的过早结束,并且还应该刷新输入行的其余部分。

这是修改后的版本:

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main (int argc,char *argv[]) {
    char password[20];
    char correctPassword[] = "random";
    bool pass = false;
    int c;

    printf("enter your password: ");
    if (scanf("%19s",password) != 1) {
        printf("invalid input\n");
        return 1;
    }
    /* read and discard the rest of the line */
    while ((c = getchar()) != EOF && c != '\n')
        continue;

    if (strcmp(password,correctPassword) == 0) {
        // compare the two strings,strcmp() returns 0 if two strings values are the same.
        pass = true;
    }
    if (pass) {
        printf("Connecting you to the central system...\n");
    } else {
        printf("Password is wrong! entry denied\n");
    }

    printf("%d\n",pass);

    return 0;
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。