struct _IO_FILE_plus
{
_IO_FILE file;
IO_jump_t *vtable;
}
我们一个一个的来看一下,首先是这个结构体里面的_IO_FILE,他也是个结构体,命名为了file,在libio.h里
源码如下:
struct_IO_FILE {
int_flags; /* 高字节是 _IO_MAGIC; 剩下是 flags. */
#define_IO_file_flags _flags
/* 下面的指针对应于c++ streambuf协议。*/
/* 注意:Tk直接使用_IO_read_ptr和_IO_read_end字段。*/
char* _IO_read_ptr; /* 当前读取指针 */
char* _IO_read_end; /* 获取区域的末尾。*/
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; /* 保留区的起点。*/
char* _IO_buf_end; /* 保留区末尾。*/
/* 以下窗口项用于支持备份和撤消。*/
char *_IO_save_base; /* 指向非当前get区域开始的指针。*/
char *_IO_backup_base; /* 指向备份区第一个有效字符的指针 */
char *_IO_save_end; /* 指向非当前get区域结尾的指针 */
struct_IO_marker *_markers;
struct_IO_FILE *_chain;
int_fileno;
int_blksize;
_IO_off_t_old_offset; /* 这个曾经是_offset但是太小了 */
#define__HAVE_COLUMN /* 暂时的 */
/* 1+pbase()的列数;0未知。*/
unsignedshort_cur_column;
signedchar_vtable_offset;
char_shortbuf[1];
/* char* _save_gptr; char* _save_egptr; */
_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};
然后是这个结构体里面的 IO_jump_t,他也是个结构体,是 *vtable指针,在libioP.h里
源码如下:
struct_IO_jump_t
{
JUMP_FIELD(_G_size_t, __dummy);
#ifdef _G_USING_THUNKS
JUMP_FIELD(_G_size_t, __dummy2);
#endif
JUMP_FIELD(_IO_finish_t, __finish);
JUMP_FIELD(_IO_overflow_t, __overflow);
JUMP_FIELD(_IO_underflow_t, __underflow);
JUMP_FIELD(_IO_underflow_t, __uflow);
JUMP_FIELD(_IO_pbackfail_t, __pbackfail);
/* showmany */
JUMP_FIELD(_IO_xsputn_t, __xsputn);
JUMP_FIELD(_IO_xsgetn_t, __xsgetn);
JUMP_FIELD(_IO_seekoff_t, __seekoff);
JUMP_FIELD(_IO_seekpos_t, __seekpos);
JUMP_FIELD(_IO_setbuf_t, __setbuf);
JUMP_FIELD(_IO_sync_t, __sync);
JUMP_FIELD(_IO_doallocate_t, __doallocate);
JUMP_FIELD(_IO_read_t, __read);
JUMP_FIELD(_IO_write_t, __write);
JUMP_FIELD(_IO_seek_t, __seek);
JUMP_FIELD(_IO_close_t, __close);
JUMP_FIELD(_IO_stat_t, __stat);
JUMP_FIELD(_IO_showmanyc_t, __showmanyc);
JUMP_FIELD(_IO_imbue_t, __imbue);
#if0
get_column;
set_column;
#endif
};
在标准的I/O库中,程序运行就会加载3个文件流stdin、stdout、stderr,分别是标准输入,标准输出,标准错误。文件描述符分别是0,1,2,因此在初始状态下,_IO_list_all 指向了一个有这些文件流构成的链表,但是需要注意的是这三个文件流位于 libc.so 的数据段。而我们使用 fopen 创建的文件流是分配在堆内存上的。进程中的 FILE 结构会通过chain 域彼此连接形成一个链表,表头为__IO_list_all。然后我们单独拿出一个file结构体,比如stdout,stdout被命名为__IO_2_1_stdout_,这个结构体就是上面说的_IO_FILE_plus封装的,所以包含了_IO_FILE file和 IO_jump_t *vtable;
其中vtable 是 IO_jump_t 类型的指针,IO_jump_t 中保存了一些函数指针
我们需要关注的是vtable = 0x7fxxxxxxxxxxxxxx <_IO_file_jumps>
IO_jump_t
表结构及对应函数
fread->_IO_XSGETN
fwrite->_IO_XSPUTN
fopen->malloc a new file struct->make file vtable->initialization file struct->puts initialzation file in file struct
fclose ->_IO_unlink_it->_IO_file_close_it->_IO_file_finish(_IO_FINISH)
为什么需要关注这个的原因就是程序执行exit后会关闭文件描述符,这样就会执行一系列的函数
如果我们能劫持fp指针,然后构造一个假的_IO_FILE结构和一个假的_IO_jump_t结构,就可以控制程序流了。
当新建立FILE结构会调用_IO_file_init 进一步初始化操作:
_IO_JUMPS (&new_f->fp) = &_IO_file_jumps;
_IO_file_init (&new_f->fp);
在_IO_file_init 函数的初始化操作中,会调用_IO_link_in 把新分配的 FILE 链入_IO_list_all 为起始的 FILE 链表中
void
_IO_link_in (fp)
struct _IO_FILE_plus *fp;
{
if ((fp->file._flags & _IO_LINKED) == 0)
{
fp->file._flags |= _IO_LINKED;
fp->file._chain = (_IO_FILE *) _IO_list_all;
_IO_list_all = fp;
++_IO_list_all_stamp;
}
}
原文始发于微信公众号(由由学习吧):IO_FILE
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论