IO_FILE源码分析:fopen 连续做了几个 IO_FILE pwn 的题目,发现自己对于 IO_FILE 漏洞的原理不是很了解,于是打算学习学习 IO_FILE 的源码
我打算先学习源码,之后再学习漏洞利用的底层原理
感谢 raycp 师傅的源码讲解!!!
fopen 是IO中最重要的函数之一,每当我们想打开文件时,第一个调用的便是这个函数
先看看它的源码:(libc-2.23)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include "libioP.h" #ifdef __STDC__ #include <stdlib.h> #endif #ifdef _LIBC # include <shlib-compat.h> #else # define _IO_new_fopen fopen #endif _IO_FILE * _IO_new_fopen (filename, mode) const char *filename; const char *mode; { struct locked_FILE { struct _IO_FILE_plus fp ; #ifdef _IO_MTSAFE_IO _IO_lock_t lock; #endif struct _IO_wide_data wd ; } *new_f = (struct locked_FILE *) malloc (sizeof (struct locked_FILE)); if (new_f == NULL ) return NULL ; #ifdef _IO_MTSAFE_IO new_f->fp.file._lock = &new_f->lock; #endif #if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T _IO_no_init (&new_f->fp.file, 0 , 0 , &new_f->wd, &_IO_wfile_jumps); #else _IO_no_init (&new_f->fp.file, 1 , 0 , NULL , NULL ); #endif _IO_JUMPS (&new_f->fp) = &_IO_file_jumps; _IO_file_init (&new_f->fp); #if !_IO_UNIFIED_JUMPTABLES new_f->fp.vtable = NULL ; #endif if (_IO_file_fopen ((_IO_FILE *) new_f, filename, mode, 1 ) != NULL ) return (_IO_FILE *) &new_f->fp; _IO_un_link (&new_f->fp); free (new_f); return NULL ; } #ifdef _LIBC strong_alias (_IO_new_fopen, __new_fopen) versioned_symbol (libc, _IO_new_fopen, _IO_fopen, GLIBC_2_1); versioned_symbol (libc, __new_fopen, fopen, GLIBC_2_1); #endif
fopen 实际上是 _IO_new_fopen
函数,前面都是一些初始化的操作:
malloc
分配内存空间
_IO_no_init
对FILE结构体进行 null
初始化
_IO_file_init
将结构体链接进 _IO_list_all
链表
_IO_file_fopen
执行系统调用打开文件
接下来便分别对它们进行分析:
malloc 分配内存空间
1 *new_f = (struct locked_FILE *) malloc (sizeof (struct locked_FILE));
这个“locked_FILE”是个结构体(就是FILE结构体),包含了3个条目:
1 2 3 4 5 6 7 8 struct locked_FILE { struct _IO_FILE_plus fp ; #ifdef _IO_MTSAFE_IO _IO_lock_t lock; #endif struct _IO_wide_data wd ; }
malloc 将会分配“locked_FILE”结构体大小的内存,最后在”fopen“执行完成后释放掉
_IO_no_init 对FILE结构体进行 null 初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 void _IO_no_init (fp, flags, orientation, wd, jmp) _IO_FILE *fp; int flags; int orientation; struct _IO_wide_data *wd ; struct _IO_jump_t *jmp ; { fp->_flags = _IO_MAGIC|flags; fp->_IO_buf_base = NULL ; fp->_IO_buf_end = NULL ; fp->_IO_read_base = NULL ; fp->_IO_read_ptr = NULL ; fp->_IO_read_end = NULL ; fp->_IO_write_base = NULL ; fp->_IO_write_ptr = NULL ; fp->_IO_write_end = NULL ; fp->_chain = NULL ; fp->_IO_save_base = NULL ; fp->_IO_backup_base = NULL ; fp->_IO_save_end = NULL ; fp->_markers = NULL ; fp->_cur_column = 0 ; #if _IO_JUMPS_OFFSET fp->_vtable_offset = 0 ; #endif #ifdef _IO_MTSAFE_IO _IO_lock_init (*fp->_lock); #endif fp->_mode = orientation; #if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T if (orientation >= 0 ) { fp->_wide_data = wd; fp->_wide_data->_IO_buf_base = NULL ; fp->_wide_data->_IO_buf_end = NULL ; fp->_wide_data->_IO_read_base = NULL ; fp->_wide_data->_IO_read_ptr = NULL ; fp->_wide_data->_IO_read_end = NULL ; fp->_wide_data->_IO_write_base = NULL ; fp->_wide_data->_IO_write_ptr = NULL ; fp->_wide_data->_IO_write_end = NULL ; fp->_wide_data->_IO_save_base = NULL ; fp->_wide_data->_IO_backup_base = NULL ; fp->_wide_data->_IO_save_end = NULL ; fp->_wide_data->_wide_vtable = jmp; } #endif }
对 _IO_FILE_plus 里面的条目进行置空操作,对 _IO_wide_data 进行赋值并置空
_IO_file_init 将结构体链接进 _IO_list_all 链表
1 2 3 4 5 6 7 8 9 10 11 12 # define _IO_new_file_init _IO_file_init void _IO_new_file_init (fp) struct _IO_FILE_plus *fp ; { fp->file._offset = _IO_pos_BAD; fp->file._IO_file_flags |= CLOSED_FILEBUF_FLAGS; _IO_link_in (fp); fp->file._fileno = -1 ; }
这个函数完成了一些设置(暂时不用管)调用了 _IO_link_in
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void _IO_link_in (fp) struct _IO_FILE_plus *fp ; { if ((fp->file._flags & _IO_LINKED) == 0 ) { fp->file._flags |= _IO_LINKED; #ifdef _IO_MTSAFE_IO _IO_lock_lock (list_all_lock); #endif fp->file._chain = (_IO_FILE *) _IO_list_all; _IO_list_all = fp; #ifdef _IO_MTSAFE_IO _IO_lock_unlock (list_all_lock); #endif } }
在 _chain
字段(指向FILE链表的下一个单元)写入 _IO_list_all
,然后在 _IO_list_all
写入 fp ,此时 fp 成为了FILE链表的头部(全局变量 _IO_list_all
永远指向了FILE链表的头部 )
// 标准的插头法,和 fastbin,smallbin,unsortedbin 差不多
_IO_file_fopen 打开文件句柄
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 # define _IO_new_file_fopen _IO_file_fopen _IO_FILE * _IO_new_file_fopen (fp, filename, mode, is32not64) _IO_FILE *fp; const char *filename; const char *mode; int is32not64; { int oflags = 0 , omode; int read_write; int oprot = 0666 ; int i; _IO_FILE *result; #if _LIBC const char *cs; #endif if (_IO_file_is_open (fp)) return 0 ; switch (*mode) { case 'r' : omode = O_RDONLY; read_write = _IO_NO_WRITES; break ; case 'w' : omode = O_WRONLY; oflags = O_CREAT|O_TRUNC; read_write = _IO_NO_READS; break ; case 'a' : omode = O_WRONLY; oflags = O_CREAT|O_APPEND; read_write = _IO_NO_READS|_IO_IS_APPENDING; break ; default : __set_errno (EINVAL); return NULL ; } for (i = 1 ; i < 4 ; ++i) { switch (*++mode) { case '\0' : break ; case '+' : omode = O_RDWR; read_write &= _IO_IS_APPENDING; continue ; case 'x' : oflags |= O_EXCL; continue ; case 'b' : default : continue ; } break ; } result = _IO_file_open (fp, filename, omode|oflags, oprot, read_write, is32not64); #if _LIBC cs = strstr (mode, ",ccs=" ); if (cs != NULL ) { struct gconv_fcts fcts ; struct _IO_codecvt *cc ; if (! _IO_CHECK_WIDE (fp) || __wcsmbs_named_conv (&fcts, cs + 5 ) != 0 ) { _IO_new_fclose (result); return NULL ; } cc = fp->_codecvt = &fp->_wide_data->_codecvt; *cc = __libio_codecvt; cc->__cd_in.__cd.__nsteps = 1 ; cc->__cd_in.__cd.__steps = fcts.towc; cc->__cd_in.__cd.__data[0 ].__invocation_counter = 0 ; cc->__cd_in.__cd.__data[0 ].__internal_use = 1 ; cc->__cd_in.__cd.__data[0 ].__flags = __GCONV_IS_LAST; cc->__cd_in.__cd.__data[0 ].__statep = &result->_wide_data->_IO_state; cc->__cd_out.__cd.__nsteps = 1 ; cc->__cd_out.__cd.__steps = fcts.tomb; cc->__cd_out.__cd.__data[0 ].__invocation_counter = 0 ; cc->__cd_out.__cd.__data[0 ].__internal_use = 1 ; cc->__cd_out.__cd.__data[0 ].__flags = __GCONV_IS_LAST; cc->__cd_out.__cd.__data[0 ].__statep = &result->_wide_data->_IO_state; result->_mode = 1 ; } #endif return result; }
程序会先检查文件描述符是否打开,然后在 Switch-Case 里面设置文件打开的模式
fopen 只是开胃菜,还没有什么漏洞,接下来的 fread 就是重点了