You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ROP链中以0作为FILE*调用fgets读取stdin失败原因咨询

问题分析与解决方案

这个问题的核心是你混淆了标准IO函数的FILE*指针和系统调用的文件描述符,这是ROP和栈溢出中很容易踩的坑,我来帮你理清楚:

1. 崩溃的根本原因:空指针解引用

你以为传入0代表stdin,但这是对函数参数的误解:

  • 像read()这类系统调用,第三个参数是文件描述符,0确实对应标准输入;
  • 但fgets()是C标准库的函数,它的第三个参数是FILE*类型的指针,指向一个包含文件操作信息的结构体(比如缓冲区、文件位置等)。stdin其实是一个宏,它展开后是指向libc中预定义的FILE结构体实例的指针(比如在32位系统中可能是0xf7f6a000这类地址),而不是数值0。

当你把0作为第三个参数传给fgets时,函数会把这个数值当作FILE*指针去解引用——也就是尝试访问内存地址0x0处的内容,这就触发了空指针解引用错误,也就是你看到的mov ecx, DWORD PTR [esi](此时esi=0)崩溃的原因。

2. 正确的ROP链修改方案

你需要传入合法的stdin指针地址,而不是0。具体操作可以分为两种情况:

情况1:ASLR未开启

如果目标程序关闭了地址空间随机化,你可以直接从libc的符号表中获取stdin的固定地址:

  • 用readelf -s /lib/i386-linux-gnu/libc.so.6 | grep stdin(32位系统)或对应64位命令,找到stdin的地址(比如输出中的0xf7f6a000)。
  • 修改你的ROP链,将第三个参数替换为这个地址:
    fgets.plt pop_pop_pop_ret buffer 0x500 0xf7f6a000
    

情况2:ASLR已开启

如果开启了ASLR,你需要先泄露libc的基地址,再计算stdin的地址:

  1. 先通过其他函数(比如puts())泄露某个已知libc函数的地址(比如puts@got中的值);
  2. 用泄露的函数地址减去该函数在libc中的偏移,得到libc基地址;
  3. 加上stdin在libc中的偏移(同样通过readelf获取),得到当前进程中stdin的实际地址;
  4. 将这个地址作为第三个参数传入fgets。

3. 备选方案:改用read函数

如果你更习惯用文件描述符传参,可以直接调用read()系统调用(或者它的plt入口),此时第三个参数传0就是正确的,函数原型为:

ssize_t read(int fd, void *buf, size_t count);

对应的ROP链可以构造为:

read.plt pop_pop_pop_ret 0 buffer 0x500

内容的提问来源于stack exchange,提问作者Neolex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 20:42:45