JAVA反射学习
2023-5-4 00:23:22 Author: 白帽子(查看原文) 阅读量:59 收藏

STATEMENT

声明

由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雷神众测及文章作者不为此承担任何责任。

雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。

Java正射

在理解java反射可以先了解"JAVA正射"

例子,这是一个person类,有成员变量以及函数:

package 反射;
public class Person { private String name; private int 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; } public void show(){ System.out.println("hello"); }}

我们在编写代码时,当需要使用到某一个类的时候,都会先了解这个类是做什么的。然后实例化这个类,接着用实例化好的对象进行操作,这就是正射。

package 反射;import 反射.Person;public class 正射 {    public static void main(String[] args) {        Person person1 =new Person();        person1.setName("小明");        person1.setAge(18);        System.out.println(person1.getName());        System.out.println(person1.getAge());        Class c = person1.getClass();//Class 类的对象作用是运行时提供或获得某个对象的类型信息。        System.out.println(c);        person1.show();    }}

Java反射

1、基本概念

Java反射机制是在运行状态时,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制。java反射机制给漏洞利用提供了很多便利,我们可以在很多java漏洞的exp中看到它的影子,所以,学习java安全是绕不开它的。

2、Java 反射组成相关的类

反射机制相关操作一般位于java.lang.reflect包中。

而java反射机制组成需要重点注意以下的类:

java.lang.Class:类对象;

java.lang.reflect.Constructor:类的构造器对象;

java.lang.reflect.Field:类的属性对象;

java.lang.reflect.Method:类的方法对象;

反射调用方法

获取类的方法:forName

实例化类对象的方法:newInstance

获取函数的方法:getMethod

执行函数的方法:invoke

3、获取Class 对象

Class 类是描述类的类。

Class 类的对象作用是运行时提供或获得某个对象的类型信息。

获取类的实例有以下的三种方法:

  • 方法一、实例化对象的getClass()方法

如果上下⽂中存在某个类的实例 obj,那么我们可以通过 obj.getClass 来获取它的类。

TestReflection testReflection = new TestReflection();Class class3 = testReflection.getClass();
  • 方法二、 使用类的 .class 方法

如果你已经加载了某个类,只是想获取到它的 java.lang.Class 对象,那么就直接拿它的 class 属性即可。这个⽅法其实不属于反射。

Class class2 = TestReflection.class;
  • 方法三、Class.forName(String className):动态加载类

如果你知道某个类的名字,想获取到这个类,就可以使⽤ forName 来获取

Class class1 = Class.forName("reflection.TestReflection");

我们可以写个简单的示例代码,分别利用这三种方法获取当前类Class对象的当前类名。

package 反射;import 反射.Person;
public class 反射{ public static void main(String[] args) throws Exception{ // 类的 .class 属性 Class c1 = Person.class; System.out.println(c1.getName());
// 实例化对象的 getClass() 方法 Person person = new Person(); Class c2 = person.getClass(); System.out.println(c2.getName());
// Class.forName(String className): 动态加载类 Class c3 = Class.forName("反射.Person"); System.out.println(c3.getName());
}}

我们一般使用第三种通过Class.forName方法去动态加载类。且使用forName就不需要import导入其他类,可以加载我们任意的类。

而使用类.class属性,需要导入类的包,依赖性太强,在大型项目中容易抛出编译错误;

而使用实例化对象的getClass()方法,需要本身创建一个对象,本身就没有了使用反射机制意义。

所以我们在获取class对象中,一般使用Class.forName方法去获取。

Java反射API

反射常用函数:

1、getMethod() getMethod()方法获取的是当前类中所有公共(public)方法。包括从父类里继承来的方
法。

2、getDeclaredMethod() getDeclaredMethod()系列方法获取的是当前类中“声明”的方法,包括private,protected 和public,不包含从父类继承来的方法。

3、getConstructor() getConstructor()方法获取的是当前类声明为公共(public)构造函数实例。

4、getDeclaredConstructor() getDeclaredConstructor() 方法获取的是当前类声明的构造函数实例,包括private, protected和public。

5、setAccessible()在获取到私有方法或构造方法后,使用 setAccessible(true); ,改变其作用域,这样及时私有的属性,方法,构造函数也都可以访问调用了

6、newInstance()将获取到的对象实例化。调用的是这个类的无参构造函数。使用 newInstance 不成功的话可能是因为:①、你使用的类没有无参构造函数,②、你使用的类构造函数是私有的。

7、invoke()调用包装在当前Method对象中的方法。nvoke传参如下图所示

Java反射到命令执行

学习ava反射机制,其实我们更关心如何利用Java反射实现命令执行。下面是Java反射命令执行的几种情况。

  • 案例1:

编写例子1

package 反射;
public class TrainPrint { { System.out.printf("Empty block initial %s\n", this.getClass()); } static { System.out.printf("Static initial %s\n", TrainPrint.class);
try { Runtime rt = Runtime.getRuntime(); String[] commands = {"calc"}; Process pc = rt.exec(commands); pc.waitFor(); } catch (Exception e) { // do nothing }
} public TrainPrint() { System.out.printf("Initial %s\n", this.getClass()); }}
package 反射;
import 反射.TrainPrint;public class exp { public static void main(String[] args) throws Exception { ref("反射.TrainPrint"); }//假设name 参数可控,我们使用runtime来弹计算器: public static void ref(String name) throws Exception { Class.forName(name); }}

在使用forName 的时候,构造函数并不会执⾏,而是执⾏类初始化。他会执行static{}静态块里面的内容,所以当forName参数可控时可以构造恶意类进行攻击:

  • 案例2:

下面是两种通过反射java.lang.Runtime来达到命令执行的方式。

由于java.lang.Runtime类的构造函数是私有的,因此不能直接使用 newInstance() 创建一个实例。那为什么这个类的构造函数会是私有的呢?

这涉及到一个“单例模式”的概念:

单例模式(Singleton)的目的是为了保证在一个进程中,某个类有且仅有一个实例。因为这个类只有一个实例,因此,自然不能让调用方使用new Xyz()来创建实例了。所以,单例的构造方法必须是private,这样就防止了调用方自己创建实例,但是在类的内部,是可以用一个静态字段来引用唯一创建的实例的。举个例子:我们在链接数据库时只有最开始链接一次,而不是用一次链接一次,如果这样的话,资源消耗太大了。因此可以将类的构造函数设为私有,再通过静态方法来获取。

由于java.lang.Runtime使用了单例模式,我们可以通过Runtime.getRuntime()来获取Runtime对象。

有两种方法进行反射命令执行:

方法一:通过getMethod()

解读:

//通过getMethod()方法获exec方法,
//exec()方法有六种调用方式(重载),我们选择最简单的String方式,则getMethod方法我们设定的入参方式为 String.class 。
//然后获取getRuntime方法后,使用invoke执行方法。
//invoke传入的参数Method对象,现转换为Method对象。
//最后在通过invoke方法调用runtime对象执行命令

package 命令执行;
import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;
public class 反射 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { Class clazz; clazz = Class.forName("java.lang.Runtime");
Method execMethod = clazz.getMethod("exec", String.class);
Method getRuntimeMethod = clazz.getMethod("getRuntime");
Object runtime = getRuntimeMethod.invoke(clazz);
execMethod.invoke(runtime, "calc.exe"); }}

方式二:通过getDeclaredConstructor

如果方法或构造函数是私有的,我们可以使用 getDeclaredMethod 或 getDeclaredConstructor 来获取执行。

首先通过Class.forName获取java.lang.Runtime。

接下来通过getDeclaredConstructor获取构造函数。

通过 setAccessible(true) 设置改变作用域,让我们可以调用他的私有构造函数。

调用exec方法,入参设置为 String.class 。

最后使用Invoke执行方法。

安恒信息

杭州亚运会网络安全服务官方合作伙伴

成都大运会网络信息安全类官方赞助商

武汉军运会、北京一带一路峰会

青岛上合峰会、上海进博会

厦门金砖峰会、G20杭州峰会

支撑单位北京奥运会等近百场国家级

重大活动网络安保支撑单位

END

长按识别二维码关注我们


文章来源: http://mp.weixin.qq.com/s?__biz=MzAwMDQwNTE5MA==&mid=2650246672&idx=1&sn=c0f1d39ee66c8e65ca8dfa62a9253fc1&chksm=82ea55b9b59ddcafcf07fd09febec65aa322fb4974da5ca54250478f90ecb27119a7eb151388#rd
如有侵权请联系:admin#unsafe.sh