ホームページ 運用・保守 Linuxの運用と保守 Linux でのシステム機能の分析の概要

Linux でのシステム機能の分析の概要

May 27, 2017 am 10:00 AM
linux system 関数

この記事は主にlinuxのシステム機能を簡単に分析するもので、興味のある方は参考にしてください

具体的な内容は次のとおりです

int
libc_system (const char *line)
{
 if (line == NULL)
  /* Check that we have a command processor available. It might
    not be available after a chroot(), for example. */
  return do_system ("exit 0") == 0;

 return do_system (line);
}
weak_alias (libc_system, system)
ログイン後にコピー

コードは glibc/sysdeps/posix/system.c にあります。ここで、system は libc_system の弱いエイリアスであり、libc_system は do_system のフロントエンド関数です。 次に、do_system 関数を確認します。

static int
do_system (const char *line)
{
 int status, save;
 pid_t pid;
 struct sigaction sa;
#ifndef _LIBC_REENTRANT
 struct sigaction intr, quit;
#endif
 sigset_t omask;

 sa.sa_handler = SIG_IGN;
 sa.sa_flags = 0;
 sigemptyset (&sa.sa_mask);

 DO_LOCK ();
 if (ADD_REF () == 0)
  {
   if (sigaction (SIGINT, &sa, &intr) < 0)
  {
   (void) SUB_REF ();
   goto out;
  }
   if (sigaction (SIGQUIT, &sa, &quit) < 0)
  {
   save = errno;
   (void) SUB_REF ();
   goto out_restore_sigint;
  }
  }
 DO_UNLOCK ();

 /* We reuse the bitmap in the &#39;sa&#39; structure. */
 sigaddset (&sa.sa_mask, SIGCHLD);
 save = errno;
 if (sigprocmask (SIG_BLOCK, &sa.sa_mask, &omask) < 0)
  {
#ifndef _LIBC
   if (errno == ENOSYS)
  set_errno (save);
   else
#endif
  {
   DO_LOCK ();
   if (SUB_REF () == 0)
    {
     save = errno;
     (void) sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
    out_restore_sigint:
     (void) sigaction (SIGINT, &intr, (struct sigaction *) NULL);
     set_errno (save);
    }
  out:
   DO_UNLOCK ();
   return -1;
  }
  }

#ifdef CLEANUP_HANDLER
 CLEANUP_HANDLER;
#endif

#ifdef FORK
 pid = FORK ();
#else
 pid = fork ();
#endif
 if (pid == (pid_t) 0)
  {
   /* Child side. */
   const char *new_argv[4];
   new_argv[0] = SHELL_NAME;
   new_argv[1] = "-c";
   new_argv[2] = line;
   new_argv[3] = NULL;

   /* Restore the signals. */
   (void) sigaction (SIGINT, &intr, (struct sigaction *) NULL);
   (void) sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
   (void) sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL);
   INIT_LOCK ();

   /* Exec the shell. */
   (void) execve (SHELL_PATH, (char *const *) new_argv, environ);
   _exit (127);
  }
 else if (pid < (pid_t) 0)
  /* The fork failed. */
  status = -1;
 else
  /* Parent side. */
  {
   /* Note the system() is a cancellation point. But since we call
   waitpid() which itself is a cancellation point we do not
   have to do anything here. */
   if (TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)) != pid)
  status = -1;
  }

#ifdef CLEANUP_HANDLER
 CLEANUP_RESET;
#endif

 save = errno;
 DO_LOCK ();
 if ((SUB_REF () == 0
    && (sigaction (SIGINT, &intr, (struct sigaction *) NULL)
    | sigaction (SIGQUIT, &quit, (struct sigaction *) NULL)) != 0)
   || sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL) != 0)
  {
#ifndef _LIBC
   /* glibc cannot be used on systems without waitpid. */
   if (errno == ENOSYS)
  set_errno (save);
   else
#endif
  status = -1;
  }
 DO_UNLOCK ();

 return status;
}

do_system
ログイン後にコピー

まず、関数は SIGINT および SIGQUIT シグナルを処理するためにいくつかのシグナル ハンドラーを設定します。ここではあまり気にしません。キー コード セグメントはここにあります。

#ifdef FORK
 pid = FORK ();
#else
 pid = fork ();
