最近学习fsop,但一直没学的很明白,所以自己逆一下几个常用的函数
测试代码
#include<stdio.h> int main() { FILE*fp=fopen("test","wb"); char *ptr=malloc(0x20); return 0; }
通过分析进入fopen函数查看,首先函数会调用__fopen_internal函数
94 _IO_FILE * 95 _IO_new_fopen (const char *filename, const char *mode) ► 96 { 97 return __fopen_internal (filename, mode, 1); 98 }
进入__fopen_internal函数看看,发现其调用的是这一部分
59 _IO_FILE * 60 __fopen_internal (const char *filename, const char *mode, int is32) 61 { 62 struct locked_FILE 63 { 64 struct _IO_FILE_plus fp; 65 #ifdef _IO_MTSAFE_IO 66 _IO_lock_t lock; 67 #endif 68 struct _IO_wide_data wd; 69 } *new_f = (struct locked_FILE *) malloc (sizeof (struct locked_FILE)); 70 71 if (new_f == NULL) 72 return NULL; 73 #ifdef _IO_MTSAFE_IO 74 new_f->fp.file._lock = &new_f->lock; 75 #endif 76 #if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T 77 _IO_no_init (&new_f->fp.file, 0, 0, &new_f->wd, &_IO_wfile_jumps); 78 #else 79 _IO_no_init (&new_f->fp.file, 1, 0, NULL, NULL); 80 #endif 81 _IO_JUMPS (&new_f->fp) = &_IO_file_jumps; 82 _IO_file_init (&new_f->fp); 83 #if !_IO_UNIFIED_JUMPTABLES 84 new_f->fp.vtable = NULL; 85 #endif 86 if (_IO_file_fopen ((_IO_FILE *) new_f, filename, mode, is32) != NULL) 87 return __fopen_maybe_mmap (&new_f->fp.file); 88 89 _IO_un_link (&new_f->fp); 90 free (new_f); 91 return NULL; 92 }
这个函数首先为结构体locked_FILE分配了一段内存空间,其结构体成员有
64 struct _IO_FILE_plus fp; 65 #ifdef _IO_MTSAFE_IO 66 _IO_lock_t lock; 67 #endif 68 struct _IO_wide_data wd;
而当由于我们可以通过上一篇将vtable劫持的时候,可以知道_IO_FILE_plus结构体是非常重要的,它包含了vtable的地址
当分配完空间后,就开始执行
_IO_no_init
跟进去简单的看了看,发现是一个初始化函数,也就是把上面分配的_IO_FILE_plus结构体里的成员全部置0
596 void 597 _IO_no_init (_IO_FILE *fp, int flags, int orientation, 598 struct _IO_wide_data *wd, const struct _IO_jump_t *jmp) 599 { 600 _IO_old_init (fp, flags); 601 fp->_mode = orientation; 602 #if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T 603 if (orientation >= 0) 604 { 605 fp->_wide_data = wd; 606 fp->_wide_data->_IO_buf_base = NULL; 607 fp->_wide_data->_IO_buf_end = NULL; 608 fp->_wide_data->_IO_read_base = NULL; 609 fp->_wide_data->_IO_read_ptr = NULL; 610 fp->_wide_data->_IO_read_end = NULL; 611 fp->_wide_data->_IO_write_base = NULL; 612 fp->_wide_data->_IO_write_ptr = NULL; 613 fp->_wide_data->_IO_write_end = NULL; 614 fp->_wide_data->_IO_save_base = NULL; 615 fp->_wide_data->_IO_backup_base = NULL; 616 fp->_wide_data->_IO_save_end = NULL; 617 618 fp->_wide_data->_wide_vtable = jmp; 619 } 620 else 621 /* Cause predictable crash when a wide function is called on a byte 622 stream. */ 623 fp->_wide_data = (struct _IO_wide_data *) -1L; 624 #endif 625 fp->_freeres_list = NULL; 626 }
接着往下看,发现有一个虚表的赋值操作,这里也是我们调用fsop的原理
_IO_JUMPS (&new_f->fp) = &_IO_file_jumps;
随后,会调用一个_IO_file_init函数,而这里面会又会调用一个_IO_link_in,我没有逆进去了,我们知道在我们每次新创建的_IO_FILE_plus结构体中,都会被链入一个在libc里的一个全局变量_IO_list_all(我们可以通过这个,遍历程序中所有的IO结构体),这里_IO_link_in
函数的功能是检查FILE结构体是否包含_IO_LINKED
标志,如果不包含则表示这个结构体没有链接进入_IO_list_all
,则再后面把它链接进入_IO_list_all
链表,同时设置FILE结构体的_chain
字段为之前的链表的值,否则直接返回。
143 _IO_new_file_init (struct _IO_FILE_plus *fp) 144 { 145 /* POSIX.1 allows another file handle to be used to change the position 146 of our file descriptor. Hence we actually don't know the actual 147 position before we do the first fseek (and until a following fflush). */ 148 fp->file._offset = _IO_pos_BAD; 149 fp->file._IO_file_flags |= CLOSED_FILEBUF_FLAGS; 150 151 _IO_link_in (fp); 152 fp->file._fileno = -1; 153 }
所以_IO_file_init
主要功能是将FILE结构体链接进入_IO_list_all
链表,在没执行_IO_file_init
函数前_IO_list_all
指向的是stderr
结构体
在执行后,指向的就是我们新申请的堆空间了
此时,我们查看查看下new_f->fp指针,发现其chain已经指向了stderr
接着就是调用_IO_file_fopen来打开文件了
_IO_file_fopen ((_IO_FILE *) new_f, filename, mode, is32)
进去查看下这个函数
255 #ifdef _LIBC 256 const char *cs; 257 const char *last_recognized; 258 #endif 259 260 if (_IO_file_is_open (fp)) 261 return 0; 262 switch (*mode) 263 { 264 case 'r': 265 omode = O_RDONLY; 266 read_write = _IO_NO_WRITES; 267 break; 268 case 'w': 269 omode = O_WRONLY; 270 oflags = O_CREAT|O_TRUNC; 271 read_write = _IO_NO_READS; 272 break; 273 case 'a': 274 omode = O_WRONLY; 275 oflags = O_CREAT|O_APPEND; 276 read_write = _IO_NO_READS|_IO_IS_APPENDING; 277 break; 278 default: 279 __set_errno (EINVAL); 280 return NULL; 281 } 282 #ifdef _LIBC 283 last_recognized = mode; 284 #endif 285 for (i = 1; i < 7; ++i) 286 { 287 switch (*++mode) 288 { 289 case '