FILE结构介绍
FILE在Linux系统的标准IO库中是用于描述文件的结构,称为文件流。 FILE结构在程序执行fopen等函数时会进行创建,并分配在堆中。我们常定义一个指向FILE结构的指针来接收这个返回值。
FILE结构定义在libio.h中,如下所示
struct _IO_FILE {
int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags
/* The following pointers correspond to the C++ streambuf protocol. */
/* 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; /* Start of putback+get area. */
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; /* Pointer to end of non-current get area. */
struct _IO_marker *_markers;
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;
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域彼此连接形成一个链表,链表头部用全局变量_IO_list_all表示,通过这个值我们可以遍历所有的FILE结构。
在标准I/O库中,每个程序启动时有三个文件流是自动打开的:stdin、stdout、stderr。因此在初始状态下,_IO_list_all指向了一个有这些文件流构成的链表,但是需要注意的是这三个文件流位于libc.so的数据段。而我们使用fopen创建的文件流是分配在堆内存上的。
我们可以在libc.so中找到stdin\stdout\stderr等符号,这些符号是指向FILE结构的指针,真正结构的符号是
_IO_2_1_stderr_
_IO_2_1_stdout_
_IO_2_1_stdin_
但是事实上_IO_FILE结构外包裹着另一种结构_IO_FILE_plus,其中包含了一个重要的指针vtable指向了一系列函数指针。
在libc2.23版本下,32位的vtable偏移为0x94,64位偏移为0xd8
struct _IO_FILE_plus
{
_IO_FILE file;
IO_jump_t *vtable;
}
对于每一个IO_FILE_plus结构体,IO_FILE部分是不同的,而vtable指针的值是相同的。
vtable是IO_jump_t类型的指针,IO_jump_t中保存了一些函数指针,在后面我们会看到在一系列标准IO函数中会调用这些函数指针。
这里的vtable实际上是一个函数指针数组。包含的函数及顺序如下表:
void * funcs[] = { //offset
1 NULL, // "extra word" 0x0
2 NULL, // DUMMY 0x8
3 exit, // finish 0x10
4 NULL, // overflow 0x18
5 NULL, // underflow 0x20
6 NULL, // uflow 0x28
7 NULL, // pbackfail 0x30
8 NULL, // xsputn #printf 0x38
9 NULL, // xsgetn 0x40
10 NULL, // seekoff 0x48
11 NULL, // seekpos 0x50
12 NULL, // setbuf 0x58
13 NULL, // sync 0x60
14 NULL, // doallocate 0x68
15 NULL, // read 0x70
16 NULL, // write 0x78
17 NULL, // seek 0x80
18 pwn, // close 0x88
19 NULL, // stat 0x90
20 NULL, // showmanyc
21 NULL, // imbue
};