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的地址:
- 先通过其他函数(比如
puts())泄露某个已知libc函数的地址(比如puts@got中的值); - 用泄露的函数地址减去该函数在libc中的偏移,得到libc基地址;
- 加上stdin在libc中的偏移(同样通过
readelf获取),得到当前进程中stdin的实际地址; - 将这个地址作为第三个参数传入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
相关产品推荐
相关产品推荐

