最近修改了某个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下载地址