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
长按识别二维码关注我们