CC1中反射调用的构造

距离上次看java,这又过去将近小半年,必须学起来了

上个说反射机制得其实很模糊,但是四种获取class对象得方式还是很有用得

再贴一下

1
2
3
4
5
6
7
8
9
10
//方法一
Class clazz1 = Class.forName("my.Student");//通过Class类中的静态方法forName,直接获取到一个类的字节码文件对象,此时该类还是源文件阶段,并没有变为字节码文件。包名为 my,类名为 Student
//方法二
Class clazz2 = Student.class; //当类被加载成.class文件时,此时Student.java类变成了Student.class,该类处于字节码阶段
//方法三
Student s=new Student(); //实例化Student对象
Class clazz3 = s.getClass(); //通过该类的实例获取该类的字节码文件对象,该类处于创建对象阶段
// 方法四 :通过类的加载器
ClassLoader classLoader = Student.class.getClassLoader();
Class clazz4 = classLoader.loadClass("Student");

在这里主要想,重新认识一下反射机制

反射机制认知

定义

java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取类的信息以及动态调用对象的方法的功能称为java语言的反射机制Reflection

理解

这就说明:Java程序可以加载一个编译期间完全未知的class,获悉其完整构造,并生成其对象实体、或对其fields设值、或唤起其methods。虽然java并不是动态语言。

java.lang.Class是反射入口。java中一切皆对象,继承自object。每一个对象都有自己的类型,通过getClass()得到一个java.lang.Class对象,(基本类型通过字面值.class获得,也叫类标记,如:int.class)这个Class对象中包含了与类型有关的信息,包括其字段、方法、父类或接口等。

Class对象

Class类的实例表示正在运行的Java应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为Class对象的一个类,所有具有相同元素类型和维数的数组都共享该Class对象。基本的Java类型(boolean、byte、char、short、int、long、floatdouble)和关键字void也表示为Class对象。

Class没有公共构造方法。Class对象是在加载类时由Java虚拟机以及通过调用类加载器中的defineClass方法自动构造的。

对于我们编写的每个类,它们都有一个Class对象。(更恰当地说,是保存在一个完全同名的.class文件中)。在运行期,一旦我们想生成那个类的一个对象,用于执行程序的Java虚拟机(JVM)首先就会检查那个类型的Class对象是否已经载入。若尚未载入,JVM就会查找同名的.class文件,并将其载入。所以Java程序启动时并不是完全载入的,这一点与许多传统语言都不同。一旦那个类型的Class对象进入内存,就用它创建那一类型的所有对象。

反射中的API

我们主要从4个方面认识反射的api

  • 获取类的基本信息:java.lang.Class
  • 获取类的实例:java.lang.Class和java.lang.reflect.Constructor<T>
  • 操作实例的属性:java.lang.reflect.Field
  • 调用实例的方法:java.lang.reflect.Method
  • 要记住一切都是由Class对象开始,java.lang.Class是反射入口。

上面这段话,我是抄的,

因为我认为比较重要得点是

  • java.lang.Class是反射入口
  • 一切皆对象,对象即可调用getMethodinvoke方法(null除外)
  • 还有一点就是,之前得四种获取class的方式都是获取class对象的句柄,并不是java.lang.Class

代码理解

个人目前阶段对此的理解,可能存在错误,更具学习进度,会一一更正

代码案例

1
2
3
4
5
//测试class对象句柄,与class对象
Class runtime = Runtime.class;
System.out.println("class字节码: "+runtime.toString());
Class cl = Runtime.class.getClass();
System.out.println("class对象: "+cl.toString());

输出如下

在这里插入图片描述

可以看到,实际Runtime.class还是Runtime对象,(说是对象,可能不太准确)

而加上getClass之后就成了Class对象了

那这就导致了一个问题,这个问题也是CC1链最终的命令执行的问题

getMethod和invoke

  • 顾名思义,getMethod就是获取对象中的方法
  • invoke就是尝试从对象中获取一个函数指针,然后调用。

    如果这个方法是一个普通方法,那么第一个参数是类对象
    • 如果这个方法是一个静态方法,那么第一个参数是类
    • 这也比较好理解了,我们正常执行方法是 [1].method([2], [3], [4]...)
    • 其实在反射里就是 method.invoke([1], [2], [3], [4]...)

如何通过这两中方式获取Runtime类得实例呢?

第一种

上文说到,object是可以直接调用getMethod

所以这里可以通过getMethod获取到getRuntime方法,然后调用

测试代码

1
2
3
4
Class runtime = Runtime.class;
System.out.println("class字节码: "+runtime.toString());
Object obj = Runtime.class.getMethod("getRuntime",new Class[]{}).invoke(null);
System.out.println("Runtime实例: "+obj.toString());

测试结果

在这里插入图片描述

然后再通过对象实例反射调用exec方法即可以实现命令执行

第二种

这也是cc1的调用方式

上文说:*java.lang.Class是反射的入口*

如何通过一个Class对象,实例化一个Runtime类呢?

上文说:每个数组属于被映射为Class对象的一个类,所有具有相同元素类型和维数的数组都共享该Class对象

  • 首先通过Class对象反射一个getMethod方法
  • 然后通过此方法,调用invoke方法获取指定类的指定的方法
  • 然后获取invoke方法
  • 执行invoke方法获取目标类的实例

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
Class cls = Runtime.class.getClass();
System.out.println("一 : 调用getClass()方法: "+cls.toString());
Method method = cls.getMethod("getMethod", String.class, Class[].class); //获取到反射的getMethod方法的Method对象
System.out.println("二 : getMethod()方法: "+method.toString());
Object a = method.invoke(Runtime.class,"getRuntime",new Class[0]);
System.out.println("三 : invoke()方法: "+a.toString());
Class a1 = a.getClass();
System.out.println("四 : getClass()方法: "+a1.toString());
Method method1 = a1.getMethod("invoke", Object.class, Object[].class);
System.out.println("五 : getMethod()方法: "+method1.toString());
Object b = method1.invoke(a,null,new Object[0]);
System.out.println("六 : invoke方法: "+b.toString());

测试结果

在这里插入图片描述

调用exec

1
2
3
4
5
Class b1 = b.getClass();
System.out.println("七 : getClass()方法: "+b1.toString());
Method method2 =b1.getMethod("exec",String.class);
System.out.println("八 : getMethod()方法: "+method2.toString());
Object c = method2.invoke(b,"calc");

这是一次完整的调用

在这里插入图片描述

这里还有一个点要注意就是,刚开始获取Class对象的类,不一定非要Runtime.class,这个迷惑了好久,这个Runtime到底在链中起了什么作用?没作用,仅仅是获取Class对象

end

这里可以看的出来

这个写法就是cc1的代码执行的位置,一摸一样的调用

在这里插入图片描述

本文主要完成了对代码执行payload的构造,不涉及链的问题