如何从程序中找到哪个函数从标准输入读取?

How to find which function from program reads from stdin?

我目前正在对一个程序进行模糊测试,代码库非常庞大。为了提高性能,我通过围绕从标准输入读取的必要函数或代码创建循环来使用持久模式。现在使用 gdb,我能够像这样枚举程序使用的所有函数:

set logging on
set confirm off
rbreak ^[^@]*$
run the binary 
continue

这给了我程序使用的所有函数,但我认为比阅读数百行更简单的方法是找到从标准输入读取的函数。我怎样才能找到从标准输入读取的函数?

How would I be able to find the function that reads from stdin?

一般来说,你的问题等同于halting problem。考虑这个函数:

ssize_t foo(int fd, void *buf, size_t count) { return read(fd, buf, count); }

这个函数是从stdin读取的吗?它可能会也可能不会(取决于输入),这就是问题所在。

P.S。您枚举所有被调用函数的方法极度效率低下。您应该考虑使用 -finstrument-functions 来构建您的程序。 Example.

因为你是 运行 Linux,几乎每个从流中读取的函数(例如标准输入)最终都会执行 read system call. (Less often, they will call readv。)

读取函数的 C 原型是

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

和大多数 Linux 系统调用一样,这几乎是实际系统调用的原型(所有整数和指针类型都放入寄存器中。)

在 x86_64 上,系统调用的第一个参数将在寄存器 rdi 中。 (参见 Calling conventions。)值为 0 表示标准输入。

所以首先我们将告诉 GDB 在进入 read 系统调用时停止进程,添加一个仅当其第一个参数为 0 时才停止的条件:

(gdb) catch syscall read
Catchpoint 1 (syscall 'read' [0])
(gdb) condition 1 $rdi == 0
(gdb) run
Starting program: cat

Catchpoint 1 (call to syscall read), 0x00007fffff13b910 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:84
84      ../sysdeps/unix/syscall-template.S: No such file or directory.

现在执行回溯以查看调用堆栈中的所有函数:

(gdb) bt
#0  0x00007fffff13b910 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:84
#1  0x00007fffff0d2a84 in __GI__IO_file_xsgetn (fp=0x7fffff3f98c0 <_IO_2_1_stdin_>, data=<optimized out>, n=4096)
    at fileops.c:1442
#2  0x00007fffff0c7ad9 in __GI__IO_fread (buf=<optimized out>, size=1, count=4096, fp=0x7fffff3f98c0 <_IO_2_1_stdin_>)
    at iofread.c:38
#3  0x00000000080007c2 in copy () at cat.c:6
#4  0x00000000080007de in main () at cat.c:12
(gdb) fr 3
#3  0x00000000080007c2 in copy () at cat.c:6
6               while ((n=fread(buf, 1, sizeof buf, stdin)) > 0)
(gdb) fr 4
#4  0x00000000080007de in main () at cat.c:12
12              copy();