反射
反射是一个很重要的内容,大致的内容是另一种操作一个类方式,不再是原本的先new一个类出来然后使用类的一些熟悉和方法,最主要是点在于使用反射可以连final变量值都可以修改掉
Java的反射(reflection)机制是指在程序运行中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。
反射机制允许我们在Java程序运行时检查,操作或者说获取任何类、接口、构造函数、方法和字段。还可以动态创建Java类实例、调用任意的类方法、修改任意的类成员变量值等操作。
在Java代码审计中学习反射机制,我们目的是可以利用反射机制操作目标方法执行系统命令。比如我们想要反射调用java.lang.runtime去执行系统命令。
简单的创建一个User类
package org.example;
public class User {
public String name = "power7089";
public String getName() {
return name;
}
public void setName(String testStr) {
this.name = name;
}
}
获取Class对象的方式
- 根据类名:类名.class
- 根据对象:对象.getClass()
- 根据全限定类名:Class.forName(全路径类名)
- 通过类加载器获得class对象:
- ClassLoader.getSystemClassLoader().loadClass("com.example.xxx");
创建一个名为GetClass的类进行测试
package org.example;
public class GetClass {
public static void main(String[] args) throws
ClassNotFoundException {
//1.通过类名.class
Class c1 = User.class; //这里的class是object的属性所以明明User里面没有这个属性也可以点出来
//2.通过对象的getClass()方法
User user = new User();
Class c2 = user.getClass();
//3.通过 Class.forName()获得Class对象;
Class c3 = Class.forName("org.example.User");
//4.通过类加载器获得class对象
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Class c4 =
classLoader.loadClass("org.example.User");
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
}
}
我的目录结构是这样的,在使用 Class.forName()的时候需要知道类的完整全路径去寻找对应的类文件
创建一个新的包用来放接下里要用到的Userinfo类的信息
package org.example.reflectinfo;
public class UserInfo {
private String name;
public int age;
private UserInfo(String name) {
this.name = name;
}
public UserInfo(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
private String introduce() {
return "我叫" + name + ",今年" + age + "岁了!";
}
public String sayHello() {
return "Hello!我叫[" + name + "]";
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
1、java.lang.Class方式
方法名 释义
getPackage() 获取该类的包
getDeclaredAnnotations() 获取该类上所有注解
getModifiers() 获取该类上的修饰符
getName() 获取类名称
getSimpleName() 获取简单类名称
getGenericSuperclass() 获取直属超类
getGenericInterfaces() 获取直属实现的接口
newInstance() 根据构造函数创建一个实例
package org.example;
import java.lang.reflect.Modifier;
public class Classfangshi {
public static void main(String[] args) throws
ClassNotFoundException {
Class clazz =
Class.forName("org.example.reflectinfo.UserInfo");
// 获取该类所在包路径
Package aPackage = clazz.getPackage();
System.out.println("getPackage运行结果:" + aPackage);
// 获取类上的修饰符
int modifiers = clazz.getModifiers();
String modifier = Modifier.toString(modifiers);
System.out.println("getModifiers运行结果:" + modifier);
// 获取类名称
String name = clazz.getName();
System.out.println("getName运行结果:" + name);
// 获取简单类名
String simpleName = clazz.getSimpleName();
System.out.println("getSimpleName运行结果:" + simpleName);
}
}
2、java.lang.reflect.Field
方法名 释义
getField("xxx") 获取目标类中声明为 public 的属性
getFields() 获取目标类中所有声明为 public 的属性
getDeclaredField("xxx") 获取目标类中声明的属性
getDeclaredFields() 获取目标类中所有声明的属性
package org.example;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class Fieldfangshi {
public static void main(String[] args) throws Exception {
Class<?> clazz =
Class.forName("org.example.reflectinfo.UserInfo");
// 获取一个该类或父类中声明为 public 的属性
Field field1 = clazz.getField("age");
System.out.println("getField运行结果:" + field1);
// 获取该类及父类中所有声明为 public 的属性
Field[] fieldArray1 = clazz.getFields();
for (Field field : fieldArray1) {
System.out.println("getFields运行结果:" + field);
}
// 获取一个该类中声明的属性
Field field2 = clazz.getDeclaredField("name");
System.out.println("getDeclaredField运行结果:" + field2);
// 获取某个属性的修饰符(该示例为获取上面name属性的修饰符)
String modifier = Modifier.toString(field2.getModifiers());
System.out.println("getModifiers运行结果: " + modifier);
// 获取该类中所有声明的属性
Field[] fieldArray2 = clazz.getDeclaredFields();
for (Field field : fieldArray2) {
System.out.println("getDeclaredFields运行结果:" + field);
}
}
}
3、java.lang.reflect.Method
方法名 释义
getMethod("setAge", String.class) 获取目标类及父类中声明为 public 的方法,需要指定 方法的入参类型
getMethods() 获取该类及父类中所有声明为 public 的方法
getDeclaredMethod() 获取一个在该类中声明的方法
getDeclaredMethods() 获取所有在该类中声明的方法
getParameters() 获取所有传参
package org.example;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
public class Methodfangshi {
public static void main(String[] args) throws Exception {
Class<?> clazz =
Class.forName("org.example.reflectinfo.UserInfo");
// 获取一个该类及父类中声明为 public 的方法,需要指定方法的入参类型
Method method = clazz.getMethod("setName", String.class);
System.out.println("01-getMethod运行结果:" + method);
// 获取所有入参
Parameter[] parameters = method.getParameters();
for (Parameter temp : parameters) {
System.out.println("getParameters运行结果 " + temp);
}
// 获取该类及父类中所有声明为 public 的方法
Method[] methods = clazz.getMethods();
for (Method temp : methods) {
System.out.println("02-getMethods运行结果:" + temp);
}
// 获取一个在该类中声明的方法
Method declaredMethod = clazz.getDeclaredMethod("getName");
System.out.println("03-getDeclaredMethod运行结果:" +
declaredMethod);
// 获取所有在该类中声明的方法
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method temp : declaredMethods) {
System.out.println("04-getDeclaredMethods运行结果:" + temp);
}
}
}
4、java.lang.reflect.Modifier
方法名 释义
getModifiers() 获取类的修饰符值
getDeclaredField("username").getModifiers() 获取属性的修饰符值
package org.example;
import java.lang.reflect.Modifier;
public class Modifierfangshi {
public static void main(String[] args) throws Exception {
Class<?> clazz =
Class.forName("org.example.reflectinfo.UserInfo");
// 获取类的修饰符值
int modifiers1 = clazz.getModifiers();
System.out.println("获取类的修饰符值getModifiers运行结果:" +
modifiers1);
// 获取属性的修饰符值
int modifiers2 = clazz.getDeclaredField("name").getModifiers();
System.out.println("获取属性的修饰符值getModifiers运行结果:" +
modifiers2);
// 获取方法的修饰符值
int modifiers4 = clazz.getDeclaredMethod("setName",
String.class).getModifiers();
System.out.println("获取方法的修饰符值getModifiers运行结果:" +
modifiers4);
// 根据修饰符值,获取修饰符标志的字符串
String modifier = Modifier.toString(modifiers1);
System.out.println("获取类的修饰符值的字符串结果:" + modifier);
System.out.println("获取属性的修饰符值字符串结果:" +
Modifier.toString(modifiers2));
}
}
5、java.lang.reflect.Constructor
方法名 释义
getConstructor() 获取一个声明为 public 构造函数实例
getConstructors() 获取所有声明为 public 构造函数实例
getDeclaredConstructor() 获取一个声明的构造函数实例
getDeclaredConstructors() 获取所有声明的构造函数实例
package org.example;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Constructorfangshi {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> clazz = Class.forName("org.example.reflectinfo.UserInfo");
// 获取一个声明为 public 构造函数实例
Constructor<?> constructor1 =
clazz.getConstructor(String.class, int.class);
System.out.println("1-getConstructor运行结果:" + constructor1);
// 根据构造函数创建一个实例
Object c1 = constructor1.newInstance("power7089", 18);
System.out.println("2-newInstance运行结果: " + c1);
// 获取所有声明为 public 构造函数实例
Constructor<?>[] constructorArray1 = clazz.getConstructors();
for (Constructor<?> constructor : constructorArray1) {
System.out.println("3-getConstructors运行结果:" +
constructor);
}
Constructor<?> constructor2 =
clazz.getDeclaredConstructor(String.class);
System.out.println("4-getDeclaredConstructor运行结果:" +
constructor2);
// 将构造函数的可访问标志设为 true 后,可以通过私有构造函数创建实例
constructor2.setAccessible(true);
Object o2 = constructor2.newInstance("Power7089666");
System.out.println("5-newInstance运行结果:" + o2);
// 获取所有声明的构造函数实例
Constructor<?>[] constructorArray2 =
clazz.getDeclaredConstructors();
for (Constructor<?> constructor : constructorArray2) {
System.out.println("6-getDeclaredConstructors运行结果:" +
constructor);
}
// 获取一个声明的构造函数实例
}
}
6、java.lang.reflect.Parameter
方法名 释义
getParameters() 获取构造函数/方法的参数
7、java.lang.reflect.AccessibleObject
是 Field 、 Method 和 Constructor 类的超类。该类提供了对类、方法、构造函数 的访问控制检查的能力(如:私有方法只允许当前类访问)。
该访问检查在设置/获取属性、调用方法、创建/初始化类的实例时执行。
setAccessible() 将可访问标志设为 true (默认为 false ),会关闭访问检 查。这样即使是私有的属性、方法或构造函数,也可以访问。
常用方法:
1、getMethod()
getMethod()方法获取的是当前类中所有公共(public)方法。包括从父类里继承来的方
法。
2、getDeclaredMethod()
getDeclaredMethod()系列方法获取的是当前类中“声明”的方法,包括private,protected
和public,不包含从父类继承来的方法。
3、getConstructor()
getConstructor()方法获取的是当前类声明为公共(public)构造函数实例。
// 获取一个声明的构造函数实例
Constructor constructor2 =clazz.getDeclaredConstructor(String.class);
System.out.println("4-getDeclaredConstructor运行结果:" + constructor2);
// 将构造函数的可访问标志设为 true 后,可以通过私有构造函数创建实例
constructor2.setAccessible(true);
Object o2 = constructor2.newInstance("Power7089666");
System.out.println("5-newInstance运行结果:" + o2);
4、getDeclaredConstructor()
getDeclaredConstructor() 方法获取的是当前类声明的构造函数实例,包括private, protected和public。
5、setAccessible()
在获取到私有方法或构造方法后,使用 setAccessible(true); ,改变其作用域,这 样及时私有的属性,方法,构造函数也都可以访问调用了。
6、newInstance()
将获取到的对象实例化。调用的是这个类的无参构造函数。
使用 newInstance 不成功的话可能是因为:①、你使用的类没有无参构造函数,②、你 使用的类构造函数是私有的。
7、invoke()
调用包装在当前Method对象中的方法。
通过反射执行命令Java.lang.Runtime
方式一:通过getMethod
package org.example;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?> clazz = Class.forName("java.lang.Runtime");
//获取该类中申明的公有方法exec并且传入执行的参数的类型
Method execmethod = clazz.getMethod("exec", String.class);
//获取该类中申明的公有方法getRuntime
Method getRuntimethod = clazz.getMethod("getRuntime");
//传入clazz执行getRuntimemethod
Object runtime = getRuntimethod.invoke(clazz);
//执行execmethod有一种反过来使用的感觉,将clazz当做参数传入
execmethod.invoke(runtime, "calc.exe");
//简化的写法
Class<?> clazz2 = Class.forName("java.lang.Runtime");
clazz2.getMethod("exec", String.class).invoke(clazz2.getMethod("getRuntime").invoke(clazz2), "calc.exe");
}
}
方式二:通过getDeclaredConstructor
如果方法或构造函数是私有的,我们可以使用 getDeclaredMethod 或 getDeclaredConstructor 来获取执行。
也就是通过setAccessible(true)
package org.example;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class RuntimeGetDeclaredConstructor {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> clazz = Class.forName("java.lang.Runtime");
//获取构造函数
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor();
//设置改变作用域,可以调用它的私有构造函数
declaredConstructor.setAccessible(true);
//调用exec方法传入参数
Method exec1 = clazz.getMethod("exec", String.class);
//执行invoke执行命令
exec1.invoke(declaredConstructor.newInstance(), "calc.exe");
}
}
方法三:通过反射之java.lang.ProcessBuilder执行命令
java.lang.ProcessBuilder有两个构造函数,构造函数也是支持重载的。
ProcessBuilder(List command)
ProcessBuilder(String... command)
package org.example;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;
public class ProcessBuilderGetConstructor {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
//写法一
Class<?> clazz = Class.forName("java.lang.ProcessBuilder");
Object o = clazz.getConstructor(List.class).newInstance(Arrays.asList("calc.exe"));
clazz.getMethod("start").invoke(o,null);
//写法二
Class<?> clazz2 = Class.forName("java.lang.ProcessBuilder");
((ProcessBuilder)clazz.getConstructor(List.class).newInstance(Arrays.asList("calc.exe"))).start();
//写法三
Class<?> clazz3 = Class.forName("java.lang.ProcessBuilder");
clazz3.getMethod("start").invoke(clazz3.getConstructor(List.class).newInstance(Arrays.asList("calc.exe")));
}
}
动态代理
代理模式
大概了解了是个什么东西
确实用代理很形象,比如一个接口是电影服务,真实对象就是播放电影,代理对象内嵌了真实对象就是电影院,电影院最终会播放电影,同时他还做了很多其他的事情。
动态代理
静态代理需要进行的代理越多写的代理类也就越多于是会产生大量的冗余,为了解决这个问题就产生了动态代理的思维
1、jdk代理
动态代理是在代码运行的时候动态的加载