前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JAVA 反射机制

JAVA 反射机制

作者头像
用户9691112
发布2023-05-18 13:58:54
3440
发布2023-05-18 13:58:54
举报
文章被收录于专栏:quan9i的安全笔记

前言

拖了很久,才开始学习JAVA 安全相关,最近很是摆烂,希望能够快速进入学习状态,冲冲冲!

同时说明一下,本文大多参考自Y4tacker大师傅的JavaSec,因此例子大多是引用的,大师傅们勿喷。

定义

什么是JAVA 反射。接下来引用一下Y4tacker大师傅的话

Java反射机制是在运行状态时,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制。

简单的说,就是通过java反射机制,我们可以获取任意类的成员方法、变量这些,同时,可以创建JAVA类实例,调用任意类方法。

还有就是想说一下这里为什么称为反射,在JAVA中,我们可以这样理解:通过new一个对象而调用其中方法的,称为”正射”,而不通过new一个对象取得其中方法的,称为”反射”。

学前需知

反射的动态机制

反射机制是java实现动态语言的关键,也就是通过反射机制实现类动态加载

1、静态加载:编译时加载相关的类,如果没有就报错,依赖性过强

2、动态加载:运行时加载需要的类,如果运行时不使用该类,即使不存在也不会报错,降低了依赖性。

示例如下

代码语言:javascript
复制
import java.util.*;
import java.lang.reflect.*;
public class Main{
    public static void main(String[] args) throws Exception{
        Scanner Scanner = new Scanner(System.in);
        System.out.println("请输入key");
        String key = Scanner.next();
        switch (key){
            case "1":
                Dog dog = new dog();
                dog.cry();
                break;
            case "2":
                Class cls = Class.forName("Person");
                Object o = cls.newInstance();
                Method m = o.getMethod("hi");
                m.invoke(o);
                break;
        }
    }
}

此时去运行javac Main.java会发现无法编译成功,因为dog类已经是静态加载机制,这个需要提前加载,而这里没有,故报错,而如果有Dog类,即使没有Person类,编译也不会报错,会正常生成class文件,但当运行时会报错。

常用方法

在JAVA的Lang包中存在一个名为Class的静态类,在JAVA程序编译加载某一个类时,JAVA.Lang.Class就会实例化出一个对象,这个对象存储了类的所有信息。因此,我们可以通过这个Class对象拿到这个类的信息。接下来我们先了解一些方法。

forName()

forName方法可以获取类中的所有属性包括类名。

示例如下

代码语言:javascript
复制
Class.forName(classname): 获取classname类中的所有属性
Class qwq = Class.forName("java.lang.Runtime");
//获取Runtime类中的所有属性,并赋值给clazz类

newInstance()

书接上文,我们在示例中将Runtime类中的所有属性赋值给了qwq类,接下来我们想要调用qwq类中的某个方法的话,需要三步

代码语言:javascript
复制
1、对类进行实例化(实例化对象)
2、获取方法
3、执行方法

接下来分步进行。

第一步,是实例化对象,此时也就引入了我们的**newInstance()**方法,对该方法进行简单解释,此方法可以实例化对象,并触发类的构造方法。

所以此时的话对象就创建完成了,接下来就是获取方法了。

getMethod()

我们在获取完对象后,对对象中的Public方法获取的方式是采用*getMethod()*函数,这个函数的具体参数如下

代码语言:javascript
复制
getMethod("方法名,参数类型(如String.class)")

此时就可以获取到方法了,接下来我们只需要进行执行方法即可,此时也就引入了我们的最后一个函数

involve()

involve可以执行方法,如果是一个普通方法,则involve的第一个参数为该方法所在的对象,如果是静态方法则第一个参数是Null或者该方法所在的类,第二个参数为要执行方法的参数

上述只是一些常见的方法,并不是全部的,比如获取类的方法,不止有forName函数,接下来开始进一步讲解。

获取类

JAVA反射操作的是java.lang.Class对象,上文中提到过Class对象,在类被编译加载时,它会存储类的相关信息,所以我们这里需要首先获取到类。上述说了一个方法,forName方法,它并不是唯一的方法,还有两种常用的方法,具体如下

