前言
首發:https://www.sec-in.com/article/307
衆所周知,Java目前影響最大的是反序列化漏洞,換一句話說Java安全是從反序列化漏洞開始,但反序列化漏洞又可以基於反射,這次筆者帶你走進Java安全的大門。
Java反序列化的payload大多與反射機制密切相關,但僅僅是因爲這個嗎?答案肯定是片面的。反射作爲大多數編程語言裏必不可缺的組成部分,對象可以通過反射獲取其他的類,類可以通過反射拿到所有的方法(包括私有方法),獲取到方法可以調用。一句話,反射給Java等類似的靜態語言賦予了“靈魂”
。
ps: 本文實驗代碼都上傳JavaLearnVulnerability項目,爲了讓更多人知道,麻煩動動小手star一下。
反射基礎
Java反射操作的對象是java.lang.Class
對象,如果想要使用Java反射,首先得獲取Class對象。下面我們看一段代碼。
public static void main(String[] args) throws Exception {
Class cls = Class.forName(className);
cls.getMethod(methodName).invoke(cls.newInstance());
}
示例代碼中,筆者演示幾個比較重要的方法:
- 獲取類對象的方法
forName
- 從獲取類對象中獲取方法
getMethod
- 執行得到獲取的方法的方法
invoke
- 實例化對象
newInstance
ps:當然反射不可能僅僅只是這些方法,下面中筆者有提及其他的方法,當然不可能是全部都一一道來,正所謂授之與魚,不如授之於漁
。更多方法建議大家去看JDK文檔,在線的文檔百度一搜就有。
類源碼
首先筆者構造了兩個類students
和classdemo1
。
實體類students
源碼:
public class Students {
private Integer age = 18;
private String name = "samny";
private Integer CardId = 332323223;
private Integer id = 10012;
private String hello = "hello world";
public Students(Integer age, String name, Integer cardId, Integer id, String hello) {
this.age = age;
this.name = name;
this.CardId = cardId;
this.id = id;
this.hello = hello;
}
@Override
public String toString() {
return "Students{" +
"age=" + age +
", name='" + name + '\'' +
", CardId=" + CardId +
", id=" + id +
", ags='" + hello + '\'' +
'}';
}
public Students() {
}
public Integer getAge() {
return age;
}
// 以下省略set,get以及toString方法。
}
實體類classdemo1
的源碼:
public class classdemo1 {
private String name;
public void print(){
System.out.println("parameterlessMethod:hello");
}
public void print2(String name){
System.out.println( "parameterMethod: hello" + name);
}
}
反射方法獲取Students類。
實例化所需要的類集合 Class[] classes = new Class[]{Integer.class,String.class,Integer.class,Integer.class,String.class};
通過構造器構造類Constructor constructor = cls.getDeclaredConstructor(classes);
通過調用有參構造器實例化類Object str = constructor.newInstance(18, “samny”, 32326663, 10021, “hello!!”);
public static void reflectmethod1(){
try {
// 獲取對象
Class cls = Class.forName("samny.reflection.Students");
// // 獲取構造器方法
Class[] classes = new Class[]{Integer.class,String.class,
Integer.class,Integer.class,String.class};
Constructor constructor = cls.getDeclaredConstructor(classes);
Object str = constructor.newInstance(18, "samny", 32326663, 10021, "hello!!");
System.out.println(str);
// 或者下面這樣子輸出
// System.out.println(constructor.newInstance(18, "samny", 32326663, 10021, "hello!!"));
} catch (ClassNotFoundException | NoSuchMethodException |
IllegalAccessException | InstantiationException | InvocationTargetException e) {
e.printStackTrace();
}
}
常規開發人員實例化類構造類demo
public static void Sts(){
Students students = new Students();
students.setAge(18);
students.setName("samny");
students.setCardId(32336666);
students.setId(1001);
students.setAgs("helloooo!!");
System.out.println(students.toString());
}
對比一下效果是沒有任何的區別,但反射操作只需要知道類名就可以完全操作類甚至是類中的私有方法之後文章會詳細說明,本文不做說明。
反射調用類中的無參和有參方法。
之前說過的知識就不在說明
開頭就說了,invoke是執行獲取類得到方法的方法
但調用有參方法和無參方法有點細微的差別
getMethod
方法的第一個參數是類方法名稱,第二個是類對象。這裏先留個疑問,如果是多個類對象呢?
invoke
函數的第一個參數是實例化
的類對象,第二個是參數值
常規方法的調用,直接Class.method()
即可
public static void reflectmethod2(){
try {
Class cls = Class.forName("samny.reflection.classdemo1");
// 無參方法調用
Object ob = cls.newInstance();
// Method mt = cls.getMethod("print"); 有沒有null均可
Method mt = cls.getMethod("print",null);
mt.invoke(ob,null);
// 有參方法調用
Method mt2 = cls.getMethod("print2", String.class);
mt2.invoke(ob,"world");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
參考
P神-Java安全漫談-反射篇