c++ 反射_Java代码审计基础之反射
先來一段反射的概念:
在程序運行的時候動態(tài)裝載類,查看類的信息,生成對象,或操作生成對象。
類在運行的時候,可以得到該類的信息,并且可以動態(tài)的修改這些信息
反射類的首要操作 - 獲取類
獲取類有 三種 方法:
假設類名為 Cat
(1)直接根據(jù)類名獲取類
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數(shù)組類型 和 一個Map類型 public Cat(String[] aa,Map bb){ System.out.println("Cat->aabbCat"); System.out.println("aa=" + Arrays.toString(aa)); System.out.println("bb=" + bb); }}無參構造方法調(diào)用
try{ //獲取到類 Class a = Cat.class; //通過反射獲取到指定類下的構造方法 //要獲取的構造方法為: //public Cat() //由于該構造方法無參數(shù),所以我們傳入一個 null 即可,也可以不傳 Constructor constructor1 = a.getConstructor(null); //實例化類 constructor1.newInstance(null);}catch (Exception e){ System.out.println(e);}輸出:
注意:
getConstructor 需要加一個異常處理。
一個參數(shù)的構造方法調(diào)用
try{ //獲取到類 Class a = Cat.class; //通過反射獲取到指定類下的構造方法 //要獲取的構造方法為: //public Cat(String a) //由于該構造方法有一 String類型 參數(shù) //在進行 getConstructor 反射時,就需要指定傳參類型為 String.class Constructor constructor1 = a.getConstructor(String.class); //在實例化時,進行傳參 constructor1.newInstance("testvalue");}catch (Exception e){ System.out.println(e);}輸出:
多個參數(shù)的私有構造方法調(diào)用
注意,這里調(diào)用的構造方法是私有的哦~
try{ //獲取到類 Class a = Class.forName("org.xiaopan.test.Cat"); //通過反射獲取到指定類下的構造方法 //要獲取的 私有 構造方法為: //private Cat(String a,Integer b) // //由于是 私有 方法,獲取私有方法的函數(shù)為 getDeclaredConstructor //由于有兩個參數(shù),所以需要在 getDeclaredConstructor 傳入對應的參數(shù)類型 Constructor c = a.getDeclaredConstructor(String.class, Integer.class); //設置強制反射 c.setAccessible(true); //構造函數(shù)實例化,并傳參 //這里有個注意點,看下文的注意 c.newInstance(new Object[]{"abcd",123456});}catch (Exception e){ System.out.println(e);}輸出:
注意:
參考了大佬的文章(參考文章在本文末尾 Referer 中),文章說 jdk1.4和 jdk1.5 處理調(diào)用的方法有區(qū)別
jdk1.4中,數(shù)組每個元素對應一個參數(shù)
jdk1.5中,整個數(shù)組是一個參數(shù),用一個對象包起來
所以我們調(diào)用的傳參的時候,需要使用這種格式:
c.newInstance(new Object[]{"abcd",123456});//即new Object[]{"abcd",123456} 的格式,用一個對象包裹起來形參為數(shù)組和Map類型的構造方法調(diào)用
字符串數(shù)組
創(chuàng)建格式:
String[] a = {"abc", "def"};Map:
java中的map,可以理解為“可自定義鍵值的數(shù)組”
形參為數(shù)組和Map類型的構造方法調(diào)用:
try{ //獲取類 Class a = (new Cat()).getClass(); //調(diào)用的構造方法為: //public Cat(String[] aa,Map bb) //照常打 xxx.class 即可。java 萬物皆對象 Constructor c = a.getConstructor(String[].class, Map.class); //創(chuàng)建一個map Map m = new HashMap(); m.put("a_key","a_value"); //實例化構造函數(shù),注意要用 Object包裹的形式 //new String[]{} 是當場初始化字符串數(shù)組,當場賦值 c.newInstance(new Object[]{new String[]{"a","b","c"}, m});}catch (Exception e){ System.out.println(e);}輸出:
反射方法
簡介:
反射方法和上文的反射構造方法差不多,如果是私有的話也是要設置強行調(diào)用,并且獲取方法的函數(shù)為 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"); }}反射無參數(shù)方法
try{ //獲取類 Class a = Class.forName("org.xiaopan.test.Cat"); //先實例化,后面調(diào)用方法的時候需要使用實例化好的類 //注意,實例化之后返回的類型就是對于的類,做好類型轉換 Cat cat =(Cat) a.newInstance(); //調(diào)用的方法為: //public void a() // //獲取方法,需要指定要獲取的方法名 Method m = a.getMethod("a", null); //調(diào)用方法,調(diào)用方法時,用 上一步代碼中獲取到的方法進行 invoke 調(diào)用操作 //而invoke方法中,第一個參數(shù)是實例化好的類 //第二個參數(shù)就是需要傳入的參數(shù) m.invoke(cat,null);}catch (Exception e){ System.out.println(e);}輸出:
反射有參數(shù)有返回值方法
try{ //獲取類 Class a = Class.forName("org.xiaopan.test.Cat"); //先實例化,后面調(diào)用方法的時候需要使用實例化好的類 //注意,實例化之后返回的類型就是對于的類,做好類型轉換 Cat cat =(Cat) a.newInstance(); //調(diào)用的方法為: //public String[] b(String[] b) // //獲取方法,需要指定要獲取的方法名 Method m = a.getMethod("b",String[].class); //調(diào)用方法,調(diào)用方法時,用 上一步代碼中獲取到的方法進行 invoke 調(diào)用操作 //調(diào)用時,如果參數(shù)是字符串數(shù)組,或者兩個以上的參數(shù) //最好使用 new Object[]{} 的形式傳入 //兼容性好 //由于有返回值,我們在調(diào)用的時候也需要進行接收 //接受類型就看調(diào)用的類返回的類型了 String[] strs = (String[]) m.invoke(cat, new Object[]{new String[]{"str1","str2","str3"}}); //打印數(shù)組: //for each打印數(shù)組,先指定抽出來的元素類型 //然后以冒號 : 分隔,左邊是抽出元素變量名,右邊是原數(shù)組 for (String str:strs){ System.out.println(str); }}catch (Exception e){ System.out.println(e);}輸出:
反射靜態(tài)方法
由于靜態(tài)方法不需要實例化類,所以在 getMethod 的時候,直接傳個 null 即可。也不需要 newInstance 類了。
try{ //獲取類 Class a = Class.forName("org.xiaopan.test.Cat"); //調(diào)用的方法為: //public static void c() // //獲取方法 Method m = a.getMethod("c"); //由于是靜態(tài)方法。直接調(diào)用,類對象中傳入null即可 m.invoke(null);}catch (Exception e){ System.out.println(e);}輸出:
反射屬性
反射屬性也大同小異
吾有一類:
class Cat{ public String name = "maomao"; //公共 String類型 屬性 public static Boolean sex = true; //公共 靜態(tài) 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);}輸出:
反射公共靜態(tài)屬性
靜態(tài)屬性也一樣,不需要實例化即可調(diào)用:
try{ //獲取類 Class a = Class.forName("org.xiaopan.test.Cat"); //屬性: //public static Boolean sex = true; // //獲取屬性 Field m = a.getField("sex"); //獲取屬性值,靜態(tài)屬性不需要實例化類,直接傳入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 哦,規(guī)范一點 System.out.println(age.toString());}catch (Exception e){ System.out.println(e);}輸出:
引用包錯誤的報錯:
用IDEA寫代碼的時候,可以會遇到奇怪報錯,如:
代碼本來就沒問題,但還是報錯了:
這個時候可以看看代碼最上面,看看IDEA是不是自動引入了錯誤的包:
發(fā)現(xiàn)有引用錯誤的包,將其刪掉即可,然后再重新在 Method上進行修復:
Runtime.getRuntime.exec 反射
了解 Runtime.getRuntime.exec
Runtime.getRuntime.exec 是Java中執(zhí)行系統(tǒng)命令的方法
簡單使用如下:
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 函數(shù)就是執(zhí)行系統(tǒng)命令用的
在去看看源碼做二次確認
返回類型是 Process 類型,所以我們調(diào)用的時候用 Process 類型接收返回值
Process類提供進程輸入、輸出等進程方法。粗淺的說就是一個進程類
通過文檔可以得知,我調(diào)用的這個exec方法需要一個String類型的參數(shù),即要執(zhí)行的系統(tǒng)命令
InputStream input = cmd.getInputStream()
其中:
InputStream ? ?輸入流,即數(shù)據(jù)流入,讀入數(shù)據(jù)
OutputStream 輸出流,即數(shù)據(jù)輸出,寫入數(shù)據(jù)
該代碼讀取上一步 Process 類型的數(shù)據(jù)流
input.read(a);
在上一步調(diào)用getInputStream后,可執(zhí)行 read 函數(shù)。
將當前的數(shù)據(jù)流讀取出來,寫入到一個 byte[]類型的變量里。
String res = new String(a);
將byte類型轉換成字符串。以便后面打印輸出
這就是一個簡單的 Java 命令執(zhí)行并回顯結果。
我們可以看到主要調(diào)用了 Runtime.getRuntime().exec
那么我們要如何通過反射的方式進行調(diào)用呢?
反射調(diào)用 Runtime.getRuntime().exec
第一種方式,通過強行反射私有構造方法,用 Runtime 實例化進行反射
這里有一個小坑,Runtime的構造函數(shù)是私有的:
所以我們要強制反射私有構造方法,而且不能直接 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();反射調(diào)用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); //調(diào)用 exec 方法,傳入對象為 Runtime類的構造方法實例化 //由于 exec 方法返回的是 Process 類型數(shù)據(jù),所以需要使用 Process 類型變量進行接收 Process p = (Process) m.invoke(r,"whoami"); //讀入數(shù)據(jù)流,讀入到 byte[] 類型的變量中 p.getInputStream().read(a); System.out.println(new String(a));}catch (Exception e){ System.out.println(e);}成功輸出:
第二種方式,不進行 Runtime實例化,直接通過getRuntime進行反射
注意點:
發(fā)現(xiàn)盲點:在本節(jié)一開頭,調(diào)用系統(tǒng)命令函數(shù) exec 的形式如下:
Runtime.getRuntime().exec("whoami");我們?nèi)ピ创a翻翻 getRuntime()是個什么函數(shù)
我們可以發(fā)現(xiàn),getRuntime就是為了返回 Runtime類 實例的,感覺應該是一個 單例模式
我們遵守源碼的規(guī)則,直接調(diào)用 getRuntime,拿到 Runtime類實例
注意點:
由于 getRuntime方法 返回的是 Runtime類實例,所以反射的時候需要顯示類型轉換。
代碼如下:
byte[] a = new byte[1024];try{ //獲取 Runtime 類 Class runtime = Class.forName("java.lang.Runtime"); //獲取getRuntime方法 Method m = runtime.getMethod("getRuntime"); //調(diào)用getRuntime方法,并用 Runtime類 類型進行接收,顯式轉換成 Runtime類 //調(diào)用的時候,在上文查看源碼時,發(fā)現(xiàn)是不需要傳入?yún)?shù)的 //不需傳入?yún)?shù),我們直接傳一個 null 進去即可 Runtime r = (Runtime) m.invoke(null); //由于上一個代碼中調(diào)用 getRuntime 方法,返回了 Runtime類 //我們直接就可以調(diào)用底下的 exec 方法了 Process p = r.exec("ifconfig"); //數(shù)據(jù)輸入流,讀入數(shù)據(jù) 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代码审计基础之反射的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《金铲铲之战》S12花仙子羁绊介绍
- 下一篇: 安装oracle 12c 还用装11g_