what is _IO_FILE & _IO_FILE_plus
항상 헷갈려했던 FILE에 대한 정의, 이번에 한번 재대로 해보고 가자는 의미해서 정리해보았다.
우리가 항상 보는 stdout, stdin , stderr. 얘가 뭐고 어디에 존재하냐고 묻는다면
바로 대답이 나와야한다. 얘내는 libc의 존재하는 전역변수들이며, _IO_FILE_plus 구조체 라는 것을
물론 잘 모를 수도 있다. 그럼 지금 부터 시작해보자
pwndbg> p stdout
$8 = (struct _IO_FILE *) 0x7ffff7dce760 <_IO_2_1_stdout_>
pwndbg> vmmap 0x7ffff7dce760
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
Start End Perm Size Offset File
0x7ffff7dcd000 0x7ffff7dcf000 rw-p 2000 1eb000 /home/iofile_aaw/libc.so.6 +0x1760
pwndbg> 우리가 gdb 에서 stdout 을 출력하면 struct _IO_FILE * 형태의 glibc의 존재하는 부분이 나온다.
이는 glibc에서 _IO_FILE 구조체로 존재하는 _IO_2_1_stdout_ 라는 전역변수가 우리가 아는 stdout 의 원래이름이기 때문이다.
더 자세히 알아보자면
pwndbg> p *(struct _IO_FILE_plus *) 0x7ffff7dce760
$10 = {
file = {
_flags = -72537977,
_IO_read_ptr = 0x7ffff7dce7e3 <_IO_2_1_stdout_+131> "",
_IO_read_end = 0x7ffff7dce7e3 <_IO_2_1_stdout_+131> "",
_IO_read_base = 0x7ffff7dce7e3 <_IO_2_1_stdout_+131> "",
_IO_write_base = 0x7ffff7dce7e3 <_IO_2_1_stdout_+131> "",
_IO_write_ptr = 0x7ffff7dce7e3 <_IO_2_1_stdout_+131> "",
_IO_write_end = 0x7ffff7dce7e3 <_IO_2_1_stdout_+131> "",
_IO_buf_base = 0x7ffff7dce7e3 <_IO_2_1_stdout_+131> "",
_IO_buf_end = 0x7ffff7dce7e4 <_IO_2_1_stdout_+132> "",
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7dcda00 <_IO_2_1_stdin_>,
_fileno = 1,
_flags2 = 0,
_old_offset = -1,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x7ffff7dcf8c0 <_IO_stdfile_1_lock>,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x7ffff7dcd8c0 <_IO_wide_data_1>,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = -1,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7dca2a0 <_IO_file_jumps>
}
_IO_FILE_plus 구조체로 되어 있다. _IO_FILE + 아래에 vtable이 추가된.
struct _IO_FILE_plus {
struct _IO_FILE file;
const struct _IO_jump_t *vtable; // 여기가 바로 vtable
};이런 형태이다. 즉, 우리가 아는 모든 file들은 _IO_FILE_plus 구조체로 되어 있다. 그중에서도 이미 저명한 stdout, stdin , stderr 얘내들은 이미 유명한 glibc에서 사용하는 얘들이다.
즉, 우리가
FILE *fp;
fp = fopen("/etc/passwd", "r");이런식으로 파일을 열면 내부에서는 _IO_FILE_plus 구조체가 열린다. 하지만, 우리가
p stdout 했을때 _IO_FILE 이 나오는 이유는 _IO_FILE_plus 구조체의 시작이 _IO_FILE 구조체의 시작과 같기 때문에 gdb 상 그렇게 보일뿐 모든 file 형태는 _IO_FILE_plus 구조체로 되어 있다.
__fopen_internal (const char *filename, const char *mode, int is32)
{
struct locked_FILE
{
struct _IO_FILE_plus fp;
#ifdef _IO_MTSAFE_IO
...실제 glibc의 fopen 코드에서도 struct _IO_FILE_plus fp; 을 사용하는 것을 볼 수 있다.
그럼 vtable 이 뭐냐?