目录
  1. 1. 什么是缓冲文件系统?
    1. 1.1. 作用
    2. 1.2. 代表函数(c语言)
  2. 2. 非缓冲文件系统
    1. 2.1. 特点
    2. 2.2. 代表函数
  3. 3. 栈溢出常见函数
  4. 4. 0x00 read()
    1. 4.1. 函数原型
    2. 4.2. 功能
    3. 4.3. 返回值
  5. 5. 0x01 write()
    1. 5.1. 函数原型
    2. 5.2. 功能
    3. 5.3. 返回值
  6. 6. 0x02 gets()
    1. 6.1. 函数原型
    2. 6.2. 功能
    3. 6.3. 注意
  7. 7. 0x03 strcpy()和memcpy()和strncpy()
    1. 7.1. 函数原型
    2. 7.2. 注意
  8. 8. 0x04 printf()和scanf()
从非缓冲系统文件到常见栈溢出函数

非缓冲和缓冲是相对而言的
了解什么是非缓冲文件系统之前,先了解一下缓冲文件系统。

什么是缓冲文件系统?

程序在运行过程中,会自动在内存开辟一个“缓冲区”,为程序中的每一个文件使用,当执行读文件的操作时,从磁盘文件将数据先读入内存“缓冲区”,装满后再从内存“缓冲区”依此读入接收的变量。执行写文件的操作时,先将数据写入内存“缓冲区”,待内存“缓冲区”装满后再写入文件。由此可以看出,内存“缓冲区”的大小,影响着实际操作外存的次数,内存“缓冲区”越大,则操作外存的次数就少,执行速度就快、效率高。一般来说,文件“缓冲区”的大小随机器而定。

作用

用缓冲区可以一次读入一批数据,或输出一批数据,而不是执行一次输入或输出函数就去访问一次磁盘,这样做的目的是减少对磁盘的实际读写次数。

代表函数(c语言)

缓冲文件系统,fopen, fclose, fread, fwrite, fgetc, fgets, fputc, fputs, freopen, fseek, ftell, rewind等

非缓冲文件系统

特点

非缓冲文件系统不由系统自动设置缓冲区,而由用户自己根据需要设置。在传统的unix系统下,用缓冲文件系统来处理文本文件,用非缓冲文件系统处理二进制文件。

代表函数

缓冲文件系统,open, close, read, write, gets, getchar, putc, putchar等

栈溢出常见函数

函数 功能 可利用漏洞
read() 读取文件内容到缓冲区 读到\x00和指定字节
write() 把缓冲区的内容写入到文件内 写到\x00和指定字节
gets() 从输入缓冲区中读取一个字符串存储到字符指针 可无限输入
puts() 输出字符串,相当于printf(“%s\n”,s) 输出直到\x00
strcpy() 只能复制字符串 一直复制直到遇到\x00
memcpy() 复制任意内容 指定字节
strncpy() 复制指定字节字符 指定字节
printf() 格式化输出 输出直到\x00
scanf() 格式化输入 输入与定义不同的类型或输入多与定义的数组元素

下面是详细内容:

0x00 read()

函数原型

1
2
#include <unistd.h>
ssize_t read (int fd, void *buf, size_t nbyte)

fd:文件描述符;fd为0从键盘读取
buf:指定的缓冲区,即指针,指向一段内存单元;
nbyte:要读入文件指定的字节数;

功能

read()会把参数fd所指的文件传送nbyte个字节到buf指针所指的内存中。若参数nbyte为0,则read()不会有作用并返回0。

返回值

成功时,read返回实际所读的字节数,如果返回的值是0,表示已经读到文件的结束了.
小于0表示出现了错误.如果错误为EINTR说明读是由中断引起的, 如果是ECONNREST表示网络连接出了问题.

0x01 write()

函数原型

1
2
#include <unistd.h>
ssize_t write(int fd,const void *buf,size_t nbytes)

fd:文件描述符;fd为1输出到显示器
buf:指定的缓冲区,即指针,指向一段内存单元;
nbyte:要写入文件指定的字节数;

功能

write()会把参数buf 所指的内存写入nbytes 个字节到参数fd 所指的文件内. 当然, 文件读写位置也会随之移动.

返回值

如果顺利write()会返回实际写入的字节数.
当有错误发生时则返回-1, 错误代码存入errno 中.

错误代码:

  • EINTR 此调用被信号所中断.
  • EAGAIN 当使用不可阻断I/O 时 (O_NONBLOCK), 若无数据可读取则返回此值.
  • EADF 参数fd 非有效的文件描述词, 或该文件已关闭

0x02 gets()

函数原型

1
2
# include <stdio.h>
char *gets(char *str);

功能

gets() 函数的功能是从输入缓冲区中读取一个字符串存储到字符指针变量 str 所指向的内存空间。

注意

使用 gets() 时,系统会将最后“敲”的换行符从缓冲区中取出来,然后丢弃,所以缓冲区中不会遗留换行符。这就意味着,如果前面使用过 gets(),而后面又要从键盘给字符变量赋值的话就不需要吸收回车清空缓冲区了,因为缓冲区的回车已经被 gets() 取出来扔掉了

gets() 时有空格也可以直接输入,但是 gets() 有一个非常大的缺陷,即它不检查预留存储区是否能够容纳实际输入的数据,换句话说,如果输入的字符数目大于数组的长度,gets 无法检测到这个问题,就会发生内存越界。

0x03 strcpy()和memcpy()和strncpy()

函数原型

1
2
3
char* strcpy(char* dest, const char* src)
void *memcpy( void *dest, const void *src, size_t count );
char *strncpy(char *dest,char *src,int size_t n);

dest:指向用于存储复制内容的目标数组。
src:要复制的字符串。
count:要读入文件指定的字节数;

注意

  • strcpy提供了字符串的复制。即strcpy只用于字符串复制,并且它不仅复制字符串内容之外,还会复制字符串的结束符’\0’。
  • 复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
  • 复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符”\0”才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。
  • 用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy
  • strncpy函数,只是将src的前n个字符复制到dest的前n个字符,不自动添加’\0’。如果src的长度小于n个字节,则以NULL填充dest直到复制完n个字节

0x04 printf()和scanf()

引入:华为的一道经典面试

i的值输出是多少呢,没有考虑栈溢出的话,i就是5。可这里栈溢出了,i=6776421 ,这是为什么呢?
进入到内存中分析:
连续定义的变量,内存地址一般也相邻
首先依次在栈区为变量开辟空间(注意方式,整形是4个字节以计算,char型一字节一计算)
a,b,c,d,e对应的ASCII码对照表的16进制为0x61,0x62,0x63,0x64,0x65

越界后:

所以最后的结果是0x676665的十进制6776421

文章作者: nocbtm
文章链接: https://nocbtm.github.io/2018/11/12/从非缓冲系统文件到常见栈溢出函数/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 nocbtm's Blog
打赏
  • 微信
  • 支付宝