如何在 C 中跳过缓冲区溢出行

发布于 2024-10-21 17:38:39 字数 878 浏览 3 评论 0原文

我想跳过 C 中的一行,即主部分中的行 x=1; 使用 bufferoverflow;但是,我不知道为什么我无法从 4002f4 跳过地址到下一个地址 4002fb,尽管我正在计算 中的 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;  
}

I want to skip a line in C, the line x=1; in the main section using bufferoverflow; however, I don't know why I can not skip the address from 4002f4 to the next address 4002fb in spite of the fact that I am counting 7 bytes form <main+35> to <main+42>.

I also have configured the options the randomniZation and execstack environment in a Debian and AMD environment, but I am still getting x=1;. What it's wrong with this procedure?

I have used dba to debug the stack and the memory addresses:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

行雁书 2024-10-28 17:38:39

您一定正在阅读为了乐趣和利润而粉碎堆栈文章。我正在阅读同一篇文章,发现了同样的问题,它没有跳过该指令。在 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();
}

You must be reading Smashing the Stack for Fun and Profit article. I was reading the same article and have found the same problem it wasnt skipping that instruction. After a few hours debug session in IDA I have changed the code like below and it is printing x=0 and 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();
}
末が日狂欢 2024-10-28 17:38:39

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

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

我使用 gdb 来确定这个值。我在 function() 处设置断点(break function),执行代码直至断点(run< /code>),检索当前堆栈帧在内存中的位置(p $rbpinfo reg ),然后检索buffer在内存中的位置(p &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

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

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

在您的情况下,保存的指令指针将指向 0x00000000004002f4 (

),即函数返回后的第一条指令。要跳过分配,您需要使保存的指令指针指向 0x00000000004002fb (
)。

您计算出的 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] 来覆盖返回地址。然而,大多数现代实现都使用诸如堆栈金丝雀之类的技术来防止这种情况。

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.

稚然 2024-10-28 17:38:39

您在这里所做的事情似乎与经典的缓冲区溢出攻击没有太大关系。缓冲区溢出攻击的整个思想是修改“function”的返回地址。反汇编程序将显示 ret 指令(假设为 x86)从何处获取其地址。您需要修改此内容以指向 main+42

我假设您想在此处显式引发缓冲区溢出,通常您需要通过操作“函数”的输入来引发它。

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

我的方法是声明一个 int* 并将其向上移动,直到到达已推送返回地址的指定地址,然后修改该值以指向 main+42 并让函数ret

What you're doing here doesn't seem to have much todo with a classic bufferoverflow attack. The whole idea of a bufferoverflow attack is to modify the return adress of 'function'. Disassembling your program will show you where the ret instruction (assuming x86) takes its adress from. This is what you need to modify to point at main+42.

I assume you want to explicitly provoke the bufferoverflow here, normally you'd need to provoke it by manipulating the inputs of 'function'.

By just declaring a buffer[5] you're moving the stackpointer in the wrong direction (verify this by looking at the generated assembly), the return adress is somewhere deeper inside in the stack (it was put there by the call instruction). In x86 stacks grow downwards, that is towards lower adresses.

I'd approach this by declaring an int* and moving it upward until I'm at the specified adress where the return adress has been pushed, then modify that value to point at main+42 and let function ret.

心如狂蝶 2024-10-28 17:38:39

你不能这样做。
这是一个经典的缓冲区溢出代码示例。看看从键盘输入 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;
 }

You can't do that this way.
Here's a classic bufferoverflow code sample. See what happens once you feed it with 5 and then 6 characters from your keyboard. If you go for more (16 chars should do) you'll overwrite base pointer, then function return address and you'll get segmentation fault. What you want to do is to figure out which 4 chars overwrite the return addr. and make the program execute your code. Google around linux stack, memory structure.

 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;
 }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文