缓冲区溢出

什么是溢出?
简单的说,溢出就是越界,跑到不属于自己的领地里去了...如:
overflow.c:

main()
{
    int a[10] = {0};
    int i;
    for (i = -10; i < 20; i++)
        printf("%d ",a[i]);
}

由于C的自由书写风格,编译系统并不对数据边界进行检测,
导致有意无意的出现溢出。

复杂点的例子:

overflowC.c:

#include <stdio.h>
#include <stdlib.h>

int
main (int argc, char *argv[])
{
    int value = 5;
    char buffer_one[8],buffer_two[8];
    
    strcpy(buffer_one,"one");
    strcpy(buffer_two,"two");
    
    printf("[BEFORE] buffer_two is at %p, contains %s\n",buffer_two, buffer_two);
    printf("[BEFORE] buffer_one is at %p, contains %s\n",buffer_one, buffer_one);
    printf("[BEFORE] value is at %p, contains %d(0x%08x)\n",&value, value, value);
    
    printf("[STRCPY] copying %d bytes into buffer_two\n",strlen(argv[1]));
    
    strcpy(buffer_two, argv[1]);
    
    printf("[AFTER] buffer_two is at %p, contains %s\n",buffer_two, buffer_two);
    printf("[AFTER] buffer_one is at %p, contains %s\n",buffer_one, buffer_one);
    printf("[AFTER] value is at %p, contains %d(0x%08x)\n",&value, value, value);
    
    
    return 0;
}

一次运行结果:
root@linux-t0jw:~/桌面> ./overflow 1234567890
[BEFORE] buffer_two is at 0xbfccc5bc, contains two
[BEFORE] buffer_one is at 0xbfccc5c4, contains one
[BEFORE] value is at 0xbfccc5cc, contains 5(0x00000005)
[STRCPY] copying 10 bytes into buffer_two
[AFTER] buffer_two is at 0xbfccc5bc, contains 1234567890
[AFTER] buffer_one is at 0xbfccc5c4, contains 90
[AFTER] value is at 0xbfccc5cc, contains 5(0x00000005)

分析:buffer_two 和 buffer_one相差0xc4-0xbc=8个单元,
而复制10个字节,导致8个字节的缓冲区不足,溢出到了buffer_one中...

再次运行:
root@linux-t0jw:~/桌面> ./overflow AAAAAAAAAAAAAAAAAA
[BEFORE] buffer_two is at 0xbf9b60bc, contains two
[BEFORE] buffer_one is at 0xbf9b60c4, contains one
[BEFORE] value is at 0xbf9b60cc, contains 5(0x00000005)
[STRCPY] copying 18 bytes into buffer_two
[AFTER] buffer_two is at 0xbf9b60bc, contains AAAAAAAAAAAAAAAAAA
[AFTER] buffer_one is at 0xbf9b60c4, contains AAAAAAAAAA
[AFTER] value is at 0xbf9b60cc, contains 16705(0x00004141)

分析:增加参数长度,会发现甚至溢出到了value(0x41是A的ASCII值)...

再次运行:

root@linux-t0jw:~/桌面> ./overflow AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
[BEFORE] buffer_two is at 0xbff6c3ec, contains two
[BEFORE] buffer_one is at 0xbff6c3f4, contains one
[BEFORE] value is at 0xbff6c3fc, contains 5(0x00000005)
[STRCPY] copying 91 bytes into buffer_two
[AFTER] buffer_two is at 0xbff6c3ec, contains AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
[AFTER] buffer_one is at 0xbff6c3f4, contains AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
[AFTER] value is at 0xbff6c3fc, contains 1094795585(0x41414141)
段错误
root@linux-t0jw:~/桌面> echo $?
139

最终,会引发一个段错误...

最经典的例子:

overflowclassic:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int check_password(char *password)
{
    int flag = 0;
    char password_buffer[16];
    
    strcpy(password_buffer, password);
    
    if (strcmp(password_buffer, "brillig") == 0)
        flag = 1;
    if (strcmp(password_buffer, "outgrabe") == 0)
        flag = 1;
    return flag;
}