#endif
 if (pid == (pid_t) 0)
  {
   /* Child side. */
   const char *new_argv[4];
   new_argv[0] = SHELL_NAME;
   new_argv[1] = "-c";
   new_argv[2] = line;
   new_argv[3] = NULL;

   /* Restore the signals. */
   (void) sigaction (SIGINT, &intr, (struct sigaction *) NULL);
   (void) sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
   (void) sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL);
   INIT_LOCK ();

   /* Exec the shell. */
   (void) execve (SHELL_PATH, (char *const *) new_argv, environ);
   _exit (127);
  }
 else if (pid < (pid_t) 0)
  /* The fork failed. */
  status = -1;
 else
  /* Parent side. */
  {
   /* Note the system() is a cancellation point. But since we call
   waitpid() which itself is a cancellation point we do not
   have to do anything here. */
   if (TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)) != pid)
  status = -1;
  }
ログイン後にコピー

まず、フロントエンド関数はシステム コール フォークを呼び出します。子プロセスを生成します。フォークには 2 つの戻り値があり、子プロセスの pid が親プロセスに返され、0 が子プロセスに返されます。したがって、子プロセスは 6 ~ 24 行のコードを実行し、親プロセスは 30 ~ 35 行のコードを実行します。

子プロセスのロジックは非常に明確であり、SHELL_PATH で指定されたプログラムを実行するために execve が呼び出され、パラメーターは new_argv を介して渡され、環境変数

はグローバル変数 environ です。 SHELL_PATHとSHELL_NAMEは次のように定義されています

#define  SHELL_PATH  "/bin/sh"  /* Path of the shell. */
#define  SHELL_NAME  "sh"    /* Name to give it. */
ログイン後にコピー

実際には、システムに渡されたコマンドを実行するために

/bin/sh -c "command"を呼び出す子プロセスを生成します。 実際に私がシステム関数を研究した理由と焦点は次のとおりです:

CTFのpwnの質問で、スタックオーバーフローを介したシステム関数の呼び出しが失敗する場合があるとマスターが言ったと聞きましたが、環境変数が上書きされます。私はずっと無知でしたが、今日、徹底的に勉強した結果、ようやく理解できました。

ここでシステム関数に必要な環境変数はグローバル変数environに格納されていますが、この変数の内容はどうなっているのでしょうか。

environ は glibc/csu/libc-start.c で定義されています。いくつかの重要なステートメントを見てみましょう。

# define LIBC_START_MAIN libc_start_main
ログイン後にコピー

libc_start_main は _start によって呼び出される関数で、プログラムの開始時に初期化作業が必要になります。これらの用語がわからない場合は、この記事を読んでください。次に、LIBC_START_MAIN 関数を見てみましょう。

STATIC int
LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
     int argc, char **argv,
#ifdef LIBC_START_MAIN_AUXVEC_ARG
     ElfW(auxv_t) *auxvec,
#endif
     typeof (main) init,
     void (*fini) (void),
     void (*rtld_fini) (void), void *stack_end)
{
 /* Result of the &#39;main&#39; function. */
 int result;

 libc_multiple_libcs = &_dl_starting_up && !_dl_starting_up;

#ifndef SHARED
 char **ev = &argv[argc + 1];

 environ = ev;

 /* Store the lowest stack address. This is done in ld.so if this is
   the code for the DSO. */
 libc_stack_end = stack_end;

    ......
 /* Nothing fancy, just call the function. */
 result = main (argc, argv, environ MAIN_AUXVEC_PARAM);
#endif

 exit (result);
}
ログイン後にコピー

19 行目で environ の値が SHARED を定義せずに定義されていることがわかります。スタートアップ プログラムは LIBC_START_MAIN を呼び出す前に、まず

文字列

を環境変数と argv に保存し (実際にはスタックに保存されます)、次に環境変数の各文字列のアドレスと argv の各文字列のアドレスを順番に保存します。アドレスと argc はスタックにプッシュされるため、環境変数 配列 は空のアドレスで区切って argv 配列のすぐ後ろに配置する必要があります。したがって、17 行目の &argv[argc + 1] ステートメントは、スタック上の環境変数配列の最初のアドレスを取得し、それを ev に保存し、最後に environ に保存します。 203 行目は、environ の値をスタックにプッシュする main 関数を呼び出します。environ のアドレスが上書きされない限り、これがスタック オーバーフローによって上書きされても問題はありません。 そのため、スタックオーバーフローの長さが大きすぎて、オーバーフローの内容が環境内のアドレスの重要な内容を覆っている場合、システム関数の呼び出しは失敗します。特定の環境変数がオーバーフロー アドレスからどの程度離れているかは、_start で中断することで確認できます。

以上がLinux でのシステム機能の分析の概要の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Linuxは実際に何に適していますか? Linuxは実際に何に適していますか? Apr 12, 2025 am 12:20 AM

