You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

缓冲区溢出如何在栈上执行指令?栈不可执行时代码为何能运行?

缓冲区溢出如何在栈上执行指令?栈不可执行时代码为何能运行?

嘿,这个问题问得特别到位——刚接触缓冲区溢出的朋友几乎都会卡在这个点上!我来给你掰扯清楚:

先搞懂基础:栈不可执行是后来才有的“保护措施”

早期的操作系统(比如老版本的Linux、Windows XP之前的系统)根本没有栈不可执行的限制。那时候缓冲区溢出的玩法特别直接:把要执行的恶意代码(也就是shellcode)写到栈里,然后通过溢出覆盖函数的返回地址,让CPU跳转到栈上的shellcode位置,直接就能运行。这是最原始、最经典的缓冲区溢出利用方式。

现在有NX/DEP保护了,怎么绕?核心是“复用现有代码”

现在主流系统都开启了NX(No Execute,也叫DEP数据执行保护),栈和堆这类数据区域默认是不可执行的——这时候直接往栈里写代码肯定跑不起来,但安全研究者早就想出了绕过的办法,核心思路就是:不在栈上放新代码,而是复用程序里已经存在的可执行代码片段。

最典型的就是ROP(Return-Oriented Programming,返回导向编程):

  • 程序的可执行段(比如.text段)里本来就散落着很多短小的指令片段(比如pop ebx; ret、mov eax, 0; ret这种,我们叫它“gadget”)
  • 我们通过缓冲区溢出,在栈上构造一个“gadget链”:把这些gadget的地址按顺序放到栈里,让CPU执行完一个gadget后,通过ret指令跳转到下一个gadget
  • 把这些零散的gadget拼接起来,就能完成复杂的操作(比如调用系统函数execve来获取shell)。因为这些gadget本来就在可执行区域里,NX保护不会拦截。

还有几种常见的变种:

  • ret2libc:这是ROP的简化版,直接跳转到系统库(比如libc)里的现成函数(比如system),只要在栈上放好函数的参数(比如/bin/sh的地址),就能直接调用函数完成攻击
  • ret2syscall:针对没有libc泄露的情况,直接拼接出调用系统调用的gadget链,触发内核执行我们要的操作

还有些特殊情况栈依然可执行

当然也不是所有场景都有NX保护:

  • 一些嵌入式系统、老旧的软件可能刻意关闭了NX
  • 有些程序(比如JIT编译的程序)会在栈上开辟可执行的内存区域,这时候也能直接用传统的shellcode方法

总结一下:栈不可执行确实挡住了最原始的缓冲区溢出,但通过复用现有可执行代码的思路,依然能完成攻击——这也是缓冲区溢出至今还是安全领域重点研究内容的原因之一。

备注:内容来源于stack exchange,提问作者FourierFlux

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.17 08:18:02