struct _IO_FILE {int _flags; /* High-order word is _IO_MAGIC; rest is flags. */#define_IO_file_flags _flags /* The following pointers correspond to the C++ streambuf protocol. */ /* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */char* _IO_read_ptr; /* Current read pointer */char* _IO_read_end; /* End of get area. */char* _IO_read_base; /* Start of putback+get area. */char* _IO_write_base; /* Start of put area. */char* _IO_write_ptr; /* Current put pointer. */char* _IO_write_end; /* End of put area. */char* _IO_buf_base; /* Start of reserve area. */char* _IO_buf_end; /* End of reserve area. */ /* The following fields are used to support backing up and undo. */char*_IO_save_base; /* Pointer to start of non-current get area. */char*_IO_backup_base; /* Pointer to first valid character of backup area */char*_IO_save_end; /* Pointer to end of non-current get area. */struct _IO_marker *_markers;struct _IO_FILE *_chain;int _fileno;#if0 int _blksize;#elseint _flags2;#endif_IO_off_t _old_offset; /* This used to be _offset but it's too small. */#define__HAVE_COLUMN /* temporary */ /* 1+column number of pbase(); 0 is unknown. */unsignedshort _cur_column;signedchar _vtable_offset;char _shortbuf[1]; /* char* _save_gptr; char* _save_egptr; */_IO_lock_t*_lock;#ifdef_IO_USE_OLD_IO_FILE#endif#ifdefined_G_IO_IO_FILE_VERSION&&_G_IO_IO_FILE_VERSION==0x20001_IO_off64_t _offset;# ifdefined_LIBC||defined_GLIBCPP_USE_WCHAR_T /* Wide character stream stuff. */struct _IO_codecvt *_codecvt;struct _IO_wide_data *_wide_data;struct _IO_FILE *_freeres_list;void*_freeres_buf;# elsevoid*__pad1;void*__pad2;void*__pad3;void*__pad4;# endifsize_t __pad5;int _mode; /* Make sure we don't get into trouble again. */char _unused2[15*sizeof (int) -4*sizeof (void*) -sizeof (size_t)];#endif};
改函数指针我们可以填充为one_gadget,这样就不用考虑参数问题;若one_gadget的环境变量都不好使,可以考虑填充为system函数地址,传参的话,多数vtable函数指针在被调用时,会将它的_IO_FILE_plus地址当作第一个参数传递,所以我们可以将_IO_FILE_plus的_flags成员填成“/bin/sh\x00”,但这种方法通常也不好用,因为调用vtable函数指针之前会对_IO_FILE_plus的结构进行检查,通常改“/bin/sh\x00”之后会导致对_flags成员的检查不通过(亲测printf不行,但House of orange利用中出现的_IO_flush_all_lockp能检查通过)
staticinlineconststruct_IO_jump_t*IO_validate_vtable (conststruct_IO_jump_t*vtable){ /* Fast path: The vtable pointer is within the __libc_IO_vtables section. */uintptr_t section_length = __stop___libc_IO_vtables - __start___libc_IO_vtables;constchar*ptr = (constchar*) vtable;uintptr_t offset = ptr - __start___libc_IO_vtables;if (__glibc_unlikely (offset >= section_length)) /* The vtable pointer is not in the expected section. Use the slow path, which will terminate the process if necessary. */_IO_vtable_check ();return vtable;}
pwndbg> p __start___libc_IO_vtables0x7fcbc6703900<_IO_helper_jumps>""pwndbg> p __stop___libc_IO_vtables0x7fcbc6704668""pwndbg> p &_IO_str_jumps(const struct _IO_jump_t *) 0x7fcbc6704500<_IO_str_jumps>pwndbg> p &_IO_wstr_jumps(const struct _IO_jump_t *) 0x7fcbc6703cc0<_IO_wstr_jumps>
int_IO_puts (constchar*str){int result = EOF;_IO_size_t len =strlen (str);_IO_acquire_lock (_IO_stdout);if ((_IO_vtable_offset (_IO_stdout)!=0||_IO_fwide (_IO_stdout,-1)==-1)&&_IO_sputn (_IO_stdout, str, len)== len&&_IO_putc_unlocked ('\n', _IO_stdout)!= EOF) result =MIN (INT_MAX, len +1);_IO_release_lock (_IO_stdout);return result;}
_IO_new_file_overflow的相关源码:
int_IO_new_file_overflow (FILE *f,int ch){if (f->_flags & _IO_NO_WRITES) /* SET ERROR */ {f->_flags |= _IO_ERR_SEEN;__set_errno (EBADF);return EOF; } /* If currently reading or no buffer allocated. */if ((f->_flags & _IO_CURRENTLY_PUTTING) ==0||f->_IO_write_base ==NULL) { /* Allocate a buffer if needed. */if (f->_IO_write_base ==NULL) {_IO_doallocbuf (f);_IO_setg (f,f->_IO_buf_base,f->_IO_buf_base,f->_IO_buf_base); } /* Otherwise must be currently reading. If _IO_read_ptr (and hence also _IO_read_end) is at the buffer end, logically slide the buffer forwards one block (by setting the read pointers to all point at the beginning of the block). This makes room for subsequent output. Otherwise, set the read pointers to _IO_read_end (leaving that alone, so it can continue to correspond to the external position). */if (__glibc_unlikely (_IO_in_backup (f))) {size_t nbackup =f->_IO_read_end -f->_IO_read_ptr;_IO_free_backup_area (f);f->_IO_read_base -=MIN (nbackup,f->_IO_read_base -f->_IO_buf_base);f->_IO_read_ptr =f->_IO_read_base; }if (f->_IO_read_ptr ==f->_IO_buf_end)f->_IO_read_end =f->_IO_read_ptr =f->_IO_buf_base;f->_IO_write_ptr =f->_IO_read_ptr;f->_IO_write_base =f->_IO_write_ptr;f->_IO_write_end =f->_IO_buf_end;f->_IO_read_base =f->_IO_read_ptr =f->_IO_read_end;f->_flags |= _IO_CURRENTLY_PUTTING;if (f->_mode <=0&&f->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))f->_IO_write_end =f->_IO_write_ptr; }if (ch == EOF)return_IO_do_write (f,f->_IO_write_base,f->_IO_write_ptr -f->_IO_write_base);if (f->_IO_write_ptr ==f->_IO_buf_end ) /* Buffer is really full */if (_IO_do_flush (f)== EOF)return EOF;*f->_IO_write_ptr++= ch;if ((f->_flags & _IO_UNBUFFERED)|| ((f->_flags & _IO_LINE_BUF) && ch =='\n'))if (_IO_do_write (f,f->_IO_write_base,f->_IO_write_ptr -f->_IO_write_base)== EOF)return EOF;return (unsignedchar) ch;}libc_hidden_ver (_IO_new_file_overflow, _IO_file_overflow)