Linuxは、サーバー、開発環境、埋め込みシステムに適しています。 1.サーバーオペレーティングシステムとして、Linuxは安定して効率的であり、多くの場合、高電流アプリケーションの展開に使用されます。 2。開発環境として、Linuxは効率的なコマンドラインツールとパッケージ管理システムを提供して、開発効率を向上させます。 3.埋め込まれたシステムでは、Linuxは軽量でカスタマイズ可能で、リソースが限られている環境に適しています。

LinuxでDockerを使用:包括的なガイド LinuxでDockerを使用:包括的なガイド Apr 12, 2025 am 12:07 AM

LinuxでDockerを使用すると、開発と展開の効率が向上する可能性があります。 1。Dockerのインストール:スクリプトを使用して、ubuntuにDockerをインストールします。 2.インストールの確認:sudodockerrunhello-worldを実行します。 3。基本的な使用法:NginxコンテナDockerrun-Namemy-Nginx-P8080を作成します:80-Dnginx。 4。高度な使用法:カスタム画像を作成し、DockerFileを使用してビルドして実行します。 5。最適化とベストプラクティス:マルチステージビルドとドッケルコンポスを使用して、DockerFilesを作成するためのベストプラクティスに従ってください。

Apacheを始める方法 Apacheを始める方法 Apr 13, 2025 pm 01:06 PM

Apacheを開始する手順は次のとおりです。Apache(コマンド:sudo apt-get install apache2または公式Webサイトからダウンロード)をインストールします(linux:linux:sudo systemctl start apache2; windows:apache2.4 "serviceを右クリックして「開始」を右クリック) (オプション、Linux:Sudo SystemCtl

Apache80ポートが占有されている場合はどうすればよいですか Apache80ポートが占有されている場合はどうすればよいですか Apr 13, 2025 pm 01:24 PM

Apache 80ポートが占有されている場合、ソリューションは次のとおりです。ポートを占有するプロセスを見つけて閉じます。ファイアウォールの設定を確認して、Apacheがブロックされていないことを確認してください。上記の方法が機能しない場合は、Apacheを再構成して別のポートを使用してください。 Apacheサービスを再起動します。

Oracleの監視を開始する方法 Oracleの監視を開始する方法 Apr 12, 2025 am 06:00 AM

Oracleリスナーを開始する手順は次のとおりです。Windowsのリスナーステータス(LSNRCTLステータスコマンドを使用)を確認し、LinuxとUNIXのOracle Services Managerで「TNSリスナー」サービスを開始し、LSNRCTL Startコマンドを使用してリスナーを起動してLSNRCTLステータスコマンドを実行してリスナーを確認します。

DebianのNginx SSLパフォーマンスを監視する方法 DebianのNginx SSLパフォーマンスを監視する方法 Apr 12, 2025 pm 10:18 PM

この記事では、Debianシステム上のNginxサーバーのSSLパフォーマンスを効果的に監視する方法について説明します。 Nginxexporterを使用して、NginxステータスデータをPrometheusにエクスポートし、Grafanaを介して視覚的に表示します。ステップ1:NGINXの構成最初に、NGINX構成ファイルのSTUB_STATUSモジュールを有効にして、NGINXのステータス情報を取得する必要があります。 NGINX構成ファイルに次のスニペットを追加します(通常は/etc/nginx/nginx.confにあるか、そのインクルードファイルにあります):location/nginx_status {stub_status

Debianシステムでリサイクルビンをセットアップする方法 Debianシステムでリサイクルビンをセットアップする方法 Apr 12, 2025 pm 10:51 PM

この記事では、デビアンシステムでリサイクルビンを構成する2つの方法を紹介します:グラフィカルインターフェイスとコマンドライン。方法1:Nautilusグラフィカルインターフェイスを使用して、ファイルマネージャーを開きます。デスクトップまたはアプリケーションメニューでNautilusファイルマネージャー(通常は「ファイル」と呼ばれる)を見つけて起動します。リサイクルビンを見つけてください:左ナビゲーションバーのリサイクルビンフォルダーを探してください。見つからない場合は、「他の場所」または「コンピューター」をクリックして検索してみてください。リサイクルビンプロパティの構成:「リサイクルビン」を右クリックし、「プロパティ」を選択します。プロパティウィンドウで、次の設定を調整できます。最大サイズ:リサイクルビンで使用可能なディスクスペースを制限します。保持時間:リサイクルビンでファイルが自動的に削除される前に保存を設定します

Apacheサーバーを再起動する方法 Apacheサーバーを再起動する方法 Apr 13, 2025 pm 01:12 PM

Apacheサーバーを再起動するには、次の手順に従ってください。Linux/MacOS:sudo systemctl restart apache2を実行します。 Windows:Net Stop apache2.4を実行し、ネット開始apache2.4を実行します。 Netstat -A |を実行しますサーバーのステータスを確認するには、STR 80を見つけます。

See all articles