Simple Analysis of system functions in linux, linuxsystem Functions

Source: Internet
Author: User

Simple Analysis of system functions in linux, linuxsystem Functions

 1 int 2 __libc_system (const char *line) 3 { 4   if (line == NULL) 5     /* Check that we have a command processor available.  It might 6        not be available after a chroot(), for example.  */ 7     return do_system ("exit 0") == 0; 8  9   return do_system (line);10 }11 weak_alias (__libc_system, system)

The code is located in glibc/sysdeps/posix/system. c. Here, system is the weak alias of _ libc_system, and _ libc_system is the front-end function of do_system. Check the parameters. Next, let's look at the do_system function.

  1 static int  2 do_system (const char *line)  3 {  4   int status, save;  5   pid_t pid;  6   struct sigaction sa;  7 #ifndef _LIBC_REENTRANT  8   struct sigaction intr, quit;  9 #endif 10   sigset_t omask; 11  12   sa.sa_handler = SIG_IGN; 13   sa.sa_flags = 0; 14   __sigemptyset (&sa.sa_mask); 15  16   DO_LOCK (); 17   if (ADD_REF () == 0) 18     { 19       if (__sigaction (SIGINT, &sa, &intr) < 0) 20     { 21       (void) SUB_REF (); 22       goto out; 23     } 24       if (__sigaction (SIGQUIT, &sa, &quit) < 0) 25     { 26       save = errno; 27       (void) SUB_REF (); 28       goto out_restore_sigint; 29     } 30     } 31   DO_UNLOCK (); 32  33   /* We reuse the bitmap in the 'sa' structure.  */ 34   __sigaddset (&sa.sa_mask, SIGCHLD); 35   save = errno; 36   if (__sigprocmask (SIG_BLOCK, &sa.sa_mask, &omask) < 0) 37     { 38 #ifndef _LIBC 39       if (errno == ENOSYS) 40     __set_errno (save); 41       else 42 #endif 43     { 44       DO_LOCK (); 45       if (SUB_REF () == 0) 46         { 47           save = errno; 48           (void) __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL); 49         out_restore_sigint: 50           (void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL); 51           __set_errno (save); 52         } 53     out: 54       DO_UNLOCK (); 55       return -1; 56     } 57     } 58  59 #ifdef CLEANUP_HANDLER 60   CLEANUP_HANDLER; 61 #endif 62  63 #ifdef FORK 64   pid = FORK (); 65 #else 66   pid = __fork (); 67 #endif 68   if (pid == (pid_t) 0) 69     { 70       /* Child side.  */ 71       const char *new_argv[4]; 72       new_argv[0] = SHELL_NAME; 73       new_argv[1] = "-c"; 74       new_argv[2] = line; 75       new_argv[3] = NULL; 76  77       /* Restore the signals.  */ 78       (void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL); 79       (void) __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL); 80       (void) __sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL); 81       INIT_LOCK (); 82  83       /* Exec the shell.  */ 84       (void) __execve (SHELL_PATH, (char *const *) new_argv, __environ); 85       _exit (127); 86     } 87   else if (pid < (pid_t) 0) 88     /* The fork failed.  */ 89     status = -1; 90   else 91     /* Parent side.  */ 92     { 93       /* Note the system() is a cancellation point.  But since we call 94      waitpid() which itself is a cancellation point we do not 95      have to do anything here.  */ 96       if (TEMP_FAILURE_RETRY (__waitpid (pid, &status, 0)) != pid) 97     status = -1; 98     } 99 100 #ifdef CLEANUP_HANDLER101   CLEANUP_RESET;102 #endif103 104   save = errno;105   DO_LOCK ();106   if ((SUB_REF () == 0107        && (__sigaction (SIGINT, &intr, (struct sigaction *) NULL)108        | __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL)) != 0)109       || __sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL) != 0)110     {111 #ifndef _LIBC112       /* glibc cannot be used on systems without waitpid.  */113       if (errno == ENOSYS)114     __set_errno (save);115       else116 #endif117     status = -1;118     }119   DO_UNLOCK ();120 121   return status;122 }
Do_system

First, the function sets up some signal processing programs to process SIGINT and SIGQUIT signals. Here we are not very concerned about it. The key code segment is here.

 1 #ifdef FORK 2   pid = FORK (); 3 #else 4   pid = __fork (); 5 #endif 6   if (pid == (pid_t) 0) 7     { 8       /* Child side.  */ 9       const char *new_argv[4];10       new_argv[0] = SHELL_NAME;11       new_argv[1] = "-c";12       new_argv[2] = line;13       new_argv[3] = NULL;14 15       /* Restore the signals.  */16       (void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL);17       (void) __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);18       (void) __sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL);19       INIT_LOCK ();20 21       /* Exec the shell.  */22       (void) __execve (SHELL_PATH, (char *const *) new_argv, __environ);23       _exit (127);24     }25   else if (pid < (pid_t) 0)26     /* The fork failed.  */27     status = -1;28   else29     /* Parent side.  */30     {31       /* Note the system() is a cancellation point.  But since we call32      waitpid() which itself is a cancellation point we do not33      have to do anything here.  */34       if (TEMP_FAILURE_RETRY (__waitpid (pid, &status, 0)) != pid)35     status = -1;36     }

First, the system calls fork through the frontend function to generate a sub-process. fork has two return values: the pid of the sub-process returned for the parent process and the pid of the sub-process returned by 0. Therefore, the child process executes 6-24 lines of code, and the parent process executes 30-35 lines of code.

The logic of the sub-process is very clear. execve is called to execute the program specified by SHELL_PATH. The parameter is passed through new_argv, and the environment variable is global variable _ environ.

The SHELL_PATH and SHELL_NAME are defined as follows:

1 #define    SHELL_PATH    "/bin/sh"    /* Path of the shell.  */2 #define    SHELL_NAME    "sh"        /* Name to give it.  */

 

In fact, it is to generate a sub-process to call the/bin/sh-c "command" to execute the command passed into the system.

 

The following is the reason and focus of my research on system functions:

In the pwn question of CTF, calling the system function through Stack Overflow sometimes fails. Let's say that the environment variables are overwritten, but they have always been ignorant. I have learned more about it today, I finally figured it out.

Here, the environment variables required by the system function are stored in the global variable _ environ. What is the content of this variable.

_ Environ is defined in glibc/csu/libc-start.c, let's look at several key statements.

# define LIBC_START_MAIN __libc_start_main

 

_ Libc_start_main is the function called by _ start, which involves some initialization work at the beginning of the program. If you do not know these terms, you can read this article. Next, let's look at the LIBC_START_MAIN function.

  1 STATIC int  2 LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),  3          int argc, char **argv,  4 #ifdef LIBC_START_MAIN_AUXVEC_ARG  5          ElfW(auxv_t) *auxvec,  6 #endif  7          __typeof (main) init,  8          void (*fini) (void),  9          void (*rtld_fini) (void), void *stack_end) 10 { 11   /* Result of the 'main' function.  */ 12   int result; 13  14   __libc_multiple_libcs = &_dl_starting_up && !_dl_starting_up; 15  16 #ifndef SHARED 17   char **ev = &argv[argc + 1]; 18  19   __environ = ev; 20  21   /* Store the lowest stack address.  This is done in ld.so if this is 22      the code for the DSO.  */ 23   __libc_stack_end = stack_end;
    ......
202 /* Nothing fancy, just call the function. */203 result = main (argc, argv, __environ MAIN_AUXVEC_PARAM);204 #endif205 206 exit (result);207 }

We can see that the value of _ environ is defined in row 19th without define SHARED. Before the program calls LIBC_START_MAIN, it will first save the environment variable and the string in argv (actually saved to the stack), and then the address of each string in the environment variable in turn, the address of each string in argv and the address of argc into the stack, so the environment variable array must be located at the front of the argv array, separated by an empty address. Therefore, the & argv [argc + 1] statement in line 1 is to take the first address of the environment variable array on the stack, save it to ev, and finally save it to _ environ. When the main function is called in Row 3, the value of _ environ is added to the stack. It is no problem to overwrite the value of _ environ by Stack Overflow. You only need to ensure that the address in _ environ is not overwritten.

Therefore, when the stack overflow length is too large and the overflow content overwrites important content in the _ environ address, calling the system function will fail. How far is the specific environment variable from the overflow address? You can view it by breaking it down in _ start.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.