问题理解系统("/bin/sh")

cos*_*sas 2 c pipe system-calls stdin

我在理解这个程序的原因时遇到问题

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int iRetval = 0;
    unsigned int uiNum;

    printf("Enter number: ");
    fflush(stdout);
    iRetval = scanf("%u", &uiNum);
    printf("\nThe number is %u, Retval: %i\n", uiNum, iRetval);
    fflush(stdout);
    if( iRetval > 0)
        system("/bin/sh");
    else
        printf("Goodbye!\n");
}
Run Code Online (Sandbox Code Playgroud)

从我的 bash shell 调用时

echo -e "3\nls\n" | myprogram
Run Code Online (Sandbox Code Playgroud)

不打印 ls 的输出。就好像system("/bin/sh")调用没有从调用者的 stdin 中读取一样。我不是一个普通的 linux 用户,所以任何关于阅读内容或尝试尝试的实验或运行命令以更好地理解system("/bin/sh");语句如何工作的帮助都会非常有帮助。

Jon*_*lin 5

原因是您使用的scanf是从 stdin 流中读取的内容。它使用缓冲区,所以它会尝试读取一个缓冲区的数据,然后才检查它真正需要多少数据。由于您的ls命令立即可用,它也会被读入缓冲区,等待再次调用 scanf(或在 stdin 上运行的任何其他缓冲 stdio 函数)以使用。因此,当然sh后尝试从 stdin 读取时,什么也没有留下,因为它看不到 C 程序内部的缓冲区。

至少有两种方法可以解决这个问题。

  1. 在 scanf 完成读取之前,请确保“ls”命令没有回显到 stdin。这有点棘手,因为您需要等待程序输出已经超过该点的证据(并非微不足道),或者然后使用一些固定延迟并希望系统永远不会在该点停顿并使延迟太短(即这是一个脆弱的解决方案)。后者看起来像:(echo 3 ; sleep 1 ; echo ls) | myprogram即三个命令按顺序执行并且都向 提供输入myprogram

  2. 您使用函数从标准输入读取 - 没有缓冲区 - 只需要最少数量的字符。例如,该read函数不使用缓冲区。你可以写一个辅助函数,比如

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>

int unbuffered_scanf(const char *fmt, ...) {
  char buffer[100]; // maximum line length
  int i;
  int ret;
  for(i=0; i<sizeof(buffer)-1; ++i) {
    if (!read(0, &buffer[i], 1)) break;
    if (buffer[i] == '\n') break;
  }
  buffer[i] = '\0';
  va_list ap;
  va_start(ap, fmt);
  ret = vsscanf(buffer, fmt, ap);
  va_end(ap);
  return ret;
}

int main()
{
    int iRetval = 0;
    unsigned int uiNum;

    printf("Enter number: ");
    fflush(stdout);
    iRetval = unbuffered_scanf("%u", &uiNum);
    printf("\nThe number is %u, Retval: %i\n", uiNum, iRetval);
    fflush(stdout);
    if( iRetval > 0)
        system("/bin/sh");
    else
        printf("Goodbye!\n");
}
Run Code Online (Sandbox Code Playgroud)

有关无缓冲函数的更多信息,请阅读信息页面,例如:https : //www.gnu.org/software/libc/manual/html_node/I_002fO-Primitives.html#I_002fO-Primitives

编辑:显然有一种方法可以禁用由scanf和其他人使用例如该setbuf函数完成的缓冲- 它可能在内部完全按照我上面的示例工作:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int iRetval = 0;
    unsigned int uiNum;

    setbuf(stdin, NULL);
    printf("Enter number: ");
    fflush(stdout);
    iRetval = scanf("%u", &uiNum);
    printf("\nThe number is %u, Retval: %i\n", uiNum, iRetval);
    fflush(stdout);
    if( iRetval > 0)
        system("/bin/sh");
    else
        printf("Goodbye!\n");
}
Run Code Online (Sandbox Code Playgroud)