三种命令执行的方式
java.lang.Runtime#exec
Runtime.getRuntime().exec("calc");
java.lang.ProcessBuilder#start
new ProcessBuilder().command("calc").start();
java.lang.ProcessImpl#start.
由于ProcessImpl类为package-private,所以无法直接使用这个类
Java中的public,protected,package-private和private之间有什么区别? https://my.oschina.net/stackoom/blog/3136101
可以使用反射来获取该类并执行其start方法:
String[] cmds = {"calc"};
Class clz = Class.forName("java.lang.ProcessImpl");
Method method = clz.getDeclaredMethod("start", String[].class, Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class);
method.setAccessible(true);
method.invoke(clz,cmds, null, null, null, false);
ProcessImpl.start()
这里需要知道Runtime.exec()和ProcessBuilder.start()执行命令都是调用ProcessImpl.start来实现的.
逐步跟踪Runtime.exec可以发现它调用了ProcessBuilder.start()
继续跟踪, 发现ProcessBuilder.start又调用了ProcessImpl.start()
Runtime#exec命令切割特性
执行: Runtime.getRuntime().exec(“/bin/sh -c \”echo 111 > 3.txt\””); 发现会执行失败
逐步跟踪:
发现exec会首先执行StringTokenizer把传进去的命令字符串安装空格分割为数组, 所以/bin/sh -c “echo 111 > 3.txt” 就成了[“/bin/sh”, “-c”, “”echo”, “111”, “>”, +1 more], 数组第一个元素会被当作可执行命令,其余为参数.
Windows下执行命令要加上cmd /c , 因为Java找不到像echo这样的指令
正确的命令执行姿势:
// Windows
Runtime.getRuntime().exec(new String[]{"cmd","/c","echo 111 > 1.txt"});
Runtime.getRuntime().exec("powershell.exe -NonI -W Hidden -NoP -Exec Bypass -Enc YwBhAGwAYwA=");
// Linux
String[] cmd = {"/bin/sh", "-c", "echo 111 > 3.txt"};
process = Runtime.getRuntime().exec(cmd);
Runtime.getRuntime().exec("bash -c {echo,dG91Y2ggL3RtcC9hYmNkZWZnLnR4dA==}|{base64,-d}|{bash,-i}");
坑
String[] cmds = {"calc"};
Class clz = Class.forName("java.lang.ProcessImpl");
Method method = clz.getDeclaredMethod("start", String[].class, Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class);
method.setAccessible(true);
method.invoke(clz,cmds, null, null, null, false);
jdk17以上直接用反射获取ProcessImpl类执行命令会出错.
本地测试jdk15以下无此问题,未找到具体原因.
"C:\Program Files\Java\jdk-17\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2021.3.3\lib\idea_rt.jar=11527:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2021.3.3\bin" -Dfile.encoding=UTF-8 -classpath C:\Users\ThinkPad\Desktop\javaSec\out\production\javatest test.CmdExcute
Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make static java.lang.Process java.lang.ProcessImpl.start(java.lang.String[],java.util.Map,java.lang.String,java.lang.ProcessBuilder$Redirect[],boolean) throws java.io.IOException accessible: module java.base does not "opens java.lang" to unnamed module @3b07d329
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199)
at java.base/java.lang.reflect.Method.setAccessible(Method.java:193)
at test.CmdExcute.main(CmdExcute.java:15)
Process finished with exit code 1