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)
 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)

linux下system函数的不难分析,linuxsystem函数

 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)

代码位于glibc/sysdeps/posix/system.c,那里system是__libc_system的弱别名,而__libc_system是do_system的前端函数,举办了参数的自作者批评,接下去看do_system函数。

澳门金沙国际 1

  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_HANDLER
101   CLEANUP_RESET;
102 #endif
103 
104   save = errno;
105   DO_LOCK ();
106   if ((SUB_REF () == 0
107        && (__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 _LIBC
112       /* glibc cannot be used on systems without waitpid.  */
113       if (errno == ENOSYS)
114     __set_errno (save);
115       else
116 #endif
117     status = -1;
118     }
119   DO_UNLOCK ();
120 
121   return status;
122 }

do_system

第①函数设置了部分信号处理程序,来处理SIGINT和SIGQUIT信号,此处大家只是多关怀,关键代码段在此间

 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   else
29     /* Parent side.  */
30     {
31       /* Note the system() is a cancellation point.  But since we call
32      waitpid() which itself is a cancellation point we do not
33      have to do anything here.  */
34       if (TEMP_FAILURE_RETRY (__waitpid (pid, &status, 0)) != pid)
35     status = -1;
36     }

首先通过前端函数调用系统调用fork发生一个子进度,其中fork有七个再次回到值,对父进度重回子进度的pid,对子进度重临0。所以子进度执行6-24行代码,父进度执行30-35行代码。

子进度的逻辑相当清晰,调用execve执行SHELL_PATH钦命的程序,参数通过new_argv传递,环境变量为全局变量__environ。

其中SHELL_PATH和SHELL_澳门金沙国际,NAME定义如下

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

 

实际上就是生成一个子进程调用/bin/sh -c “命令”来施行向system传入的吩咐。

 

上边其实是本身商讨system函数的原因与重大:

在CTF的pwn题中,通过栈溢出调用system函数有时会破产,听师傅们就是环境变量被覆盖,不过一向都以懵懂,后天深刻学习了一晃,总算搞了然了。

在此间system函数需要的环境变量储存在全局变量__environ中,那么这些变量的内容是如何吗。

__environ是在glibc/csu/libc-start.c中定义的,大家来看多少个根本语句。

# define LIBC_START_MAIN __libc_start_main

 

__libc_start_main是_start调用的函数,那事关到程序开首时的部分起始化工作,对这个名词不通晓的话能够看一下那篇文章。接下来看LIBC_START_MAIN函数。

  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 #endif
205 
206   exit (result);
207 }

大家得以看出,在未曾define
SHARED的动静下,在第叁9行概念了__environ的值。运转程序调用LIBC_START_MAIN以前,会先将环境变量和argv中的字符串保存起来(其实是保留到栈上),然后依次将环境变量中种种字符串的地址,argv中各项字符串的地点和argc入栈,所以环境变量数组一定位于argv数组的正后方,以3个空地址间隔。所以第27行的&argv[argc
+
1]讲话就是取环境变量数组在栈上的首地址,保存到ev中,最后保存到__environ中。第203行调用main函数,会将__environ的值入栈,这几个被栈溢出覆盖掉没什么难点,只要保障__environ中的地址处不被遮住即可。

由此,当栈溢出的长度过大,溢出的故事情节覆盖了__linux下system函数的归纳解析,Linux上程序执行的进口。environ中地址中的主要内容时,调用system函数就会失利。具体环境变量距离溢出地址有多少距离,能够由此在_start中下断查看。

