x86 4字节对齐场景下shellcode字符串入栈空终止方案的区别与选择
两种空终止方案的核心区别
先拆解两个指令的本质:
- 方案1
\x68\x20\x20\x00(完整指令为5字节,对应push 32位立即数操作):本质是将空终止符直接作为待压入立即数的一部分,和字符串剩余内容凑满4字节后一次性压入栈。其中\x20是占位符,用于填充字符串不足4字节的空位,实际可替换为任意不影响业务逻辑的字符。 - 方案2
\x6A\x00(完整指令为2字节,对应push 8位立即数操作):本质是单独压入一个空值,x86架构下push imm8会将8位立即数符号扩展为32位,最终在栈上占用4字节空间,满足栈对齐要求。
二者的核心差异点如下:
- 指令长度不同:方案2仅2字节,比方案1的5字节更短,但方案1是将字符串和终止符合并压入,不需要额外单独压终止符,实际总长度需要结合字符串长度判断。
- 字节特征不同:二者默认都包含
\x00空字节,方案1的空字节位置由字符串长度决定,方案2的空字节固定在指令末尾。 - 实现逻辑不同:方案1是单次压入完成字符串+终止符的写入,方案2需要先压终止符、再压字符串,是两次压栈操作。
场景选择规则
根据你要压入的字符串长度和shellcode的约束条件选择即可:
- 当字符串长度为1/2/3字节时,优先选方案1:可以把空终止符直接补到字符串末尾凑满4字节,单次压入即可完成,总指令长度仅5字节,比「方案2压终止符+压字符串」的2+5=7字节更短,体积更小。
- 当字符串长度刚好为4字节(比如你举例的
open)时,优先选方案2:4字节的字符串已经占满整个32位立即数的空间,无法再塞下空终止符,只能单独压入终止符,此时2字节的方案2是最短的选择。 - 当shellcode要求无空字节时(比如漏洞利用场景中,输入源是C风格字符串,遇到
\x00会被截断),两个方案都不可用,需要改用无空字节的压0方式,比如xor eax,eax; push eax(对应字节序列\x31\xc0\x50),通过寄存器清零后压入寄存器的方式得到栈上的空值,且不会引入空字节。
内容的提问来源于stack exchange,提问作者Teererai Marange
相关产品推荐
相关产品推荐

