首頁  >  文章  >  Java  >  實例詳解Java反序列化之反射機制

實例詳解Java反序列化之反射機制

WBOY
WBOY轉載
2022-03-03 17:45:151803瀏覽

本篇文章為大家帶來了關於java的相關知識,其中主要介紹了java反射機制的相關問題,動態獲取程式資訊以及動態呼叫物件的功能稱為Java語言的反射機制,希望對大家有幫助。

實例詳解Java反序列化之反射機制

推薦學習:《java教學

#每次聽到大佬在講或看論壇等一些方式學java反序列化漏洞時,都會有一個詞叫做反射機制,大佬順勢藉著這個詞,就給你造出一個payload,對於剛學java反序列化的我們,可能有點會懵圈,反正我是懵了,所以就趕緊學了一波,不然和大佬差距越來越大。所以這篇文章主要講述java反射機制

Java反射機制

Java的反射(reflection)機制是指在程式的運作狀態中,可以建構任一個類別的對象,可以了解任意一個物件所屬的類,可以了解任意一個類別的成員變數和方法,可以呼叫任意一個物件的屬性和方法。這種動態取得程式資訊以及動態呼叫物件的功能稱為Java語言的反射機制。反射被視為動態語言的關鍵。

我不太擅長文字表達,還是上圖操作把

不用反射機制的例子

//定义一个animals接口interface animals {
    public abstract void print();}//定义类来实现animals接口的抽象方法class Dog implements animals {
    public void print() {
        System.out.println("Dog");
    }}class Cat implements animals {
    public void print() {
        System.out.println("Cat");
    }}// 构造一个zoo类// 之后如果我们在添加其他的实例的时候只需要修改zoo类class zoo {

    public static animals getInstance(String animalsName) {
        animals a = null;
        if ("Dog".equals(animalsName)) {
            a = new Dog();
        }
        if ("Cat".equals(animalsName)) {
            a = new Cat();
        }
        return a;
    }}public class reflection {
    public static void main(String[] args) {
        //借助zoo类寻找对应的类来实现接口
        animals a=zoo.getInstance("Cat");
        if(a!=null)
            a.print();
    }}

這時候加入動物,只需要

  • #新增類別
  • 修改zoo
  • 修改main函數的動物類別

#把上面修改為反射機制

//定义一个animals接口interface animals {
    public abstract void print();}//定义类来实现animals接口的抽象方法class Dog implements animals {
    public void print() {
        System.out.println("Dog");
    }}class Cat implements animals {
    public void print() {
        System.out.println("Cat");
    }}// 构造一个zoo类// 之后如果我们在添加其他的实例的时候只需要修改zoo类class zoo {

    public static animals getInstance(String className) {
        animals a = null;
        try {
            //借助Class.forName寻找类名,并用newInstance实例化类似于new
            a = (animals) Class.forName(className).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return a;
    }}public class reflection {
    public static void main(String[] args) {
        //借助zoo类寻找对应的类来实现接口(classname为当前包名加类名)
        animals a = zoo.getInstance("com.cc1.Dog");
        if (a != null)
            a.print();
    }}

這時候加入動物只需要

  • 加入類別
  • 修改main函數的動物類別

#省了一步,傳入類別名稱可控,發現好像是存在的類別都可以調

反射機制方法

我們用的最多的可能是

  • forName(呼叫類別)
  • getMethod(呼叫類別下方法)
  • invoke(執行)
  • newInstance(實例化物件)
Class.forName(className).getMethod(methodName).invoke(Class.forName(className).newInstance());

下面我們用反射機制來彈出電腦(calc)或記事本( notepad)

由於彈出電腦有點多這次我就彈記事本把,總而言之,能彈出來就很美妙

Runtime.getRuntime().exec("notepad");

實例詳解Java反序列化之反射機制
我們看下getRuntime函數
實例詳解Java反序列化之反射機制
得知,函數是Runtime類別取得物件的方式,個人感覺是每用一次就調一次比較麻煩,為了不呼叫一次建立一個物件所以封裝成了函數

類別物件取得方式

  • Class.forName(類別名稱取得)
  • zoo.class(已經載入過的類別)
  • obj .class(實例)
    實例詳解Java反序列化之反射機制

類初始化

#修改zoo類,增加初始區塊、靜態初始區塊、和建構子

class zoo {
    //初始块
    {
        System.out.println("1  " + this.getClass());
    }

    //静态初始块
    static {
        System.out.println("2  " + zoo.class);
    }

    public zoo() {
        System.out.println("3  " + this.getClass());
    }

