-
-
[原创]IO-FILE中的stdin介绍
-
发表于: 2020-9-8 16:52 7640
-
本文为科普文,便于新手更好的了解IO-FILE中的stdin。
网上的很多教程对IO-FILE其实讲的都比较详细了,尤其是这里,从源码的角度看fread函数,讲的非常透彻。本文换一个角度,从几个关键指针的角度来看IO-FILE中的stdin,让大家搞明白IO-FILE中的stdin大概是怎么回事。IO-FILE中的stdin,一些常见的输入函数都会涉及,如scanf,fread函数,这些函数最关键的地方在于有一块输入缓冲区,用户输入的数据会先放到输入缓冲区中,函数再根据需要从缓冲区内读取数据(如fread的size大小,scanf中的格式化字符串等)。
直接看一段代码便于理解
程序首先调用fread函数,在这个函数中,程序会调用系统函数读取终端输入。在读取终端输入之前,系统会开辟一个输入缓冲区,_IO_buf_base指向这个输入缓冲区的起始位置,_IO_buf_end指向输入缓冲区的末尾。此时_IO_read_ptr和_IO_read_end都为0。然后系统从终端中读入数据,我这边输入的数据是aaaaaaaaaa10a20,来看一下执行完fread之后指针的变化。
可以看出,_IO_read_ptr指向的是缓冲区内还未被读取的数据起始位置。_IO_read_end指向的是缓冲区内还未被读取的数据末尾位置。_IO_read_base指向读入数据的起始位置。
程序继续执行,执行第一个scanf函数,此时会将缓冲区内的数字10读入。_IO_read_ptr向后移动,继续指向缓冲区内还未被读取的数据起始位置。
此时程序继续执行,执行第二个scanf函数,但是由于此时缓冲区内存放的是一个字符a,因此scanf并不会将其读取(读取失败情况下,scanf函数会返回0),因此_IO_read_ptr并不会改变。
继续执行程序,执行第三个scanf函数,显然由于_IO_read_ptr仍然指向字符a,因此依然会读取失败。
继续执行程序,执行getc(stdin),该函数会从缓冲区中读一个字节,因此会将_IO_read_ptr+1,执行完该函数之后。
程序继续执行,执行第四个scanf,由于此时_IO_read_ptr又指向了一个数字,因此scanf将该数据读了出来。
此时_IO_read_ptr指向换行符,我们再次调用getc(stdin)让其加一,此时_IO_read_ptr便与_IO_read_end相等了。
,也就是说,缓冲区内的数据都被我们读完了,那么我们调用第五次scanf的时候,便又要从终端读入数据了。
这里我输入666bbbbbbbb,第五次scanf结束之后的指针情况
可以看到数据重新从输入缓冲区的起始位置开始存放,但是缓冲区内的老数据并未清零,只是被覆盖了。
来一个非调试版的。
关于任意地址写
当我们篡改了_IO_buf_base与_IO_buf_end,将其改为目标地址开始与结尾时,那么我们下一次的终端输入就会被写到目标地址处了。
写此文的灵感主要来源于做whctf2017的stackoverflow的时候,当我们篡改了_IO_buf_base与_IO_buf_end到malloc_hook处的时候,还需要经过数次循环(多次调用sub_4008c8),原因就是_IO_read_ptr还小于_IO_read_end,此时如果不调用getc(stdin)的的话_IO_read_ptr是没法等于
_IO_read_end,那么程序也就不会从终端去读取输入了。当然有细心的人会发现这里存在scanf函数,scanf函数其实也是可以从缓冲区内读取输入的,但是由于我们缓冲区内的数据都是不可见字符,因此scanf是会读取失败的,因此并不能增加_IO_read_ptr。
对stdin的介绍就到这里了,写的不对的地方,欢迎大家指正。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
- [原创]IO-FILE中的stdin介绍 7641
- [原创]pwn中one_gadget的使用技巧 16104
- [原创]关于fastbin合并问题的研究 10207
- [求助]p452页 内存偏移rva的问题 3159