| :: [etc] How i smashed the stack on fc4. :: | ||||
| HOME |
|
[Date Prev][Date Next][Date Index] [etc] How i smashed the stack on fc4.Below is the program (test.c) with the offset values set as shown. --------------SNIP--------------------
#include <stdio.h>
int offset1 = 28;
int offset2 = 13;
void function(int a, int b, int c) {
unsigned char buffer1[5] = "abcde";
int *ret,i;ret = buffer1 + offset1; (*ret) += offset2; } int main() {
int x; x = 5;
function(1,2,3);
x = 1;
printf("x should be 1 but it is %d\n",x);
}
--------------SNIP--------------------
#> gcc -o test test.c
Machine used: Linux mia.ece.uic.edu 2.6.12-1.1376_FC3 ... i686 i686 i386 GNU/Linux
Top of stack ==> 4 bytes [int i]
4 bytes [int *ret]
16 bytes [char buffer1[5]]
4 bytes [* _libc_csu_init]
4 bytes [* _GLOBAL_OFFSET_TABLE_]
4 bytes [* Frame_pointer of prev Stack frame]
4 bytes [ return address]
4 bytes [ parameter 1]
4 bytes [ parameter 2]
4 bytes [ parameter 3]
1: Compile the program using the -g option so that we can debug it using gdb. #> gcc -g -o test test.c 2: Run gdb on it as: #>gdb test --snip-- Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/tls/libthread_db.so.1". (gdb) start Breakpoint 1 at 0x80483b7: file test.c, line 16. Starting program: /home/shashank/ece594/test Reading symbols from shared object read from target memory...done. Loaded system supplied DSO at 0xb7f82000 main () at test.c:16 16 x = 5; (gdb) 3. Disassemble the main function and find out the possible return address of the call function. (gdb) disassemble main (gdb) disassemble main Dump of assembler code for function main: 0x0804839b <main+0>: push %ebp 0x0804839c <main+1>: mov %esp,%ebp 0x0804839e <main+3>: sub $0x8,%esp 0x080483a1 <main+6>: and $0xfffffff0,%esp 0x080483a4 <main+9>: mov $0x0,%eax 0x080483a9 <main+14>: add $0xf,%eax 0x080483ac <main+17>: add $0xf,%eax 0x080483af <main+20>: shr $0x4,%eax 0x080483b2 <main+23>: shl $0x4,%eax 0x080483b5 <main+26>: sub %eax,%esp 0x080483b7 <main+28>: movl $0x5,0xfffffffc(%ebp) 0x080483be <main+35>: sub $0x4,%esp 0x080483c1 <main+38>: push $0x3 0x080483c3 <main+40>: push $0x2 0x080483c5 <main+42>: push $0x1 0x080483c7 <main+44>: call 0x8048368 <function> ---------------------------------------------------- 0x080483cc .... <== This is the return address after call. ---------------------------------------------------- 0x080483cf <main+52>: movl $0x1,0xfffffffc(%ebp) 0x080483d6 <main+59>: sub $0x8,%esp 0x080483d9 <main+62>: pushl 0xfffffffc(%ebp) 0x080483dc <main+65>: push $0x80484d2 0x080483e1 <main+70>: call 0x80482b0 <_init+56> 0x080483e6 <main+75>: add $0x10,%esp 0x080483e9 <main+78>: leave 0x080483ea <main+79>: ret End of assembler dump. (gdb) 4. Stepi till u reach the first instruction in "function". --snip-- (gdb) s 17 function(1,2,3); (gdb) s function (a=1, b=2, c=3) at test.c:5 5 unsigned char buffer1[5] = "abcde"; (gdb) s 8 ret = buffer1 + offset1; ... 5. Now your stackframe has been initialized. Take a look at it. you can use gdb's "examine" command for this purpose. Here I had to do some trial and error. I knew that "buffer1 will be allocated some space. But I did not know where the "ret" and "i" will be allocated. So i wrote another small program that initializes i=0xff and examined all 40 bytes before and after the pointer to buffer1 as below: a. 40 bytes after buffer1 --snip-- gdb) x /40xb buffer1 0xbfd7fd60: 0x61 0x62 0x63 0x64 0x65 0x00 0x00 0x00 0xbfd7fd68: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xbfd7fd70: 0xec 0x83 0x04 0x08 0xd4 0x95 0x04 0x08 0xbfd7fd78: 0xa8 0xfd 0xd7 0xbf 0xcc 0x83 0x04 0x08 0xbfd7fd80: 0x01 0x00 0x00 0x00 0x02 0x00 0x00 0x00 --snip-- Note that first five bytes represent ascii values of "a, b, c, d, e" respectively. From the above I can see that 16 bytes have been reserved for buffer1 as below 0xbfd7fd60: 0x61 0x62 0x63 0x64 0x65 0x00 0x00 0x00 0xbfd7fd68: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 The next 4 bytes, i.e. "0xec 0x83 0x04 0x08" represent some address "0x080483ec". Looks like some pointer to some address. To see where it points to i do (gdb) x 0x080483ec 0x80483ec <__libc_csu_init>: 0x55 Seems like it is a pointer to some internal routine of glibc. The next 4 bytes i.e. "0xd4 0x95 0x04 0x08" again look like some pointer to something. Again (gdb) x 0x080495d4 0x80495d4 <_GLOBAL_OFFSET_TABLE_>: 0x08 shows that its the global offset table. The next 4 bytes again are a pointer "0xa8 0xfd 0xd7 0xbf" are pointer to somewhere in stack. I conclude that it is the frame pointer for the previous frame. The last four bytes "0xcc 0x83 0x04 0x08" are the return address in my main function. After this we have the parameters 1 (0x01 0x00 0x00 0x00), 2 (0x02 0x00 0x00 0x00) and 3(not shown) which are integers and hence have 4 byte wide fields.
Note: buffer1 exists between the square brackets above. The 4 bytes (0xbfd7fde0) preceeding buffer1 must be int *ret and the preceeding 4 bytes (0x0177ff8e) must be the interger i. (you can actually see that these bytes are initialized to 0xff if that is done in the program). |