sun.misc.unsafe類的使用

 

sun.misc.unsafe類的使用


    這個帖子是關於JAVA中不爲人知的特性的後續更新,若是想獲得下次在線討論的更新,請經過郵件訂閱,而且不要忘了在評論區留下你的意見和建議。java


    Java是一個安全的開發工具,它阻止開發人員犯不少低級的錯誤,而大部份的錯誤都是基於內存管理方面的。若是你想搞破壞,可使用Unsafe這個類。這個類是屬於sun.* API中的類,而且它不是J2SE中真正的一部份,所以你可能找不到任何的官方文檔,更可悲的是,它也沒有比較好的代碼文檔。git


    實例化sun.misc.Unsafe程序員

    若是你嘗試建立Unsafe類的實例,基於如下兩種緣由是不被容許的。github

    1)、Unsafe類的構造函數是私有的;數組

    2)、雖然它有靜態的getUnsafe()方法,可是若是你嘗試調用Unsafe.getUnsafe(),會獲得一個SecutiryException。這個類只有被JDK信任的類實例化。安全

    可是這總會是有變通的解決辦法的,一個簡單的方式就是使用反射進行實例化:數據結構


  1. Field f = Unsafe.class.getDeclaredField("theUnsafe"); //Internal reference  
    f.setAccessible(true);  
    Unsafe unsafe = (Unsafe) f.get(null);


    注:IDE如Eclipse對會這樣的使用報錯,不過不用擔憂,直接運行代碼就行,能夠正常運行的。app

    (譯者注:還有一種解決方案,就是將Eclipse中這種限制獲取由錯誤,修改成警告,具體操做爲將Windows->Preference...->Java->Compiler->Errors/Warnings中的"Deprecated and restricted API",級別由Error修改成Warning就能夠了)ide

    如今進入主題,使用這個對象咱們能夠作以下「有趣的」事情。函數


    使用sun.misc.Unsafe

    1)、突破限制建立實例

    經過allocateInstance()方法,你能夠建立一個類的實例,可是卻不須要調用它的構造函數、初使化代碼、各類JVM安全檢查以及其它的一些底層的東西。即便構造函數是私有,咱們也能夠經過這個方法建立它的實例。

    (這個對單例模式情有獨鍾的程序員來講將會是一個噩夢,它們沒有辦法阻止這種方式調用大笑

    看下面一個實例(注:爲了配合這個主題,譯者將原實例中的public構造函數修改成了私有的):

 


  1. public class UnsafeDemo {  

  2.     public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, InstantiationException {  

  3.         Field f = Unsafe.class.getDeclaredField("theUnsafe"); // Internal reference  

  4.         f.setAccessible(true);  

  5.         Unsafe unsafe = (Unsafe) f.get(null);  

  6.   

  7.         // This creates an instance of player class without any initialization  

  8.         Player p = (Player) unsafe.allocateInstance(Player.class);  

  9.         System.out.println(p.getAge()); // Print 0  

  10.   

  11.         p.setAge(45); // Let's now set age 45 to un-initialized object  

  12.         System.out.println(p.getAge()); // Print 45  

  13.     }  

  14. }  

  15.   

  16. class Player {  

  17.     private int age = 12;  

  18.   

  19.     private Player() {  

  20.         this.age = 50;  

  21.     }  

  22.   

  23.     public int getAge() {  

  24.         return this.age;  

  25.     }  

  26.   

  27.     public void setAge(int age) {  

  28.         this.age = age;  

  29.     }  

  30. }  



    2)、使用直接獲取內存的方式實現淺克隆

    如何實現淺克隆?在clone(){...}方法中調用super.clone(),對嗎?這裏存在的問題是首先你必須繼續Cloneable接口,而且在全部你須要作淺克隆的對象中實現clone()方法,對於一個懶懶的程序員來講,這個工做量太大了。

    我不推薦上面的作法而是直接使用Unsafe,咱們能夠僅使用幾行代碼就實現淺克隆,而且它能夠像某些工具類同樣用於任意類的克隆。

    這個戲法就是把一個對象的字節碼拷貝到內存的另一個地方,而後再將這個對象轉換爲被克隆的對象類型。

    

    3)、來自黑客的密碼安全

    這個好似頗有趣吧?實事就是這樣的。開發人員建立密碼或者是保證密碼到字符串中,而後在應用程序的代碼中使用這些密碼,使用事後,聰明的程序員會把字符串的引用設爲NULL,所以它就不會被引用着而且很容易被垃圾收集器給回收掉。

    可是從你將引用設爲NULL到被垃圾收集器收集的這個時間段以內(原文:But from the time, you made the reference null to the time garbage collector kicks in),它是處於字符串池中的,而且在你係統中進行一個複雜的攻擊(原文:And a sophisticated attack on your system),也是能夠讀取到你的內存區域而且得到密碼,雖然機會很小,可是老是存在的。

    這就是爲何建議使用char[]數組存放密碼,當使用完事後,你能夠迭代處理當前數組,修改/清空這些字符。

    另一個方式就是使用魔術類Unsafe。你能夠建立另一個和當前密碼字符串具備相同長度的臨時字符串,將臨時密碼中的每一個字符都設值爲"?"或者"*"(任何字符均可以),當你完成密碼的邏輯後,你只須要簡單的將臨時密碼中的字節數組拷貝到原始的密碼串中,這就是使用臨時密碼覆蓋真實的密碼。

    示例代碼可能會是這樣:

