c++ 反射_Java代码审计基础之反射
先來一段反射的概念:
在程序運行的時候動態裝載類,查看類的信息,生成對象,或操作生成對象。
類在運行的時候,可以得到該類的信息,并且可以動態的修改這些信息
反射類的首要操作 - 獲取類
獲取類有 三種 方法:
假設類名為 Cat
(1)直接根據類名獲取類
Class a = Cat.class;(2)通過Class.forName 獲取類,需要打全指定類的路徑
Class a = Class.forName("org.xiaopan.test.Cat");注意:
Class.forName 需要一個異常處理。不然編輯器無法通過。
(3)通過實例化的方式獲取類
Class a = (new Cat()).getClass();反射構造方法
吾有一類,曰:
class Cat{ //構造方法,形參為空 public Cat(){ System.out.println("Cat->nullCat"); } //構造方法,形參為 一個String類型 public Cat(String a){ System.out.println("Cat->aCat,a=" + a); } //私有構造方法,形參為 一個String類型 和 一個Integer類型 private Cat(String a,Integer b){ System.out.println("Cat->abCat,a=" + a + "b=" + b.toString()); } //構造方法,形參為 一個String數組類型 和 一個Map類型 public Cat(String[] aa,Map bb){ System.out.println("Cat->aabbCat"); System.out.println("aa=" + Arrays.toString(aa)); System.out.println("bb=" + bb); }}無參構造方法調用
try{ //獲取到類 Class a = Cat.class; //通過反射獲取到指定類下的構造方法 //要獲取的構造方法為: //public Cat() //由于該構造方法無參數,所以我們傳入一個 null 即可,也可以不傳 Constructor constructor1 = a.getConstructor(null); //實例化類 constructor1.newInstance(null);}catch (Exception e){ System.out.println(e);}輸出:
注意:
getConstructor 需要加一個異常處理。
一個參數的構造方法調用
try{ //獲取到類 Class a = Cat.class; //通過反射獲取到指定類下的構造方法 //要獲取的構造方法為: //public Cat(String a) //由于該構造方法有一 String類型 參數 //在進行 getConstructor 反射時,就需要指定傳參類型為 String.class Constructor constructor1 = a.getConstructor(String.class); //在實例化時,進行傳參 constructor1.newInstance("testvalue");}catch (Exception e){ System.out.println(e);}輸出:
多個參數的私有構造方法調用
注意,這里調用的構造方法是私有的哦~
try{ //獲取到類 Class a = Class.forName("org.xiaopan.test.Cat"); //通過反射獲取到指定類下的構造方法 //要獲取的 私有 構造方法為: //private Cat(String a,Integer b) // //由于是 私有 方法,獲取私有方法的函數為 getDeclaredConstructor //由于有兩個參數,所以需要在 getDeclaredConstructor 傳入對應的參數類型 Constructor c = a.getDeclaredConstructor(String.class, Integer.class); //設置強制反射 c.setAccessible(true); //構造函數實例化,并傳參 //這里有個注意點,看下文的注意 c.newInstance(new Object[]{"abcd",123456});}catch (Exception e){ System.out.println(e);}輸出:
注意:
參考了大佬的文章(參考文章在本文末尾 Referer 中),文章說 jdk1.4和 jdk1.5 處理調用的方法有區別
jdk1.4中,數組每個元素對應一個參數
jdk1.5中,整個數組是一個參數,用一個對象包起來
所以我們調用的傳參的時候,需要使用這種格式:
c.newInstance(new Object[]{"abcd",123456});//即new Object[]{"abcd",123456} 的格式,用一個對象包裹起來形參為數組和Map類型的構造方法調用
字符串數組
創建格式:
String[] a = {"abc", "def"};Map:
java中的map,可以理解為“可自定義鍵值的數組”
形參為數組和Map類型的構造方法調用:
try{ //獲取類 Class a = (new Cat()).getClass(); //調用的構造方法為: //public Cat(String[] aa,Map bb) //照常打 xxx.class 即可。java 萬物皆對象 Constructor c = a.getConstructor(String[].class, Map.class); //創建一個map Map m = new HashMap(); m.put("a_key","a_value"); //實例化構造函數,注意要用 Object包裹的形式 //new String[]{} 是當場初始化字符串數組,當場賦值 c.newInstance(new Object[]{new String[]{"a","b","c"}, m});}catch (Exception e){ System.out.println(e);}輸出:
反射方法
簡介:
反射方法和上文的反射構造方法差不多,如果是私有的話也是要設置強行調用,并且獲取方法的函數為 getDeclaredxxxx
吾有一類:
class Cat{ public void a(){ System.out.println("a invoke"); } public String[] b(String[] b){ return b; } public static void c(){ System.out.println("cccccc"); }}反射無參數方法
try{ //獲取類 Class a = Class.forName("org.xiaopan.test.Cat"); //先實例化,后面調用方法的時候需要使用實例化好的類 //注意,實例化之后返回的類型就是對于的類,做好類型轉換 Cat cat =(Cat) a.newInstance(); //調用的方法為: //public void a() // //獲取方法,需要指定要獲取的方法名 Method m = a.getMethod("a", null); //調用方法,調用方法時,用 上一步代碼中獲取到的方法進行 invoke 調用操作 //而invoke方法中,第一個參數是實例化好的類 //第二個參數就是需要傳入的參數 m.invoke(cat,null);}catch (Exception e){ System.out.println(e);}輸出:
反射有參數有返回值方法
try{ //獲取類 Class a = Class.forName("org.xiaopan.test.Cat"); //先實例化,后面調用方法的時候需要使用實例化好的類 //注意,實例化之后返回的類型就是對于的類,做好類型轉換 Cat cat =(Cat) a.newInstance(); //調用的方法為: //public String[] b(String[] b) // //獲取方法,需要指定要獲取的方法名 Method m = a.getMethod("b",String[].class); //調用方法,調用方法時,用 上一步代碼中獲取到的方法進行 invoke 調用操作 //調用時,如果參數是字符串數組,或者兩個以上的參數 //最好使用 new Object[]{} 的形式傳入 //兼容性好 //由于有返回值,我們在調用的時候也需要進行接收 //接受類型就看調用的類返回的類型了 String[] strs = (String[]) m.invoke(cat, new Object[]{new String[]{"str1","str2","str3"}}); //打印數組: //for each打印數組,先指定抽出來的元素類型 //然后以冒號 : 分隔,左邊是抽出元素變量名,右邊是原數組 for (String str:strs){ System.out.println(str); }}catch (Exception e){ System.out.println(e);}輸出:
反射靜態方法
由于靜態方法不需要實例化類,所以在 getMethod 的時候,直接傳個 null 即可。也不需要 newInstance 類了。
try{ //獲取類 Class a = Class.forName("org.xiaopan.test.Cat"); //調用的方法為: //public static void c() // //獲取方法 Method m = a.getMethod("c"); //由于是靜態方法。直接調用,類對象中傳入null即可 m.invoke(null);}catch (Exception e){ System.out.println(e);}輸出:
反射屬性
反射屬性也大同小異
吾有一類:
class Cat{ public String name = "maomao"; //公共 String類型 屬性 public static Boolean sex = true; //公共 靜態 String類型 屬性 private Integer age = 10; //私有 Integer類型 屬性}反射公共屬性
try{ //獲取類 Class a = Class.forName("org.xiaopan.test.Cat"); //先實例化,后面獲取屬性的時候需要使用實例化好的類 Cat cat = (Cat) a.newInstance(); //屬性: //public String name = "maomao"; // //獲取屬性 Field m = a.getField("name"); //獲取屬性值 //需要傳入實例化類作為對象 String name = (String) m.get(cat); System.out.println(name);}catch (Exception e){ System.out.println(e);}輸出:
反射公共靜態屬性
靜態屬性也一樣,不需要實例化即可調用:
try{ //獲取類 Class a = Class.forName("org.xiaopan.test.Cat"); //屬性: //public static Boolean sex = true; // //獲取屬性 Field m = a.getField("sex"); //獲取屬性值,靜態屬性不需要實例化類,直接傳入null作為類對象即可 Boolean b = (Boolean) m.get(null); System.out.println(b); //設置屬性值 m.set(null,false); b = (Boolean) m.get(null); System.out.println(b);}catch (Exception e){ System.out.println(e);}輸出:
反射私有屬性
私有屬性也一樣,需要暴力反射
try{ //獲取類 Class a = Class.forName("org.xiaopan.test.Cat"); //先實例化,后面獲取屬性的時候需要使用實例化好的類 Cat cat = (Cat) a.newInstance(); //屬性: //private Integer age = 10; // //獲取私有屬性 Field m = a.getDeclaredField("age"); //設置強制反射 m.setAccessible(true); //獲取屬性值 Integer age = (Integer) m.get(cat); //注意輸出的時候要將非String類型 toString 哦,規范一點 System.out.println(age.toString());}catch (Exception e){ System.out.println(e);}輸出:
引用包錯誤的報錯:
用IDEA寫代碼的時候,可以會遇到奇怪報錯,如:
代碼本來就沒問題,但還是報錯了:
這個時候可以看看代碼最上面,看看IDEA是不是自動引入了錯誤的包:
發現有引用錯誤的包,將其刪掉即可,然后再重新在 Method上進行修復:
Runtime.getRuntime.exec 反射
了解 Runtime.getRuntime.exec
Runtime.getRuntime.exec 是Java中執行系統命令的方法
簡單使用如下:
byte[] a = new byte[1024];Process cmd = Runtime.getRuntime().exec("whoami");InputStream input = cmd.getInputStream();input.read(a);String res = new String(a);System.out.println(res);我們來一一分析下,重點就兩大塊:Process 和 InputStream
Process cmd = Runtime.getRuntime().exec(“whoami”)
首先先看看 Runtime.getRuntime().exec 是什么東西,返回值類型是什么樣的:
在手冊上查看描述:
可知 exec 函數就是執行系統命令用的
在去看看源碼做二次確認
返回類型是 Process 類型,所以我們調用的時候用 Process 類型接收返回值
Process類提供進程輸入、輸出等進程方法。粗淺的說就是一個進程類
通過文檔可以得知,我調用的這個exec方法需要一個String類型的參數,即要執行的系統命令
InputStream input = cmd.getInputStream()
其中:
InputStream ? ?輸入流,即數據流入,讀入數據
OutputStream 輸出流,即數據輸出,寫入數據
該代碼讀取上一步 Process 類型的數據流
input.read(a);
在上一步調用getInputStream后,可執行 read 函數。
將當前的數據流讀取出來,寫入到一個 byte[]類型的變量里。
String res = new String(a);
將byte類型轉換成字符串。以便后面打印輸出
這就是一個簡單的 Java 命令執行并回顯結果。
我們可以看到主要調用了 Runtime.getRuntime().exec
那么我們要如何通過反射的方式進行調用呢?
反射調用 Runtime.getRuntime().exec
第一種方式,通過強行反射私有構造方法,用 Runtime 實例化進行反射
這里有一個小坑,Runtime的構造函數是私有的:
所以我們要強制反射私有構造方法,而且不能直接 newInstance Class:
錯誤寫法:
直接用Class來進行實例化
Class runtime = Class.forName("java.lang.Runtime");runtime.newInstance();會報錯:
java.lang.IllegalAccessException: Class org.xiaopan.test.Main can not access a member of class java.lang.Runtime with modifiers “private”
正確寫法:
先強制反射Runtime的構造方法,再實例化構造方法。
Class runtime = Class.forName("java.lang.Runtime");Constructor c = runtime.getDeclaredConstructor();c.setAccessible(true);c.newInstance();反射調用Runtime.gettime.exec
byte[] a = new byte[1024];try{ //獲取Runtime類 Class runtime = Class.forName("java.lang.Runtime"); //獲取Runtime類構造方法 Constructor c_runtime = runtime.getDeclaredConstructor(); //設置強制反射 c_runtime.setAccessible(true); //Runtime類的構造方法 實例化 //由于 Runtime類構造方法返回類型為 Runtime類,所以需要使用 Runtime 類型變量進行接收 Runtime r = (Runtime) c_runtime.newInstance(); //獲取 Runtime類的方法 exec Method m = runtime.getMethod("exec", String.class); //調用 exec 方法,傳入對象為 Runtime類的構造方法實例化 //由于 exec 方法返回的是 Process 類型數據,所以需要使用 Process 類型變量進行接收 Process p = (Process) m.invoke(r,"whoami"); //讀入數據流,讀入到 byte[] 類型的變量中 p.getInputStream().read(a); System.out.println(new String(a));}catch (Exception e){ System.out.println(e);}成功輸出:
第二種方式,不進行 Runtime實例化,直接通過getRuntime進行反射
注意點:
發現盲點:在本節一開頭,調用系統命令函數 exec 的形式如下:
Runtime.getRuntime().exec("whoami");我們去源碼翻翻 getRuntime()是個什么函數
我們可以發現,getRuntime就是為了返回 Runtime類 實例的,感覺應該是一個 單例模式
我們遵守源碼的規則,直接調用 getRuntime,拿到 Runtime類實例
注意點:
由于 getRuntime方法 返回的是 Runtime類實例,所以反射的時候需要顯示類型轉換。
代碼如下:
byte[] a = new byte[1024];try{ //獲取 Runtime 類 Class runtime = Class.forName("java.lang.Runtime"); //獲取getRuntime方法 Method m = runtime.getMethod("getRuntime"); //調用getRuntime方法,并用 Runtime類 類型進行接收,顯式轉換成 Runtime類 //調用的時候,在上文查看源碼時,發現是不需要傳入參數的 //不需傳入參數,我們直接傳一個 null 進去即可 Runtime r = (Runtime) m.invoke(null); //由于上一個代碼中調用 getRuntime 方法,返回了 Runtime類 //我們直接就可以調用底下的 exec 方法了 Process p = r.exec("ifconfig"); //數據輸入流,讀入數據 InputStream res = p.getInputStream(); res.read(a); System.out.println(new String(a));}catch (Exception e){ System.out.println(e);}成功輸出:
Referer:
java手冊:
https://www.oracle.com/cn/java/technologies/java-se-api-doc.html
大佬文章:
https://blog.csdn.net/ju_362204801/article/details/90578678
精彩推薦
總結
以上是生活随笔為你收集整理的c++ 反射_Java代码审计基础之反射的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《金铲铲之战》S12花仙子羁绊介绍
- 下一篇: c++ string 堆还是栈_5 个刁