堆栈粉碎代码在 Linux 内核 2.6.38.7 上不起作用...请帮助

Posted

技术标签:

【中文标题】堆栈粉碎代码在 Linux 内核 2.6.38.7 上不起作用...请帮助【英文标题】:Stack smashing code not working on Linux kernel 2.6.38.7... Please help 【发布时间】:2011-10-31 06:46:45 【问题描述】:

我一直在阅读“The Shellcoders Handbook”并参考this 链接来练习堆栈溢出。但似乎 Linux 内核开发人员已经使内核非常安全。这是我的问题。

1) 这段代码

void function(int a, int b, int c) 
   char buffer1[8];
   char buffer2[10];
   int* ret;

   ret = buffer1 + 6;
   *ret+=8;


void main() 
  int x;

  x = 0;
  function(1,2,3);
  x = 1;
  printf("%d\n",x);

给出输出

$ cc smash.c
smash.c: In function ‘function’:
smash.c:7:8: warning: assignment from incompatible pointer type
$ ./a.out
1

但是用*ret=8 替换行*ret+=8 会得到以下输出

*** stack smashing detected ***: ./a.out terminated
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(__fortify_fail+0x50)[0xa86df0]
/lib/i386-linux-gnu/libc.so.6(+0xe5d9a)[0xa86d9a]
./a.out[0x8048448]
./a.out[0x8048477]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xe7)[0x9b7e37]
./a.out[0x8048381]
======= Memory map: ========
003df000-003e0000 r-xp 00000000 00:00 0          [vdso]
009a1000-00afb000 r-xp 00000000 08:01 3277633    /lib/i386-linux-gnu/libc-2.13.so
00afb000-00afc000 ---p 0015a000 08:01 3277633    /lib/i386-linux-gnu/libc-2.13.so
00afc000-00afe000 r--p 0015a000 08:01 3277633    /lib/i386-linux-gnu/libc-2.13.so
...
...

如果我将ret = buffer1 + 6 替换为ret = buffer1 + 7,结果与上面相同。 如果我将ret = buffer1 + 6 替换为ret=buffer1+8(或任何更大的值),则上述两种情况的堆栈都会被破坏(即我是否将值*ret 增加8 或将其更改为8)。

请告诉我这是怎么发生的。有用的链接也将不胜感激。 最重要的是,我怎样才能禁用 Linux 内核的这个安全功能,以便我可以使用这本书?

平台:i386 内核:2.6.38

【问题讨论】:

【参考方案1】:

要禁用堆栈粉碎检测,请在编译时使用-fno-stack-protector。在阅读“The Shellcoders Handbook”时,您可能还想使用 -ggdb 和 -mpreferred-stack-boundary=4 来启用 GDB 符号并标准化堆栈。

编辑: 当我编译您提供的代码 (gcc -fno-stack-protector -ggdb -mpreferred-stack-boundary=4 -o sc in.c) 时,编译器重新排列了 function 中局部变量的顺序。我通过使用 GDB 发现了这一点:

willi@ubuntu:~/testing$ gdb sc
(gdb) set disassembly-flavor intel
(gdb) disassemble function
Dump of assembler code for function function:
   0x080483c4 <+0>: push   ebp
   0x080483c5 <+1>: mov    ebp,esp
   0x080483c7 <+3>: sub    esp,0x20
   0x080483ca <+6>: lea    eax,[ebp-0xc]
   0x080483cd <+9>: add    eax,0x6
   0x080483d0 <+12>:    mov    DWORD PTR [ebp-0x4],eax
   0x080483d3 <+15>:    mov    eax,DWORD PTR [ebp-0x4]
   0x080483d6 <+18>:    mov    eax,DWORD PTR [eax]
   0x080483d8 <+20>:    lea    edx,[eax+0x8]
   0x080483db <+23>:    mov    eax,DWORD PTR [ebp-0x4]
   0x080483de <+26>:    mov    DWORD PTR [eax],edx
   0x080483e0 <+28>:    leave  
   0x080483e1 <+29>:    ret    