[java] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. String password = new String("l00k@myHor$e");  

  2. String fake = new String(password.replaceAll(".""?"));  

  3. System.out.println(password); // l00k@myHor$e  

  4. System.out.println(fake); // ????????????  

  5.    

  6. getUnsafe().copyMemory(fake, 0L, null, toAddress(password), sizeOf(password));  

  7.    

  8. System.out.println(password); // ????????????  

  9. System.out.println(fake); // ????????????  


    運行時動態建立類

    咱們能夠在運行時運態的建立類,例如經過編譯後的.class文件,操做方式就是將.class文件讀取到字節數據組中,並將其傳到defineClass方法中。

[java] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. //Sample code to craeet classes  

  2. byte[] classContents = getClassContent();  

  3. Class c = getUnsafe().defineClass(null, classContents, 0, classContents.length);  

  4. c.getMethod("a").invoke(c.newInstance(), null);  

  5.    

  6. //Method to read .class file  

  7. private static byte[] getClassContent() throws Exception {  

  8.     File f = new File("/home/mishadoff/tmp/A.class");  

  9.     FileInputStream input = new FileInputStream(f);  

  10.     byte[] content = new byte[(int)f.length()];  

  11.     input.read(content);  

  12.     input.close();  

  13.     return content;  

  14. }  

    

    4)、超大數組

    從所周知,常量Integer.MAX_VALUE是JAVA中數組長度的最大值,若是你想建立一個很是大的數組(雖然在一般的應用中不可能會用上),能夠經過對內存進行直接分配實現。

    下面這個示例將會建立分配一段連續的內存(數組),它的容易是容許最大容量的兩倍。

[java] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. class SuperArray {  

  2.     private final static int BYTE = 1;  

  3.     private long size;  

  4.     private long address;  

  5.        

  6.     public SuperArray(long size) {  

  7.         this.size = size;  

  8.         //獲得分配內存的起始地址  

  9.         address = getUnsafe().allocateMemory(size * BYTE);  

  10.     }  

  11.     public void set(long i, byte value) {  

  12.         getUnsafe().putByte(address + i * BYTE, value);  

  13.     }  

  14.     public int get(long idx) {  

  15.         return getUnsafe().getByte(address + idx * BYTE);  

  16.     }  

  17.     public long size() {  

  18.         return size;  

  19.     }  

  20. }  

    應用示例

[java] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. long SUPER_SIZE = (long)Integer.MAX_VALUE * 2;  

  2. SuperArray array = new SuperArray(SUPER_SIZE);  

  3. System.out.println("Array size:" + array.size()); // 4294967294  

  4. for (int i = 0; i < 100; i++) {  

  5.     array.set((long)Integer.MAX_VALUE + i, (byte)3);  

  6.     sum += array.get((long)Integer.MAX_VALUE + i);  

  7. }  

  8. System.out.println("Sum of 100 elements:" + sum);  // 300  


    但請注意這可能會致使JVM掛掉。

    

    結束語

    sun.misc.Unsafe provides almost unlimited capabilities for exploring and modification of VM’s runtime data structures. Despite the fact that these capabilities are almost inapplicable in Java development itself, Unsafe is a great tool for anyone who want to study HotSpot VM without C++ code debugging or need to create ad hoc profiling instruments.

    sun.misc.Unsafe提供了能夠隨意查看及修改JVM中運行時的數據結構,儘管這些功能在JAVA開發自己是不適用的,Unsafe是一個用於研究學習HotSpot虛擬機很是棒的工具,由於它不須要調用C++代碼,或者須要建立即時分析的工具。


    參考

    http://mishadoff.github.io/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/

相關文章
相關標籤/搜索