getenv
不store堆栈上环境变量的值。它是already从进程启动开始就在堆栈上,并且getenv
获得一个指向它的指针。
请参阅 i386 System V ABI 对进程启动时 argv[] 和 envp[] 所在位置的描述:上面[esp]
.
_start
在调用之前不会复制它们main
,只是计算指向它们的指针作为参数传递给main
。 (最新版本的链接位于https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI,其中维护官方当前版本。)
您的代码将指向堆栈内存(包含环境变量的值)的指针转换为函数指针并通过它进行调用。查看编译器生成的 asm(例如https://godbolt.org/ https://godbolt.org/): 会是这样的call getenv
/ call eax
.
-zexecstack
in your kernel version1 makes all your pages executable, not just the stack. It also applies to .data
, .bss
, and .rodata
sections, and memory allocated with malloc
/ new
.
GNU/Linux 上的确切机制是“read-implies-exec”进程范围标志,它会影响所有未来的分配,包括手动使用mmap
. See 当程序集文件包含在项目中时,来自 mmap 的意外执行权限 https://stackoverflow.com/questions/58260465/unexpected-exec-permission-from-mmap-when-assembly-files-included-in-the-project了解更多关于GNU_STACK
ELF 头的东西。
脚注1:Linux 5.4 左右之后仅使堆栈本身可执行,而不是 READ_IMPLIES_EXEC:Linux 可执行文件 .data 部分的默认行为在 5.4 和 5.9 之间发生了变化? https://stackoverflow.com/questions/64833715/linux-default-behavior-against-data-section/64837581#64837581
有趣的事实:获取访问其父局部变量的嵌套函数的地址可以让 gcc 启用-zexecstack
。它将可执行“蹦床”的代码存储到堆栈上,该堆栈将“静态链”指针传递给实际的嵌套函数,从而允许它引用其父级的堆栈帧。
如果您想将数据作为代码执行而无需-zexecstack
,你会用mprotect(PROT_EXEC|PROT_READ|PROT_WRITE)
在包含该环境变量的页面上。 (它是堆栈的一部分,因此您不应删除写入权限;例如,它可能与 main 的堆栈框架位于同一页面中。)
Related:
使用 GNU/Linuxld
在 2018 年末左右之前从 binutils 中,.rodata
部分链接到与.text
部分,因此const char code[] = {0xc3}
或字符串文字是可执行的。
Current ld
gives .rodata
它自己的段在没有 exec 的情况下映射读取,因此不再可能在只读数据中找到 ROP / Spectre“小工具”,除非您使用-zexecstack
。即使这样也不适用于当前的内核;char code[] = ...;
作为函数内部的局部变量,会将数据放在实际可执行的堆栈上。看如何让c代码执行hex机器代码? https://stackoverflow.com/questions/9960721/how-to-get-c-code-to-execute-hex-machine-code/55893781#55893781了解详情。