你有遇到适用的场景吗
你有没有遇到需要修改 class 文件重新打包的场景呢?
巧得很,最近刚好遇到需要修改一个已存在的 jar 包,为了满足自己的需求,
不过对于 class 文件修改的是比较简单的字符常量,尚未涉及到比较复杂的逻辑,
用于记录一下如何修改,如何快速修改完成需求,希望可以抛砖引玉,剩下的较为复杂的逻辑修改就交给你们了 :)
比如我有这样一个修改 class 文件的场景:
- 问题:
某弹窗文本显示不正确
- 描述:
此弹窗代码是在某第三方 jar 包里,显示内容格式是: 客服:配置内容
,
我的需求是不需要 jar 内置的固定格式(看代码其实就是字符串拼接),我需要去掉 客服:
这个固定前缀,让配置内容
更加灵活,
- 预期:
比如我想要的显示效果是:客服QQ:2464113103
那么想要实现这个,我想到的是通过修改 jar 包,去除显示格式 客服:
固定前缀,实现内容灵活配置。
这里简单记录两种修改方式
-
第一种方式是直接使用
javac 命令
-
第二种方式是借助工具 jclasslib bytecode editor 快速修改
稍微温故 jar 命令
- jar 简洁手册:
jar -h
用法: jar {ctxui}[vfmn0PMe] [jar-file] [manifest-file] [entry-point] [-C dir] files ...\
选项:
-c 创建新档案
-t 列出档案目录
-x 从档案中提取指定的 (或所有) 文件
-u 更新现有档案
-v 在标准输出中生成详细输出
-f 指定档案文件名
-m 包含指定清单文件中的清单信息
-n 创建新档案后执行 Pack200 规范化
-e 为捆绑到可执行 jar 文件的独立应用程序
指定应用程序入口点
-0 仅存储; 不使用任何 ZIP 压缩
-P 保留文件名中的前导 '/' (绝对路径) 和 ".." (父目录) 组件
-M 不创建条目的清单文件
-i 为指定的 jar 文件生成索引信息
-C 更改为指定的目录并包含以下文件
如果任何文件为目录, 则对其进行递归处理。清单文件名, 档案文件名和入口点名称的指定顺序
与 'm', 'f' 和 'e' 标记的指定顺序相同。
复制代码
- 打包生成 jar
将两个 class 文件打包为 classes.jar 文件
jar cvf classes.jar Foo.class Bar.class
复制代码
将 folder/ 目录下的所有文件打包成 classes.jar 文件
jar cvf classes.jar -C folder/ .
复制代码
- 查看 jar 文件列表:
jar -tvf classes.jar
复制代码
- 解压 jar 文件:
jar -xvf classes.jar
复制代码
工具下载
方式一:javac 命令
1、使用 JD-GUI 打开 jar 文件并导出 java 代码,对代码进行修改
2、使用 javac 命令把 .java 文件转换为 .class 文件
javac -classpath [待生成的文件名.jar] [已修改的文件.java]
复制代码
实际操作过程中直接执行该命令可能往往不是自己期望的那么顺利,那么我们要对特殊情况进行特殊处理。
- 当遇到编码问题时:需要添加额外参数
-encoding utf-8
- 当遇到程序包找不到时:需要在
已修改.java文件同级目录下
放置缺失的 jar 包(可以使用分号分隔输入多个 jar 包参数)
注意:
这里有个坑,如果打包过程有依赖 android jar,那么!!!android.jar 必须是 android sdk 目录下的 jar,不能随便找一个,否则编译失败。`
3、经过上述操作,最后使用的命令可能是这样的
javac -encoding utf-8 -classpath [依赖的1.jar;依赖的2.jar;依赖的3.jar;...] [已修改的单个 .java 文件或者待转换的 .java 文件所在目录]
复制代码
例如
javac -encoding utf-8 -classpath android.jar;classes-dex2jar.jar GRAppStoreActivity.java
复制代码
这就成功把 java 代码编译为 class 代码
4、把编译好了的 class 文件替换原先的文件打包成 jar
jar cvf [新文件名.jar] -C [待打包的class文件目录] [输出到指定目录]
复制代码
例如
jar cvf jsonlili.jar -C primer/ .
复制代码
到这里就打包成功了,如果遇到什么问题欢迎评论 :)
看起来操作步骤是有点繁琐。那么下面简明介绍可以快速实现的方法。
方式二:jclasslib 工具
1、把 jar 包拖入工具中并打开指定 class 文件【也可以使用 jar 命令先解压获取 class 文件】
2、代码定位并修改(我们以开头的客服提示弹窗为例)
在原始 classes.all.dex.jar 文件中定位到客服:
固定前缀在 MyMainActivity
类的某个匿名方法中。
我们知道匿名实现经过编译成 class 文件之后生成单独的文件,且文件名往往带有$
符号,所以我们解压 jar 文件之后可以缩小范围查找。
一番查阅后定位到 MyMainActivity$2$1.class
文件中,Methods -》onClick -》Code
ldc:是 JVM 指令,将从常量池中取出值
JVM 相关知识我自己也要加强,不过多介绍,这里推荐一个学习地址 字节码指令
3、修改并保存 从刚才的 ldc #61 <客服:>
中点击 #61
跳转到编辑处
到这里,其实我们对 class 文件的修改已经完成了,省去了方法一中的 java 代码转换为 class 的操作和 jar 打包操作。
最后的最后
我在想,如果想要修改更复杂的代码满足更大的需求呢?怎么办?
个人总结了一点,那就是:持续学习
比如,我对 JVM 指令掌握的不够,那我需要深入学习;我对反编译相关知识掌握不足,也需要找资源学习.....
推荐一个反编译工具的下载地址: 在线破解工具包: