Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/67.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 一种预期的缓冲区溢出,它并不总是导致程序崩溃_C_Memory_Stack_Buffer Overflow - Fatal编程技术网

C 一种预期的缓冲区溢出,它并不总是导致程序崩溃

C 一种预期的缓冲区溢出,它并不总是导致程序崩溃,c,memory,stack,buffer-overflow,C,Memory,Stack,Buffer Overflow,考虑以下最小C程序: 案例编号1: #include <stdio.h> #include <string.h> void foo(char* s) { char buffer[10]; strcpy(buffer,s); } int main(void) { foo("01234567890134567"); } void main() { foo("012345678901345678");

考虑以下最小C程序:

案例编号1

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

void foo(char* s)
{
    char buffer[10];
    strcpy(buffer,s);
}

int main(void)
{
    foo("01234567890134567");
}
void main()
{
    foo("012345678901345678");
                          ^   
}
程序因分段错误而崩溃

看起来除了堆栈中保留的10个字符之外,还有一个额外的空间容纳8个字符。因此,第一个程序不会崩溃。但是,如果再添加一个字符,则会开始访问无效内存。我的问题是:

  • 为什么我们在堆栈中保留了这些额外的8个字符
  • 这是否与内存中的char数据类型对齐有关
  • 在这种情况下,我还有一个疑问,那就是操作系统(本例中为Windows)是如何检测坏内存访问的?通常根据Windows文档,默认堆栈大小为1MB。因此,我看不出操作系统如何检测被访问的地址在进程内存之外,特别是当最小页面大小通常为4k时。在这种情况下,操作系统是否使用SP检查地址

    PD:我正在使用以下环境进行测试
    Cygwin
    通用条款4.8.3
    Windows 7操作系统

    编辑

    #include <stdio.h>
    #include <string.h>
    
    void foo(char* s)
    {
        char buffer[10];
        strcpy(buffer,s);
    }
    
    int main(void)
    {
        foo("01234567890134567");
    }
    
    void main()
    {
        foo("012345678901345678");
                              ^   
    }
    
    这是从生成的程序集,但是使用GCC4.8.2,我在可用的编译器中看不到GCC4.8.3。但我想生成的代码应该是类似的。我构建的代码没有任何标志。我希望具有汇编专业知识的人能够解释foo函数中发生了什么,以及为什么额外的字符会导致seg错误

        foo(char*):
        pushq   %rbp
        movq    %rsp, %rbp
        subq    $48, %rsp
        movq    %rdi, -40(%rbp)
        movq    %fs:40, %rax
        movq    %rax, -8(%rbp)
        xorl    %eax, %eax
        movq    -40(%rbp), %rdx
        leaq    -32(%rbp), %rax
        movq    %rdx, %rsi
        movq    %rax, %rdi
        call    strcpy
        movq    -8(%rbp), %rax
        xorq    %fs:40, %rax
        je  .L2
        call    __stack_chk_fail
    .L2:
        leave
        ret
    .LC0:
        .string "01234567890134567"
    main:
        pushq   %rbp
        movq    %rsp, %rbp
        movl    $.LC0, %edi
        call    foo(char*)
        movl    $0, %eax
        popq    %rbp
        ret
    

    堆栈中的下一个条目是64位系统中的函数地址开关必须与8对齐,这样就有足够的空间容纳16个字符


    您可以通过在数组后声明一个int变量来验证这一点。Int将与4对齐,字符空间将减少,所以程序将在较小的数字上崩溃

    我相信您理解您已经实施了一些导致未定义行为的措施。因此,很难回答为什么它使用额外的字符串失败,而不使用原始字符串。它可能与受编译标志(如对齐、优化等)影响的内部编译器实现有关

    您可以尝试反汇编二进制代码或创建汇编代码,并查看缓冲区在堆栈上的确切位置。您可以对不同的优化级别执行相同的操作,以检查汇编代码和行为中的更改

    操作系统(本例中为Windows)如何检测坏内存访问? 通常根据Windows文档,默认堆栈大小为 1MB堆栈大小。所以我看不出操作系统是如何检测到地址 被访问在进程内存之外,特别是当 最小页面大小通常为4k。在这种情况下,操作系统是否使用SP 要查地址吗

    操作系统不会监视您执行的代码。硬件(CPU)执行(因为它执行此代码)。一旦您的代码尝试访问未分配给您的进程(不是为您的程序)的地址,操作系统将得到指示,因为硬件将触发#PF(页面错误)异常。另一种情况是,您试图访问分配给您的地址,但权限不正确(例如,您试图从没有“执行”权限的数据页执行二进制数据),或者转到代码页,但偏移量错误,并且您读取的指令不存在,或者(甚至更糟)它存在并解码成你不期望的东西(我们以前说过未定义的行为吗?)

    通常,您的代码很可能不会在strcpy上失败(如果您写入足够的数据以访问某些禁止的地址,则可能会失败,但很可能不是这样)-当它从
    foo
    函数返回时失败
    strcpy
    只需重写下一条指令指针,该指针指向
    foo
    函数之后的下一条指令。因此,指令指针被“012345678901345678”字符串中的数据填充,并尝试从“junky”地址获取下一条指令,但由于上述原因失败


    这种“方法”/“bug”被称为“”,并在黑客中广泛使用,以使您的代码(通常是以更高权限执行的OS/BIOS/VMM/SMM代码)执行黑客提供的恶意代码。只需确保用事先准备好的代码地址覆盖指令指针。

    官方的系统无关答案是:

    您的代码将数据写入目标阵列的末尾之外,行为未定义,任何事情都可能发生,包括什么也不发生或太空探测器在火星表面坠毁。您观察到的缓冲区结束后最多8个字节的无明显影响,以及超出缓冲区的分段错误导致的崩溃都是未定义行为的可能影响,在预期结果范围内

    您感兴趣的额外实施细节:

    实际行为将取决于许多情况,例如您使用的编译器、操作系统和ABI(应用程序二进制接口)等

    您的程序是在64位Windows环境中编译和执行的。在此环境中,堆栈在64位边界或16字节边界上保持对齐,以允许从堆栈位置直接加载和存储MMX寄存器。数组
    缓冲区[10]
    占用堆栈上的16个字节。给定堆栈是如何在此处理器上建立的,它将位于函数
    foo
    用于将任何保存的寄存器和返回地址存储到调用函数
    main
    中的位置的正下方。额外的6个字节是在数组之前还是之后是编译器的选择。它可以将这个空间用于其他局部变量,也可以忽略它

    如果填充在数组之后,超过
    缓冲区结尾写入最多6个字节可能是无害的,另外8个字节可能不会产生任何明显的影响(关闭保存的
    rbp
    寄存器,