当前位置: 首页 > news >正文

不同输入函数的区别比较及send和sendline使用

不同输入函数的区别比较及send和sendline使用

read()

  • read的内容都是从缓存区中读入的

    • 也就是说不管你输入的数据是大于等于还是小于,他都是先放在输入缓冲区中的
    • 然后再从输入缓冲区中读取conunt个数据到buf里
  • 这时候就可能有三种情况

    • 输入字节数>count
      • 此时多余的字节会仍然存放在出入缓冲区中
    • 输入字节数<count
      • 会读取所有输入字节,包括回车(0x0a)
    • 输入字节数=count
      • 会读取所有输入字节,包括回车(0x0a)
  • 实验如下

    • 注:参考了XiDP和ZIKH26两位师傅的代码,参考文章见。
    • malloc(初始时将内存均设置为‘b')
        // step 1 测试read函数,限定输入大小为8字节,查看分别输入 a*6 a*8 a*10 后内存的样子printf("read a*6\n");read(0, ptr[0], 8);while (getchar() != '\n' && getchar() != EOF); // 为了防止残留的\x0a影响后续的输入,这里采用了getchar来把多余的\x0a吃掉printf("read a*8\n");read(0, ptr[1], 8);while (getchar() != '\n' && getchar() != EOF);printf("read a*10\n");read(0, ptr[2], 8);while (getchar() != '\n' && getchar() != EOF);sleep(0.1);printf("ptr[0]-%s",ptr[0]);printf("ptr[1]-%s",ptr[1]);printf("ptr[2]-%s",ptr[2]);
    

    效果

read a*6
aaaaaaread a*8
aaaaaaaa
read a*10
aaaaaaaaaa
ptr[0]-aaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbptr[1]-aaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbptr[2]-aaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb

内存

pwndbg> x/8gx 0x555555559290
0x555555559290: 0x0000000000000000      0x0000000000000041
0x5555555592a0: 0x620a616161616161      0x6262626262626262
0x5555555592b0: 0x6262626262626262      0x6262626262626262
0x5555555592c0: 0x6262626262626262      0x6262626262626262
pwndbg>
0x5555555592d0: 0x0000000000000000      0x0000000000000041
0x5555555592e0: 0x6161616161616161      0x6262626262626262
0x5555555592f0: 0x6262626262626262      0x6262626262626262
0x555555559300: 0x6262626262626262      0x6262626262626262
pwndbg>
0x555555559310: 0x0000000000000000      0x0000000000000041
0x555555559320: 0x6161616161616161      0x6262626262626262
0x555555559330: 0x6262626262626262      0x6262626262626262
0x555555559340: 0x6262626262626262      0x6262626262626262

当不处理结尾换行符时

    // step 1 测试read函数,限定输入大小为8字节,查看分别输入 a*6 a*8 a*10 后内存的样子printf("read a*6\n");read(0, ptr[0], 8);//while (getchar() != '\n' && getchar() != EOF); // 为了防止残留的\x0a影响后续的输入,这里采用了getchar来把多余的\x0a吃掉printf("read a*8\n");read(0, ptr[1], 8);//while (getchar() != '\n' && getchar() != EOF);printf("read a*10\n");read(0, ptr[2], 8);//while (getchar() != '\n' && getchar() != EOF);sleep(0.1);printf("ptr[0]-%s",ptr[0]);printf("ptr[1]-%s",ptr[1]);printf("ptr[2]-%s",ptr[2]);

效果

read a*6
aaaaaa
read a*8
aaaaaaaa
read a*10
ptr[0]-aaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbptr[1]-aaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbptr[2]-
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb

内存

pwndbg> x/8gx 0x555555559290
0x555555559290: 0x0000000000000000      0x0000000000000041
0x5555555592a0: 0x620a616161616161      0x6262626262626262
0x5555555592b0: 0x6262626262626262      0x6262626262626262
0x5555555592c0: 0x6262626262626262      0x6262626262626262
pwndbg>
0x5555555592d0: 0x0000000000000000      0x0000000000000041
0x5555555592e0: 0x6161616161616161      0x6262626262626262
0x5555555592f0: 0x6262626262626262      0x6262626262626262
0x555555559300: 0x6262626262626262      0x6262626262626262
pwndbg>
0x555555559310: 0x0000000000000000      0x0000000000000041
0x555555559320: 0x626262626262620a      0x6262626262626262
0x555555559330: 0x6262626262626262      0x6262626262626262
0x555555559340: 0x6262626262626262      0x6262626262626262

可以发现当提示输入read a*10时,程序并没有让我们读入就直接跳到后面的输出函数了,因为当我们输入‘a'*8加回车时,缓存区里回车和\x00并没有移入buf,因此read a*10时只读入换行就结束了。

综上,我们可以发现read函数除非读入count字节数或者遇到\x00,不然输入其他东西都无法阻止read停止,直到读取完后放入指定的内存中,结尾也不会添加\x00,没读取到的部分则保持原样不动。

fgets()

    // step 2 测试fgets函数,限定输入大小为8字节,查看分别输入 a*6 a*8 a*10 后内存的样子printf("fgets a*6\n");fgets(ptr[4], 8, stdin);while (getchar() != '\n' && getchar() != EOF);printf("fgets a*8\n");fgets(ptr[5], 8, stdin);    while (getchar() != '\n' && getchar() != EOF);printf("fgets a*10\n");fgets(ptr[6], 8, stdin);while (getchar() != '\n' && getchar() != EOF);sleep(0.1);

效果

pwndbg> x/8gx 0x555555559390
0x555555559390: 0x0000000000000000      0x0000000000000041
0x5555555593a0: 0x000a616161616161      0x6262626262626262
0x5555555593b0: 0x6262626262626262      0x6262626262626262
0x5555555593c0: 0x6262626262626262      0x6262626262626262
pwndbg>
0x5555555593d0: 0x0000000000000000      0x0000000000000041
0x5555555593e0: 0x0061616161616161      0x6262626262626262
0x5555555593f0: 0x6262626262626262      0x6262626262626262
0x555555559400: 0x6262626262626262      0x6262626262626262
pwndbg>
0x555555559410: 0x0000000000000000      0x0000000000000041
0x555555559420: 0x0061616161616161      0x6262626262626262
0x555555559430: 0x6262626262626262      0x6262626262626262
0x555555559440: 0x6262626262626262      0x6262626262626262

char *fgets(char *str, int size, FILE *stream);

fgets与read不同的是,fgets 最多读取 size - 1 个字符,并在末尾添加 \0

当不处理结尾换行符时,效果一样,说明fgets读取后剩下的字节并不为下一次读入所用。

scanf()

    printf("scanf a*6\n");scanf("%8s", ptr[8]);while (getchar() != '\n' && getchar() != EOF);printf("scanf a*8\n");scanf("%8s", ptr[9]);while (getchar() != '\n' && getchar() != EOF);printf("scanf a*10\n");scanf("%8s", ptr[10]);while (getchar() != '\n' && getchar() != EOF);sleep(0.1);

内存

pwndbg>
0x555555559490: 0x0000000000000000      0x0000000000000041
0x5555555594a0: 0x6200616161616161      0x6262626262626262
0x5555555594b0: 0x6262626262626262      0x6262626262626262
0x5555555594c0: 0x6262626262626262      0x6262626262626262
pwndbg>
0x5555555594d0: 0x0000000000000000      0x0000000000000041
0x5555555594e0: 0x6161616161616161      0x6262626262626200
0x5555555594f0: 0x6262626262626262      0x6262626262626262
0x555555559500: 0x6262626262626262      0x6262626262626262
pwndbg>
0x555555559510: 0x0000000000000000      0x0000000000000041
0x555555559520: 0x6161616161616161      0x6262626262626200
0x555555559530: 0x6262626262626262      0x6262626262626262
0x555555559540: 0x6262626262626262      0x6262626262626262

scanf是从第一个非空白字符(空格 换行 制表符)开始读入的,就是你输入的数据,在按下回车的之前,输入的数据都会被存储在输入缓冲区(包括回车),当按下回车键之后,scanf就会开始从输入缓冲区里面读取数据,把读取的数据都传送到你指定的地址,直到遇见了空白符然后停止。它仅仅是遇见空白符停止了,但是空白符以及空白符后面的内容依然在输入缓冲区里面。

gets()

    // step 4 测试gets函数输入之后是什么样子的,仅输入一次,输入6个字节的aprintf("gets a*6\n");gets(ptr[12]);while (getchar() != '\n' && getchar() != EOF);sleep(0.1);

效果

pwndbg>
0x555555559590: 0x0000000000000000      0x0000000000000041
0x5555555595a0: 0x6200616161616161      0x6262626262626262
0x5555555595b0: 0x6262626262626262      0x6262626262626262
0x5555555595c0: 0x6262626262626262      0x6262626262626262

使用 gets() 时,系统会将最后输入的换行符(也就是回车)从缓冲区中取出来,然后给舍弃,因此缓冲区中不会遗留换行符

总结---send和sendline选择

  • read建议用send

  • fgets gets scanf 这三个函数只能使用sendline,但是在使用时也可能会因为多余的\x00影响栈帧结构

参考文章

不同输入函数之间的区别 | XiDP

zikh26.github.io

http://www.jsqmd.com/news/447037/

相关文章:

  • 为什么React Dev Inspector是2024年前端开发的必备工具?
  • 如何快速掌握Mio高性能I/O库:探索os-poll和net核心功能的终极指南
  • 如何快速生成精准上传漏洞字典?upload-fuzz-dic-builder完全使用指南
  • 交流异步电动机变频矢量控制仿真的控制效果良好
  • EVE API完全指南:函数对象与SIMD类型深度解析
  • USWDS与现代化框架集成终极指南:React、Angular和Vue的最佳实践
  • aHash高级特性:编译时/运行时RNG选择与no_std环境配置
  • 如何使用MagicClothing:AI服装驱动图像合成的完整指南
  • 自动驾驶数据集标注与检测对比:Streamlit Demo: The Udacity Self-driving Car Image Browser 实战教程
  • 彻底解决结构化数据痛点:TensorFlow Fold动态计算图实战指南
  • 为何 LoRA 初始化 B=0 而 A 为高斯分布初始化?
  • wps加载项打包成exe
  • 10分钟上手pretty-quick:让代码格式化效率提升10倍的实战指南
  • wit-bindgen高级特性:类型别名、资源管理与异步支持完全攻略
  • 岐金兰空论
  • 为什么gh_mirrors/cr/cross_browser能跨浏览器追踪用户?核心算法解析
  • Glass终极隐私保护:为什么它永远不会出现在屏幕录制中
  • Input Leap企业部署终极指南:多用户环境下的配置管理与安全策略
  • 终极Githug插件系统架构解析:如何快速扩展Git学习功能的完整指南
  • 如何快速掌握Facebook xctool测试框架:从入门到精通的完整指南
  • twentytwenty实战案例:打造响应式图片对比界面的简单步骤
  • Mutate高级配置:如何设置热键、关键词和主题个性化
  • cryptocurrency-arbitrage:揭秘800+加密货币跨50个市场的套利机会计算器
  • 本地化JSON 处理新方案:基于 Docker的JSON Hero部署全记录
  • Pux Mux类深度解析:掌握路由定义与分发核心机制
  • STM32——DMA(十四) - 指南
  • 如何为xh HTTP请求工具开发自定义功能:完整插件开发指南
  • 如何优雅处理Vuelidate异步验证:从API请求到数据验证的完整指南
  • 基于Docker的Kafka服务
  • 从0到1:使用Job Iteration重构长时运行的Rails后台任务