Java反射
终于准备学习Java反序列化了,先放个反射弹计算器镇楼
1 2 3 4 5 6 7 8 9 10 11
| import java.lang.reflect.Method;
public class reflect { public static void main(String[] args) throws Exception { Class exec = Class.forName("java.lang.Runtime"); Method ExecMethod = exec.getMethod("exec", String.class); Method RuntimeMethod = exec.getMethod("getRuntime"); Object runtime = RuntimeMethod.invoke(exec); ExecMethod.invoke(runtime, "calc"); } }
|
本文的后续就围绕着这块代码来讲解
0x01 正射反射
上面的代码是一个反射的例子,那有反射肯定有正射,上面代码正射是什么样子呢
1 2 3 4 5 6
| public class zs { public static void main(String[] args) throws Exception { Runtime runtime = Runtime.getRuntime(); runtime.exec("calc"); } }
|
我敲这不就是类的实例化然后调用类中方法吗
那可以直接实例化为啥还要用反射呢
这里引入p神提到的名词动态特性
打个比方php中的一句话木马
1
| <?php eval($_POST['pass']); ?>
|
一行php代码可以执行所有的php代码
在java中没有eval这种函数,如果我想实例化一个类就只能实例化一个类
不过有了反射之后情况就不同了,可以写一个方法将需要调用的类和类中方法作为参数传递到反射函数中去,可以在一定程度上实现任意执行java代码的功能
类似下面这样
1 2 3 4
| public static void execute(String className, String methodName) throws Exception { Class clazz = Class.forName(className); clazz.getMethod(methodName).invoke(clazz.newInstance()); }
|
0x02 获取Class类方法介绍
Class.forName
要求JVM查找后加载指定类,初始化该类
注意这里是初始化类,不是实例化类,不会触发构造方法
但是会触发静态代码块中的代码
先来看下面的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| public class User { public static int number = 222; private static int pri_number = 222; public int no_static_number = 666; static { System.out.println("静态"); }
public User(){ System.out.println("无参数构造"); }
public User(int a1){ System.out.println(a1 + "参数构造"); } private User(String a1, String a2){ System.out.println(a1 + "私有构造函数" + a2); }
public static int HelloWorld(){ System.out.println("HelloWorld"); return 0; }
public static int HelloWorld(String id){ System.out.println("HelloWorld" + id); return 0; }
private static int Pri_HelloWorld(){ System.out.println("Pri_HelloWorld"); return 0; }
private static int Pri_HelloWorld(String param){ System.out.println("Pri_HelloWorld" + param); return 0; }
public int NoStatic_HelloWorld(){ System.out.println("NoStatic_HelloWorld"); return 0; } public static void main(String[] args) {
System.out.println(""); } }
|
反射代码
1 2 3 4 5 6 7 8 9 10
| import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method;
public class reflect { public static void main(String[] args) throws Exception { Class exec = Class.forName("User"); } }
|
这里就不贴图了,师傅们可以试试看,只会执行静态代码块中代码
getClass()
该方法用于获取实例化的对象的Class
1 2 3 4 5 6 7 8 9 10 11
| import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method;
public class reflect { public static void main(String[] args) throws Exception { User user = new User(); Class exec = user.getClass(); } }
|
实例化会触发构造方法
.class
该方法可以直接获取类的Class
1 2 3 4 5 6 7 8
| import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method;
public class reflect { public static void main(String[] args) throws Exception { Class exec = User.class; } }
|
不会触发静态代码块里的方法
没有任何输出
0x02 方法介绍
getMethod
获取成员方法的函数
1
| getMethod(String name, Class<?>... parameterTypes)
|
第一个参数为成员方法名称,第二个参数为成员方法的类型
因为java中有重载所以会出现同名函数,可以通过指定后面的参数类型来确认调用什么函数
同样的还是User.java
这次的反射代码为
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import java.lang.reflect.Method;
public class reflect { public static void main(String[] args) throws Exception { Class exec = Class.forName("User"); Method ExecMethod = exec.getMethod("HelloWorld"); ExecMethod.invoke(exec); Method ExecMethod1 = exec.getMethod("HelloWorld", String.class); ExecMethod1.invoke(exec, "00"); } }
|
可以看到调用根据getMethod的第二个参数可以调用不同参数的同名函数
要是成员方法为受保护的和私有的改怎么办
这时候就需要下面的方法了
getDeclaredMethod
1 2 3 4 5 6 7 8 9 10 11 12
| import java.lang.reflect.Method;
public class reflect { public static void main(String[] args) throws Exception { Class exec = Class.forName("User"); Method ExecMethod = exec.getDeclaredMethod("Pri_HelloWorld"); ExecMethod.setAccessible(true); ExecMethod.invoke(exec); } }
|
其实没有啥区别的
setAccessible可以启用和禁用安全检查,参数为true时则禁用
禁用后可以调用私有方法,别的就完全相同了
getField
该方法可以获取类中公开成员变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import java.lang.reflect.Field;
public class reflect {
public static void main(String[] args) throws Exception { Class exec = Class.forName("User"); Field NumberField = exec.getField("number"); System.out.println(NumberField.get(exec)); NumberField.set(exec, 234); System.out.println(NumberField.get(exec)); } }
|
用get可以得到类中成员的值,set可以修改类中成员的值
同样的下面的方法可以得到类中受保护和私有
getDeclaredField
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import java.lang.reflect.Field;
public class reflect {
public static void main(String[] args) throws Exception { Class exec = Class.forName("User"); Field NumberField = exec.getDeclaredField("pri_number"); NumberField.setAccessible(true); System.out.println(NumberField.get(exec)); NumberField.set(exec, 234); System.out.println(NumberField.get(exec));
} }
|
关于这块最后再来看看下面的代码就可以结束了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import java.lang.reflect.Field; import java.lang.reflect.Method; public class reflect {
public static void main(String[] args) throws Exception { Class exec = Class.forName("User"); Field[] Field_ALL = exec.getDeclaredFields(); Field[] Field_Public = exec.getFields(); Method[] Method_ALL = exec.getDeclaredMethods(); Method[] Method_Public = exec.getMethods(); } }
|
invoke
这就是反射取得类后执行方法的函数
1 2 3 4 5 6 7 8 9 10 11 12
| import java.lang.reflect.Method;
public class reflect { public static void main(String[] args) throws Exception { Class exec = Class.forName("User"); Method ExecMethod = exec.getDeclaredMethod("Pri_HelloWorld", String.class); ExecMethod.setAccessible(true); ExecMethod.invoke(exec, "test"); } }
|
按照getDeclaredMethod的第二个参数来指定执行同名的什么函数
getMethod同理
invoke第一个参数为对象实例
后面的参数要与getDeclaredMethod指定的一致
这里需要注意下invoke的第一个参数,如果要取得的时静态方法和静态成员变量,第一个参数可以为null,不影响使用,可以看到上面在User.java中取得的方法和变量都是静态的
如果需要调用非静态的方法则需要把取得的类实例化
需要用到下面的函数
newInstance()
该方法可以实例化取得的类
可以触发构造函数
如果调用的方法和变量不为静态则需要实例化后再获取
下面看看代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import java.lang.reflect.Field; import java.lang.reflect.Method;
public class reflect { public static void main(String[] args) throws Exception { Class exec = Class.forName("User"); Method ExecMethod = exec.getMethod("NoStatic_HelloWorld"); Field NumberField = exec.getField("no_static_number"); Object new_exec = exec.newInstance(); ExecMethod.invoke(new_exec); System.out.println(NumberField.get(new_exec)); } }
|
这样就可以调用非静态的方法和变量了
问题又来了,类中可能有多个构造方法,改怎么获取呢
需要用到下面的方法
getConstructor
该方法可以指定newInstance实例化使用什么构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13
| import java.lang.reflect.Constructor; public class reflect { public static void main(String[] args) throws Exception { Class exec = Class.forName("User"); Constructor gz = exec.getConstructor(); gz.newInstance(); Constructor gz1 = exec.getConstructor(int.class); gz1.newInstance(1); } }
|
同样的下面的函数可以得到私有的构造方法
getDeclaredConstructor
1 2 3 4 5 6 7 8 9 10 11
| import java.lang.reflect.Constructor; public class reflect { public static void main(String[] args) throws Exception { Class exec = Class.forName("User"); Constructor pri_gz = exec.getDeclaredConstructor(String.class, String.class); pri_gz.setAccessible(true); pri_gz.newInstance("_","_"); } }
|
同样的也有获取全部公开构造函数的方法和获取全部构造函数的方法
1 2 3 4 5 6 7 8
| import java.lang.reflect.Constructor; public class reflect { public static void main(String[] args) throws Exception { Class exec = Class.forName("User"); Constructor[] Constructor_ALL = exec.getDeclaredConstructors(); Constructor[] Constructor_Public = exec.getConstructors(); } }
|