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

C程序中函数返回后跳过代码行,10字节增量的技术意义是什么?

为什么修改返回地址时要加10?底层原理是什么?

先看你给出的示例代码及运行结果:

#include <stdio.h>

void f()
{   
    unsigned int *array = (unsigned int *) &array;
    array[4] = array[4] + 10;
}

void main()
{
    f();
    printf("I am skipped\n");
}

编译运行后无输出,说明printf确实被成功跳过,下面拆解底层逻辑和数字10的意义:

核心逻辑:篡改函数返回地址

这段代码的本质是利用栈布局,定位到函数f()的返回地址(即main函数中f()调用完成后,CPU要执行的下一条指令地址),通过修改该地址,让CPU直接跳过printf的执行流程。

为什么用索引4定位返回地址?

当执行unsigned int *array = (unsigned int *) &array;时,array指针指向了自身在栈上的存储地址。在x86架构、gcc 7.5.0默认编译选项下,函数f()的栈帧布局大致为:

  • 高地址区域:保存的EBP寄存器值(占4字节)
  • 局部变量array的存储空间(占4字节)
  • 中间栈对齐或隐含填充数据
  • 低地址区域:函数调用时压入的返回地址(占4字节)

从array的位置到返回地址,正好间隔了4个unsigned int的长度(每个4字节,共16字节),因此array[4]能精准定位到返回地址的位置。

数字10的来源:指令字节总长度

10是main函数中,从f()返回后的第一条指令,到printf执行完毕后下一条指令的总字节数。我们可以通过编译后的汇编代码验证:

main函数关键部分的汇编(简化版):

main:
    push    ebp
    mov     ebp, esp
    call    f                  ; 调用f,此时返回地址是下一条指令的地址
    push    OFFSET FLAT:.LC0   ; 压入字符串地址,指令占4字节
    call    printf             ; 调用printf,指令占5字节
    add     esp, 4             ; 清理栈,指令占1字节
    ; 后续是main函数返回的指令

从返回地址(push OFFSET FLAT:.LC0的地址)开始,到跳过printf需要跳到add esp,4之后的指令,这段指令的总长度是4+5+1=10字节。把返回地址加10后,CPU会直接跳到这段指令的末尾,完全跳过printf的执行。

注意:这个数字是环境依赖的

10这个值仅在x86架构、gcc 7.5.0、默认无优化编译选项下有效。如果切换到x64架构、开启编译优化(比如-O2)、更换编译器版本,指令序列的长度会发生变化,对应的增量值也会不同。

内容的提问来源于stack exchange,提问作者Jason Ivey

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 07:15:11