關(guān)于system函數(shù)在linux下的分析介紹

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

簡單分析了linux下system函數(shù)的相關(guān)內(nèi)容,具體內(nèi)容如下

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的前端函數(shù),進(jìn)行了參數(shù)的檢查,接下來看do_system函數(shù)。

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>首先函數(shù)設(shè)置了一些信號處理程序,來處理SIGINT和SIGQUIT信號,此處我們不過多關(guān)心,關(guān)鍵代碼段在這里</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>首先通過前端函數(shù)調(diào)用系統(tǒng)調(diào)用fork產(chǎn)生一個子進(jìn)程,其中fork有兩個返回值,對父進(jìn)程返回子進(jìn)程的pid,對子進(jìn)程返回0。所以子進(jìn)程執(zhí)行6-24行代碼,父進(jìn)程執(zhí)行30-35行代碼。</p><p>子進(jìn)程的邏輯非常清晰,調(diào)用execve執(zhí)行SHELL_PATH指定的程序,參數(shù)通過new_argv傳遞,環(huán)境<a href="http://www.php.cn/wiki/1497.html" target="_blank">linux</a>為全局變量environ。</p><p>其中SHELL_PATH和SHELL_NAME定義如下</p><pre class="brush:bash;">#define??SHELL_PATH??"/bin/sh"??/*?Path?of?the?shell.?*/  #define??SHELL_NAME??"sh"????/*?Name?to?give?it.?*/

其實就是生成一個子進(jìn)程調(diào)用/bin/sh -c “命令”來執(zhí)行向system傳入的命令。?

下面其實是我研究system函數(shù)的原因與重點:

在CTF的pwn題中,通過棧溢出調(diào)用system函數(shù)有時會失敗,聽師傅們說是環(huán)境變量被覆蓋,但是一直都是懵懂,今天深入學(xué)習(xí)了一下,總算搞明白了。

在這里system函數(shù)需要的環(huán)境變量儲存在全局變量environ中,那么這個變量的內(nèi)容是什么呢。

environ是在glibc/csu/libc-start.c中定義的,我們來看幾個關(guān)鍵語句。

#?define?LIBC_START_MAIN?libc_start_main

libc_start_main是_start調(diào)用的函數(shù),這涉及到程序開始時的一些初始化工作,對這些名詞不了解的話可以看一下這篇文章。接下來看LIBC_START_MAIN函數(shù)。

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的值。啟動程序調(diào)用LIBC_START_MAIN之前,會先將環(huán)境變量和argv中的linux保存起來(其實是保存到棧上),然后依次將環(huán)境變量中各項字符串的地址,argv中各項字符串的地址和argc入棧,所以環(huán)境變量linux一定位于argv數(shù)組的正后方,以一個空地址間隔。所以第17行的&argv[argc + 1]語句就是取環(huán)境變量數(shù)組在棧上的首地址,保存到ev中,最終保存到environ中。第203行調(diào)用main函數(shù),會將environ的值入棧,這個被棧溢出覆蓋掉沒什么問題,只要保證environ中的地址處不被覆蓋即可。

所以,當(dāng)棧溢出的長度過大,溢出的內(nèi)容覆蓋了environ中地址中的重要內(nèi)容時,調(diào)用system函數(shù)就會失敗。具體環(huán)境變量距離溢出地址有多遠(yuǎn),可以通過在_start中下斷查看。

以上就是關(guān)于system函數(shù)在

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點贊10 分享