如何在GCC,Windows XP,x86中编写缓冲区溢出漏洞?

taor4pac  于 10个月前  发布在  Windows
关注(0)|答案(7)|浏览(218)
void function(int a, int b, int c) {
   char buffer1[5];
   char buffer2[10];
   int *ret;

   ret = buffer1 + 12;
   (*ret) += 8;//why is it 8??
}

void main() {
  int x;

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

字符串
上面的demo来自这里:
http://insecure.org/stf/smashstack.html
但在这里行不通:

D:\test>gcc -Wall -Wextra hw.cpp && a.exe
hw.cpp: In function `void function(int, int, int)':
hw.cpp:6: warning: unused variable 'buffer2'
hw.cpp: At global scope:
hw.cpp:4: warning: unused parameter 'a'
hw.cpp:4: warning: unused parameter 'b'
hw.cpp:4: warning: unused parameter 'c'
1


我不明白为什么是8,但作者认为:
一个小的数学告诉我们距离是8字节。
我的gdb dump调用如下:

Dump of assembler code for function main:
0x004012ee <main+0>:    push   %ebp
0x004012ef <main+1>:    mov    %esp,%ebp
0x004012f1 <main+3>:    sub    $0x18,%esp
0x004012f4 <main+6>:    and    $0xfffffff0,%esp
0x004012f7 <main+9>:    mov    $0x0,%eax
0x004012fc <main+14>:   add    $0xf,%eax
0x004012ff <main+17>:   add    $0xf,%eax
0x00401302 <main+20>:   shr    $0x4,%eax
0x00401305 <main+23>:   shl    $0x4,%eax
0x00401308 <main+26>:   mov    %eax,0xfffffff8(%ebp)
0x0040130b <main+29>:   mov    0xfffffff8(%ebp),%eax
0x0040130e <main+32>:   call   0x401b00 <_alloca>
0x00401313 <main+37>:   call   0x4017b0 <__main>
0x00401318 <main+42>:   movl   $0x0,0xfffffffc(%ebp)
0x0040131f <main+49>:   movl   $0x3,0x8(%esp)
0x00401327 <main+57>:   movl   $0x2,0x4(%esp)
0x0040132f <main+65>:   movl   $0x1,(%esp)
0x00401336 <main+72>:   call   0x4012d0 <function>
0x0040133b <main+77>:   movl   $0x1,0xfffffffc(%ebp)
0x00401342 <main+84>:   mov    0xfffffffc(%ebp),%eax
0x00401345 <main+87>:   mov    %eax,0x4(%esp)
0x00401349 <main+91>:   movl   $0x403000,(%esp)
0x00401350 <main+98>:   call   0x401b60 <printf>
0x00401355 <main+103>:  leave
0x00401356 <main+104>:  ret
0x00401357 <main+105>:  nop
0x00401358 <main+106>:  add    %al,(%eax)
0x0040135a <main+108>:  add    %al,(%eax)
0x0040135c <main+110>:  add    %al,(%eax)
0x0040135e <main+112>:  add    %al,(%eax)
End of assembler dump.

Dump of assembler code for function function:
0x004012d0 <function+0>:        push   %ebp
0x004012d1 <function+1>:        mov    %esp,%ebp
0x004012d3 <function+3>:        sub    $0x38,%esp
0x004012d6 <function+6>:        lea    0xffffffe8(%ebp),%eax
0x004012d9 <function+9>:        add    $0xc,%eax
0x004012dc <function+12>:       mov    %eax,0xffffffd4(%ebp)
0x004012df <function+15>:       mov    0xffffffd4(%ebp),%edx
0x004012e2 <function+18>:       mov    0xffffffd4(%ebp),%eax
0x004012e5 <function+21>:       movzbl (%eax),%eax
0x004012e8 <function+24>:       add    $0x5,%al
0x004012ea <function+26>:       mov    %al,(%edx)
0x004012ec <function+28>:       leave
0x004012ed <function+29>:       ret


在我的情况下,距离应该是- = 5,对吗?但它似乎不工作。
为什么function需要56字节的局部变量?(sub $0x38,%esp

46qrfjad

46qrfjad1#

正如joveha所指出的,call指令保存在堆栈上的EIP值(返回地址)需要增加7字节(0x00401342-0x0040133b =7),以便跳过x = 1;指令(movl $0x1,0xfffffffc(%ebp))。
你是正确的,56个字节被保留给局部变量(sub $0x38,%esp),所以缺少的部分是在堆栈上超过buffer1多少字节是保存的EIP。
一些测试代码和内联汇编告诉我,我的测试的神奇值是28。我不能提供一个明确的答案,为什么它是28,但我会假设编译器添加填充和/或堆栈金丝雀。
以下代码使用GCC 3.4.5(MinGW)编译,并在Windows XP SP3(x86)上进行了测试。

unsigned long get_ebp() {
   __asm__("pop %ebp\n\t"
           "movl %ebp,%eax\n\t"
           "push %ebp\n\t");
}

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

   /* distance in bytes from buffer1 to return address on the stack */
   printf("test %d\n", ((get_ebp() + 4) - (unsigned long)&buffer1));

   ret = (int *)(buffer1 + 28);

   (*ret) += 7;
}

void main() {
   int x;

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

字符串
我可以很容易地使用gdb来确定这个值。
(编译w/-g以包含调试符号)

(gdb) break function
...
(gdb) run
...
(gdb) p $ebp
$1 = (void *) 0x22ff28
(gdb) p &buffer1
$2 = (char (*)[5]) 0x22ff10
(gdb) quit


0x22ff28 + 4)- 0x22ff10 =28
(ebp值+字的大小)-缓冲区1的地址=字节数
除了Smashing The Stack For Fun And Profit,我建议你阅读一些我在回答你之前的问题时提到的文章和/或其他关于这个主题的材料。很好地理解这种类型的漏洞是如何工作的应该会对你有帮助。

xj3cbfub

xj3cbfub2#

很难预测buffer1 + 12真正指向什么。编译器可以将buffer1buffer2放在堆栈上的任何位置,甚至根本不为buffer2保存空间。真正知道buffer1在哪里的唯一方法是查看编译器的汇编输出,而且很有可能它会随着不同的优化设置或同一编译器的不同版本而跳来跳去。

yvt65v4c

yvt65v4c3#

我还没有在自己的机器上测试代码,但你考虑过内存对齐吗?尝试用gcc反汇编代码。我认为汇编代码可能会给予你对代码有更进一步的理解。:-)

yyhrrdl8

yyhrrdl84#

这段代码在OpenBSD和FreeBSD上也能打印出1,在Linux上则会给出一个分段错误。
这种攻击严重依赖于特定机器的指令集以及编译器和操作系统的调用约定。堆栈布局的所有内容都是由实现定义的,而不是C语言。本文假设x86上的Linux,但看起来你使用的是Windows,而且你的系统可能是64位的,尽管你可以用-m32将gcc切换到32位。
你需要调整的参数是12,这是从栈尖到返回地址的偏移量,8是你想要跳过的main的字节数。正如文章所说,你可以使用gdb来检查函数的反汇编,以查看(a)当你调用function时,栈被推了多远,以及(B)main中的指令的字节偏移。

oprakyz7

oprakyz75#

+8字节部分是他希望保存的EIP增加多少。EIP被保存是为了在function完成后程序可以返回到最后一次分配-现在他想通过向保存的EIP添加8字节来跳过它。
所以他只是想“跳过”

x = 1;

字符串
在您的例子中,保存的EIP将指向0x0040133b,即function返回后的第一条指令。要跳过赋值,您需要使保存的EIP指向0x00401342。这是7个字节。
这实际上是一个“RET EIP的混乱”,而不是一个缓冲区溢出的例子。
至于局部变量的56个字节,这可能是你的编译器提供的任何东西,比如填充,堆栈金丝雀等。
编辑:
这表明在C中制作缓冲区溢出示例是多么困难。buffer1的偏移量12假定了某种填充样式和编译选项。GCC现在很乐意插入堆栈金丝雀(它将成为一个局部变量,用于“保护”已保存的EIP),除非您告诉它不要这样做。另外,他想跳转到的新地址(printf调用的开始指令)真的必须从汇编中手动解析。在他的情况下,在他的机器上,在他的操作系统上,在他的编译器上,在那天.它是8。

7hiiyaii

7hiiyaii6#

你正在用C++编译器编译一个C程序,你会发现它可以编译。

vh0rcniy

vh0rcniy7#

8是以跳过这两条指令的方式更改返回指针的字节数:

0x80004a8 <main+24>:    addl   $0xc,%esp
0x80004ab <main+27>:    movl   $0x1,0xfffffffc(%ebp)

字符串

相关问题