C에서 execve 사용

Abdul Mateen 2023년10월12일
  1. C에서 exec 시스템 호출
  2. C에서 execve 시스템 호출
C에서 execve 사용

이 튜토리얼에서는 execve를 사용하여 Linux 표준 명령과 C에서 실행 파일을 모두 실행하는 방법에 대해 설명합니다.

먼저 exec 시스템 호출과 exec의 패밀리에 대해 설명합니다. 다음으로 C에서 실행 파일을 호출하고 마지막으로 표준 Linux 명령과 실행 파일에 대해 설명합니다.

C에서 exec 시스템 호출

exec 시스템 호출은 실행 중인 프로세스를 다른 실행 가능한 프로세스로 대체합니다. 실행 중인 프로세스의 주소 공간은 새 프로세스의 주소 공간으로 대체됩니다.

새 프로그램이 동일한 주소 공간에 로드된다는 점에 유의하는 것이 중요합니다. 프로세스 ID는 동일하게 유지됩니다.

새 프로그램은 독립적으로 실행됩니다. 즉, 시작점이 새 프로그램의 진입점이 됩니다. exec 시스템 호출에는 많은 변형이 있습니다.

  1. execl
  2. execle
  3. execlp
  4. execv
  5. execve
  6. execvp

이러한 함수는 동일한 기본 exec 뒤에 하나 이상의 문자를 사용합니다. 추가 문자에 대한 자세한 내용은 다음과 같습니다.

  1. e - 여기서 e는 환경 변수용입니다. 이 함수에는 환경 변수를 가리키는 포인터 배열이 있습니다. 환경 변수 목록은 새로 로드된 프로그램에 명시적으로 전달됩니다.
  2. l - 여기서 l은 명령줄 인수용입니다. 함수에 명령줄 인수 목록을 제공할 수 있습니다.
  3. p - 여기서 p는 환경 변수 경로입니다. 이 함수에서 경로 변수는 새로 로드된 프로세스에 인수로 전달된 파일을 찾는 데 도움이 됩니다.
  4. v - 여기에서 v는 명령줄 인수용이기도 합니다. 그러나 이 함수에서 명령줄 인수는 새로 로드된 프로세스에 포인터 배열로 전달됩니다.

기본 exec 시스템 호출에서 현재 프로세스 주소 공간은 새로 로드된 프로그램의 주소 공간으로 대체됩니다. 결과적으로 현재 실행 중인 프로세스가 종료됩니다. 새로 로드된 프로세스는 이 시스템 호출에서 인수로 전달됩니다.

새로 로드된 프로세스는 동일한 프로세스 ID, 동일한 환경 변수 및 동일한 파일 설명자 세트를 가집니다. 그러나 CPU 통계 및 가상 메모리는 영향을 받습니다.

exec 호출 구문

여기에는 기본 exec 시스템 호출의 변형인 6개의 시스템 호출 구문이 있습니다.

int execl(const char* path, const char* arg, ...) int execle(
    const char* path, const char* arg, ...,
    char* const envp
        []) int execlp(const char* file, const char* arg,
                       ...) int execv(const char* path,
                                      const char* argv
                                          []) int execve(const char* path,
                                                         const char* argv[],
                                                         char* const envp
                                                             []) int execvp(const char*
                                                                                file,
                                                                            const char* argv
                                                                                []) int execvpe(const char*
                                                                                                    file,
                                                                                                const char* argv
                                                                                                    [],
                                                                                                char* const envp
                                                                                                    [])

첫째, 모든 함수의 반환 유형은 int입니다. 그러나 성공적인 작업의 경우(즉, 새 프로그램이 로드되고 교체됨) 현재 프로그램이 더 이상 반환 값을 받을 수 없기 때문에 아무 것도 반환되지 않습니다.

일부 오류로 인한 실패 시 새 프로그램이 로드되지 않고 -1이 기존 프로그램으로 반환됩니다.

첫 번째 인수에는 경로와 파일 사이에 차이가 있습니다. p, execlp, execvpexecvpe에는 경로 대신 파일이 있습니다.