代码语言:javascript
复制
1、getClass(): 当上下文中存在某个类的实例化对象,我们可以通getClass()方法直接获取它的类,示例如下:
String s = "qwq";
Class cla = s.getclass();

2、类名.class: 如果已经加载了一个类,我们知道类中的一个静态变量或是其他,此时便可直接获取,示例如下:
Class cla = String.class

对于forName方法,获取类的方式如下

代码语言:javascript
复制
3、Class.forName("完整类名"),当我们知道一个类的完整类名时,可以通过静态方法Class.forName()获取
Class cla = Class.forName("java.lang.String") 
//获取java.lang.String的全部方法 

获取字段

上文中说了获取类的方法,接下来说一下获取字段的方式,获取字段的话,主要用到以下两个函数

代码语言:javascript
复制
getFields():获得某个类的所有的公共(public)的字段,包括父类中的字段。
getDeclaredFields():获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段。

这里借用一下Y4大师傅的示例。

代码语言:javascript
复制
import java.lang.reflect.Field;

public class test {
    public static void main(String[] args) throws Exception {
        Class stiClass = StuInfo.class;
        // 获取public字段"age":
        System.out.println(stiClass.getField("age"));
        // 获取继承的public字段"name":
        System.out.println(stiClass.getField("name"));
        // 获取private字段"money":
        System.out.println(stiClass.getDeclaredField("money"));
        // 获得值,name.get里面参数需要该类对象,而不是.class
        Field name = stiClass.getField("name");
        System.out.println(name.get(stiClass.newInstance()));
        // 设置值
        StuInfo stuInfo = new StuInfo();
        Field money = stiClass.getDeclaredField("money");
        money.setAccessible(true);
        money.set(stuInfo,2333333);
        System.out.println(stuInfo);




    }
}

class StuInfo extends PersonInfo{
    public int age;
    private int money;

    @Override
    public String toString() {
        return "StuInfo{" +
            "name=" + name +
            ", money=" + money +
            '}';
    }
}

class PersonInfo{
    public String name = "quan9i";
}

这里需要说明的是**money.setAccessible(true);*这个语句,它是取消了money*变量的特权模式,本来money是private的,不可被更改,访问这些,而当有这个语句后,就变的与public相同了(小白个人观点,可能有误)。

获取方法

这里涉及了四个函数,具体如下所示

代码语言:javascript
复制
1、Method getMethod(name, Class...):获取某个public的方法(包括父类)
2、Method getDeclaredMethod(name, Class...):获取当前类的某个方法(不包括父类)
3、Method[] getMethods():获取所有public的方法(包括父类)
4、Method[] getDeclaredMethods():获取当前类的所有方法(不包括父类)

示例如下

代码语言:javascript
复制
String name = "quan9i";
Method substring = String.class.getMethod("substring", int.class);
System.out.println(substring.invoke(name,3));

如果调用的方法是静态方法。那么invoke方法传入的第一个参数永远为null

代码语言:javascript
复制
// 获取Integer.parseInt(String)方法,参数为String:
Method m = Integer.class.getMethod("parseInt", String.class);
// 调用该静态方法并获取结果:
Integer n = (Integer) m.invoke(null, "23333");
System.out.println(n);

反射执行

命令

java.lang.Runtime因为有一个exec方法可以执行本地命令,所以在很多的payload中我们都能看到反射调用Runtime类来执行本地系统命令,通过学习如何反射Runtime类也能让我们理解反射的一些基础用法。

执行的Payload如下

代码语言:javascript
复制
Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime")),"calc");
image-20230514105718783
image-20230514105718783

这个的话我们其实可以把他进行拆分,这句话拆分为五段,如下所示

代码语言:javascript
复制
Class clazz = Class.forname("java.lang.Runtime");
Method execMethod = clazz.getMethod("exec", String.class);
Method getRuntime = clazz.getMethod("getRuntime");
Object currentRuntime = getRuntime.invoke(clazz);
execMethod.invoke(currentRuntime, "calc.exe");

对其进行简单讲解