1
int 2 __libc_system ( const char * line) 3 { 4 if (line == NULL) 5
/* Check that we have a command processor available. It…

main()函数,想必大家都不生疏了,从刚早先写程序的时候,大家便起头写main(),我们都知晓main是程序的入口。那main作为2个函数,又是哪个人调用的它,它是怎么被调用的,再次回到给什么人,重临的又是什么样?这一次我们来探索一下那些题材。

代码位于glibc/sysdeps/posix/system.c,那里system是__libc_system的弱别名,而__libc_system是do_system的前端函数,举行了参数的反省,接下去看do_system函数。

代码位于glibc/sysdeps/posix/system.c,那里system是__libc_system的弱别名,而__libc_system是do_system的前端函数,举行了参数的检查,接下去看do_system函数。

  1. main()函数的花样
    先来说说main函数的概念,较早伊始写C程序的一定都用过这么的概念void
    main(){},其实翻翻C/C++标准,一向没有定义过void main()。
    在C标准中main的概念唯有二种:
            int main(void)
            int main(int argc, char *argv[])
            在C++标准中main的定义也唯有二种:
            int main( )
            int main(int argc, char *argv[])
       
        换句话说:当您的主次不须求命令行参数的时候用int
    main(), 当需求命令行参数的时候请使用int main(int argc, char
    *argv[])

       
       
    不过正式归专业,在不相同的阳台上,分化的编译器中对main()的定义格局总有自个儿的贯彻,比如早期编译器对void
    main()的支撑(今后gcc也扶助,不过会提交三个warning)。尤其的,因为历史的原由,在Unix-like平台上,大多还协助
            int main(int argc, char *argv[], char *envp[])
        其行使办法大家稍后再谈。

  2. main()函数的归来    
        int main(…)
    意味着须要return二个int值,假如不写,有的编译器会自动帮你添加2个return
    0;,而一些则会再次来到2个随机值。为了避免不要求的题材,指出写的时候依然增进3个return
    0;,浪费不了你有个别日子,不是啊?
        所以二个完好的test.c文件应当为:
        int main(int argc, char *argv[])
        {
            return 0;
        }
        当然大家也足以品尝着让main重临1个long,
    double甚至是struct,更改main函数中的形参定义。那在某些编译器上是能编译通过的,但是恐怕会有局地警示(如GCC)。然而运转的时候如若编译器能做转换的幸亏,如重回long,float.
    如若不能的话(如重返struct,大概main(int argc, char *argv0,char
    *argv1,char *argv2))会造成segmentation fault。
       
       

  3. main()的调用和重返
       
    在了解了main()函数的定义和再次来到格局后,我们再来看看main函数是怎么被调用的,它又”return”给了何人。在”gcc的编译过程”一中,大家回想了程序从源码到可执行程序的长河,在”应用程序在linux上是怎样被执行的”一文中,大家想起了可执行文件怎么被操作系统加载的,后天我们继续这么些进度。
    上文提到不管是在load_elf_binary()中只怕采用了动态链接库,终极都实施到了应用程序的输入。可是那个进口不是main.而是_start()
    执行
        gcc -o test test.c
        readelf -a test
        可以看出test文件的Entry point
    address是0x80482e0,在以往看,这些地方是.text的地点(代码段的初阶),也是_start()的地址。在_start()中又会调用__libc_start_main(),首要做一些先后的起初化工作,感兴趣的同桌可以读读glibc中的源码,注释很了然。然后主演登场了,在__libc_start_main()中最后会调用
        int result = main (argc, argv, __environ
    MAIN_AUXVEC_PARAM);//这是Unix-like下main函数的调用情势,那下大家掌握main函数中形参的由来了啊。
        result中放着main函数的再次回到值,然后带着这些值脱离。
        exit (result);

澳门金沙国际 2澳门金沙国际 3

澳门金沙国际 4澳门金沙国际 5

专注:即使main函数是壹个卓绝的函数,是程序运转的入口,但它说到底也是二个函数,是可以被调用的。如:
    int   main()  
    {  
        if(…)  
            return   0;  
        main();  
        return   0;  
    }  
    可是要小心调用格局,和剥离标准,避免无穷递归。

  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_HANDLER
101   CLEANUP_RESET;
102 #endif
103 
104   save = errno;
105   DO_LOCK ();
106   if ((SUB_REF () == 0
107        && (__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 _LIBC
112       /* glibc cannot be used on systems without waitpid.  */
113       if (errno == ENOSYS)
114     __set_errno (save);
115       else
116 #endif
117     status = -1;
118     }
119   DO_UNLOCK ();
120 
121   return status;
122 }
  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_HANDLER