경로는 실행/로드할 파일의 전체 경로를 지정합니다. 파일은 새 프로그램의 파일을 찾는 데 도움이 되는 경로 이름을 지정합니다.

두 번째 인수에서 차이점은 v가 있는 함수는 여러 문자열(파일 이름 포함)이 있는 char 유형의 2차원 배열을 갖는다는 것입니다.

대조적으로, 다른 프로시저에는 char 유형의 하나 이상의 1차원 배열이 있습니다. 여기서 이 목록의 첫 번째 요소에는 file 이름이 포함되고 두 번째 요소에는 일부 매개 변수 등이 포함될 수 있습니다.

마지막으로 e가 있는 함수의 경우 세 번째/마지막 매개 변수에는 포인터 배열로 환경 변수가 있습니다.

exec 시스템 콜 코딩 예제

더 논의하기 전에 예를 보는 것이 좋습니다. 이 예에서는 프로그램의 소스 코드를 프로그램의 입력으로 사용하고 있습니다.

여기에서 실행 코드 디렉토리에 이 프로그램(이름 execl0.c)을 저장했습니다. 이는 소스 코드와 실행 코드가 모두 동일한 디렉토리에 존재함을 의미합니다.

#include <stdio.h>
#include <unistd.h>
int main(void) {
  char binaryPath[] = "/bin/wc";
  char arg1[] = "wc";
  char arg2[] = "-w";
  char arg3[] = "execl0.c";
  printf("First line of current program\n");
  execl(binaryPath, arg1, arg2, arg3, NULL);
  printf("Last line of current program\n");
  return 1;
}

위의 코드는 execl 시스템 호출을 사용하며 char* 유형의 몇 가지 간단한 변수만 있습니다. 첫 번째 변수에는 (실행할) 새 프로그램의 경로와 이름이 포함되고 두 번째 변수에는 wc 매개 변수(다시 말하지만 프로그램 이름)가 있습니다.

세 번째 변수에는 wc -w로 명령을 실행하여 소스 파일의 단어 수를 계산하는 -w 매개 변수가 있습니다.

두 개의 추가 print 문을 기록하는 것도 중요합니다. 첫 번째는 시스템 호출 이전이고 두 번째는 프로그램 종료입니다.

출력:

First line of current program
32 execl0.c