End of assembler dump.

0x080483ca 告诉我 ebp - 0xC 是 buffer1,而 0x080483d0 告诉我 ebp - 0x4 是 ret。因此,变量不存在于堆栈中,因为它们存在于我们的 C 代码中。鉴于ret 是我们最顶层的局部变量,我们可以直接使用它。不过,让我们使用您的代码。

要修改返回指针,我们需要更改保存的 ebp 正下方存储的地址,即ebp + 0x4。因此,要从我们的变量 buffer1 中获取返回指针,我们必须添加 0xC(以获取 ebp),然后添加 0x4(返回指针是 ebp 下的 0x4)。现在我们可以进行修改了。

我从您的 C 代码中获取您希望跳过分配 x = 1 并直接返回到 printf 的内容。我反汇编了main,找到了对返回指针的适当修改:

(gdb) disassemble main
Dump of assembler code for function main:
   0x080483e2 <+0>: push   ebp
   0x080483e3 <+1>: mov    ebp,esp
   0x080483e5 <+3>: and    esp,0xfffffff0
   0x080483e8 <+6>: sub    esp,0x20
   0x080483eb <+9>: mov    DWORD PTR [esp+0x1c],0x0
   0x080483f3 <+17>:    mov    DWORD PTR [esp+0x8],0x3
   0x080483fb <+25>:    mov    DWORD PTR [esp+0x4],0x2
   0x08048403 <+33>:    mov    DWORD PTR [esp],0x1
   0x0804840a <+40>:    call   0x80483c4 <function>
   0x0804840f <+45>:    mov    DWORD PTR [esp+0x1c],0x1
   0x08048417 <+53>:    mov    eax,DWORD PTR [esp+0x1c]
   0x0804841b <+57>:    mov    DWORD PTR [esp+0x4],eax
   0x0804841f <+61>:    mov    DWORD PTR [esp],0x80484f0
   0x08048426 <+68>:    call   0x80482f4 <printf@plt>
   0x0804842b <+73>:    leave  
   0x0804842c <+74>:    ret    
End of assembler dump.

不修改返回指针,在0x0804840a处对function的调用返回到0x0804840f。但是我们想跳过这个并返回下一条指令。下一条指令从 0x08048417 开始,也就是 0x8 字节。所以我们对返回指针的修改必须将它的值增加0x8。

考虑到这些因素,我使用以下代码打印“0”而不是“1”:

void function(int a, int b, int c) 
   char buffer1[8];
   char buffer2[10];
   int* ret;

   ret = buffer1 + 0x10;
   *ret+=8;


void main() 
  int x;

  x = 0;
  function(1,2,3);
  x = 1;
  printf("%d\n",x);

【讨论】:

好答案,但我不知道t understand one thing. Why for getting return address from function`你加0x20x20xc(关于0xc我明白,但不是0x2)?跨度> ebp 存放保存的栈指针,返回地址直接存放在其下。所以我们需要在保存的栈指针地址上加上sizeof(void *),得到返回地址的地址。我更新了帖子以使用0x4 而不是0x2 来反映sizeof(void *)(不知道为什么我有0x2)。【参考方案2】:

可以像这样禁用堆栈粉碎保护:

$ gcc -ggdb -m32 -o buffer1 -fno-stack-protector -mpreferred-stack-boundary=4 buffer1.c

This link 提供有关 Linux 安全功能的更多信息

【讨论】:

以上是关于堆栈粉碎代码在 Linux 内核 2.6.38.7 上不起作用...请帮助的主要内容,如果未能解决你的问题,请参考以下文章

为啥粉碎后没有立即出现“检测到堆栈粉碎”?

Java Interposer 中的堆栈粉碎

在函数中操作int的数组时堆栈粉碎错误

使用 c++ 偶尔检测到 *** 堆栈粉碎***

检测到堆栈粉碎 glGetTexImage

代码在我的系统上运行良好,但是当我将它提交给应该检查它的机器人时会导致堆栈粉碎错误