感谢raycp师傅:https://xz.aliyun.com/t/5445
fclose
测试程序
#include <stdio.h> #include <stdlib.h> int main () { char data[20 ] = "flag{grxer}" ; FILE* fp = fopen("flag" , "wb" ); fwrite(data,1 ,0x20 ,fp); fclose(fp); return 0 ; }
断点断到执行到fclose,缓冲区已经初始化
_IO_new_fclose include/stdio.h
#define fclose(fp) _IO_new_fclose(fp)
libio/iofclose.c
int _IO_new_fclose (_IO_FILE *fp) { int status; CHECK_FILE(fp, EOF); #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1) if (_IO_vtable_offset (fp) != 0 ) return _IO_old_fclose (fp); #endif if (fp->_IO_file_flags & _IO_IS_FILEBUF) _IO_un_link ((struct _IO_FILE_plus *) fp); _IO_acquire_lock (fp); if (fp->_IO_file_flags & _IO_IS_FILEBUF) status = _IO_file_close_it (fp); else status = fp->_flags & _IO_ERR_SEEN ? -1 : 0 ; _IO_release_lock (fp); _IO_FINISH (fp); if (fp->_mode > 0 ) { #if _LIBC struct _IO_codecvt *cc = fp->_codecvt; __libc_lock_lock (__gconv_lock); __gconv_release_step (cc->__cd_in.__cd.__steps); __gconv_release_step (cc->__cd_out.__cd.__steps); __libc_lock_unlock (__gconv_lock); #endif } else { if (_IO_have_backup (fp)) _IO_free_backup_area (fp); } if (fp != _IO_stdin && fp != _IO_stdout && fp != _IO_stderr) { fp->_IO_file_flags = 0 ; free (fp); } return status; }
依旧是先检测魔数位来到,检查_IO_IS_FILEBUF
文件指针 fp
是否与文件缓冲区相关联,
_IO_un_link 脱链 _IO_un_link ((struct _IO_FILE_plus *) fp)
libio/genops.c
void _IO_un_link (struct _IO_FILE_plus *fp) { if (fp->file._flags & _IO_LINKED) { struct _IO_FILE **f ; #ifdef _IO_MTSAFE_IO _IO_cleanup_region_start_noarg (flush_cleanup); _IO_lock_lock (list_all_lock); run_fp = (_IO_FILE *) fp; _IO_flockfile ((_IO_FILE *) fp); #endif if (_IO_list_all == NULL ) ; else if (fp == _IO_list_all) { _IO_list_all = (struct _IO_FILE_plus *) _IO_list_all->file._chain; ++_IO_list_all_stamp; } else for (f = &_IO_list_all->file._chain; *f; f = &(*f)->_chain) if (*f == (_IO_FILE *) fp) { *f = fp->file._chain; ++_IO_list_all_stamp; break ; } fp->file._flags &= ~_IO_LINKED; #ifdef _IO_MTSAFE_IO _IO_funlockfile ((_IO_FILE *) fp); run_fp = NULL ; _IO_lock_unlock (list_all_lock); _IO_cleanup_region_end (0 ); #endif } } libc_hidden_def (_IO_un_link)
IO_link_in逆过程,把文件流结构体从链上拿下来,先检测_IO_LINKED(0x80),看文件流结构体是不是在IO_list_all链中,随后把_IO_list_all指向chain字段也就是下一个
再把_IO_LINKED标志位置空
_IO_file_close_it 缓冲区数据写回文件,释放缓冲区,关闭文件 libio/fileops.c
返回_IO_new_fclose 调用_IO_file_close_it (fp);
# define _IO_new_file_close_it _IO_file_close_it
int _IO_new_file_close_it (_IO_FILE *fp) { int write_status; if (!_IO_file_is_open (fp)) return EOF; if ((fp->_flags & _IO_NO_WRITES) == 0 && (fp->_flags & _IO_CURRENTLY_PUTTING) != 0 ) write_status = _IO_do_flush (fp); else write_status = 0 ; _IO_unsave_markers (fp); int close_status = ((fp->_flags2 & _IO_FLAGS2_NOCLOSE) == 0 ? _IO_SYSCLOSE (fp) : 0 ); #if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T if (fp->_mode > 0 ) { if (_IO_have_wbackup (fp)) _IO_free_wbackup_area (fp); _IO_wsetb (fp, NULL , NULL , 0 ); _IO_wsetg (fp, NULL , NULL , NULL ); _IO_wsetp (fp, NULL , NULL ); } #endif _IO_setb (fp, NULL , NULL , 0 ); _IO_setg (fp, NULL , NULL , NULL ); _IO_setp (fp, NULL , NULL ); _IO_un_link ((struct _IO_FILE_plus *) fp); fp->_flags = _IO_MAGIC|CLOSED_FILEBUF_FLAGS; fp->_fileno = -1 ; fp->_offset = _IO_pos_BAD; return close_status ? close_status : write_status; } libc_hidden_ver (_IO_new_file_close_it, _IO_file_close_it)
首先检测不可写标志(0x8),再检测_IO_CURRENTLY_PUTTING(0x800)
文件是否处于输出状态。
在fwrite中设置了_IO_CURRENTLY_PUTTING标志位
_IO_do_flush (fp) 缓冲区数据写回文件 # define _IO_do_flush(_f) \ _IO_do_write(_f, (_f)->_IO_write_base, \ (_f)->_IO_write_ptr-(_f)->_IO_write_base)
int _IO_new_do_write (_IO_FILE *fp, const char *data, _IO_size_t to_do) { return (to_do == 0 || (_IO_size_t) new_do_write (fp, data, to_do) == to_do) ? 0 : EOF; } libc_hidden_ver (_IO_new_do_write, _IO_do_write)
static _IO_size_t new_do_write (_IO_FILE *fp, const char *data, _IO_size_t to_do) { _IO_size_t count; if (fp->_flags & _IO_IS_APPENDING) fp->_offset = _IO_pos_BAD; else if (fp->_IO_read_end != fp->_IO_write_base) { _IO_off64_t new_pos = _IO_SYSSEEK (fp, fp->_IO_write_base - fp->_IO_read_end, 1 ); if (new_pos == _IO_pos_BAD) return 0 ; fp->_offset = new_pos; } count = _IO_SYSWRITE (fp, data, to_do); if (fp->_cur_column && count) fp->_cur_column = _IO_adjust_column (fp->_cur_column - 1 , data, count) + 1 ; _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base); fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_buf_base; fp->_IO_write_end = (fp->_mode <= 0 && (fp->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED)) ? fp->_IO_buf_base : fp->_IO_buf_end); return count; }
和fwrite里分析的一样_IO_SYSWRITE调用了==vtable中__write对应的_IO_new_file_write
==
写入文件并,刷新输出缓冲区的值
_IO_SYSCLOSE(fp) 关闭文件 回到_IO_new_file_close_it
_IO_FLAGS2_NOCLOSE(32)
表示一个流不应该被 fclose()
函数关闭。为流设置 _IO_FLAGS2_NOCLOSE
标志将防止 fclose()
函数关闭与流相关联的文件描述符。这在文件描述符正在被程序的其他部分使用或流正在用于与需要文件描述符保持打开状态的设备进行通信的情况下非常有用
调用_IO_SYSCLOSE(fp)
#define _IO_SYSCLOSE(FP) JUMP0 (__close, FP)
==虚表中的__close,即_IO_file_close函数==
/libio/fileops.c
int _IO_file_close (_IO_FILE *fp) { return close_not_cancel (fp->_fileno); }
#define close_not_cancel(fd) \ __close (fd)
直接系统调用close关闭文件
销毁文件流结构体内容,释放缓冲区 回到**_IO_new_file_close_it**_mode=-1不符合条件
_IO_setb (fp, NULL , NULL , 0 ); _IO_setg (fp, NULL , NULL , NULL ); _IO_setp (fp, NULL , NULL );
void _IO_setb (_IO_FILE *f, char *b, char *eb, int a) { if (f->_IO_buf_base && !(f->_flags & _IO_USER_BUF)) free (f->_IO_buf_base); f->_IO_buf_base = b; f->_IO_buf_end = eb; if (a) f->_flags &= ~_IO_USER_BUF; else f->_flags |= _IO_USER_BUF; }
释放缓冲区,_IO_buf_base,_IO_buf_end置空,设置_IO_USER_BUF位域
#define _IO_setg(fp, eb, g, eg) ((fp)->_IO_read_base = (eb),\ (fp)->_IO_read_ptr = (g), (fp)->_IO_read_end = (eg)) #define _IO_setp(__fp, __p, __ep) \ ((__fp)->_IO_write_base = (__fp)->_IO_write_ptr \ = __p, (__fp)->_IO_write_end = (__ep))
macro展开后
((fp)->_IO_read_base = (NULL ), (fp)->_IO_read_ptr = (NULL ), (fp)->_IO_read_end = (NULL )); ((fp)->_IO_write_base = (fp)->_IO_write_ptr = NULL , (fp)->_IO_write_end = (NULL ));
把read write相关缓冲区指针置空
fp->_flags = _IO_MAGIC|CLOSED_FILEBUF_FLAGS; fp->_fileno = -1 ; fp->_offset = _IO_pos_BAD;
确认文件关闭,释放文件流结构体内存 IO_new_file_finish 回到 _IO_new_fclose _IO_FINISH (fp);
#define _IO_FINISH(FP) JUMP1 (__finish, FP, 0)
==虚表中的_finish,对应_IO_new_file_finish函数==
libio/fileops.c
void _IO_new_file_finish (_IO_FILE *fp, int dummy) { if (_IO_file_is_open (fp)) { _IO_do_flush (fp); if (!(fp->_flags & _IO_DELETE_DONT_CLOSE)) _IO_SYSCLOSE (fp); } _IO_default_finish (fp, 0 ); } libc_hidden_ver (_IO_new_file_finish, _IO_file_finish)
#define _IO_file_is_open(__fp) ((__fp)->_fileno != -1)
直接来到_IO_default_finish(fp)
void _IO_default_finish (_IO_FILE *fp, int dummy) { struct _IO_marker *mark ; if (fp->_IO_buf_base && !(fp->_flags & _IO_USER_BUF)) { free (fp->_IO_buf_base); fp->_IO_buf_base = fp->_IO_buf_end = NULL ; } for (mark = fp->_markers; mark != NULL ; mark = mark->_next) mark->_sbuf = NULL ; if (fp->_IO_save_base) { free (fp->_IO_save_base); fp->_IO_save_base = NULL ; } _IO_un_link ((struct _IO_FILE_plus *) fp); #ifdef _IO_MTSAFE_IO if (fp->_lock != NULL ) _IO_lock_fini (*fp->_lock); #endif } libc_hidden_def (_IO_default_finish)
都不符合要求来到_IO_un_linkif (fp->file._flags & _IO_LINKED)
不符合,返回_IO_new_fclose
if (fp != _IO_stdin && fp != _IO_stdout && fp != _IO_stderr) { fp->_IO_file_flags = 0 ; free (fp); }
置零后释放文件流结构体内存
close
函数调用的vtable函数
在清空缓冲区的_IO_do_write
函数中会调用vtable中__write
对应的_IO_new_file_write
的函数。
关闭文件描述符_IO_SYSCLOSE
函数为_close
,即_IO_file_close
函函数。
_IO_FINISH
函数为vtable _finish,对应_IO_new_file_finish
函数