int
main (int argc, char *argv[])
{
    if (argc < 2) {
        fprintf(stderr, "Usage %s <password>\n", argv[0]);
        exit(1);
     }
    if (check_password(argv[1])) {
            printf("----------\n");
            printf("Access Granted.\n");
            printf("----------\n");
     } else {
        printf("\nAccess Denied.\n");
      }
     
    return 0;
}

运行结果:
root@linux-t0jw:~/桌面> ./overflowclassic brillig
----------
Access Granted.
----------
root@linux-t0jw:~/桌面> ./overflowclassic okok

Access Denied.
root@linux-t0jw:~/桌面> ./overflowclassic AAAAAAAAAAAAAAAAAAAAAAA
----------
Access Granted.
----------

加入调试信息,重新编译运行:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int check_password(char *password)
{
    int flag = 0;
    char password_buffer[16];
    
    strcpy(password_buffer, password);
    
    printf("flag is at %p contains %d \n",&flag, flag);
    printf("buffer is at %p contains %s \n",password_buffer, password_buffer);
    if (strcmp(password_buffer, "brillig") == 0)
        flag = 1;
    if (strcmp(password_buffer, "outgrabe") == 0)
        flag = 1;
    return flag;
}

int
main (int argc, char *argv[])
{
    if (argc < 2) {
        fprintf(stderr, "Usage %s <password>\n", argv[0]);
        exit(1);
     }
    if (check_password(argv[1])) {
            printf("----------\n");
            printf("Access Granted.\n");
            printf("----------\n");
     } else {
        printf("\nAccess Denied.\n");
      }
     
    return 0;
}

root@linux-t0jw:~/桌面> gcc -Wall  overflow.c -o overflowclassic
root@linux-t0jw:~/桌面> ./overflowclassic AAAAAAAAAAAAAAAAAAAAAAA
flag is at 0xbfa3895c contains 1094795585
buffer is at 0xbfa3894c contains AAAAAAAAAAAAAAAAAAAAAAA
----------
Access Granted.
----------

可知:flag和password_buffer相差16的字节,最后flag可怜被溢出的数据覆盖了...

使用以下调试过程可以更清晰地看出:

root@linux-t0jw:~/桌面> gcc -Wall -g  overflow.c -o  overflowclassic
root@linux-t0jw:~/桌面> gdb -q ./overflowclassic
Reading symbols from /home/ly/桌面/overflowclassic...done.
(gdb) list 1
1    #include <stdio.h>
2    #include <string.h>
3    #include <stdlib.h>
4    
5    int check_password(char *password)
6    {
7        int flag = 0;
8        char password_buffer[16];
9        
10        strcpy(password_buffer, password);
(gdb)
11        
12        printf("flag is at %p contains %d \n",&flag, flag);
13        printf("buffer is at %p contains %s \n",password_buffer, password_buffer);
14        if (strcmp(password_buffer, "brillig") == 0)
15            flag = 1;
16        if (strcmp(password_buffer, "outgrabe") == 0)
17            flag = 1;
18        return flag;
19    }
20    
(gdb) break 10
Breakpoint 1 at 0x8048551: file overflow.c, line 10.
(gdb) break 18
Breakpoint 2 at 0x80485d5: file overflow.c, line 18.
(gdb) run AAAAAAAAAAAAAAAAAAAAAAA
Starting program: /home/ly/桌面/overflowclassic AAAAAAAAAAAAAAAAAAAAAAA
Missing separate debuginfo for /lib/ld-linux.so.2
Try: zypper install -C "debuginfo(build-id)=b6b00f5560b849cf9fac5e6efb9f403c21f508dd"
Missing separate debuginfo for /lib/libc.so.6
Try: zypper install -C "debuginfo(build-id)=6478c346f66a284b77eb5ca82ab8f2f4f9561600"

Breakpoint 1, check_password (password=0xbffff3a4 'A' <repeats 23 times>)
    at overflow.c:10