    public static animals getInstance(String className) {
        animals a = null;
        try {
            //借助Class.forName寻找类名,并用newInstance实例化类似于new
            a = (animals) Class.forName(className).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return a;
    }}

類別初始化執行順序:靜態初始區塊
實例詳解Java反序列化之反射機制
#類別實例化執行順序:靜態初始區塊- > 初始區塊- > 建構子
實例詳解Java反序列化之反射機制
由此得知,類別初始化和類別實例化不一樣

接下來增加zoo1類別繼承zoo類別

class zoo1 extends zoo{
    //初始块
    {
        System.out.println("11  " + this.getClass());
    }

    //静态初始块
    static {
        System.out.println("12  " + zoo.class);
    }

    public zoo1() {
        System.out.println("13  " + this.getClass());
    }}

子類別初始化順序:父類別靜態初始化區塊- > 子類別靜態初始化區塊
實例詳解Java反序列化之反射機制
子類別實例化順序:父類靜態初始化塊- > 子類靜態初始化塊- > 父類初始化塊- > 父類構造函數- > 子類初始化塊- >子類構造函數
實例詳解Java反序列化之反射機制
#以上可以得知,使用Class.forName時,且類別靜態初始化區塊可控,可以執行任意程式碼

#呼叫內部類別

Class.forName(“java.lang.Runtime”)來取得類別(java.lang.Runtime是Runtime類別的完整路徑)

getMethod



################################# #####getMethod 的作用是透過反射來取得類別的某個特定的公有方法。 ### java支援類別重載,但不能只透過一個函數名稱來決定一個函數,所以在呼叫getMethod時,需要傳給它方法的參數型別清單### Class.forName(“java.lang.Runtime”) .getMethod(“exec”, String.class)############invoke######

静态和动态方法的区别
實例詳解Java反序列化之反射機制

invoke方法在getMethod类下,作用时传递参数,执行方法
public Object invoke(Object obj, Object… args)
第一个参数是getMethod获取的方法的类对象(如果方法是静态方法则传类)
获取exec函数的类对象
Class.forName(“java.lang.Runtime”).getMethod(“getRuntime”).invoke(Class.forName(“java.lang.Runtime”))
由于getRuntime是静态方法,所以传类
invoke(Class.forName(“java.lang.Runtime”).getMethod(“getRuntime”).invoke(Class.forName(“java.lang.Runtime”)),“calc.exe”)

最后我们合并一下

Class.forName("java.lang.Runtime").
                getMethod("exec", String.class).
                invoke(Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime")), "notepad");

實例詳解Java反序列化之反射機制

指定构造方法生成实例

String str="notepad";ProcessBuilder pb = new ProcessBuilder(str);pb.start();

getConsturctor(函数可以选定指定接口格式的构造函数(由于构造函数也可以根据参数来进行重载)
选定后我们可以通过newInstance(),并传入构造函数的参数执行构造函数

ProcessBuilder类有两个构造函数

  • public ProcessBuilder(String… command)(String…变长的字符串数组String[].class)
  • public ProcessBuilder(List command)

分别使用构造方法

  • Class.forName(“java.lang.ProcessBuilder”).getConstructor(String[].class).newInstance(new String[][]{{“notepad”}})
  • Class.forName(“java.lang.ProcessBuilder”).getConstructor(List.class).newInstance(Arrays.asList(“notepad”))

执行完构造方法实例后,在进行强制转化使用start函数即可

( (ProcessBuilder) Class.forName(“java.lang.ProcessBuilder”).getConstructor(List.class).newInstance(Arrays.asList(“notepad”))).start();

实际中,肯定用不了,哪有这么好的事,还是接着反射把

Class.forName(“java.lang.ProcessBuilder”).getMethod(“start”).invoke(clazz.getConstructor(List.class).newInstance(Arrays.asList(“notepad”)));
實例詳解Java反序列化之反射機制
这里可能有人会好奇我写的里那的另一个构造函数,String…command这个传参为什么用new String[][]{{“notepad”}},不应该是new String[]{“notepad”},现在用应该的

((ProcessBuilder) Class.forName(“java.lang.ProcessBuilder”).getConstructor(String[].class).newInstance(new String[]{“notepad”})).start();

在这行打断点调试
實例詳解Java反序列化之反射機制
我们传的是一个字符串数组到了实例化的时候变成了一个字符串,再看看另一个构造函数(List)

( (ProcessBuilder) Class.forName(“java.lang.ProcessBuilder”).getConstructor(List.class).newInstance(Arrays.asList(“notepad”))).start();

依旧还是这行打断点

實例詳解Java反序列化之反射機制
由此可知,List传入时会被当作Object的第一项,而String[]会被当做Object,所以多加一层[]{}

执行私有方法

通过函数getDeclaredConstructor获取私有方法,再利用setAccessible(true)打破私有方法限制

Class cls = Class.forName("java.lang.Runtime"); 
Constructor m = cls.getDeclaredConstructor();
 m.setAccessible(true); 
 cls.getMethod("exec", String.class).invoke(m.newInstance(), "notepad");

 推荐学习:《java视频教程

以上是實例詳解Java反序列化之反射機制的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除