栈溢出导致的栈攻击问题

我今天看视频学习C++内存管理学到栈攻击的知识点时遇到了一个问题:
以下程序在linux中通过

gcc 00_code.c -z execstack -fno-stack-protector

开启堆栈可执行机制并关闭栈保护机制后,在func1中通过数组越界访问的方式成功获取func2的使用权
我不理解的地方是为什么在func1中通过printf("a[n] = %d\n",a[6]);会成功调用func2,通过函数名怎么可能会让函数执行的呢?还是说在func1运行完出栈的时候调用的func2呢?


#include <stdio.h>

int func2()
{
    printf("stackflow success!\n");
    while(1);
}

void func1()
{
    int a[4];
    a[6] = func2;
    printf("a[n] = %d\n",a[6]);
}

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

    return 0;
}

img

我觉得有可能是在func1函数出栈的时候调用了func2,我通过在func1最后加了一个while(1)循环,发现只要func1不退出,func2就不会被调用(只是猜测,具体我还是一头雾水)

img

【以下回答由 GPT 生成】

栈溢出导致的栈攻击是一种常见的安全漏洞。让我们来详细解释下你在代码中遇到的问题和相应的解决方案。

首先,让我们来理解栈的工作原理。在函数调用时,函数的局部变量、参数和返回地址等信息都会被存储在栈中。栈是一种后进先出(LIFO)结构,每当调用一个新函数时,就会在栈中创建一个新的帧。

在你的代码中,问题出现在func1函数中的数组越界操作a[6] = func2。当你访问一个数组超出其边界的位置时,会覆盖到栈上的其他数据,包括函数的返回地址。因为在func1函数的栈帧上,局部变量a的下标为 6 的位置处超过了a的实际长度(大小为 4),所以会覆盖到func1函数的返回地址。

因此,func2函数的地址被写入到了func1函数的返回地址位置上。当func1函数执行完毕并返回时,CPU会根据返回地址跳转到该地址对应的代码,从而执行了func2函数。

为了解决这个问题,你可以采取以下措施:

  1. 避免数组越界:确保在访问数组时不超出其边界范围。可以检查下标是否越界,或者使用更安全的数据结构,如动态分配内存。

  2. 使用编译器提供的栈保护:大多数编译器都提供了栈保护机制,可以检测到栈溢出并进行相应的处理。你在编译时使用的-fno-stack-protector选项是关闭了栈保护功能,建议不要关闭这个选项,以提高代码的安全性。

  3. 使用内存安全的函数:你在代码中使用了printf函数,然而它并不是一个内存安全的函数。当使用不当时,printf可能导致安全漏洞,例如格式字符串漏洞。建议使用安全的版本,如snprintf,并在使用时遵循安全的使用规则。

  4. 对用户输入进行验证和过滤:避免接收不受信任的输入数据,对输入进行验证和过滤,可以减少栈溢出攻击的风险。

综上所述,对于栈溢出导致的栈攻击问题,应当尽量避免数组越界和关闭栈保护机制,使用内存安全的函数,并对用户输入进行验证和过滤,以提高代码的安全性。这样可以降低栈溢出攻击的风险。



【相关推荐】



如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^

链接寄存器覆盖,缓冲区溢出攻击,是黑客常用的方式。

这段代码中存在两个问题:

在func1函数中,数组a只被定义了4个元素,但是在访问第6个元素时并没有出现编译错误,这是因为C语言中数组的下标从0开始计数,访问第6个元素实际上是访问了数组之外的内存,这种行为被称为数组越界访问,可能会导致程序崩溃或产生不可预料的行为。

将函数指针赋值给整型变量的行为是不合法的,因为这相当于将指针类型的值转换为整型类型的值。正确的做法是定义一个指向函数的指针,将函数的地址赋值给该指针,再通过指针来调用函数。
因为都是指针,
所以。运行的时候,不管是函数指针,还是数组的指针,都一样的被运行了。