12

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

我还在 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;  
}
4

4 回答 4

4

In order to alter the return address within function() to skip over the x = 1 in main(), you need two pieces of information.

1. The location of the return address in the stack frame.

I used gdb to determine this value. I set a breakpoint at function() (break function), execute the code up to the breakpoint (run), retrieve the location in memory of the current stack frame (p $rbp or info reg), and then retrieve the location in memory of buffer (p &buffer). Using the retrieved values, the location of the return address can be determined.

(compiled w/ GCC -g flag to include debug symbols and executed in a 64-bit environment)

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

(frame pointer address + size of word) - buffer address = number of bytes from local buffer variable to return address
(0x7fffffffe270 + 8) - 0x7fffffffe260 = 24

If you are having difficulties understanding how the call stack works, reading the call stack and function prologue Wikipedia articles may help. This shows the difficulty in making "buffer overflow" examples in C. The offset of 24 from buffer assumes a certain padding style and compile options. GCC will happily insert stack canaries nowadays unless you tell it not to.

2. The number of bytes to add to the return address to skip over x = 1.

In your case the saved instruction pointer will point to 0x00000000004002f4 (<main+35>), the first instruction after function returns. To skip the assignment you need to make the saved instruction pointer point to 0x00000000004002fb (<main+42>).

Your calculation that this is 7 bytes is correct (0x4002fb - 0x4002fb = 7).

I used gdb to disassemble the application (disas main) and verified the calculation for my case as well. This value is best resolved manually by inspecting the disassembly.


Note that I used a Ubuntu 10.10 64-bit environment to test the following code.

#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;  
}

output

x = 0


This is really just altering the return address of function() rather than an actual buffer overflow. In an actual buffer overflow, you would be overflowing buffer[5] to overwrite the return address. However, most modern implementations use techniques such as stack canaries to protect against this.

于 2011-04-06T18:28:48.507 回答
4

您一定正在阅读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();
}
于 2011-03-25T22:52:49.523 回答
2

您在这里所做的似乎与经典的缓冲区溢出攻击没有太大关系。缓冲区溢出攻击的整个想法是修改“函数”的返回地址。反汇编你的程序会告诉你ret指令(假设是 x86)从哪里获取它的地址。这是您需要修改以指向main+42.

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

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

我会通过声明 anint*并将其向上移动来解决此问题,直到我到达已推送返回地址的指定地址,然后修改该值以指向main+42和 let function ret

于 2011-03-12T07:17:15.247 回答
1

你不能这样做。这是一个经典的缓冲区溢出代码示例。看看当你用键盘输入 5 个然后 6 个字符后会发生什么。如果你想要更多(16 个字符应该这样做),你将覆盖基指针,然后函数返回地址,你会得到分段错误。您要做的是找出哪些 4 个字符覆盖了返回地址。并使程序执行您的代码。谷歌围绕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;
 }
于 2011-03-12T15:53:41.657 回答