IO_FILE attack
IO_file struct 认识
FILE在linux的标准IO库中用于描述文件的结构,称为文件流。FILE结构在程序执行fopen等函数是才会创建,并将其分配到堆中。
我们通常定义一个指向FILE结构体的指针来接受这样一个返回值
FILE struct
struct _IO_FILE {
int _flags; /* 文件流的状态标志,包括控制、状态和错误信息等。高阶字包含魔数(_IO_MAGIC),其余字节包含其他标志。 */
#define _IO_file_flags _flags/*对 _flags 进行重命名,用于简化使用。*/
/* 用于支持 C++ streambuf 协议的指针, */
/* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
char* _IO_read_ptr; /* Current read pointer */
char* _IO_read_end; /* End of get area. */
char* _IO_read_base; /* 表示当前读取位置、读取区域的结束和读取区域的起始位置。 */
char* _IO_write_base; /* Start of put area. */
char* _IO_write_ptr; /* Current put pointer. */
char* _IO_write_end; /* End of put area. */
char* _IO_buf_base; /* Start of reserve area. */
char* _IO_buf_end; /* End of reserve area. */
/* The following fields are used to support backing up and undo. */
char *_IO_save_base; /* Pointer to start of non-current get area. */
char *_IO_backup_base; /* Pointer to first valid character of backup area */
char *_IO_save_end; /* 用于支持备份和撤销的指针,表示非当前读取区域的起始位置、备份区域的起始位置和非当前读取区域的结束位置。 */
struct _IO_marker *_markers;/*指向 _IO_marker 结构体的指针,用于支持标记操作。*/
struct _IO_FILE *_chain;/*指向下一个文件流结构体的指针,用于构成一个链表。*/
int _fileno;
#if 0
int _blksize;
#else
int _flags2;
#endif
_IO_off_t _old_offset; /* This used to be _offset but it's too small. */
#define __HAVE_COLUMN /* temporary */
/* 1+column number of pbase(); 0 is unknown. */
unsigned short _cur_column;
signed char _vtable_offset;/*虚函数表(vtable)的偏移量。*/
char _shortbuf[1];/*一个字符的缓冲区,用于性能优化。*/
/* char* _save_gptr; char* _save_egptr; */
_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};
struct _IO_FILE_complete
{
struct _IO_FILE _file;
#endif
#if defined _G_IO_IO_FILE_VERSION && _G_IO_IO_FILE_VERSION == 0x20001
_IO_off64_t _offset;
# if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
/* Wide character stream stuff. */
struct _IO_codecvt *_codecvt;
struct _IO_wide_data *_wide_data;
struct _IO_FILE *_freeres_list;
void *_freeres_buf;
# else
void *__pad1;
void *__pad2;
void *__pad3;
void *__pad4;
size_t __pad5;
int _mode;
/* Make sure we don't get into trouble again. */
char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
#endif
};
如注释所说,对于进程中的FILE结构,他会通过_chain域彼此链接将所有的FILE结构体连接成一个链表,链表头部是全局变量 _IO_LIST_ALL表示,并且可以依靠该变量来遍历整个链表中的所有FILE结构。
在标准I/O库中,在每个程序启动时会有三个文件流是自动打开的,分别是 stdin,stdout,stderr。所以一开始_IO_list_all会指向这样一个拥有这些文件流的链表, 但是,这里我们需要注意的是文件流位于libc.so的数据段,而我们通过fopen函数创建的文件流是分配在堆上面的
在libc文件中我们可以找到stdin,stdout,stderr以及_IO_2_1_stderr_ IO_2_1_stdout _IO_2_1_stdin这些符号,而他们之间有分别是指向关系,stdin -> _IO_2_1_stdin(这才是真正的结构file)
事实上_IO_FILE结构外围还包裹着一层 _IO_FILE_plus结构:
struct _IO_FILE_plus
{
_IO_FILE file;
IO_jump_t *vtable;
}
/*其中*vtable的偏移在libc.2.23下:32位:偏移为0x94;64位:偏移为0xd8*/
可以看到*vtable指针被定义为IO_jump_t类型,其中保存了一些函数指针,在后面我们会看到一些标准IO函数调用这里面的的一些函数指针。
void * funcs[] = {
1 NULL, // "extra word"
2 NULL, // DUMMY
3 exit, // finish
4 NULL, // overflow
5 NULL, // underflow
6 NULL, // uflow
7 NULL, // pbackfail
8 NULL, // xsputn #printf
9 NULL, // xsgetn
10 NULL, // seekoff
11 NULL, // seekpos
12 NULL, // setbuf
13 NULL, // sync
14 NULL, // doallocate
15 NULL, // read
16 NULL, // write
17 NULL,