做题技巧
_IO_do_write
题目中使用到了puts函数,会最终调用到IO_new_file_overflow,该函数会最终使用_IO_do_write进行真正的输出。在输出的时候如果有缓冲区,会输出_IO_write_base开始的缓冲区内容,直到_IO_write_ptr(也就是将IO_write_base一直到_IO_write_ptr部分的值当作缓冲区,在无缓冲区时,两个指针指向同一位置,位于该结构体附近,也就是libc中),但是在setbuf后,理论上会不使用缓冲区,然而如果能修改_IO_2_1_stdout_结构体的 flags 部分,使得其认为 stdout 有缓冲区,再将_IO_write_base处的值进行 partiwal overwrite,就可以泄露出 libc 了
涉及到的相关代码:
puts函数最终会调用到该函数,我们需要满足部分 flag 要求使其能够进入_IO_do_write
int
_IO_new_file_overflow (_IO_FILE *f, int ch)
{
if (f->_flags & _IO_NO_WRITES)
{
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)
{
:
:
}
if (ch == EOF)
return _IO_do_write (f, f->_IO_write_base, f->_IO_write_ptr - f->_IO_write_base);
// 第二个参数为需要调用的目标,如果使得_IO_write_base < _IO_write_ptr,且 _IO_write_base 处存在有价值的地址 (libc 地址)则可进行泄露
// 在正常情况下,_IO_write_base == _IO_write_ptr 且位于 libc 中,所以可进行部分写
进入后的部分
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) /* 需要满足 */
/* 在没有正确实现 O_APPEND 的系统上,
你需要在这里调用 sys_seek(0, SEEK_END),
但在类 Unix 或类 Posix 系统上,这是不需要的,也不推荐这样做。
相反,只需表明偏移量(前后)是不可预测的。 */
fp->_offset = _IO_pos_BAD;
else if (fp->_IO_read_end != fp->_IO_write_base)
{
............
}
count = _IO_SYSWRITE (fp, data, to_do); // 这里真正进行 write
也就是说,为调用到目标函数地址,需要满足部分flags的需求,具体需要满足的flags:
_flags = 0xfbad0000 //Magic Number
_flags &= ~_IO_NO_WRITES //_flags = 0xfbad0000
_flags |= _IO_CURRENTLY_PUTTING //_flags = 0xfbad0800
_flags |= _IO_IS_APPENDING //_flags = 0xfbad1800