scanf 在uefi中调用_IOFILE中的stdin介绍

本文从关键指针角度科普IO-FILE中的stdin,通过scanf和fread等函数说明输入数据如何在缓冲区处理。通过示例代码解析缓冲区读取过程,阐述如何在不同情况下读取数据。
摘要由CSDN通过智能技术生成
5239bb32e1e4592f3cd63b077d22b3f0.png

本文为看雪论坛优秀文章

看雪论坛作者ID:Mr.Bean

本文为科普文,便于新手更好的了解IO-FILE中的stdin。


网上的很多教程对IO-FILE其实讲的都比较详细了,尤其是这里,从源码的角度看fread函数,讲的非常透彻。

本文换一个角度,从几个关键指针的角度来看IO-FILE中的stdin,让大家搞明白IO-FILE中的stdin大概是怎么回事。

IO-FILE中的stdin,一些常见的输入函数都会涉及,如scanf,fread函数,这些函数最关键的地方在于有一块输入缓冲区,用户输入的数据会先放到输入缓冲区中,函数再根据需要从缓冲区内读取数据(如fread的size大小,scanf中的格式化字符串等)。773dd9c49eac44104df6233651083b47.png直接看一段代码便于理解:

#include#includeint main(){    char buf[10];    int number1;    int number2;    int number3;    int number4;    int number5;    fread(buf,1,10,stdin);    puts("buf:");    write(1,buf,10);    printf("\n");    puts("----------");    scanf("%d",&number1);    printf("number1:%d\n",number1);    printf("----------------\n");    scanf("%d",&number2);        printf("number2:%d\n",number2);    printf("----------------\n");    scanf("%d",&number3);        printf("number3:%d\n",number3);        printf("----------------\n");    getc(stdin);    scanf("%d",&number4);    printf("number4:%d\n",number4);    getc(stdin);    scanf("%d",&number5);        printf("number5:%d\n",number5);}
程序首先调用fread函数,在这个函数中,程序会调用系统函数读取终端输入。 在读取终端输入之前,系统会开辟一个输入缓冲区,_IO_buf_base指向这个输入缓冲区的起始位置,_IO_buf_end指向输入缓冲区的末尾。 此时_IO_read_ptr和_IO_read_end都为0。然后系统从终端中读入数据,我这边输入的数据是aaaaaaaaaa10a20,来看一下执行完fread之后指针的变化。 2ab4ced66f5be9c970e19b10569ce2d3.png 可以看出,_IO_read_ptr指向的是缓冲区内还未被读取的数据起始位置。_IO_read_end指向的是缓冲区内还未被读取的数据末尾位置。_IO_read_base指向读入数据的起始位置。


程序继续执行,执行第一个scanf函数,此时会将缓冲区内的数字10读入。_IO_read_ptr向后移动,继续指向缓冲区内还未被读取的数据起始位置。

b7b9cdc40375017200ea95a442e89e10.png

3d86de4666a24bb939b472847d02ac01.png

此时程序继续执行,执行第二个scanf函数,但是由于此时缓冲区内存放的是一个字符a,因此scanf并不会将其读取(读取失败情况下,scanf函数会返回0),因此_IO_read_ptr并不会改变。

86c3a885b39423b6355ef328938f6331.png

624581ffdfb422bd879c4197e84918b1.png

继续执行程序,执行第三个scanf函数,显然由于_IO_read_ptr仍然指向字符a,因此依然会读取失败。 43c425f4fbcabbad5c8f7497cb1398d1.png

d7641a06631a189fd6ee706402369022.png

继续执行程序,执行getc(stdin),该函数会从缓冲区中读一个字节,因此会将_IO_read_ptr+1,执行完该函数之后。

b525f1126eaf17842f8d006e1c74f27f.png

程序继续执行,执行第四个scanf,由于此时_IO_read_ptr又指向了一个数字,因此scanf将该数据读了出来。

3c154d6ad5a964416db057d71f4255ca.png

此时_IO_read_ptr指向换行符,我们再次调用getc(stdin)让其加一,此时_IO_read_ptr便与_IO_read_end相等了。

facbb2ee08a8ddf9963f216ca0980048.png

也就是说,缓冲区内的数据都被我们读完了,那么我们调用第五次scanf的时候,便又要从终端读入数据了。


这里我输入666bbbbbbbb,第五次scanf结束之后的指针情况:

0cb4569625d58cf44c183fee79e25bf6.png

可以看到数据重新从输入缓冲区的起始位置开始存放,但是缓冲区内的老数据并未清零,只是被覆盖了。


来一个非调试版的:

906af9a3e6c98038de39c9ff24f87c12.png 关于任意地址写

当我们篡改了_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。

676b1959d481023abc42d83b1675f317.png

对stdin的介绍就到这里了,写的不对的地方,欢迎大家指正。

3e70c9ec31651004a4bb4ca4a362fed9.gif - End -

acb03b70b657ae4a64a5738152975351.png

看雪ID:Mr.Bean

https://bbs.pediy.com/user-home-853476.htm

  *本文由看雪论坛 Mr.Bean 原创,转载请注明来自看雪社区。

6178e1cda10d17c250b14aacf1797e3b.png

推荐文章++++

3983f3f87bc0e3f972a8d80934b60401.png

* 基于inlinehook免重打包实现持久化NativeHook

* 反汇编代码还原之优化方式

* 从钓鱼邮件到窃密木马的完整分析

* 记使用Trace还原ollvm混淆的函数

* java类加载的过程概述

eaa2b4bf210094b2a3c507ba4b5ffe2b.png 公众号ID:ikanxue 官方微博:看雪安全 商务合作:wsc@kanxue.com 9b9a6dd8-ae38-eb11-8da9-e4434bdf6706.svg

求分享

9e9a6dd8-ae38-eb11-8da9-e4434bdf6706.svg

求点赞

9a91624bf38408f3e7aa352a198a2270.gif

求在看

2b54e9314c310ddd580d2afe79c64083.gif
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值