ESP 上的返回地址?

逆向工程 地址 特别是
2021-06-19 16:42:45

我读过函数调用的返回地址总是被推送到 ESP 堆栈上。但是当我试图看到使用 时gdb,我没有找到它。这是我用过的用 C 编写的程序:

#include <stdio.h>
#include <stdlib.h>

void test_function(int a, int b, int c, int d) {
int flag;
char buffer[10];
flag = 31337;
buffer[0] = 'A';
}

int main() {
  test_function(1, 2, 3, 4);
}

gdb 这段代码的反汇编给了我:

(gdb) disas main
Dump of assembler code for function main:
   0x0804846c <+0>: push   ebp
   0x0804846d <+1>: mov    ebp,esp
   0x0804846f <+3>: and    esp,0xfffffff0
   0x08048472 <+6>: sub    esp,0x10
   0x08048475 <+9>: mov    DWORD PTR [esp+0xc],0x4
   0x0804847d <+17>:    mov    DWORD PTR [esp+0x8],0x3
   0x08048485 <+25>:    mov    DWORD PTR [esp+0x4],0x2
   0x0804848d <+33>:    mov    DWORD PTR [esp],0x1
=> 0x08048494 <+40>:    call   0x804843d <test_function>
   0x08048499 <+45>:    leave  
   0x0804849a <+46>:    ret    
End of assembler dump.
(gdb) x/8xw $esp
0xffffd650: 0x00000001  0x00000002  0x00000003  0x00000004
0xffffd660: 0x080484a0  0x00000000  0x00000000  0xf7e21a83

如您所见,我在0x08048494(在函数调用之前)放置了一个断点我可以看到参数被压入堆栈 (1,2,3,4) 但是,我没有看到返回地址,在这种情况下应该是0x08048499,对吧?

2个回答

让我们以这个小示例代码为例:

#include <stdio.h>
#include <stdlib.h>

int foo (int a)
{
  return a ? a << 2: 1000;
}

int main()
{
  printf("The result of foo(10) is %d\n", foo(10));

  return EXIT_SUCCESS;
}

一旦组装,我们得到:

0000000000400506 <foo>:
  400506:       55                      push   %rbp
  400507:       48 89 e5                mov    %rsp,%rbp
  40050a:       89 7d fc                mov    %edi,-0x4(%rbp)
  40050d:       83 7d fc 00             cmpl   $0x0,-0x4(%rbp)
  400511:       74 08                   je     40051b <foo+0x15>
  400513:       8b 45 fc                mov    -0x4(%rbp),%eax
  400516:       c1 e0 02                shl    $0x2,%eax
  400519:       eb 05                   jmp    400520 <foo+0x1a>
  40051b:       b8 e8 03 00 00          mov    $0x3e8,%eax
  400520:       5d                      pop    %rbp
  400521:       c3                      retq   

0000000000400522 <main>:
  400522:       55                      push   %rbp
  400523:       48 89 e5                mov    %rsp,%rbp
  400526:       bf 0a 00 00 00          mov    $0xa,%edi
  40052b:       e8 d6 ff ff ff          callq  400506 <foo>
  400530:       89 c6                   mov    %eax,%esi
  400532:       bf d4 05 40 00          mov    $0x4005d4,%edi
  400537:       b8 00 00 00 00          mov    $0x0,%eax
  40053c:       e8 9f fe ff ff          callq  4003e0 <printf@plt>
  400541:       b8 00 00 00 00          mov    $0x0,%eax
  400546:       5d                      pop    %rbp
  400547:       c3                      retq   
  400548:       0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
  40054f:       00 

让我们看看下面的gdb会话:

(gdb) break *0x40052b
Breakpoint 1 at 0x40052b: file frame.c, line 13.
(gdb) break *0x400530
Breakpoint 2 at 0x400530: file frame.c, line 13.

我们只是在foo过程调用之前和之后设置了一个断点

(gdb) break foo
Breakpoint 3 at 0x40050d: file frame.c, line 7.

我们在foo程序中设置了一个断点

(gdb) run
Starting program: /home/fleury/tmp/tests/frame 

Breakpoint 1, 0x000000000040052b in main () at frame.c:13
13    printf("The result of foo(10) is %d\n", foo(10));

我们启动程序并在调用foo.

(gdb) info frame
Stack level 0, frame at 0x7fffffffe0c0:
 rip = 0x40052b in main (frame.c:13); saved rip = 0x7ffff7a54b45
 source language c.
 Arglist at 0x7fffffffe0b0, args: 
 Locals at 0x7fffffffe0b0, Previous frame's sp is 0x7fffffffe0c0
 Saved registers:
  rbp at 0x7fffffffe0b0, rip at 0x7fffffffe0b8

我们询问了有关堆栈帧环境的信息。我们可以注意到save rip = 0x7ffff7a54b45(这是程序的返回地址main)。

(gdb) continue
Continuing.

Breakpoint 3, foo (a=10) at frame.c:7
7     return a ? a << 2: 1000;

我们继续执行程序并在foo程序内部停止(第三个断点)。让我们询问堆栈帧:

 (gdb) info frame
 Stack level 0, frame at 0x7fffffffe0b0:
  rip = 0x40050d in foo (frame.c:7); saved rip = 0x400530
 called by frame at 0x7fffffffe0c0
 source language c.
 Arglist at 0x7fffffffe0a0, args: a=10
 Locals at 0x7fffffffe0a0, Previous frame's sp is 0x7fffffffe0b0
 Saved registers:
  rbp at 0x7fffffffe0a0, rip at 0x7fffffffe0a8

请注意,saved rip = 0x400530这正是call foo.

(gdb) continue
Continuing.

Breakpoint 2, 0x0000000000400530 in main () at frame.c:13
13    printf("The result of foo(10) is %d\n", foo(10));

我们继续执行,并在foo程序出口处到达第二个断点再次,让我们询问退货地址:

(gdb) info frame
Stack level 0, frame at 0x7fffffffe0c0:
 rip = 0x400530 in main (frame.c:13); saved rip = 0x7ffff7a54b45
 source language c.
 Arglist at 0x7fffffffe0b0, args: 
 Locals at 0x7fffffffe0b0, Previous frame's sp is 0x7fffffffe0c0
 Saved registers:
  rbp at 0x7fffffffe0b0, rip at 0x7fffffffe0b8

当我们弹出时它已经恢复到原始值foo

事实上,info frame(缩写为i f)还告诉在哪里可以找到堆栈上存储的返回地址:

 Saved registers:
  rbp at 0x7fffffffe0b0, rip at 0x7fffffffe0b8

而且,如果您要求gdb显示内容,0x7fffffffe0b8您应该看到0x7ffff7a54b45

(gdb) print /x *0x7fffffffe0b8
$1 = 0x7ffff7a54b45

Guntram Blohm 的评论说明了您的情况。由于您将断点放在call,指令尚未执行,您还没有看到返回的地址被压入堆栈。但是如果让它执行(例如通过键入si),那么您将看到返回的地址被压入堆栈。