linux下關于system函數的簡單分析

這篇文章主要簡單分析了linux下systemlinux,具有一定的參考價值,感興趣的小伙伴們可以參考一下

簡單分析了linux下system函數的相關內容,具體內容如下

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?(&amp;sa.sa_mask);    ?DO_LOCK?();  ?if?(ADD_REF?()?==?0)  ??{  ???if?(sigaction?(SIGINT,?&amp;sa,?&amp;intr)?<p>首先函數設置了一些信號處理程序,來處理SIGINT和SIGQUIT信號,此處我們不過多關心,關鍵代碼段在這里</p><pre class="brush:bash;">#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,?&amp;intr,?(struct?sigaction?*)?NULL);  ???(void)?sigaction?(SIGQUIT,?&amp;quit,?(struct?sigaction?*)?NULL);  ???(void)?sigprocmask?(SIG_SETMASK,?&amp;omask,?(sigset_t?*)?NULL);  ???INIT_LOCK?();    ???/*?Exec?the?shell.?*/  ???(void)?execve?(SHELL_PATH,?(char?*const?*)?new_argv,?environ);  ???_exit?(127);  ??}  ?else?if?(pid?<p>首先通過前端函數調用系統調用fork產生一個子進程,其中fork有兩個返回值,對父進程返回子進程的pid,對子進程返回0。所以子進程執行6-24行代碼,父進程執行30-35行代碼。</p><p>子進程的邏輯非常清晰,調用execve執行SHELL_PATH指定的程序,參數通過new_argv傳遞,環境<a href="http://www.php.cn/wiki/1497.html" target="_blank">linux</a>為全局變量environ。</p><p>其中SHELL_PATH和SHELL_NAME定義如下</p><p class="jb51code"><br></p><pre class="brush:bash;">#define??SHELL_PATH??"/bin/sh"??/*?Path?of?the?shell.?*/  #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函數。

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?'main'?function.?*/  ?int?result;    ?libc_multiple_libcs?=?&amp;_dl_starting_up?&amp;&amp;?!_dl_starting_up;    #ifndef?SHARED  ?char?**ev?=?&amp;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);  }

我們可以看到,在沒有define SHARED的情況下,在第19行定義了environ的值。啟動程序調用LIBC_START_MAIN之前,會先將環境變量和argv中的linux保存起來(其實是保存到棧上),然后依次將環境變量中各項字符串的地址,argv中各項字符串的地址和argc入棧,所以環境變量linux一定位于argv數組的正后方,以一個空地址間隔。所以第17行的&argv[argc + 1]語句就是取環境變量數組在棧上的首地址,保存到ev中,最終保存到environ中。第203行調用main函數,會將environ的值入棧,這個被棧溢出覆蓋掉沒什么問題,只要保證environ中的地址處不被覆蓋即可。

所以,當棧溢出的長度過大,溢出的內容覆蓋了environ中地址中的重要內容時,調用system函數就會失敗。具體環境變量距離溢出地址有多遠,可以通過在_start中下斷查看。

? 版權聲明
THE END
喜歡就支持一下吧
點贊9 分享