【问题标题】:Buffer overflow example with gcc使用 gcc 的缓冲区溢出示例
【发布时间】:2017-04-08 23:58:17
【问题描述】:

我试图在 C 中的 strcmp 函数中演示缓冲区溢出。 我有strcpyV.c 文件:

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

int main(int argc, char *argv[])
{

    char a[8];
    char b[8];

    // function causes buffer overflow
    strcpy(b, "01234567");

    // buffer overflow again
    strcpy(a, "89abcdef");

    printf("\nb = %s\n", b);

    return 0;
}

我使用 gcc 编译器编译这个程序。

gcc -o strcpyV strcpyV.c

当我在 raspberry py B+ (Raspbian wheezy) 中执行此操作并运行时:

./strcpyV

我得到了预期的结果:

b = 0123456789abcdef

但是当我在 Ubuntu 16.04 中执行这整个过程时,结果是:

b = 01234567

有没有办法在没有这种内存保护的情况下编译代码?

【问题讨论】:

  • 变量的布局由编译器决定。无论如何,这是未定义的行为。
  • 但是当我在 Raspberry 上执行此操作时……就像我写的那样
  • 未定义行为包括预期行为。
  • 没有未定义行为的预期结果,抱歉。
  • @MichaelWalz:不包括! :)

标签: c gcc memory


【解决方案1】:

您可以将数组打包在一个结构中。然后将适用 struct 打包规则。标准没有定义结构体的打包规则,但是如果我们使用gcc,在这种情况下char数组将是连续的(注意b现在放在a之前):

#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
    struct ab {
        char b[8];   
        char a[8];
    } s;

    // function causes buffer overflow
    strcpy(s.b, "01234567");

    // I removed this buffer overflow by making the string fit.
    // A second overflow doesn't add anything - quite the opposite.
    // The first overflow is sufficient to get the
    // behaviour you want.
    strcpy(s.a, "89abcde");

    printf("\nb = %s\n", s.b);

    return 0;
}

【讨论】:

  • 这有什么变化?它仍然是未定义的行为 -> 任何事情都可能发生。并且标准不保证在成员之间添加 no 填充。
  • @Olaf 你说得对,第二次溢出减损了示例的价值,所以我在回答中删除了它。我确实指出我依赖 gcc 使用的填充规则,但我已经进一步澄清了这一点。
  • 填充规则不是由编译器定义的,而是典型的 ABI。 gcc 像任何其他平台编译器一样必须符合它。一个平台,例如16 字节缓存行可能对成员使用 16 字节对齐,因为这样可以将它们最佳地存储在缓存中。无论如何,访问超出其边界的数组是 UB。编译器可以假设您不这样做并生成适当的代码。例如,一个 64 位的 arch 可以在单个寄存器中传递整个数组;现在,如果您访问该数组之外的内容呢?猜测会发生什么或它是否有效是毫无用处的。
  • @Olaf 了解和识别 UB 的常见结果对于诊断和修复错误非常有价值。如果行为发生变化,那么 OP 将需要重写他的代码来演示(我认为是向他的学生)缓冲区溢出的常见结果。
  • 抱歉,这是错误的。如果遇到 UB,看看会发生什么很好。但是尝试为 undefined 行为生成显示 defined 行为的代码是没有用的。 - 除非你想编写 SO 不支持的恶意软件。
【解决方案2】:

您可以禁用安全检查:

来自手册:

-fstack-protector
      Emit extra code to check for buffer overflows, such as stack smashing attacks.  >This is done by adding a guard variable to functions with
      vulnerable objects.  This includes functions that call alloca, and functions with >buffers larger than 8 bytes.  The guards are initialized when
      a function is entered and then checked when the function exits.  If a guard check >fails, an error message is printed and the program exits.

-fstack-protector-all
      Like -fstack-protector except that all functions are protected.

如果您想禁用此功能,请在选项名称中输入 no-

-fno-stack-protector -fno-stack-protector-all

缓冲区溢出示例:

int main(){
    int valid = 0;
    char str1 = ["START"];
    char str2 = [8];

    gets(str2);
    if(strncmp(str1, str2, 8) == 0){
        valid = 1;
        cout << "buffer: " << str1 << ", " << str2 << ", " << valid << endl;
    }

}

【讨论】:

    猜你喜欢
    • 2021-09-16
    • 1970-01-01
    • 2012-01-31
    • 2019-04-17
    • 1970-01-01
    • 2015-12-16
    • 1970-01-01
    • 2010-11-11
    相关资源
    最近更新 更多