web-dev-qa-db-ja.com

問題理解システム( "/ bin / sh")

このプログラムの理由を理解するのに問題があります

_#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");
}
_

私のbashシェルから呼び出されたとき

_echo -e "3\nls\n" | myprogram
_

lsの出力を出力しません。 system("/bin/sh")呼び出しが呼び出し元のstdinから読み取らないかのようです。私は通常のLinuxユーザーではないので、system("/bin/sh");ステートメントがどのように機能するかを理解するために、何を読んだり、実験したり、コマンドを実行したりするのに役立つと非常に役立ちます。

2
cosas

その理由は、stdinストリームから読み取るscanfを使用しているためです。バッファを使用するため、1つのバッファに相当するデータを読み取ろうとし、実際に必要なデータの量を確認するだけです。 lsコマンドはすぐに使用できるため、バッファーにも読み込まれ、scanf(またはstdinで動作する他のバッファー付きstdio関数)の別の呼び出しが使用されるのを待ちます。したがって、shが標準入力から読み取ろうとすると、Cプログラムの内部にあるバッファが認識されないため、何も残っていません。

これを解決するには、少なくとも2つの方法があります。

  1. Scanfが読み取りを完了する前に、「ls」コマンドがstdinにエコーされないようにします。プログラムがそのポイントを通過したという証拠を出力するのを待つ必要があるため(これは重要ではない)、または一定の遅延を使用する必要があるため、これは少しトリッキーです。つまり、これはもろい解決策です)。後者は次のようになります。(echo 3 ; sleep 1 ; echo ls) | myprogramつまり3つのコマンドが順番に実行され、すべてがmyprogramに入力を提供します。

  2. 関数を使用して、バッファーなしでstdinから読み取り、必要な最小文字数のみを読み取ります。たとえば、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");
}

バッファリングされていない関数の詳細については、たとえばこちらの情報ページをご覧ください。 https://www.gnu.org/software/libc/manual/html_node/I_002fO-Primitives.html#I_002fO-Primitives

EDIT:どうやら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");
}
4
Jonas Berlin