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

为何获取到缓冲区末尾地址而非起始地址?(C栈调试疑问)

栈缓冲区地址疑惑:lea rax,[rbp-0x100]为何指向缓冲区末尾而非起始?

问题背景

我写了如下C代码:

int hey(char *param_1)
{
  char buf[256];
  
  strcpy(buf,param_1);
  printf("Hey ! %s\n",buf);
  return 0;
}

该函数由main调用,对应的汇编代码如下:

0x0000555555555189 <+0>: endbr64 
   0x000055555555518d <+4>: push   rbp
   0x000055555555518e <+5>: mov    rbp,rsp
   0x0000555555555191 <+8>: sub    rsp,0x110
   0x0000555555555198 <+15>:    mov    QWORD PTR [rbp-0x108],rdi
   0x000055555555519f <+22>:    mov    rdx,QWORD PTR [rbp-0x108]
   0x00005555555551a6 <+29>:    lea    rax,[rbp-0x100]
=> 0x00005555555551ad <+36>:    mov    rsi,rdx
   0x00005555555551b0 <+39>:    mov    rdi,rax
   0x00005555555551b3 <+42>:    call   0x555555555070 <strcpy@plt>
   0x00005555555551b8 <+47>:    lea    rax,[rbp-0x100]
   0x00005555555551bf <+54>:    mov    rsi,rax
   0x00005555555551c2 <+57>:    lea    rdi,[rip+0xe3b]        # 0x555555556004
   0x00005555555551c9 <+64>:    mov    eax,0x0
   0x00005555555551ce <+69>:    call   0x555555555090 <printf@plt>
   0x00005555555551d3 <+74>:    mov    eax,0x0
   0x00005555555551d8 <+79>:    leave  
   0x00005555555551d9 <+80>:    ret

用GDB设置断点后,寄存器内容如下:

rax            0x7fffffffdc80
rbp            0x7fffffffdd80

按逻辑计算,缓冲区地址应该是0x7fffffffdc80 (=0x7fffffffdd80-0x100),但用x/40x查看栈内容时发现,0x7fffffffdc80实际是缓冲区的末尾地址,真实起始地址是0x7fffffffdc80 - 0x100 = 0x7fffffffdb80——我已经通过填充NOP、shellcode以及大量'A'验证了这个结论:

(gdb) x/40x 0x7fffffffdb80
0x7fffffffdb80: 0x90909090  0x90909090  0x90909090  0x90909090
0x7fffffffdb90: 0xbb48c031  0x91969dd1  0xff978cd0  0x53dbf748
0x7fffffffdba0: 0x52995f54  0xb05e5457  0x41050f3b  0x41414141
0x7fffffffdbb0: 0x41414141  0x41414141  0x41414141  0x41414141
0x7fffffffdbc0: 0x41414141  0x41414141  0x41414141  0x41414141
0x7fffffffdbd0: 0x41414141  0x41414141  0x41414141  0x41414141
0x7fffffffdbe0: 0x41414141  0x41414141  0x41414141  0x41414141
0x7fffffffdbf0: 0x41414141  0x41414141  0x41414141  0x41414141
0x7fffffffdc00: 0x41414141  0x41414141  0x41414141  0x41414141
0x7fffffffdc10: 0x41414141  0x41414141  0x41414141  0x41414141

我搞不懂这个现象:按常理lea rax,[rbp-0x100]应该把缓冲区的起始地址传入rax,给strcpy做准备,为什么实际指向的是缓冲区的末尾地址?


问题根源:栈生长方向与栈溢出的误解

你遇到的问题是两个核心概念混淆导致的:

1. x86-64栈的生长方向

x86-64的栈是向下生长的:栈地址从高到低分配,rbp指向当前栈帧的底部(高地址端),rsp指向栈帧的顶部(低地址端)。当函数执行sub rsp,0x110时,是从rbp向下(低地址方向)划出272字节的栈空间,用于存储局部变量、寄存器备份等。

2. buf的实际位置与栈溢出

汇编里的lea rax,[rbp-0x100]确实是buf的起始地址——strcpy的第一个参数是目标地址,也就是buf[0]的位置,strcpy会从这个地址开始,向高地址方向逐字节复制内容(因为C数组的下标0对应低地址,下标递增对应地址递增)。

你看到的0x7fffffffdb80区域的内容,并不是buf本身的空间,而是栈溢出覆盖的上一个栈帧的内容:

  • buf的实际范围是0x7fffffffdc80(rbp-0x100)到0x7fffffffdd7F(rbp-0x100 + 255),共256字节,完全在当前函数的栈帧内(rsp=rbp-0x110=0x7fffffffdc70,buf的起始地址0x7fffffffdc80比rsp高16字节,属于当前栈帧)。
  • 你填充的字符串长度远大于256字节,strcpy会溢出buf的边界,向低地址方向(栈顶方向)覆盖,也就是覆盖上一个栈帧的内存(0x7fffffffdb80属于这个区域),所以你误以为这里是buf的起始地址。

验证方法

你可以在strcpy执行后,查看0x7fffffffdc80开始的内存:

x/40x 0x7fffffffdc80

会看到你填充的内容从这个地址开始,向高地址排列,这才是buf本身的存储区域。而你之前查看的0x7fffffffdb80是溢出后的覆盖区域,不属于buf。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 01:45:36