fclose함수

dandb3·2023년 5월 31일
0

pwnable

목록 보기
8/17

뭔가 fclose니까 할당 해제, 초기화와 관련된 것들만 다 해주고 끝날 것 같다.
자세히 알아보자.

#   define fclose(fp) _IO_new_fclose (fp)

int
_IO_new_fclose (FILE *fp)
{
  int status;
  CHECK_FILE(fp, EOF);
#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1)
  /* We desperately try to help programs which are using streams in a
     strange way and mix old and new functions.  Detect old streams
     here.  */
  if (_IO_vtable_offset (fp) != 0)
    return _IO_old_fclose (fp);
#endif
  /* First unlink the stream.  */
  if (fp->_flags & _IO_IS_FILEBUF)
    _IO_un_link ((struct _IO_FILE_plus *) fp);
  _IO_acquire_lock (fp);
  if (fp->_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)
    {
      /* This stream has a wide orientation.  This means we have to free
	 the conversion functions.  */
      struct _IO_codecvt *cc = fp->_codecvt;
      __libc_lock_lock (__gconv_lock);
      __gconv_release_step (cc->__cd_in.step);
      __gconv_release_step (cc->__cd_out.step);
      __libc_lock_unlock (__gconv_lock);
    }
  else
    {
      if (_IO_have_backup (fp))
	  	_IO_free_backup_area (fp);
    }
  _IO_deallocate_file (fp);
  return status;
}

당연히 그냥 불러올 리가 없다. 매크로에 의해 _IO_new_fclose를 호출하게 된다.
_IO_IS_FILEBUF가 설정되어 있다면 _IO_un_link를 하게 된다.
또한, _IO_file_close_it를 호출해서 내부적으로 close 함수를 호출해서 파일을 닫아준다.
마지막으로, _IO_FINISH를 해 주고, _IO_deallocate_file을 해 준다.
_IO_IS_FILEBUF매크로가 무엇을 뜻하는지는 잘 모르겠다. 왜 얘가 있을때 unlink, close를 해 주는 거지..?
내부적으로 호출해주는 세 함수에 대해 알아보자.

  • _IO_un_link

    void
    _IO_un_link (struct _IO_FILE_plus *fp)
    {
      if (fp->file._flags & _IO_LINKED)
        {
          FILE **f;
    #ifdef _IO_MTSAFE_IO
          _IO_cleanup_region_start_noarg (flush_cleanup);
          _IO_lock_lock (list_all_lock);
          run_fp = (FILE *) fp;
          _IO_flockfile ((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;
          else
        	for (f = &_IO_list_all->file._chain; *f; f = &(*f)->_chain)
          	  if (*f == (FILE *) fp)
            	{
              	  *f = fp->file._chain;
              	  break;
            	}
          fp->file._flags &= ~_IO_LINKED;
    #ifdef _IO_MTSAFE_IO
          _IO_funlockfile ((FILE *) fp);
          run_fp = NULL;
          _IO_lock_unlock (list_all_lock);
          _IO_cleanup_region_end (0);
    #endif
        }
    }
    libc_hidden_def (_IO_un_link)

    별 건 없다. 말 그대로 unlink해주는 함수이다.
    전역변수 _IO_list_all에서 순회하면서 해당하면 unlink 시켜주고, 플래그도 해제해준다.

  • _IO_file_close_it

    int
    _IO_new_file_close_it (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);
      /* Free buffer. */
      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);
        }
      _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)

    만약 write 모드로 열렸고, 버퍼에 쓴 내용이 남아있는 경우, _IO_do_flush를 통해서 남은 내용을 write 해 준다.
    그 후 _IO_SYSCLOSE를 통해 close 해준다.
    마지막으로 _IO_setb 함수에서 버퍼를 free 해주고, unlink까지 해 준 후 함수는 종료된다.

  • _IO_FINISH

    #define _IO_FINISH(FP) JUMP1 (__finish, FP, 0)
    
    void
    _IO_new_file_finish (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)

    자꾸 이런저런 함수들을 호출하는데, 결국 제대로 닫혔는지 이중, 삼중으로 확인하는 것 같다. 결국 큰 흐름은 _IO_new_fclose를 통해 이루어진다고 보면 될 것 같다.

다음에는 지금까지 알아본 내용들을 바탕으로 어떻게 exploit에 이용할 수 있을 지 알아보자.

profile
공부 내용 저장소

0개의 댓글