粉碎堆栈 - 无法找到 return 地址

Smashing the Stack - Trouble finding return address

我一直在阅读 "Smashing the Stack for Fun and Profit" 并且似乎遇到了与其他人 运行 过去遇到的问题类似的问题;但是我无法弄清楚为什么我的代码仍然无法正常工作。

我想做什么:

考虑以下代码:

void function1(int a, int b, int c){
    char buffer1[8];
    // char buffer2[10];
    int *ret;

    ret = (int *) buffer1 + 24;
    (*ret) += 7; 
}

void main() {
    int x;

    x = 0;
    function1(1,2,3);
    x = 1;
    printf("%d\n", x);
}

该示例的目标是覆盖 *function1 的 return 地址 * 并跳过 main 中的 x = 1 行。所以,程序应该输出 0 而不是 1。但是,我的输出仍然是 1。所以我不确定我哪里出错了。

使用gdb计算return地址和偏移量

来自程序集的 objdump:

00000000000006db <main>:
 6db:   55                      push   %rbp
 6dc:   48 89 e5                mov    %rsp,%rbp
 6df:   48 83 ec 10             sub    [=12=]x10,%rsp
 6e3:   c7 45 fc 00 00 00 00    movl   [=12=]x0,-0x4(%rbp)
 6ea:   ba 03 00 00 00          mov    [=12=]x3,%edx
 6ef:   be 02 00 00 00          mov    [=12=]x2,%esi
 6f4:   bf 01 00 00 00          mov    [=12=]x1,%edi
 6f9:   e8 b2 ff ff ff          callq  6b0 <function1>
 6fe:   c7 45 fc 01 00 00 00    movl   [=12=]x1,-0x4(%rbp)
 705:   8b 45 fc                mov    -0x4(%rbp),%eax
 708:   89 c6                   mov    %eax,%esi
 70a:   48 8d 3d 93 00 00 00    lea    0x93(%rip),%rdi        # 7a4 <_IO_stdin_used+0x4>
 711:   b8 00 00 00 00          mov    [=12=]x0,%eax
 716:   e8 45 fe ff ff          callq  560 <printf@plt>
 71b:   90                      nop
 71c:   c9                      leaveq 
 71d:   c3                      retq   
 71e:   66 90                   xchg   %ax,%ax

因为我试图跳过 x = 1;,所以 0x705 - 0x6fe = 0x7,我相信这是正确的。 为了确定 buffer1 和调用堆栈上的 return 地址之间的偏移量,我检查了 gdb:

Breakpoint 1, function1 (a=1, b=2, c=3) at example3.c:12
12      ret = (int *) buffer1 + 24;
(gdb) p $rbp
 = (void *) 0x7fffffffea80
(gdb) p &buffer1
 = (char (*)[8]) 0x7fffffffea70

buffer1 和 return 地址之间的距离应该是:0x7fffffffea80 - 0x7ffffffffea70 + 8 = 24

我还验证了字长是 8(使用 arch 我知道它是 x86_64)。

我的猜测是 return 地址偏移量可能因堆栈金丝雀之类的原因而有所不同,但我如何才能找到实际的 return 地址?

编辑:

函数 1 的反汇编:

00000000000006b0 <function1>:
 6b0:   55                      push   %rbp
 6b1:   48 89 e5                mov    %rsp,%rbp
 6b4:   89 7d ec                mov    %edi,-0x14(%rbp)
 6b7:   89 75 e8                mov    %esi,-0x18(%rbp)
 6ba:   89 55 e4                mov    %edx,-0x1c(%rbp)
 6bd:   48 8d 45 f0             lea    -0x10(%rbp),%rax
 6c1:   48 83 c0 44             add    [=14=]x44,%rax
 6c5:   48 89 45 f8             mov    %rax,-0x8(%rbp)
 6c9:   48 8b 45 f8             mov    -0x8(%rbp),%rax
 6cd:   8b 00                   mov    (%rax),%eax
 6cf:   8d 50 07                lea    0x7(%rax),%edx
 6d2:   48 8b 45 f8             mov    -0x8(%rbp),%rax
 6d6:   89 10                   mov    %edx,(%rax)
 6d8:   90                      nop
 6d9:   5d                      pop    %rbp
 6da:   c3                      retq   

不确定这是否是您的主要问题,但我正在查看

   ret = (int *) buffer1 + 24;

Cast 的优先级高于二进制 +,所以这是 ((int *)buffer1) + 24。结果是 ret 被设置为比 buffer1 高 96 字节的地址,因为 sizeof(int) == 4.

你可能打算写

   ret = (int *)(buffer1 + 24);

另外,return地址是64位,int是32位。因此,您可能希望将 ret 声明为 unsigned long * 而不是 int *,并相应地更改转换,以便您的 *ret += 7 是 64 位添加。它不太可能因为对齐而真正产生影响,但它看起来仍然更正确。