zoukankan      html  css  js  c++  java
  • Linux下栈溢出的初步之控制程序执行流程

    昨天看了最基本的Linux下的栈溢出,今天用一个实例来练习一下一个Crackme

    serial.c

    #include<stdlib.h>
    #include<stdio.h>
    #include<string.h>
    int valid_serial(char * psz)
    {
        size_t len=strlen(psz);
        unsigned total=0;
        size_t i;
        if(len<10)
            return 0;
        for(i=0;i<len;i++)
        {
            if((psz[i]<'0')||(psz[i]>'z'))
                return 0;
            total+=psz[i];
        }
        if(total%853==83)
            return 1;
        return 0;
    }
    int validate_serial()
    {
        char serial[24];
        fscanf(stdin,"%s",serial);
        if(valid_serial(serial))
           return 1;
        else
           return 0;
    }
    int do_valid_stuff()
    {
        printf("The serial number is valid!\n");
        exit(0);
    }
    int do_invalid_stuff()
    {
        printf("Invaild serial number!\nExiting...\n");
        exit(1);
    }
    int main()
    {
        if(validate_serial())
          do_valid_stuff();
         else
          do_invalid_stuff();
         return 0;
    }

     输入正确的序列号可以通过验证,这里在使用fscanf()函数的时候直接将用户的输入保存在了serial[24]这个数组中而没有验证大小,所以存在溢出。

    尝试用44个‘a’时程序崩溃

    root@bt:~/Desktop/code# ./serial
    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    Invaild serial number!
    Exiting...
    root@bt:~/Desktop/code# ./serial
    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    Segmentation fault

    现在用Gdb调试:

    查看main函数:

    (gdb) disas main
    Dump of assembler code for function main:
    0x08048551 <+0>: push %ebp
    0x08048552 <+1>: mov %esp,%ebp
    0x08048554 <+3>: sub $0x8,%esp
    0x08048557 <+6>: and $0xfffffff0,%esp
    0x0804855a <+9>: mov $0x0,%eax
    0x0804855f <+14>: add $0xf,%eax
    0x08048562 <+17>: add $0xf,%eax
    0x08048565 <+20>: shr $0x4,%eax
    0x08048568 <+23>: shl $0x4,%eax
    0x0804856b <+26>: sub %eax,%esp
    0x0804856d <+28>: call 0x80484cf <validate_serial>
    0x08048572 <+33>: test %eax,%eax
    0x08048574 <+35>: je 0x804857d <main+44>
    0x08048576 <+37>: call 0x8048515 <do_valid_stuff>
    0x0804857b <+42>: jmp 0x8048582 <main+49>
    0x0804857d <+44>: call 0x8048533 <do_invalid_stuff>
    0x08048582 <+49>: mov $0x0,%eax
    0x08048587 <+54>: leave
    0x08048588 <+55>: ret
    End of assembler dump.

    可以看到在0x0804856d处调用了validate_serial()函数,假如我们把返回地址覆盖成0x08048576 ,即让验证程序直接返回到通过验证do_valid_stuff()这个分支。

    有了这个想法我们先找一下溢出点:

    (gdb) disas validate_serial
    Dump of assembler code for function validate_serial:
    0x080484cf <+0>: push %ebp
    0x080484d0 <+1>: mov %esp,%ebp
    0x080484d2 <+3>: sub $0x48,%esp
    0x080484d5 <+6>: lea -0x28(%ebp),%eax
    0x080484d8 <+9>: mov %eax,0x8(%esp)
    0x080484dc <+13>: movl $0x804864c,0x4(%esp)
    0x080484e4 <+21>: mov 0x80497a4,%eax
    0x080484e9 <+26>: mov %eax,(%esp)
    0x080484ec <+29>: call 0x804834c <__isoc99_fscanf@plt>
    0x080484f1 <+34>: lea -0x28(%ebp),%eax
    0x080484f4 <+37>: mov %eax,(%esp)
    0x080484f7 <+40>: call 0x804842c <valid_serial>
    0x080484fc <+45>: test %eax,%eax
    0x080484fe <+47>: je 0x8048509 <validate_serial+58>
    0x08048500 <+49>: movl $0x1,-0x2c(%ebp)
    0x08048507 <+56>: jmp 0x8048510 <validate_serial+65>
    0x08048509 <+58>: movl $0x0,-0x2c(%ebp)
    0x08048510 <+65>: mov -0x2c(%ebp),%eax
    0x08048513 <+68>: leave
    0x08048514 <+69>: ret
    End of assembler dump.

    给0x080484ec下断点,

    (gdb) break *0x080484ec
    Breakpoint 1 at 0x80484ec

    运行程序:

    (gdb) run
    Starting program: /root/Desktop/code/serial

    Breakpoint 1, 0x080484ec in validate_serial ()
    (gdb) x/20x $esp
    0xbffff4c0: 0xb7fc9440 0x0804864c 0xbffff4e0 0xb7fc8ff4
    0xbffff4d0: 0xb7f77d19 0xb7ea22a5 0xbffff4e8 0xb7e899d5
    0xbffff4e0: 0xb7fc8ff4 0x08049774 0xbffff4f8 0x08048338
    0xbffff4f0: 0xb7ff1030 0x08049774 0xbffff528 0x080485b9
    0xbffff500: 0xb7fc9324 0xb7fc8ff4 0xbffff528 0x08048572


    可以看到现在加粗的地方就是我们要覆盖的返回地址,输入43个‘a’。

    (gdb) continue
    Continuing.
    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

    Breakpoint 2, 0x08048514 in validate_serial ()

    查看堆栈:

    (gdb) x/20x 0xbffff4c0
    0xbffff4c0: 0xbffff4e0 0x0804864c 0xbffff4e0 0xb7fc8ff4
    0xbffff4d0: 0xb7f77d19 0xb7ea22a5 0xbffff4e8 0x00000000
    0xbffff4e0: 0x61616161 0x61616161 0x61616161 0x61616161
    0xbffff4f0: 0x61616161 0x61616161 0x61616161 0x61616161
    0xbffff500: 0x61616161 0x61616161 0x00616161 0x08048572


    现在可以看到再继续输入将会覆盖返回地址。

    我们bash shell的printf函数,利用管道把printf的输出重定向到溢出程序,攻击字符串:

    “aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x76\x85\x04\x08”

    其中\x76\x85\x04\x08是do_valid_stuff函数的地址。

    root@bt:~/Desktop/code# printf "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x76\x85\x04\x08" | ./serial
    The serial number is valid!

    成功了哈,我们成功的控制了程序的验证分支,虽然只是一个很简单的例子,我也是个菜鸟,希望可以对初学的同学有帮助,大家一起

    交流 :)

    标题: 作者: 时间: 链接:
  • 相关阅读:
    数据结构-循环队列(Python实现)
    数据结构-堆栈和队列最简单的实现(Python实现)
    数据结构-双向链表(Python实现)
    ArrayList集合对象的sort()方法
    编写泛型
    super通配符
    面向对象(上)综合练习1:银行存取
    面向对象(上)综合练习1:Account和Customer
    泛型之extends通配符
    Comparable<T>泛型接口之实现按name或者age进行排序
  • 原文地址:https://www.cnblogs.com/Lamboy/p/2581986.html
Copyright © 2011-2022 走看看