101   CLEANUP_RESET;
102 #endif
103 
104   save = errno;
105   DO_LOCK ();
106   if ((SUB_REF () == 0
107        && (__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 _LIBC
112       /* glibc cannot be used on systems without waitpid.  */
113       if (errno == ENOSYS)
114     __set_errno (save);
115       else
116 #endif
117     status = -1;
118     }
119   DO_UNLOCK ();
120 
121   return status;
122 }
  1. shell中举行顺序
       
    通过前五回和地方的解析,大家好不简单基本弄清了应用程序的实施过程,再回首三遍:
    在某些交互式shell中敲入./test, 此shell
    fork()/clone()出多个子经过,那么些子进程执行
        
        execve(“./test”,char * const argv[], char * const envp[])
        
       
    execve加载./test,并把参数argv[],envp[]一步一步传递下去。加载了./test之后,从./test的输入先导推行,即ELF文件中的_start(),_start()调用__libc_start_main(),最后到了main。
        
        int main(int argc, char *argv[], char *envp[])
        
       
    望着这么些main的定义和execve相似吧,没错main中的参数都以execve一步步传递下去的。argc是命令行参数个数,argv[]储存着各种参数的指针(注意argv[0]一般是程序名,argv[1]始发才是命令行参数。那是由shell设置的),envp[]储存着环境变量表。然则在标准C中只定义了int
    main(int argc, char
    *argv[]),所以unix-like平台也提供了全局变量environ指向环境变量表。
        extern char **environ;
        当然也足以用getenv和putenv来访问特定的环境变量。

do_system

do_system

   
对了,父shell还在wait()./test的完毕吗,不错,test中main函数return的值,在被__libc_start_main()
exit之后,终于被父shell抓住了,可以用$?访问。
    如$> ./test
      $> echo $?
   
可以赢得test再次回到的值。那样,你就清楚main()函数中return的含义,以及哪些在shell中拔取了啊。尽管可以return任何值,也提出用return
0来表示程序不奇怪化甘休。那样外人用shell脚本调用你写的次序的时候,就足以$?等于0来判定你的程序是还是不是正规实施了。

首先函数设置了部分信号处理程序,来处理SIGINT和SIGQUIT信号,此处大家只是多关注,关键代码段在此地

第③函数设置了有个别信号处理程序,来处理SIGINT和SIGQUIT信号,此处大家只是多关切,关键代码段在此间

末段统计一下:

 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   else
29     /* Parent side.  */
30     {
31       /* Note the system() is a cancellation point.  But since we call
32      waitpid() which itself is a cancellation point we do not
33      have to do anything here.  */
34       if (TEMP_FAILURE_RETRY (__waitpid (pid, &status, 0)) != pid)
35     status = -1;
36     }
 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   else
29     /* Parent side.  */
30     {
31       /* Note the system() is a cancellation point.  But since we call
32      waitpid() which itself is a cancellation point we do not
33      have to do anything here.  */
34       if (TEMP_FAILURE_RETRY (__waitpid (pid, &status, 0)) != pid)
35     status = -1;
36     }
  1. 防止拔取void main(),尽量拔取int main() 或许 int main(int argc, char
    *argv[])。
    2. 在main的结尾记得
    return int;, 最好用return 0;表示程序的健康为止。
  2. main函数和平凡函数一样也是能被调用的。
  3. main
    return的值最终会回来给其调用者,如shell中举行的程序,可以在shell中用$?得到其重返值。
  4. 在unix-like环境中,能够动用int main(int argc, char *argv[], char
    *envp[]), extern char **environ; , getenv()等措施来取得环境变量。

首先通过前端函数调用系统调用fork暴发多个子进度,其中fork有几个再次来到值,对父进度重返子进度的pid,对子进程重回0。所以子进度执行6-24行代码,父进度执行30-35行代码。

率先通过前端函数调用系统调用fork爆发一个子经过,其中fork有多个再次回到值,对父进程重回子进度的pid,对子进度再次回到0。所以子进度执行6-24行代码,父进度执行30-35行代码。

子进程的逻辑相当清楚,调用execve执行SHELL_PATH内定的程序,参数通过new_argv传递,环境变量为全局变量__environ。

子进度的逻辑相当明晰,调用execve执行SHELL_PATH指定的顺序,参数通过new_argv传递,环境变量为全局变量__environ。

其中SHELL_PATH和SHELL_NAME定义如下

其中SHELL_PATH和SHELL_NAME定义如下

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

 

 

事实上就是生成两个子历程调用/bin/sh -c
“命令”来推行向system传入的授命。

实际就是生成三个子历程调用/bin/sh -c
“命令”来举办向system传入的命令。

 

 

上边其实是小编商量system函数的原委与根本:

下边其实是本身切磋system函数的来由与重大:

在CTF的pwn题中,通过栈溢出调用system函数有时会破产,听师傅们就是环境变量被遮盖,不过平昔都是懵懂,前天深刻学习了弹指间,总算搞精晓了。

在CTF的pwn题中,通过栈溢出调用system函数有时会战败,听师傅们就是环境变量被掩盖,可是一贯都是懵懂,明日长远学习了眨眼之间间,总算搞领悟了。

在那边system函数要求的环境变量储存在全局变量__environ中,那么那一个变量的内容是怎么样吧。

在那边system函数须求的环境变量储存在全局变量__environ中,那么那些变量的内容是怎么样吗。

__environ是在glibc/csu/libc-start.c中定义的,我们来看多少个基本点语句。

__environ是在glibc/csu/libc-start.c中定义的,大家来看多少个第二语句。

# define LIBC_START_MAIN __libc_start_main
# define LIBC_START_MAIN __libc_start_main

 

 

__libc_start_main是_start调用的函数,那关系到程序初阶时的局地开头化工作,对这几个名词不打听的话可以看一下那篇小说。接下来看LIBC_START_MAIN函数。

__libc_start_main是_start调用的函数,那事关到程序开首时的部分初阶化工作,对那么些名词不打听的话可以看一下这篇小说。接下来看LIBC_START_MAIN函数。

  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 #endif
205 
206   exit (result);
207 }
  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 #endif
