最近修改了某個android的第三方jar包里的代碼,在此記錄一下心得
一開始想用jd-gui轉成.java文件,修改后再重新編譯回class,然后放進jar中覆蓋掉原來的class文件。然而在編譯回去時發現因為代碼引用了部分android的類,用java命令等方式無法編譯回class文件。於是我嘗試通過直接修改class文件來實現修改代碼。
- 找到jar包中對應的class文件的路徑
- 建一個可以直接執行的java類
- 調用javassist.jar的API來寫代碼實現修改對應代碼(示例代碼在底下,關於javassist.jar的API請自行百度)
- 執行修改class文件的代碼
- 生成的已修改class位於項目根目錄下對應的class文件路徑下(例:com.a.bb.A.class, 程序執行后class文件會生成在: 當前項目/com/a/bb/A.class
- 覆蓋掉出來的class文件夾中原先的class文件
- 使用cmd命令:
jar cvf test.jar
命令重新打包 - 然后在項目中測試一下修改后的jar就大功告成了!
很煩躁的是前幾天整理硬盤時候沒注意把平常java測試用項目刪掉了,只能貼出聊天記錄中的部分代碼
public static void main(String[] args) {
ClassPool pool = ClassPool.getDefault();
try {
//取得需要反編譯的jar文件,設定路徑
pool.insertClassPath("G://abc//XXSDK_Android.jar");
//取得需要反編譯修改的文件,注意是完整路徑(注意:因為代碼在內部類中,所以我讀取的是WebViewActivity$2文件
// CtClass cc1 = pool.get("com.objectplanet.chart.a");
CtClass cc1 = pool.get("com.xx.webview.WebViewActivity$2");
//取得需要修改的方法
CtMethod method = cc1.getDeclaredMethod("shouldUrlLoading");
//插入修改項,我們讓他直接返回(注意:根據方法的具體返回值返回,因為這個方法返回值是void,所以直接return;)
method.instrument(
new ExprEditor() {
public void edit(MethodCall m)
throws CannotCompileException {
System.out.println(m.getClassName() + ", " + m.getMethodName());
// 在這里搜索class中符合條件的邏輯代碼,並替換成我想改的
if ( m.getClassName().equals("java.lang.String")
&& m.getMethodName().equals("startsWith")) {
m.replace(
"$_ = $proceed(\"http\") ;");
}
}
});
//寫入保存
cc1.writeFile();
} catch (NotFoundException e) {
e.printStackTrace();
} catch (CannotCompileException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
PS:
- 如果發現修改不了對應的代碼,那是因為對應的代碼不是在那個A.class文件中。(沒錯,你沒聽錯) 舉例來說:我想修改匿名內部類中的代碼,需要修改的class文件是
A$.class系列,而不是A.class本身,因為編譯成class文件后,內部類的代碼實際上是放在A$.class文件中的 - 把class文件直接丟進jar包中有可能會導致用不了,這時候用命令重新打包試試
- 按住Shift+右鍵,在右鍵菜單中會出現“在此處打開命令窗口(W)”的選項
如果只是要修改某個字符串,可以使用JBE(Java Bytecode Editor)。 JBE是一個Java字節碼編輯工具,可以查看和編輯該方法的字節碼, 字節碼化的代碼雖然不容易看懂. 但是字符串內容不會變. 所以很適用於只需要修改某個字符串(比如正則)內容的需求. 找到對應方法的字節碼,點擊Code Editor找到對應字符串直接編輯完點保存就能拿到一個修改好的class文件.
JBE使用教程
JBE下載地址