本文分析的是glibc2.31中的scanf相关源码,目的不是研究scanf的算法,而是说明scanf在IO attack中的利用方法,属于CTF的范畴
scanf.c
其实就是对__vscanf_internal的封装
__scanf (const char *format, ...)
{
va_list arg;
int done;
va_start (arg, format);
done = __vfscanf_internal (stdin, format, arg, 0); //实际是对__vfscanf_internal的封装,arg指向可变参数
va_end (arg);
return done;
}
ldbl_strong_alias (__scanf, scanf)
vscanf-internal.c
- 函数原型
int __vfscanf_internal(FILE *s, const char *format, va_list argptr, unsigned int mode_flags)
- 原理:
- 在vfscanf中有两个流:一个是指导读入格式的字符串流format,一个是被读入的流s
- vfscanf先逐字符解析format,解析出一个要读入的格式后,就会调用inchar()从流s中逐字符读入
- 例如format="%d%",输入"13\n"
- vfcanf解析出%d后指导要从流s中读入一个十进制整数,于是调用inchar()得到字符'1',继续调用inchar()得到'2',再次调用inchar()得到字符'\n',vfscanf就知道一个整数已经结束了,就把'\n'放回流s中,然后把读入的"12"转化为int 12
- 其余的同理
- 总结:vfscanf与流的接口如下,搞CTF不需要了解这个解析算法是怎么实现的,因此下面专注于流相关调用过程
//向流s中放回一个字符c
#define ungetc(c, s) ((void)((int)c == EOF || (--read_in, \
_IO_sputbackc(s, (unsigned char)c))))
#define ungetc_not_eof(c, s) ((void)(--read_in, \
_IO_sputbackc(s, (unsigned char)c)))
//从流s中取出一个字符:c=_IO_getc_unlocked(s); ++read_in;
#define inchar() (c == EOF ? ((errno = inchar_errno), EOF) \
: ((c = _IO_getc_unlocked(s)),