【问题标题】:Does gets() ignore '\0'?gets() 是否忽略'\0'?
【发布时间】:2017-05-08 07:08:07
【问题描述】:

我正在使用此源代码了解缓冲区溢出:

#include <stdio.h>
int main()
{
    char buf[16];
    gets(buf);
    printf("buf @ %8p\n", (void*)&buf);
    return 0;
}

我尝试将 Null 字符 ('\0') 写入 buf 变量。

首先,在 gdb 中,我在第 6 行,在 gets() 函数之后设置断点并使用 r &lt;&lt;&lt; $(python -c 'print "\0"*11 + "AAAA"') 运行它

当我探索堆栈时,我意识到它只会将“AAAA”写入buf。会发生什么?

(gdb) x/16xw &buf
0xffffcf80: 0x41414141  0xffffd000  0xffffd04c  0x080484a1
0xffffcf90: 0xf7fb43dc  0xffffcfb0  0x00000000  0xf7e1a637
0xffffcfa0: 0xf7fb4000  0xf7fb4000  0x00000000  0xf7e1a637
0xffffcfb0: 0x00000001  0xffffd044  0xffffd04c  0x00000000

但是,当我使用r &lt;&lt;&lt; $(python -c 'print "\1"*11 + "AAAA"') 运行程序时,buf 将是:

(gdb) x/16xw &buf
0xffffcf80: 0x01010101  0x01010101  0x41010101  0x00414141
0xffffcf90: 0xf7fb43dc  0xffffcfb0  0x00000000  0xf7e1a637
0xffffcfa0: 0xf7fb4000  0xf7fb4000  0x00000000  0xf7e1a637
0xffffcfb0: 0x00000001  0xffffd044  0xffffd04c  0x00000000

所以gets() 函数不会接收到Null 字符或者stdin 会忽略它?

P/S:我在 gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609 上使用gcc -m32 -fno-stack-protector -g stack.c -o stack 构建它。


更新:经过一些建议,我试试这个:

#include <stdio.h>
int main()
{
    char buf[16];
    gets(buf);
    printf("buf @ %8p\n", (void*)&buf);
    for (int i = 0; i < 16; ++i) // this is for loop all the buf
    {
        printf("%02x ", buf[i]);
    }
    return 0;
}

它适用于 '\0'

$ gcc -g j_stack.c -o j_stack
$ python -c 'print "AAAA" + "\0"*6 + "AAAA"'| ./j_stack 
buf @ 0xffffcfbc
41 41 41 41 00 00 00 00 00 00 41 41 41 41 00 ffffffff

但是如何在gdb 程序中提供包含'\0'buf 的输入

【问题讨论】:

  • 小提示:如果您将 buf 初始化为所有 0xFF 或类似的东西,阅读此输出会更容易
  • getsfgets 函数不特别对待 '\0'。他们可以很好地读取空字符。我使用您的程序读取了字符串"ab\0c\n",它按预期工作。
  • 我认为这是由于您使用“这里的字符串”来提供输入(&lt;&lt;&lt;)。这是将输入传递给程序的一种非常复杂的方式,你不觉得吗?将 gdb 排除在外,只使用 shell 中的标准管道;然后你应该看到它工作。让我们知道这是否属实并因此值得回答。
  • 抱歉...我的记忆是用\0 代替\n。答案已删除。
  • 运行:od -c &lt;&lt;&lt; $(python -c 'print "\0"*11 + "AAAA"');我得到的输出是四个 A 和一个换行符。经诊断,问题出在 Bash heredoc 处理中,而不是您的程序或 gets()。当然,除了测试溢出,你应该知道gets() is too dangerous to be used — ever!。 (你仍然可以使用fgets();你可以简单地写fgets(buf, 4096, stdin);,就像buf一样大。)

标签: c gcc buffer-overflow


【解决方案1】:

不,它没有。

此行为与gets() 或Python 字符串无关;这是由于您使用 subshel​​l 和 Bash“herestring”语法(performs some manipulations on whatever you give it,显然包括删除空字节)向程序提供输入的方式:

# python -c 'print "\0"*11 + "AAAA"' | wc -c
16
# python -c 'print "\0"*11 + "AAAA"' | hexdump
0000000 0000 0000 0000 0000 0000 4100 4141 0a41
0000010

# cat <<< $(python -c 'print "\0"*11 + "AAAA"') | wc -c
5
# hexdump <<< $(python -c 'print "\0"*11 + "AAAA"')
0000000 4141 4141 000a
0000005

# echo $(python -c 'print "\0"*11 + "AAAA"') | wc -c
5

如果您使用简单的管道运行程序,您应该会看到预期的结果:

python -c 'print "\0"*11 + "AAAA"' | ./myProgram

【讨论】:

  • 谢谢。那么如何在gdb 中为程序提供输入呢?
  • 谢谢。我想我每次运行程序时都应该写一个输入文件,因为我不能在gdb 中使用这里的字符串。 :(
【解决方案2】:

不,gets 不会忽略 '\0'

我将您的程序更改为包含

for(i = 0; i < 16; i++) printf("%02x", buf[i]);
printf("\n");

在致电gets 之后。我在输入上运行程序

abc\n

看到了

61626300000000000000000000000000

正如我所料。然后我在输入上运行程序

ab\0c\n

看到了

61620063000000000000000000000000

这也是我的预期。


附:我不确定你为什么看到你所做的行为,但我承认我不确定你在用&lt;&lt;&lt; 和那些 python 片段做什么。我,我用过

echo abc | a.out

echo 616200630a | unhex | a.out

unhex 是我在 bin 目录中的一个小程序,它的作用显而易见。

【讨论】:

  • 如果您提到您使用的编译器和操作系统会更有帮助。问题中提到的行为可能特定于一种设置。
  • 我在MacOS下使用clang。但我不相信这种行为是特定于系统的。 getsfgets 没有记录为特别对待 '\0',我从未遇到过这样的实现。
  • 我不认为它应该是特定于系统的,但我认为问题中没有任何明显的东西会使他们的观察无效。
猜你喜欢
  • 1970-01-01
  • 2016-01-24
  • 2013-01-30
  • 2023-04-07
  • 1970-01-01
  • 2015-04-19
  • 2020-11-16
  • 1970-01-01
  • 2023-01-30
相关资源
最近更新 更多