【问题标题】:How to skip a line doing a buffer overflow in C如何在 C 中跳过执行缓冲区溢出的行
【发布时间】:2011-07-13 22:33:52
【问题描述】:

我想跳过C语言中的一行,主要部分中的x=1;行使用bufferoverflow;但是,我不知道为什么我不能将地址从4002f4 跳过到下一个地址4002fb,尽管我从<main+35><main+42> 计算了7 个字节。

我还在 Debian 和 AMD 环境中配置了 randomniZation 和 execstack 环境选项,但我仍然收到x=1;。这个过程有什么问题?

我用dba调试过栈和内存地址:

0x00000000004002ef <main+30>:    callq  0x4002a4 **<function>**  
**0x00000000004002f4** <main+35>:    movl   $0x1,-0x4(%rbp)  
**0x00000000004002fb** <main+42>:    mov    -0x4(%rbp),%esi  
0x00000000004002fe <main+45>:    mov    $0x4629c4,%edi  

void function(int a, int b, int c)  
{
  char buffer[5];
  int *ret;

  ret = buffer + 12;
  (*ret) += 8; 
}

int main()
{
   int x = 0; 
   function(1, 2, 3);
   x = 1;
   printf("x = %i \n", x);  
   return 0;  
}

【问题讨论】:

  • 我不确定我是否应该帮助您调试您的攻击策略,但您应该转储 x/i10 堆栈 $esp 并将其添加到您的解释中。还显示当您从 function 开始向前执行指令时会发生什么
  • 我建议您忽略程序的输出,并在调试器中单步执行它,一次一条汇编指令。您将在每一步看到它实际在做什么(寄存器值、堆栈),这将告诉您为什么它没有按照您的预期进行。
  • 请解释你为什么要这样做。
  • 过去,恶意软件作者在早餐前写了缓冲区溢出。现在他们在 Stackoverflow 上发布问题。我问世界也将要发生什么?
  • @jim:根据我的教授的说法,这是一个老问题,大多数操作系统都有避免这种攻击的策略,我只是一个试图了解指针如何通过寄存器移动的学生

标签: c buffer-overflow cracking


【解决方案1】:

您一定正在阅读Smashing the Stack for Fun and Profit 文章。我正在阅读同一篇文章,并发现了同样的问题,它没有跳过该指令。在 IDA 中进行了几个小时的调试会话后,我更改了如下代码,它正在打印 x=0 和 b=5。

#include <stdio.h>

void function(int a, int b) {
     int c=0;
     int* pointer;

     pointer =&c+2;
     (*pointer)+=8;
}

void main() {
  int x =0;
  function(1,2);
  x = 3;
  int b =5;
  printf("x=%d\n, b=%d\n",x,b);
  getch();
}

