gpt4 book ai didi

c - 预期的缓冲区溢出并不总是导致程序崩溃

转载 作者:行者123 更新时间:2023-12-04 02:17:35 28 8
gpt4 key购买 nike

考虑以下最小的 C 程序:

案例编号 1 :

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

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

int main(void)
{
foo("01234567890134567");
}

这不会导致崩溃转储

如果只添加一个字符,那么新的主要内容是:

案例编号 2 :
void main()
{
foo("012345678901345678");
^
}

程序因段错误而崩溃。

看起来除了堆栈中保留的 10 个字符外,还有一个额外的空间可容纳 8 个额外的字符。因此第一个程序不会崩溃。但是,如果再添​​加一个字符,就会开始访问无效内存。我的问题是:
  • 为什么我们在堆栈中保留了这些额外的 8 个字符?
  • 这是否与内存中的 char 数据类型对齐有关?

  • 在这种情况下,我的另一个疑问是操作系统(在这种情况下为 Windows)如何检测错误的内存访问?通常,根据 Windows 文档,默认堆栈大小为 1MB Stack Size 。所以我没有看到操作系统如何检测到被访问的地址在进程内存之外,特别是当最小页面大小通常为 4k 时。在这种情况下,操作系统是否使用 SP 来检查地址?

    PD:我使用以下环境进行测试
    赛格温
    海湾合作委员会 4.8.3
    视窗 7 操作系统

    编辑 :

    这是从 http://gcc.godbolt.org/# 生成的程序集,但使用 GCC 4.8.2,我在可用编译器中看不到 GCC 4.8.3。但我想生成的代码应该是相似的。我构建了没有任何标志的代码。我希望具有汇编专业知识的人可以阐明 foo 函数中发生的事情以及为什么额外的字符会导致段错误
        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

    最佳答案

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

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

    how does the OS (Windows in this case) detects the bad memory access? Normally as per the Windows documentation the default stack size is 1MB Stack Size. So I don't see how the OS detects that the address being accessed is outside the process memory specially when the minimum page size is normally 4k. Does the OS use the SP in this case to check the address?



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

    一般来说,您的代码很可能不会在 strcpy 上失败(如果您编写足够的数据来访问某些禁止地址,它可以,但很可能不是这种情况) - 当它从 foo 函数返回时失败。 strcpy 只是覆盖了指向 foo 函数之后的下一条指令的下一条指令指针。因此,指令指针填充了来自“012345678901345678”字符串的数据,并尝试从“垃圾”地址获取下一条指令,但由于上述原因而失败。

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

    关于c - 预期的缓冲区溢出并不总是导致程序崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33013195/

    28 8 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com