【问题标题】:ret address points to nowhere (?)ret 地址指向无处(?)
【发布时间】:2014-01-13 02:12:53
【问题描述】:

我编写了一个小程序来了解堆栈和缓冲区溢出是如何工作的。

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]) {

    char array[20];
    printf("-=-=-=-=-=-=-=-=-=-= The prize pool is 2000$ -=-=-=-=-=-=-=-=-=--=\n");

    printf("-=-=-=-=-=- Whatever you supply goes to array! -=-=-=-=-=-=-=\n");

    strcpy(array, argv[1]);

    printf("Array now is %p \n\n", &array);

}

我正在运行提供的程序

(gdb) run `perl -e 'print "A"x20 . "\x95\x84\x04\x08"x4'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /root/tests/c-tests/myownexamples/simpleover2 `perl -e 'print "A"x20 . "\x95\x84\x04\x08"x4'`

通过在最后一个 printf 中设置断点,我们可以看到:

Breakpoint 3, main (argc=134513813, argv=0x8048495 <main+65>)
at simpleover2.c:19
19  printf("Array now is %p \n\n", &array);
(gdb) info frame
Stack level 0, frame at 0xbffff4a0:
eip = 0x8048489 in main (simpleover2.c:19); saved eip **0x8048495**
 source language c.
 Arglist at 0xbffff498, args: argc=134513813, argv=0x8048495 <main+65>
 Locals at 0xbffff498, Previous frame's sp is 0xbffff4a0
 Saved registers:
 ebp at 0xbffff498, eip at 0xbffff49c
 (gdb) disassemble main
 Dump of assembler code for function main:
  0x08048454 <+0>:  push   ebp
  0x08048455 <+1>:  mov    ebp,esp
  0x08048457 <+3>:  sub    esp,0x1c
  0x0804845a <+6>:  mov    DWORD PTR [esp],0x8048560
  0x08048461 <+13>: call   0x8048384 <puts@plt>
  0x08048466 <+18>: mov    DWORD PTR [esp],0x80485a4
  0x0804846d <+25>: call   0x8048384 <puts@plt>
  0x08048472 <+30>: mov    eax,DWORD PTR [ebp+0xc]
  0x08048475 <+33>: add    eax,0x4
  0x08048478 <+36>: mov    eax,DWORD PTR [eax]
  0x0804847a <+38>: mov    DWORD PTR [esp+0x4],eax
  0x0804847e <+42>: lea    eax,[ebp-0x14]
  0x08048481 <+45>: mov    DWORD PTR [esp],eax
  0x08048484 <+48>: call   0x8048364 <strcpy@plt>
  => 0x08048489 <+53>:  mov    eax,0x80485e2
  0x0804848e <+58>: lea    edx,[ebp-0x14]
  0x08048491 <+61>: mov    DWORD PTR [esp+0x4],edx
  0x08048495 <+65>: mov    DWORD PTR [esp],eax
  0x08048498 <+68>: call   0x8048374 <printf@plt>
  0x0804849d <+73>: leave  
  0x0804849e <+74>: ret    

所以我已经覆盖了 RET 地址,但地址位于当前堆栈帧中。

逐步执行直到离开和返回指令

(gdb) nexti
0x0804849e  21  }
(gdb) i r eip
eip            0x804849e    0x804849e <main+74>
(gdb) i r eip
eip            0x804849e    0x804849e <main+74>
(gdb) nexti
0x08048495 in main (argc=1435550665, argv=0xc35de589) at simpleover2.c:19
19  printf("Array now is %p \n\n", &array);
(gdb) i r eip
 eip            0x8048495   0x8048495 <main+65>

我们看到执行 INDEED 到那里并跟随 nexti 我们得到:

 (gdb) nexti
 0x08048498 19  printf("Array now is %p \n\n", &array);
 (gdb) i r eip
 eip            0x8048498   0x8048498 <main+68>
 (gdb) nexti

 Program received signal SIGSEGV, Segmentation fault.
 0xb7eeae97 in strchrnul () from /lib/tls/i686/cmov/libc.so.6

那么为什么会出现段错误?而且它看起来确实是第二次调用 printf() 但它不会出现..

 ./simpleover2 `perl -e 'print "A"x20 . "\x95\x84\x04\x08"x4'`
-=-=-=-=-=-=-=-=-=-= The prize pool is 2000$ -=-=-=-=-=-=-=-=-=--=
-=-=-=-=-=- Whatever you supply goes to array! -=-=-=-=-=-=-=
Array now is 0xbf8ed894 

Segmentation fault

再一次,为什么它没有 printf() 显示,因为 ret 指向那个?以及自从执行了实际弹出整个堆栈帧的 leave 和 ret 指令后,如何继续执行?

【问题讨论】:

  • 不清楚你的问题是什么。您正在打印array 的地址。你预计会发生什么?
  • 我预计打印 2 次而不是 1 次,因为执行再次转到 0x8048495 地址

标签: c++ c gdb stack stack-overflow


【解决方案1】:

LEAVE 恢复程序的原始寄存器后,你覆盖的返回地址变为 main+65。因此,在“返回”到 main+65 之后,您调用 printf 时使用的参数与您想象的不同,因此 printf 可以尝试打印不以 null 结尾的内容。然后它可能会在已分配页面的末尾运行并在未分配页面 -> segfault 上导致意外的页面错误。 不知道你用的是什么ABI,所以不知道用哪些寄存器来传递参数给printf。

【讨论】:

    【解决方案2】:

    您正在更改array 的内容,而不是地址(实际上您无法更改地址!!)。 &amp;array 表示“array-的地址”,我敢肯定这不是您想要的。如果您想知道堆栈中字符数组的地址,只需参考不带&amp; 运算符的array

    【讨论】:

    • 我的意图是改变执行路径,这确实发生了。问题是它为什么会出现段错误以及 leave/ret 指令是否会更改堆栈帧(弹出)。
    • @user3124171 因为使用&amp;array 会调用未定义的行为,所以会出现段错误。你不能得到这个指针的地址,它是固定的。
    • @user3124171 顺便说一句:请在您的示例中更改哪个执行路径?没有任何分支,很简单。
    • 您可能缺少 gdb 输出。特定的 printf() 被调用了 2 次,因为提供的返回地址覆盖了位于堆栈帧之外的 ret。问题是为什么它不打印数组地址的 2 倍。
    • '可能你缺少 gdb 输出' 你认为&lt;main+68&gt; 指的是什么?那是……在我打赌的当前有效堆栈范围之外,因此会出现段错误。正如我已经提到的,这是未定义的行为,调试这样的行为毫无用处。
    猜你喜欢
    • 2013-10-30
    • 1970-01-01
    • 2017-12-25
    • 1970-01-01
    • 1970-01-01
    • 2021-07-05
    • 1970-01-01
    • 2020-12-30
    • 1970-01-01
    相关资源
    最近更新 更多