首頁  >  文章  >  Java  >  如何閱讀 Java 字節碼既有趣又有利可圖

如何閱讀 Java 字節碼既有趣又有利可圖

PHP中文网
PHP中文网原創
2024-10-22 13:03:131004瀏覽

踏上 Java 字節碼世界之旅?本文涵蓋了入門所需了解的所有內容。

如何閱讀 Java 字節碼既有趣又有利可圖

什麼是字節碼?

早在 1995 年,Java 程式設計的創建者 Sun Microsystems語言,提出了大膽的主張。他們說 Java 可以讓你「寫一次,隨處運行」。這意味著編譯後的二進位檔案將能夠在任何系統架構上運行,這是 C 無法做到的,並且至今仍然是編寫 Java 的核心租戶。

為了實現這種跨平台功能,Java 採用了編譯時採用獨特的方法。 Java 不是將原始程式碼直接轉換為機器碼(機器碼特定於每個系統架構),而是將其程式編譯為稱為字節碼的中間形式。字節碼是一組指令,既不依賴特定的機器語言,也不依賴任何特定的硬體架構。這種抽像是Java可移植性的關鍵。

解釋和執行Java字節碼指令的程式稱為Java虛擬機器(JVM)。 JVM 將每個字節碼指令翻譯成其運作所在的特定係統架構本機的機器碼。此過程通常稱為「即時」(JIT) 編譯,允許 Java 字節碼在任何給定平台上盡可能有效率地執行。

查看字節碼

字節碼是不過,它不僅僅對 JVM 有用。由於 Java 類別的字節碼有助於逆向工程、效能最佳化、安全研究和其他靜態分析功能,因此 JDK 附帶了實用程式來幫助您和我檢查它。

看一下以下範例字節碼,請考慮以下來自`java.lang.Boolean`、`booleanValue` 和`valueOf(boolean)` 的兩個方法,它們分別對`boolean` 原始型別進行拆箱和裝箱:

public boolean booleanValue() {
    return value;
}

public static Boolean valueOf(boolean b) {
    return (b ? TRUE : FALSE);
}

使用` javap` 指令是JDK 附帶的,我們可以看到每個指令的字節碼。您可以透過使用“-c”命令和類別的完全限定名稱來執行“javap”來完成此操作,如下所示:

javap -c java.lang.Boolean

結果是“中所有公共方法的字節碼” java .lang.Boolean`。這裡我只複製了`booleanValue` 和`valueOf(boolean)` 的字節碼:

public boolean booleanValue();
    code:
    0: aload_0
    1: getfield		#7                  // Field value:Z
    4: ireturn
    
public static java.lang.Boolean valueOf(boolean);
    Code:       
    0: iload_0
    1: ifeq          	10
    4: getstatic     	#27                 // Field TRUE:Ljava/lang/Boolean;
    7: goto          	13
    10: getstatic     	#31                 // Field FALSE:Ljava/lang/Boolean;
    13: areturn

剖析字節碼

乍一看,這是一門全新的需要學習的語言。然而,當您了解每個指令的作用以及 Java 使用堆疊進行操作時,它很快就會變得簡單。

以 `booleanValue` 的三個字節碼指令為例:

  • `aload_n` 表示將局部變數的參考放入堆疊。在類別實例中,`aload_0` 引用 `this`。

  • `getfield` 表示從`this`(堆疊中最下面的一項)讀取成員變數並將其放置將值入堆疊

    • `#7`指引用在常數池中的索引

    • `//字段值:Z`告訴我們`#7`指的是一個名為`value`的`boolean`類型的欄位(Z)

  • `ireturn`表示彈出一個原始值從堆疊中取出並傳回它

長話短說,這三個指令尋找實例的「value」欄位並傳回它。

作為第二個範例,採用看下一個方法, `valueOf(boolean)`:

  • `iload_n` 堆疊表示將一個原始局部變數放入堆疊中意味著。 `iload_0` 指的是第一個方法參數(因為第一個方法參數是一個原語)

  • `ifeq    n` 表示將值從堆疊中彈出並查看是否為true;如果是,則繼續到下一行,否則跳到行`n`

  • `getstatic #n` 表示將靜態成員讀取到堆疊

    • `#27` 指的是常數池中靜態成員的索引

    • `// 字段TRUE:Ljava/lang/Boolean` 告訴我們`#27`指的是什麼,類型為`Boolean

  • `goto n` 的靜態成員名為`TRUE` 表示現在跳到字節碼中的` n` 行

  • `areturn` 表示從堆疊中彈出引用並返回它

換句話說,這些說明說,如果為true ,則採用第一個方法參數,然後傳回`Boolean.TRUE`;否則,傳回`Boolean.FALSE`。

利用字節碼分析

我之前提到這對於逆向工程、效能最佳化和安全研究很有幫助。現在讓我們擴展一下這些內容。

逆向工程

使用第三方函式庫或閉源元件時,字節碼分析成為一個強大的工具。反編譯字節碼可以讓您了解這些函式庫的內部工作原理,有助於整合、故障排除並確保相容性。

在遇到專有或閉源 Java 程式碼的情況下,讀取字節碼可能是唯一可行的方法的方式來了解其功能。字節碼分析可讓您進行逆向工程並理解閉源應用程式的行為,從而促進互通性或自訂。

舉一個現實生活中的例子,我最近嘗試將第三方包纏結分析工具整合到我們的 Ci 系統中。不幸的是,該供應商是閉源的,並且只有有關如何透過其專有 UI 存取該庫的文檔。透過分析字節碼,我能夠對底層分析引擎的預期輸入和輸出進行逆向工程。


以上是如何閱讀 Java 字節碼既有趣又有利可圖的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
上一篇:重新拋出例外下一篇:重新拋出例外