繁体   English   中英

C程序的主要功能是否曾经回收过堆栈?

[英]Does the main function of a C program ever reclaim the stack?

我正在OverTheWire战争游戏中工作,我的一项攻击行为是用system地址覆盖main的返回地址。 然后,我使用了这样一个事实,即在main返回时, esp仍指向我的局部变量之一,因此我可以用我希望system运行的命令(例如sh;# )填充它。

我的困惑来自于我认为C语言中的函数在返回之前会回收堆栈,因此在该点返回地址称为堆栈指针将指向返回地址,而不是局部变量。 但是,我的漏洞利用仍然有效,因此似乎在调用返回地址时,我的堆栈指针指向了局部变量。

与其他挑战相比,与该挑战相比,我注意到的主要事情是,它在结尾处调用exit(0)而不是仅仅结束,因此程序集不会以leave结尾,这可能就是这种行为的原因。

我没有包含实际的代码,因为它已经很长了,我希望对所看到的内容有一个一般的解释,但是请告诉我汇编是否有用。

#include <stdio.h>
int main ( void )
{
    printf("hello\n");
    return(0);
}

有趣的相关部分。

0000000000400430 <main>:
  400430:   48 83 ec 08             sub    $0x8,%rsp
  400434:   bf d4 05 40 00          mov    $0x4005d4,%edi
  400439:   e8 c2 ff ff ff          callq  400400 <puts@plt>
  40043e:   31 c0                   xor    %eax,%eax
  400440:   48 83 c4 08             add    $0x8,%rsp
  400444:   c3                      retq   
  400445:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  40044c:   00 00 00 
  40044f:   90                      nop



0000000000400450 <_start>:
  400450:   31 ed                   xor    %ebp,%ebp
  400452:   49 89 d1                mov    %rdx,%r9
  400455:   5e                      pop    %rsi
  400456:   48 89 e2                mov    %rsp,%rdx
  400459:   48 83 e4 f0             and    $0xfffffffffffffff0,%rsp
  40045d:   50                      push   %rax
  40045e:   54                      push   %rsp
  40045f:   49 c7 c0 c0 05 40 00    mov    $0x4005c0,%r8
  400466:   48 c7 c1 50 05 40 00    mov    $0x400550,%rcx
  40046d:   48 c7 c7 30 04 40 00    mov    $0x400430,%rdi
  400474:   e8 97 ff ff ff          callq  400410 <__libc_start_main@plt>
  400479:   f4                      hlt    
  40047a:   66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

在大多数情况下,main和printf都没有什么特别的,等等,这些只是符合调用约定的函数。 正如重新提出的SO问题将显示的那样,有时编译器在看到main()否则会添加额外的堆栈或其他调用。 但是它仍然是一个需要符合调用约定的函数。 如本例所示,将堆栈指针放回找到的位置。

在操作系统(Linux,Windows,MacOS等)甚至无法考虑运行某个程序之前,它需要为该程序分配一些空间并根据处理器和OS的功能以某种方式为该程序标记该内存,等。然后从任何媒体加载程序,然后在指定的二进制文件和/或众所周知的入口点启动它。 程序的干净退出将使操作系统释放该内存,.text,.data,.bss和stack是琐碎/显而易见的,随着内存的消失而消失。 可以/应该释放可能已经分配并与此程序相关联的其他项目,打开文件,运行时分配的(而非堆栈)内存等,这取决于os和/或C库的设计,如何实现。

在上述情况下,我们看到引导程序调用main和main返回,然后命中了hlt,这是一个应用程序而不是内核代码,因此应该引起陷阱,导致操作系统清理。 显式exit()应该与printf()或puts()或fopen()或最终对操作系统进行一个或多个syscall的任何其他函数没有什么不同。 对于这些类型的操作系统(Linux,Windows,MacOS),您可能会找到的只是syscall。 内存的释放发生在程序外部,因为程序无法控制它,这可能是鸡和鸡蛋的问题,程序会释放用于释放mmu表的mmu表...

为主要而不是整个程序编译和反汇编对象

0000000000000000 <main>:
   0:   48 83 ec 08             sub    $0x8,%rsp
   4:   bf 00 00 00 00          mov    $0x0,%edi
   9:   e8 00 00 00 00          callq  e <main+0xe>
   e:   31 c0                   xor    %eax,%eax
  10:   48 83 c4 08             add    $0x8,%rsp
  14:   c3                      retq 

与以前一样,这并不奇怪,我们所需要了解的是,在返回之前已清除了堆栈。 而且那个主并不特殊:

#include <stdio.h>
int notmain ( void )
{
    printf("hello\n");
    return(0);
}

0000000000000000 <notmain>:
   0:   48 83 ec 08             sub    $0x8,%rsp
   4:   bf 00 00 00 00          mov    $0x0,%edi
   9:   e8 00 00 00 00          callq  e <notmain+0xe>
   e:   31 c0                   xor    %eax,%eax
  10:   48 83 c4 08             add    $0x8,%rsp
  14:   c3                      retq   

现在,如果您要问main中是否存在exit(),请确保它不会命中main中的返回点,以便使堆栈指针偏移任意量。 但是,如果main调用某个函数,而该函数调用某个函数,则该函数调用exit(),则将堆栈指针留在第二个函数的堆栈帧点处,再加上任何调用(这是x86)加上exit()堆栈框架添加到它。 您不能简单地假设在调用exit()时(如果调用)是堆栈指针指向的内容。 您必须检查对exit()的调用,exit()代码及其所调用的任何内容的反汇编,才能弄清楚这一点。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM