IO_FILE——leak 任意读

本文介绍了如何使用 IO_FILE 在没有 show 函数的情况下实现任意读取。通过分析 _IO_puts 和相关函数的内部工作原理,讨论了如何设置 _flags 以绕过检查,并最终调用 _IO_SYSWRITE 来执行系统调用,泄露内存中的数据。文章还列举了相关 CTF 挑战并提供了参考资料。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Python微信订餐小程序课程视频

https://edu.csdn.net/course/detail/36074

Python实战量化交易理财系统

https://edu.csdn.net/course/detail/35475
在堆题没有show函数时,我们可以用 IO_FILE 进行leak,本文就记录一下如何实现这一手法。

拿一个输出函数 puts 来说,它在源码里的表现形式为 _IO_puts 。

\_IO\_puts (const char *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_puts 又调用了一个叫 _IO_sputn 的函数。

#define \_IO\_sputn(\_\_fp, \_\_s, \_\_n) \_IO\_XSPUTN (\_\_fp, \_\_s, \_\_n)

它是一个宏,它的作用就是调用 _IO_2_1_stdout_ 里 vtable 所指向的 _IO_XSPUTN,也就是 _IO_new_file_xsputn

\_IO\_size\_t
\_IO\_new\_file\_xsputn (\_IO\_FILE *f, const void *data, \_IO\_size\_t n)
{
 const char *s = (const char *) data;
 \_IO\_size\_t to\_do = n;
 int must\_flush = 0;
 \_IO\_size\_t count = 0;

 ............
 else if (f->\_IO\_write\_end > f->\_IO\_write\_ptr)
 count = f->\_IO\_write\_end - f->\_IO\_write\_ptr; /* Space available. */

  /* Then fill the buffer. */
  if (count > 0)
 {
............
 if (\_IO\_OVERFLOW (f, EOF) == EOF)

当 f->_IO_write_end > f->_IO_write_ptr 时,会调用 memcpy 拷贝数据至缓冲区。之后还会判断目标输出数据是否还有剩余。如果还有剩余就要调用 _IO_OVERFLOW 函数,刷新缓冲区。这个函数在 vtable 中为 _IO_overflow ,也就是 _IO_new_file_overflow 。

int
\_IO\_new\_file\_overflow (\_IO\_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\_wr
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值