PWN系列-初探IO
PWN系列-初探IO
记录一下自己理解的IO,文章的代码均是glibc-2.23版本下的代码。
由于笔者才疏学浅,学识有限,文章中难免会有疏漏和错误之处,恳请各位师傅批评指正,多多包涵。
什么是IO
IO中的I其实就是in,O其实就是out,一个输入,一个输出。
IO长什么样子
_IO_FILE_plus
首先先来认识一下_IO_FILE_plus结构体
struct _IO_FILE_plus
{
_IO_FILE file; /* 基础的文件结构体 */
const struct _IO_jump_t *vtable; /* 虚表指针 */
};
总结起来,_IO_FILE_plus 结构体通过包含 _IO_FILE 结构体和一个虚表指针。
_IO_FILE
_IO_FILE结构体定义:
struct _IO_FILE {
int _flags; /* 文件状态标志 */
char* _IO_read_ptr; /* 读取缓冲区的当前位置 */
char* _IO_read_end; /* 读取缓冲区的结束位置 */
char* _IO_read_base; /* 读取缓冲区的起始位置 */
char* _IO_write_base; /* 写入缓冲区的起始位置 */
char* _IO_write_ptr; /* 写入缓冲区的当前位置 */
char* _IO_write_end; /* 写入缓冲区的结束位置 */
char* _IO_buf_base; /* 缓冲区的起始位置 */
char* _IO_buf_end; /* 缓冲区的结束位置 */
char *_IO_save_base; /* 保存位置指针 */
char *_IO_backup_base;/* 备份缓冲区指针 */
char *_IO_save_end; /* 保存结束指针 */
struct _IO_marker *_markers; /* 标记 */
struct _IO_FILE *_chain; /* 文件链表 */
int _fileno; /* 文件描述符 */
#if 0
int _blksize; /* 块大小 */
#else
int _flags2; /* 额外标志 */
#endif
_IO_off_t _old_offset; /* 旧的偏移量 */
#if defined _G_IO_IO_FILE_VERSION && _G_IO_IO_FILE_VERSION == 0x20001
unsigned short _cur_column; /* 当前列号 */
signed char _vtable_offset; /* 虚表偏移量 */
char _shortbuf[1]; /* 短缓冲区 */
#endif
_IO_lock_t *_lock; /* 锁 */
#if defined _LIBC_REENTRANT
_IO_lock_t *_lock_owner; /* 锁的拥有者 */
unsigned int _lock_count; /* 锁计数 */
#endif
_IO_off64_t _offset; /* 偏移量 */
void *__pad1; /* 填充1 */
void *__pad2; /* 填充2 */
void *__pad3; /* 填充3 */
void *__pad4; /* 填充4 */
size_t __pad5; /* 填充5 */
int _mode; /* 模式 */
char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
/* 未使用的填充空间 */
};
此结构体定义了标准C库中用于文件操作的内部数据结构。以下是每个成员变量的简要说明:
_flags: 文件状态标志,表示文件的各种状态和属性。_IO_read_ptr,_IO_read_end,_IO_read_base: 分别指向读取缓冲区的当前位置、结束位置和起始位置。_IO_write_base,_IO_write_ptr,_IO_write_end: 分别指向写入缓冲区的起始位置、当前位置和结束位置。_IO_buf_base,_IO_buf_end: 分别指向缓冲区的起始位置和结束位置。_IO_save_base,_IO_backup_base,_IO_save_end: 分别用于保存和备份缓冲区的位置指针。_markers: 指向文件流标记的指针。_chain: 指向链表中下一个文件流的指针。_fileno: 文件描述符。_flags2: 额外的文件状态标志。_old_offset: 旧的偏移量。_cur_column,_vtable_offset,_shortbuf: 用于输入输出流的辅助变量。_lock: 指向文件流锁的指针,用于线程安全。_offset: 当前的文件偏移量。__pad1,__pad2,__pad3,__pad4: 用于填充的指针,确保结构体对齐。__pad5: 用于填充的大小。_mode: 文件模式。_unused2: 未使用的填充空间,用于确保结构体的大小和对齐。
_IO_jump_t
struct _IO_jump_t 定义如下:
struct _IO_jump_t
{
size_t __dummy;
size_t __dummy2;
_IO_finish_t __finish;
_IO_overflow_t __overflow;
_IO_underflow_t __underflow;
_IO_underflow_t __uflow;
_IO_pbackfail_t __pbackfail;
_IO_xsputn_t __xsputn;
_IO_xsgetn_t __xsgetn;
_IO_seekoff_t __seekoff;
_IO_seekpos_t __seekpos;
_IO_setbuf_t __setbuf;
_IO_sync_t __sync;
_IO_doallocate_t __doallocate;
_IO_read_t __read;
_IO_write_t __write;
_IO_seek_t __seek;
_IO_close_t __close;
_IO_stat_t __stat;
_IO_showmanyc_t __showmanyc;
_IO_imbue_t __imbue;
};
这些字段是指向不同函数类型的指针,用于实现多态性和灵活的文件操作。以下是这些函数指针的大致功能说明:
__finish: 完成流的操作。__overflow: 处理输出缓冲区溢出的操作。__underflow: 处理输入缓冲区空的操作。__uflow: 处理输入缓冲区下溢的操作。__pbackfail: 处理向输入缓冲区放回字符失败的操作。__xsputn: 写入多个字符的操作。__xsgetn: 读取多个字符的操作。__seekoff: 相对偏移位置的文件定位操作。__seekpos: 绝对位置的文件定位操作。__setbuf: 设置缓冲区的操作。__sync: 同步文件缓冲区的操作。__doallocate: 分配缓冲区的操作。__read: 读取数据的操作。__write: 写入数据的操作。__seek: 文件定位的操作。__close: 关闭文件的操作。__stat: 获取文件状态的操作。__showmanyc: 显示字符数的操作。__imbue: 设置区域设置信息的操作。
_IO_list_all
_IO_list_all 是一个全局变量,在 GNU C 库(glibc)中用于管理所有打开的文件流。它是一个指向 _IO_FILE 结构体链表的指针。通过这个链表,glibc 可以跟踪和操作所有当前打开的文件流。
链表结构
_IO_list_all 是一个指向 _IO_FILE 结构体的指针,而 _IO_FILE 结构体本身包含一个 _chain 指针,指向下一个文件流。因此,所有打开的文件流形成了一个链表。这个链表的头部是 _IO_list_all。

作用
- 管理文件流: 通过维护这个链表,glibc 能够方便地管理所有打开的文件流。每当有新的文件流创建时,会将其添加到链表中;当文件流关闭时,会将其从链表中移除。
- 文件操作: 许多内部文件操作函数,例如刷新所有文件流、关闭所有文件流等,都需要遍历这个链表,以对每个文件流执行相应的操作。
IO的利用思路
IO利用的大致思路都是构造fake_io结构体,布置一些数据和函数,通过程序正常的执行流程走到IO操作来劫持程序流来getshell。

这就是我目前所认识的IO,IO涉及到很多攻击手法,我应该会另写一篇文章来进行记录各种板子。
这篇文章中的图片来源两位师傅的博客,但是我一下子找不到这两位师傅的博客地址了,故没有在文章末尾添加参考文章,先冒昧的直接拿来了,望见谅。

浙公网安备 33010602011771号