205 
206   exit (result);
207 }

大家可以看到,在并未define SHARED的气象下,在第①9行定义了__environ的值。运维程序调用LIBC_START_MAIN以前,会先将环境变量和argv中的字符串保存起来(其实是保存到栈上),然后挨家挨户将环境变量中各队字符串的地方,argv中各个字符串的地址和argc入栈,所以环境变量数组一定位于argv数组的正后方,以3个空地址间隔。所以第27行的&argv[argc

我们可以观察,在未曾define SHARED的情形下,在第②9行定义了__environ的值。运转程序调用LIBC_START_MAIN以前,会先将环境变量和argv中的字符串保存起来(其实是保留到栈上),然后挨家挨户将环境变量中各项字符串的地方,argv中各队字符串的地址和argc入栈,所以环境变量数组一定位于argv数组的正后方,以三个空地址间隔。所以第①7行的&argv[argc

  • 1]说话就是取环境变量数组在栈上的首地址,保存到ev中,最后保存到__environ中。第203行调用main函数,会将__environ的值入栈,这些被栈溢出覆盖掉没什么难点,只要保证__environ中的地址处不被遮住即可。
  • 1]说话就是取环境变量数组在栈上的首地址,保存到ev中,最后保存到__environ中。第203行调用main函数,会将__environ的值入栈,那一个被栈溢出覆盖掉没什么难题,只要保障__environ中的地址处不被遮盖即可。

据此,当栈溢出的长短过大,溢出的内容覆盖了__environ中地址中的首要内容时,调用system函数就会失利。具体环境变量距离溢出地址有多少路程,可以经过在_start中下断查看。

据此,当栈溢出的尺寸过大,溢出的始末覆盖了__environ中地址中的首要内容时,调用system函数就会破产。具体环境变量距离溢出地址有多少距离,可以经过在_start中下断查看。

相关文章