相信很多人都“免費激活”過 IDEA吧,在IDEA 的vmoptions配置裡,加行配置就行:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5yN3cDN4AjM5kTZ3EzM3EDNxQzMkNmY0YjZ0IWN4MmNh9CXwMzLcNDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL4M3Lc9CX6MHc0RHaiojIsJye.png)
或者是這樣“拖到IDEA視窗中”的形式:
再或者用過一些APM工具,在JVM啟動腳本上增加了-javaagent:/path/to/apm-agent.jar,就可以自動進行追蹤。再或者用過Arthas之類的JVM診斷工具,這些工具都是通過Java Agent的技術去實作的。**
比如上面說的“免費激活”,其實就是在運作時期修改了驗證license的相關代碼。JAVA 裡 Agent 這麼強大的功能,你難道不打算自己親自寫一個試試嗎?
Java Agent 算是JVM的一個插件,以一個Jar包的形式存在。可以做到在運作時期,修改你的位元組碼檔案,進而達到增強、修改等效果,通過 JVM 提供的 Instrumentation API來實作。
一個Java Agent,由以下幾個元件構成:
Agent Class - Agent的功能類
Packaging - 在MANIFEST.MF檔案中定義Agent Class的位置和方式
“裝載點”,比如-javaagent:<jarfile>[=arguments],指定加載的agent.jar檔案
廢話不多說,下面正式開始編寫這個Agent
首先要建立一個Agent Class,這個Class作為我們Agent插件的入口類。配置好-javaagent後,JVM在啟動時會執行我們Agent Class的premain方法
在premain方法中,除了args參數,還有一個instrumentation對象。這個是Java Agent的核心對象,通過該對象可以注冊ClassFileTransformer。
**ClassFileTransformer **就是負責位元組碼轉換的核心接口了,已注冊的ClassFileTransformer可以攔截JVM中所有類的加載,并且可以擷取到已加載類的位元組碼,來看一下這個接口的源碼:
了解了ClassFileTransformer接口之後,現在來寫一個ClassLoggerTransformer實作類。為了簡單,這個實作類隻有一個功能:将已加載的位元組碼轉儲到檔案中
好了,第一個Agent 的功能代碼部分已經完成了,下面需要 Agent 解決入口的配置
現在我們需要将代碼建構成一個Jar,并且Jar内的MANIFEST.MF檔案中,需要包含Agent Class的配置,最終我們的MANIFEST.MF檔案應該是這樣:
通過Maven的建構插件,很容易完成MANIFEST.MF檔案的配置:
隻需要在項目的src/main/resources/META-INF/路徑下,增加一個MANIFEST.MF的模闆,模闆檔案裡按照上面介紹的定義即可,最後mvn clean package就可以建構出我們的agent jar了(預設目錄在${projectpath}/target/),這個jar包内會包含我們上面的MANIFEST.MF模闆檔案
好了,大功告成,我們第一款Agent已經開發完了,下面來測試一下:
增加agent運作後,所有運作時期加載的Class的位元組碼,就會轉儲到我們的classes目錄下了
如果你是在 IDEA 中運作,也可以在Run/Debug Configurations面闆中,add vm options
上面這個例子好像有點過于簡單,隻是“攔截”了位元組碼資料進行了轉儲,并沒有進行位元組碼的修改。其實ClassFileTransformer.transform的傳回值,就是我們要替換的資料;隻需要在transform方法中傳回新的位元組碼資料,就可以做到增強/替換類了(不過這個增強/替換是有一些限制的,比如不能修改方法簽名之類的,本文不做過多介紹)
介紹完了基本的Agent實作,下面來學習一個Agent的實際例子:通過Agent來“免費激活”
前言中提到的,IDEA“免費激活”的工具也是通過Agent實作的,其實基本原理很簡單,就是寫一個Agent,動态修改驗證license的那些代碼而已。
比如我們使用一款需要授權許可證的Java 軟體,其内部驗證許可證的代碼是下面這段(僞代碼)
那麼我們隻需要通過Agent,動态的來修改這個verifyLicense方法,将驗證結果修改為直接通過,就可以繞過這個許可驗證機制了,還不用修改原始Jar包
那麼怎麼修改這個類方法呢?
有兩種方式:
提前解壓jar包,反編譯那個Class檔案,得到Java檔案後修改verifyLicense方法後重新編譯
在ClassFileTransformer實作中,通過傳入的該類位元組碼資料,使用一些位元組碼操作工具進行修改
本文例子為了簡單,使用第一種方式,提前反編譯、修改,再儲存重新編譯的Class檔案到Agent 項目裡:
隻需要建立一個ClassFileTransformer,進行這個驗證許可證Class的替換:
然後在Agent Class中,注冊這個HackVerifierClassFileTransformer
最後隻需要像上面那樣,配置下MANIFEST.MF的生成,然後建構Agent Jar包,就完成了我們這個“免費激活”的Agent 插件
以後運作該 Java 軟體時,隻需要增加-javaagent:/path/to/hack-agent.jar,就實作了“免費激活”
文中例子完整的代碼在https://github.com/kongwu-/agent-samples,有需要的同學可以自行下載下傳
ASM
cglib
javaassist
ByteBuddy
以上的幾個位元組碼操作類庫,最推薦的是ByteBuddy,使用方式上最簡單