代码语言:javascript
复制
1、首先通过反射获取到Runtime类
2、通过反射获取到Runtime类的exec方法
3、通过反射获取到Runtime类的getRuntime方法
4、创建Runtime对象并调用exec方法
5、调用exec方法并执行命令exec "calc.exe"

一些其他引用反射的方式

  • 我们刚才说到可以通过forname拿到了一个类,并且继续利用反射或实例化调用其中的方法,如果一个类没有无参构造方法或者也没有类似单例模式里的静态方法,那我们应该怎样通过反射实例化该类呢?
  • 如果一个方法或构造方法是私有方法,我们应该怎么去执行它呢?

ProcessBuilder

第一个问题,我们可以用一个新的反射方法getConstructor

代码语言:javascript
复制
getConstructor(Class...):获取某个public的Constructor;
getDeclaredConstructor(Class...):获取某个Constructor;
getConstructors():获取所有public的Constructor;
getDeclaredConstructors():获取所有Constructor。

和getMethod类似,getConstructor接收的参数是构造函数的的列表类型,因为构造函数也支持重载,所以要用参数列表类型才能唯一确定一个构造函数

比如我们常用的另一种执行命令的方式ProcessBuilder,我们使用反射来获取其构造函数,然后 调用start()来执行命令

接下来简单介绍一下ProcessBuilder。

ProcessBuilder用于创建操作系统进程,它提供一种启动和管理进程(也就是应用程序)的方法,我们可以通过实例化这个类并且通过反射调用其中的start方法来开启一个子进程 。当getRuntime被禁用时,可以用ProcessBuilder来执行命令。它有两种构造函数

代码语言:javascript
复制
public ProcessBuilder(List<String> command)
public ProcessBuilder(String... commang)

接下来看这个执行命令的Payload

代码语言:javascript
复制
Class clazz = Class.forName("java.lang.ProcessBuilder");
 ((ProcessBuilder)clazz.getConstructor(List.class).newInstance(Arrays.asList("calc.exe"))).start();

执行过程如下

代码语言:javascript
复制
1、首先利用反射获取ProcessBuilder类;
2、获取clazz(ProcessBuilder)形参列表为List<String> command的构造函数;
3、将获取到的构造函数利用newInstance进行实例化,调用构造函数;
4、对构造函数传入的参数为 calc.exe,并且用Arrays.asList方法将要执行的命令转为List类型;
5、返回List类型的command;
image-20230514162052363
image-20230514162052363

如何执行私有方法

使用getDeclared系列方法,函数介绍如下

代码语言:javascript
复制
getConstructor(Class...):获取某个public的Constructor;
getDeclaredConstructor(Class...):获取某个Constructor;
getConstructors():获取所有public的Constructor;
getDeclaredConstructors():获取所有Constructor。
  • getMethod系列方法获取的是当前类中所有公共方法,包括从父类继承的方法;
  • getDeclaredMethod系列方法获取的是当前类中“声明”的方法,是实写在这个类里的,包括私有的方法,但从父类里继承来的就不包含了。

举个例子,我们之前提到过Runtime的构造方法是私有的,所以我们要通过Runtime.getRuntime()来获取对象,其实我们也可以直接用getDeclaredConstructor来获取这个私有的构造方法实例化对象,进而执行命令:

代码语言:javascript
复制
Class clazz = Class.forName("java.lang.Runtime");
        Constructor m =clazz.getDeclaredConstructor();
        m.setAccessible(true);
        clazz.getMethod("exec",String.class).invoke(m.newInstance(), "calc.exe");

这里我们在获取到私有方法后,要用setAccessible()方法使这个私有方法可以被访问,其他的就和之前介绍的反射一样了,如果不用setAccessible()方法修改作用域这个方法是仍然不能调用的

image-20230514162816396
image-20230514162816396
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023/05/09 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 定义
  • 学前需知
    • 反射的动态机制
      • 常用方法
        • forName()
        • newInstance()
        • getMethod()
        • involve()
    • 获取类
    • 获取字段
    • 获取方法
    • 反射执行
    • 命令
    • 一些其他引用反射的方式
      • ProcessBuilder
        • 如何执行私有方法
        相关产品与服务
        对象存储
        对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档