출력은 새 프로그램이 성공적으로 로드되고 실행되었음을 보여줍니다. 그러나 첫 번째 print 문이 실행된다는 점에 유의하십시오(출력의 첫 번째 라인 참조('현재 프로그램의 첫 번째 라인').

새 프로그램이 성공적으로 로드되면 현재 프로그램이 자동으로 종료되기 때문에 마지막 print 문은 실행되지 않습니다.

두 번째 출력 라인은 execl0.c 파일의 단어 수를 보여줍니다.

C에서 execve 시스템 호출

이제 execve 호출에 대해 자세히 설명하겠습니다.

통사론:

int execve(const char* path, const char* argv[], char* const envp[])

여기서 첫 번째 인수는 경로입니다. 이미 논의한 바와 같이 path 환경 변수는 새 프로그램으로 실행할 프로그램을 찾는 데 도움이 됩니다.

두 번째 인수는 문자의 2차원 배열 또는 명령줄 인수 목록이 있는 문자열의 1차원 배열입니다.

세 번째 인수는 다시 2차원 문자 배열 또는 환경 변수 목록이 있는 문자열의 1차원 배열입니다.

exec 계열에서 execve는 3개의 인수 경로, 명령줄 인수 목록 및 환경 변수 목록이 있는 매력적인 명령입니다. 프로그램에서 echo 명령을 실행하는 코드를 살펴보겠습니다.

#include <stdio.h>
#include <unistd.h>
int main(void) {
  char *binaryPath = "/bin/bash";
  char *args[] = {binaryPath, "-c",
                  "echo visit $HOSTNAME:Fun with your browser", "", NULL};
  char *const env[] = {"HOSTNAME=www.delftstack.com", "port=8080", NULL};
  execve(binaryPath, args, env);
  return 1;
}

기본 기능의 첫 번째 줄에서 /bin/bash는 명령이 존재하는 경로입니다. 두 번째 줄에서 명령줄 인수 목록에는 인수를 종료하는 NULL 앞에 세 개의 매개변수가 포함되어 있습니다.

다시, 첫 번째 인수는 경로이고 두 번째 매개변수인 -c는 코드를 문자열로 전달할 수 있는 cmd를 나타냅니다.

세 번째 매개변수는 명령입니다. 코드에서와 같이 echo는 명령입니다.

코드의 세 번째 줄에는 HOSTNAMEport라는 두 개의 환경 변수가 있는 두 개의 문자열이 있습니다. 마지막으로 코드의 출력은 다음과 같습니다.

visit www.delftstack.com : Fun with your browser

이 코드에서는 프로그램에서 Linux 명령을 실행했습니다. 다음으로 현재 프로그램 내에서 외부 실행 프로그램을 실행합니다.

먼저 다음 프로그램을 참조하십시오.

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

int main(int argc, char *args[]) {
  int i;
  int count = atoi(args[1]);
  for (i = 1; i <= count; i++) printf("[%d]", i);
  printf("\n");
  return 0;
}

이 프로그램은 명령줄 인수를 사용합니다. 명령줄 인수(문자열로 전달됨)는 main 함수의 두 번째 줄에서 정수로 변환됩니다.

다음으로, 우리는 1에서 카운트까지 루프를 실행하고 대괄호 안에 카운팅을 인쇄합니다. 이 코드의 출력을 참조하십시오.

$./ test 4 [1][2][3][4]

test라는 이름으로 실행 파일을 만들었습니다. 명령 프롬프트에서 4 매개변수를 사용하여 test 파일을 실행했습니다.

출력에서 대괄호 안에 1에서 4까지 세는 것을 볼 수 있습니다.

다음으로, 이 프로그램 test를 다른 프로그램의 외부 명령으로 실행해야 합니다. 이를 위해 실행 가능한 test 프로그램의 경로를 지정해야 합니다.

여기에서 전체 경로는 /home/mateen/Documents/test입니다. 따라서 다음 프로그램에서 이 경로를 지정하여 실행 파일을 찾습니다.

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char *ar[]) {
  printf("This is the first line\n");
  char *binaryPath = "/bin/bash";
  char name[80] = "/home/mateen/Documents/test ";
  strcat(name, ar[1]);
  char *args[] = {binaryPath, "-c", name, NULL};
  char *env_args[] = {"/bin/bash", (char *)0};
  execve(binaryPath, args, env_args);
  printf("This is the last line\n");
  return 1;
}

문자열을 연결하는 함수를 사용하기 위해 다른 라이브러리를 포함했습니다. main 함수의 세 번째 줄에는 Linux 명령이 아니기 때문에 완전한 경로와 파일 이름이 있습니다. 대신, 이것은 사용자 정의 실행 프로그램입니다(이미 자세히 논의됨).

다음 줄에서는 현재 프로그램에 전달된 명령줄 인수를 새 프로그램의 이름과 연결합니다. 다시 다섯 번째 줄에는 -c 경로가 있는 명령줄 인수가 있습니다.

세 번째 매개 변수는 경로 + 실행 파일 이름 + 현재 프로그램에 전달된 인수를 갖는 변수 이름입니다.

출력:

$ ./a.out 5
This is the first line
[1][2][3][4][5]

현재 프로그램을 명령줄 매개변수 5로 실행하고 있습니다. 출력의 첫 번째 줄에는 첫 번째 print 문이 있습니다.

다음으로 테스트 프로그램이 실행되는 것을 볼 수 있습니다. 1에서 5까지 세는 것은 대괄호 안에 표시됩니다.

마지막으로 결론은 execve를 사용하여 Linux 명령과 실행 프로그램을 모두 실행할 수 있다는 것입니다. Linux 명령의 경우 Linux 프로그램을 찾는 경로를 전달할 수 있습니다.

다른/외부 실행 파일의 경우 파일 이름과 함께 전체 경로를 제공할 수 있습니다. 이 경우 프로그램은 지정된 경로에 자동으로 배치됩니다. 이 경우 명령은 main 함수의 세 번째 줄에 있는 경로 변수를 무시합니다.