How to write a buffer-overflow exploit in GCC,windows XP,x86?

自闭症网瘾萝莉.ら 提交于 2019-12-17 20:08:24

问题


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

The above demo is from here:

http://insecure.org/stf/smashstack.html

But it's not working here:

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

And I don't understand why it's 8 though the author thinks:

A little math tells us the distance is 8 bytes.

My gdb dump as called:

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

In my case the distance should be - = 5,right?But it seems not working..

Why function needs 56 bytes for local variables?( sub $0x38,%esp )


回答1:


As joveha pointed out, the value of EIP saved on the stack (return address) by the call instruction needs to be incremented by 7 bytes (0x00401342 - 0x0040133b = 7) in order to skip the x = 1; instruction (movl $0x1,0xfffffffc(%ebp)).

You are correct that 56 bytes are being reserved for local variables (sub $0x38,%esp), so the missing piece is how many bytes past buffer1 on the stack is the saved EIP.


A bit of test code and inline assembly tells me that the magic value is 28 for my test. I cannot provide a definitive answer as to why it is 28, but I would assume the compiler is adding padding and/or stack canaries.

The following code was compiled using GCC 3.4.5 (MinGW) and tested on 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);
}

I could have just as easily used gdb to determine this value.

(compiled w/ -g to include debug symbols)

(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 value + size of word) - address of buffer1 = number of bytes


In addition to Smashing The Stack For Fun And Profit, I would suggest reading some of the articles I mentioned in my answer to a previous question of yours and/or other material on the subject. Having a good understanding of exactly how this type of exploit works should help you write more secure code.




回答2:


It's hard to predict what buffer1 + 12 really points to. Your compiler can put buffer1 and buffer2 in any location on the stack it desires, even going as far as to not save space for buffer2 at all. The only way to really know where buffer1 goes is to look at the assembler output of your compiler, and there's a good chance it would jump around with different optimization settings or different versions of the same compiler.




回答3:


I do not test the code on my own machine yet, but have you taken memory alignment into consideration? Try to disassembly the code with gcc. I think a assembly code may give you a further understanding of the code. :-)




回答4:


This code prints out 1 as well on OpenBSD and FreeBSD, and gives a segmentation fault on Linux.

This kind of exploit is heavily dependent on both the instruction set of the particular machine, and the calling conventions of the compiler and operating system. Everything about the layout of the stack is defined by the implementation, not the C language. The article assumes Linux on x86, but it looks like you're using Windows, and your system could be 64-bit, although you can switch gcc to 32-bit with -m32.

The parameters you'll have to tweak are 12, which is the offset from the tip of the stack to the return address, and 8, which is how many bytes of main you want to jump over. As the article says, you can use gdb to inspect the disassembly of the function to see (a) how far the stack gets pushed when you call function, and (b) the byte offsets of the instructions in main.




回答5:


The +8 bytes part is by how much he wants the saved EIP to the incremented with. The EIP was saved so the program could return to the last assignment after the function is done - now he wants to skip over it by adding 8 bytes to the saved EIP.

So all he tries to is to "skip" the

x = 1;

In your case the saved EIP will point to 0x0040133b, the first instruction after function returns. To skip the assignment you need to make the saved EIP point to 0x00401342. That's 7 bytes.

It's really a "mess with RET EIP" rather than an buffer overflow example.

And as far as the 56 bytes for local variables goes, that could be anything your compiler comes up with like padding, stack canaries, etc.

Edit:

This shows how difficult it is to make buffer overflows examples in C. The offset of 12 from buffer1 assumes a certain padding style and compile options. GCC will happily insert stack canaries nowadays (which becomes a local variable that "protects" the saved EIP) unless you tell it not to. Also, the new address he wants to jump to (the start instruction for the printf call) really has to be resolved manually from assembly. In his case, on his machie, with his OS, with his compiler, on that day.... it was 8.




回答6:


You're compiling a C program with the C++ compiler. Rename hw.cpp to hw.c and you'll find it will compile.



来源:https://stackoverflow.com/questions/2543725/how-to-write-a-buffer-overflow-exploit-in-gcc-windows-xp-x86

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!