10        strcpy(password_buffer, password);
(gdb) x/s password_buffer
0xbffff07c:     "\370\203\004\b\310\360\377\277\364\237\004\b\002"
(gdb) x/x &flag
0xbffff08c:    0x00
(gdb) print 0xbffff08c - 0xbffff07c
$1 = 16
(gdb) x/16xw password_buffer
0xbffff07c:    0x080483f8    0xbffff0c8    0x08049ff4    0x00000002
0xbffff08c:    0x00000000    0x08048490    0x00000000    0xbffff0b8
0xbffff09c:    0x08048624    0xbffff3a4    0xb7e78b45    0x08048679
0xbffff0ac:    0xb7fb2ff4    0x08048670    0x00000000    0x00000000
(gdb) continue
Continuing.
flag is at 0xbffff08c contains 1094795585
buffer is at 0xbffff07c contains AAAAAAAAAAAAAAAAAAAAAAA

Breakpoint 2, check_password (password=0xbffff3a4 'A' <repeats 23 times>)
    at overflow.c:18
18        return flag;
(gdb) x/16xw password_buffer
0xbffff07c:    0x41414141    0x41414141    0x41414141    0x41414141
0xbffff08c:    0x41414141    0x00414141    0x00000000    0xbffff0b8
0xbffff09c:    0x08048624    0xbffff3a4    0xb7e78b45    0x08048679
0xbffff0ac:    0xb7fb2ff4    0x08048670    0x00000000    0x00000000
(gdb) x/x &flag
0xbffff08c:    0x41414141
(gdb) x/dw &flag
0xbffff08c:    1094795585
(gdb)

最后显示,flag的值被覆盖了...

如果对以上代码改动一下:
以反序定义password_buffer和flag

reverse.c:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int check_password(char *password)
{
    
    char password_buffer[16];
    
    int flag = 0;
    
    strcpy(password_buffer, password);
    
    printf("flag is at %p contains %d \n",&flag, flag);
    printf("buffer is at %p contains %s \n",password_buffer, password_buffer);
    if (strcmp(password_buffer, "brillig") == 0)
        flag = 1;
    if (strcmp(password_buffer, "outgrabe") == 0)
        flag = 1;
    return flag;
}

int
main (int argc, char *argv[])
{
    if (argc < 2) {
        fprintf(stderr, "Usage %s <password>\n", argv[0]);
        exit(1);
     }
    if (check_password(argv[1])) {
            printf("----------\n");
            printf("Access Granted.\n");
            printf("----------\n");
     } else {
        printf("\nAccess Denied.\n");
      }
     
    return 0;
}

运行结果:

root@linux-t0jw:~/桌面> ./overflowclassic AAAAAAAAAAAAAAAAAAAAAAA111111111111111111111111111111
flag is at 0xbfb9057c contains 0
buffer is at 0xbfb90580 contains AAAAAAAAAAAAAAAAAAAAAAA111111111111111111111111111111
段错误

会发现flag 位于password_buffer之前(堆栈是由高到低扩展),从而无法被覆盖掉...
但是会溢出到返回地址ret的单元里,从而导致段错误

通过gdb反汇编调试可以得到更多的内容。

PS:
对以上验证密码程序可以使用strncpy等安全的函数,避免使用gets,strcpy等不安全的函数,此外,对数组边界的检查也要格外小心。
修改如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int check_password(char *password)
{
    
    char password_buffer[16];
    
    int flag = 1;
    
    strncpy(password_buffer,password,15); //strncpy()
    
    printf("flag is at %p contains %d \n",&flag, flag);
    printf("buffer is at %p contains %s \n",password_buffer, password_buffer);
    if (strcmp(password_buffer, "brillig") == 0)
        flag = 0;
    if (strcmp(password_buffer, "outgrabe") == 0)
        flag = 0;
    return flag;
}

int
main (int argc, char *argv[])
{
    if (argc < 2) {
        fprintf(stderr, "Usage %s <password>\n", argv[0]);
        exit(1);
     }
    if (check_password(argv[1]) == 0) {
            printf("----------\n");
            printf("Access Granted.\n");
            printf("----------\n");
     } else {
        printf("\nAccess Denied.\n");
      }
     
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值