【讨论】:

    【解决方案2】:

    要更改function() 中的返回地址以跳过main() 中的x = 1,您需要两条信息。

    1。返回地址在栈帧中的位置。

    我使用 gdb 来确定这个值。我在function() (break function) 处设置断点,执行代码到断点 (run),检索当前堆栈在内存中的位置帧(p $rbpinfo reg),然后检索内存中 buffer 的位置(p &amp;buffer)。使用检索到的值,可以确定返回地址的位置。

    (使用 GCC -g 标志编译以包含调试符号并在 64 位环境中执行)

    (gdb) break function
    ...
    (gdb) run
    ...
    (gdb) p $rbp
    $1 = (void *) 0x7fffffffe270
    (gdb) p &buffer
    $2 = (char (*)[5]) 0x7fffffffe260
    (gdb) quit
    

    (帧指针地址 + 字大小)- 缓冲区地址 = 从本地缓冲区变量到返回地址的字节数
    (0x7fffffffe270 + 8) - 0x7fffffffe260 = 24

    如果您难以理解调用堆栈的工作原理,阅读 call stackfunction prologue 维基百科文章可能会有所帮助。这显示了在 C 中制作“缓冲区溢出”示例的难度。buffer 的偏移量 24 假定了某种填充样式和编译选项。 GCC 现在很乐意插入stack canaries,除非你告诉它不要这样做。

    2。要添加到返回地址以跳过 x = 1 的字节数。

    在您的情况下,保存的指令指针将指向0x00000000004002f4 (&lt;main+35&gt;),即函数返回后的第一条指令。要跳过分配,您需要使保存的指令指针指向0x00000000004002fb (&lt;main+42&gt;)。

    您认为这是 7 个字节的计算是正确的 (0x4002fb - 0x4002fb = 7)。

    我使用 gdb 反汇编应用程序 (disas main) 并验证了我的案例的计算。最好通过检查反汇编手动解决此值。


    请注意,我使用的是 Ubuntu 10.10 64 位环境来测试以下代码。

    #include <stdio.h>
    
    void function(int a, int b, int c)  
    {
        char buffer[5];
        int *ret;
    
        ret = (int *)(buffer + 24);
        (*ret) += 7; 
    }
    
    int main()
    {
         int x = 0; 
         function(1, 2, 3);
         x = 1;
         printf("x = %i \n", x);  
         return 0;  
    }
    

    输出

    x = 0


    这实际上只是改变了function() 的返回地址,而不是实际的缓冲区溢出。在实际的缓冲区溢出中,您将溢出buffer[5] 以覆盖返回地址。但是,大多数现代实现使用诸如 stack canaries 之类的技术来防止这种情况发生。

    【讨论】:

    • 更正:ret = (int *)(buffer + 24); 应该是 ret = (int *)(buffer + 6); C 将 24 乘以 4 得到一个兼容的指针,得到 96。但偏移量是“24”,因此我们需要 6 而不是 24(6* 4=24)。您也可以使用传递的参数的位置(在本例中为 a、b、c)来到达堆栈上所需的位置,而不是声明缓冲区。 @jschmier
    • (不确定我是否标记了你)
    【解决方案3】:

    您在这里所做的似乎与经典的缓冲区溢出攻击无关。缓冲区溢出攻击的整个想法是修改“函数”的返回地址。反汇编您的程序将显示ret 指令(假设x86)从哪里获取地址。这就是你需要修改的指向main+42

    我假设你想在这里显式地引发缓冲区溢出,通常你需要通过操纵“函数”的输入来引发它。

    通过声明buffer[5],您将堆栈指针移动到错误的方向(通过查看生成的程序集来验证这一点),返回地址位于堆栈内部更深处的某个位置(它被调用指令放在那里)。在 x86 中,堆栈向下增长,即朝向较低地址。

    我会通过声明int* 并将其向上移动直到我到达返回地址被推送的指定地址来解决这个问题,然后将该值修改为指向main+42 并让函数ret .

    【讨论】:

    • 我不确定有多少字节指针从一个寄存器移动到另一个寄存器,所以我不知道这些行是否 - ret = buffer + 12; (*ret) += 8; - 和数字 12 和 8 并正确跳过 x=1;在地址 0x00000000004002fb
    • @tony "多少字节指针从一个寄存器移动到另一个"你很困惑。
    【解决方案4】:

    你不能这样做。 这是一个经典的缓冲区溢出代码示例。看看当你用键盘输入 5 个然后 6 个字符后会发生什么。如果你想要更多(16 个字符应该这样做),你将覆盖基指针,然后函数返回地址,你会得到分段错误。您要做的是找出哪些 4 个字符覆盖了返回地址。并使程序执行您的代码。 Google 围绕 linux 堆栈、内存结构。

     void ff(){
         int a=0; char b[5];
         scanf("%s",b);
         printf("b:%x a:%x\n" ,b ,&a);
         printf("b:'%s' a:%d\n" ,b ,a);
     }
    
     int main() {
         ff();
         return 0;
     }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-06-06
      • 1970-01-01
      • 2021-12-03
      • 2012-02-05
      • 1970-01-01
      • 2011-01-06
      • 2011-09-07
      • 1970-01-01
      相关资源
      最近更新 更多