keac's Bolg.

Java 反射机制

字数统计: 945阅读时长: 3 min
2022/05/23 Share

前言

Java反射机制可以无视类方法和变量去访问权限修饰符,并且可以调用任何类的任何方法,访问并修改成员变量值。也就是说能控制反射的类名、方法名和参数的前提下,如果我们发现了一个java反射调用的漏洞,我们就可以为所欲为了。

反射 Reflection

反射是java的特性之一,在C/C++语言中不存在反射这个概念,有了反射java可以更加灵活调控自身运行时的信息,操作类和对象的内部属性。

反射的用途非常广,在我们使用IDE工具进行开发的时候,我们输入一个对象并且想调用它的属性或者方法的时候,IDE会自动列出它的属性和方法,这些就是通过反射来实现的。再比如JavaBean和JSP之间的调用也是通过反射来实现的。最重要的就是各种通用用途的开发框架,比如Spring框架以及ORM框架,他们都是通过反射机制来实现的。

用途

获取对象类

1
2
3
4
5
6
7
Class.forName("java.lang.Runtime")
Runtime.class
runtime.getClass()
ClassLoader.getSystemClassLoader().loadClass("java.lang.Runtime") // 和forName类似 但是不会执行static代码

Class.forName("[D") // 相当于double[].class
Class.forName("[[Ljava.lang.String")// 相当于 String[][].class

反射调用内部类的时候需要用$

如 com.xxx.test 内有个H的类,调用的时候就需要写成com.xxx.test$H

获取类方法

1
2
3
4
5
6
getDeclaredMethods() //获取所有方法,不包括继承方法
getMethods() //获取所有public方法,包括继承的 Method[]
getMethod("exec", String.class) //获取一个特定的方法,后面的对应参数类型的Class对象 只可以public修饰符
getDeclaredMethod("exec", String.class) //获取所有修饰符的方法,跟getMethod一样
getDeclaredConstructor // 获取类构造方法,可以获取到私有
getConstructor //获取类构造方法

如果构造方法有一个或多个参数的情况下我们应该在获取构造方法时候传入对应的参数类型数组

1
clazz.getDeclaredConstructor(String.class, String.class)

获取到Constructor 后就可以拿来创建实例,有参数需要传入

1
constructor.newInstance("test","test")

反射调用方法

1
getRuntime.invoke(runtime,"whoami") //第二个参数不是必须的

获取成员变量

1
2
3
4
5
6
7
Field fields = xxx.getDeclaredFields //获取所有成员字段,不包括父类的声明字段
getFields //获取所有public字段,包括父类的字段
getDeclaredField //获取单独的成员变量
getField //获取单独的成员变量只限public,包括父类

Object obj =field.get(String.class) //获取变量值
field.set("对象","修改值")

如果修改的值没有权限也可以使用

1
field.setAccessible(true)

如果修改被final关键词修饰(最终方法,无法被子类重写)

1
2
3
4
5
6
7
8
9
10
11
// 反射获取Field类的modifiers
Field modifiers = field.getClass().getDeclaredField("modifiers");

// 设置modifiers修改权限
modifiers.setAccessible(true);

// 修改成员变量的Field对象的modifiers值 ~取反 & 与操作
modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);

// 修改成员变量值
field.set(类实例对象, 修改后的值);

调用Runtime

Java任何一个类都有一个或多个构造方法,如果没有会在类编译的时候自己创建一个无参构造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 获取Runtime类对象
Class runtimeClass1 = Class.forName("java.lang.Runtime");

// 获取构造方法,构造方法是私有的所以需要通过反射去修改权限
Constructor constructor = runtimeClass1.getDeclaredConstructor();
constructor.setAccessible(true);

// 创建Runtime类示例,等价于 Runtime rt = new Runtime();
Object runtimeInstance = constructor.newInstance();

// 获取Runtime的exec(String cmd)方法
Method runtimeMethod = runtimeClass1.getMethod("exec", String.class);

// 调用exec方法,等价于 rt.exec(cmd);
Process process = (Process) runtimeMethod.invoke(runtimeInstance, cmd);

// 获取命令执行结果
InputStream in = process.getInputStream();

// 输出命令执行结果
System.out.println(IOUtils.toString(in, "UTF-8"));
CATALOG
  1. 1. 前言
  2. 2. 反射 Reflection
    1. 2.1. 用途
      1. 2.1.1. 获取对象类
      2. 2.1.2. 获取类方法
      3. 2.1.3. 获取成员变量
    2. 2.2. 调用Runtime