IO利用之stdout任意读
基础知识
bss段与stdin、out、err
- 在有些情况,程序中的
bss
段其实保存着stdin
、stdout
、stderr
这三个IO
结构体的地址。 - 大概率应该是因为这三句初始化输入输出的语句,需要用到
stdin
、stdout
、stderr
。
1 | setvbuf(stdin, 0LL, 2, 0LL); |
缓冲区
缓冲区是一块用于临时存储数据的区域,通常用于平衡数据生产者和数据消费者之间速度的差异。例如当我们调用printf()
、fgets
这种比较上层分装的IO
函数。它们就会先将数据读(写)入缓冲区(这一部分是由stdin(stdout)管理),然后再从中取出数据,放进对应的地址中。当满足一定条件时就会触发缓冲区刷新也就会将在缓冲区的数据读(写)入对应的目标地址中去。这样就完成了一次与设备与内存之间的(读)写。
缓冲区刷新可以在下面这几种情况下发生:
- 缓冲区满:当缓冲区写满的时候,系统会自动将缓冲区中的数据写入到实际的输出设备,并清空缓冲区,接着等待之后的数据进入缓冲区
- 手动刷新:C语言程序提供一个函数
fflush(FILE *stream)
函数来手动刷新缓冲区。 - 正常退出:当程序正常退出的时候(即调用
glibc
中的exit()
,而不是直接syscall exit
)此时所有打开的文件都会自动刷新缓冲区 - 行缓冲模式:对于行缓冲模式(通常是标准输出
stdout
在交互模式下默认的模式),在输出新行字符\n
时会自动刷新缓冲区。
输入输出缓冲模式
- 对于上面的一个
setvbuf()
函数到底是什么东西呢?其实该函数是用来设置输入、输出缓冲模式的。缓冲模式分为以下三种:无缓冲模式
:调用上层IO
函数时会,会直接写入或读入到目标位置,不会数据不会呆在缓冲区。行缓冲模式
:输入输出的数据一开始会被放入到缓冲区中,但是当缓冲区中有\n
存进来就会立即刷新缓冲区,将数据写入或读入到目标地址。全缓冲模式
:只有当缓冲区数据存储满后,才会进行刷新缓冲区的操作。或者当退出程序的时候libc_start_call_main
会调用libc
中的exit
函数,这样它就会刷新缓冲区。
- 对与这三种模式,在
stdio.h
头文件中有这几个函数可以用于设置的,可以在Linux中使用man
函数查看,其中setvbuf()
这个函数比较全面,对于全缓冲还可以设置缓冲区的地址:void setbuf(FILE *stream, char *buf)
void setbuffer(FILE *stream, char *buf, size_t size)
void setlinebuf(FILE *stream)
int setvbuf(FILE *stream, char *buf, int mode, size_t size)
实验
- 对于利用
stdout
实现地址任意读,主要关注IO_FILE
的这几个成员,对于实验中的几个lab
,将分别设置stdout
为无缓冲、行缓冲、全缓冲
模式,观察这三种模式下调用上层的输入输出函数如下的几个成员数据的变化。最后再做一个使用stdout
实现任意地址读的这些数据应该如何构造的一个总结。
1 | int _flags; // 文件状态标志(高位是 _IO_MAGIC,其余是标志位) |
lab1_无缓冲模式
lab2_行缓冲模式
lab3_全缓冲模式
题目1_MoeCTF_2023 feedback(改)
- 这题给了源码,但是
libc
版本是2.31
的,所以我稍微修改了一下源码,在glibc2.23
的环境下进行编译。改编后的题目会比原题简单,主要是为了理解stdout
如何实现任意地址写。 - 改编后的代码如下:
1 | //gcc feedback.c -o feedback -g -z now |
题目1_分析1
- 先查看一下保护机制
- 直接使用
IDA pro
逆向这个程序。
题目1_分析2
题目1_exp
题目2_MoeCTF_2023 feedback(原)
- 接下来直接使用
Docker
文件,在glibc2.31
分析一下原题。
题目3_de1ctf_2019_weapon
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 iyheart的博客!