在 scanf() 之后使用 printf() 时的异常行为
An unusual behaviour when using printf() after scanf()
我正在使用 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(真)。
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", 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);
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
的输出是轻微的副作用,但是足够长的输入会破坏 return 地址,并在从 main()
和巧妙构造的 returning 时导致分段错误输入可能允许攻击者执行任意代码。更改程序可能会改变副作用,因为局部变量的分配可能会有所不同,例如当您获取 &pass
的地址时。 None 这是可以预见的。
还要注意 printf("%x\n", &pass);
也有未定义的行为,因为 %x
需要一个 unsigned int
,而不是 bool *
,你应该写 printf("%p\n", (void *)&pass);
有一种简单的方法可以通过将长度字段传递给 scanf()
来防止这种情况:
scanf("%19s", password);
但是请注意,您还应该检查 return 值以检测文件过早结束,并且您还应该刷新输入行的其余部分。
这是修改后的版本:
#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;
}
我正在使用 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(真)。
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", 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);
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
的输出是轻微的副作用,但是足够长的输入会破坏 return 地址,并在从 main()
和巧妙构造的 returning 时导致分段错误输入可能允许攻击者执行任意代码。更改程序可能会改变副作用,因为局部变量的分配可能会有所不同,例如当您获取 &pass
的地址时。 None 这是可以预见的。
还要注意 printf("%x\n", &pass);
也有未定义的行为,因为 %x
需要一个 unsigned int
,而不是 bool *
,你应该写 printf("%p\n", (void *)&pass);
有一种简单的方法可以通过将长度字段传递给 scanf()
来防止这种情况:
scanf("%19s", password);
但是请注意,您还应该检查 return 值以检测文件过早结束,并且您还应该刷新输入行的其余部分。
这是修改后的版本:
#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;
}