Bytecode Viewer 2.13 完整指南:Java/Android 逆向工程套件的安装、CLI 反编译与插件开发实战
Bytecode Viewer 2.13 完整指南:Java/Android 逆向工程套件的安装、CLI 反编译与插件开发实战
Bytecode Viewer(BCV)是一款用 Java 编写的轻量级 Java/Android 逆向工程套件,集反编译、字节码查看、编辑、调试与插件扩展于一体。本文以仓库根目录 README.md 为主线,结合源码(入口 BytecodeViewer.java、CLI 解析器 BCVCommandLine.java 等)与示例插件,系统讲解其核心特性、安装启动、命令行动态参数、GUI 使用流程、插件机制与常见问题排查,读完即可上手完成 JAR/APK 的加载、反编译、批量导出与自定义插件编写。
1. 项目概览:它到底能做什么
BCV 定位为"Advanced Lightweight Java/Android Reverse Engineering Suite"(高级轻量级 Java/Android 逆向工程套件),由 Konloch 维护,完全使用 Java 编写并开源,当前仓库版本为 2.13.2(见 pom.xml 中 <version>2.13.2</version>)。其核心能力可概括为:
- 拖放即反编译:将 JAR、Class、APK、DEX、XAPK、APKM、WAR、JSP 以及图片/文本资源直接拖入即可自动解码;
- 多引擎并行:内置 6 个 Java 反编译器(Krakatau、CFR、Procyon、FernFlower、JADX、JD-GUI)、3 个字节码反汇编器、2 个汇编器(Krakatau 与 Smali/BakSmali),以及 2 个 APK 转换器(Dex2Jar 与 Enjarify);
- 可编辑可编译:内置 Java 编译器,支持对 Java 源码与字节码进行修改后重新编译;
- 静态搜索:提供高级静态搜索功能(方法调用、LDC 字符串、正则等);
- 插件化:内置插件系统与脚本引擎,支持 Java、JavaScript、Groovy、Python、Ruby 五种插件语言;
- 恶意代码扫描 API:内置 MalwareScanner 相关模块(malwarescanner),可对加载的类做安全审计;
- 多语言:翻译超过 30 种语言,包括阿拉伯语、德语、日语、中文(Mandarin)、俄语、西班牙语等;
- 导出能力:可将结果导出为可运行的 Jar、Zip、APK,或"全部反编译为 Zip"。
从源码看,这些能力都有明确的落点:反编译器统一继承自 AbstractDecompiler,实现类位于 decompilers/impl;APK 转换工具位于 util/apk2Jar(包含 Apk2Jar.java、Dex2Jar.java、Enjarify.java)。
1.1 版本相关的新特性
README 的 New Features 列出了当前版本值得注意的改进,其中包含安全与工程化层面的变化:
- Jump to Declaration:支持跳转到声明,便于快速定位类与成员定义;
- Draggable tabs:标签页可拖拽重组;
- 安全修复:已修补 CVE-2022-21675(README 建议升级到 v2.11.X 以上),使用旧版本务必升级;
- 深色模式与多主题:默认启用深色模式,可在视觉设置中切换多套主题;
- 30+ 语言翻译:含阿拉伯语、德语、日语、中文、俄语、西班牙语等;
- Plugin Writer:可以在 BCV 内部直接创建和编辑外部插件(对应 PluginWriter.java);
- 修复 Java 与字节码编辑/编译;
- 标签式插件控制台:插件输出以 Tab 形式组织在控制台窗口中;
- 资源列表与搜索面板的右键菜单;
- Javap 反汇编器:对应 JavapDisassembler.java;
- XAPK 支持;
- 依赖升级:包含 CFR、JD-GUI 等反编译器在内的最新依赖;
- 支持 JDK > 13 编译的 class 文件;
- 迁移到 Maven 构建:仓库根目录的 pom.xml 即为 Maven 工程描述文件。
2. 安装与启动
2.1 获取发布包并运行
README 给出的安装方式非常直接:从发布页下载最新版本(README 中示例为 Bytecode-Viewer-2.10.x.jar,实际当前仓库版本为 2.13.2),然后运行:
java -jar Bytecode-Viewer-2.13.2.jar
如果双击无法启动,则必须在命令行中执行上面的 java -jar 命令。启动时终端会先打印版本横幅与版权信息(见 BytecodeViewer.java 的 main 方法)。
2.2 从源码编译
README 明确给出编译方式:"Just clone this repo and run mvn package. It's that simple!"。仓库采用 Maven 构建,源码编译流程为:
git clone https://gitcode.com/gh_mirrors/by/bytecode-viewer
cd bytecode-viewer
mvn package
若要在 IDE(如 IntelliJ)中打开工程开发,直接以 pom.xml 作为项目文件导入即可。pom.xml 的 <properties> 中声明了完整的依赖版本矩阵,例如 ASM 9.9.1、CFR 0.152、Procyon 0.6.0、JADX 1.4.7、JD-GUI 1.6.6bcv、dex2jar 2.4.34、smali 3.0.9、apktool 2.12.1 等,编译目标为 Java 8(<java.version>1.8</java.version>),这也是"支持 JDK > 13 编译的 class 文件"能够成立的基础——BCV 自身运行在较老 JDK 上,但能解析更新的字节码版本。
2.3 启动参数与内存配置
- 堆内存不足(java.lang.OutOfMemoryError):加载大型 JAR/APK 时容易触发,README 建议用更大的堆启动,例如
java -Xmx3G -jar BCV.jar; - Windows 文件权限问题(java.io.FileNotFoundException):右键 jar 文件 → 属性 → 常规选项卡底部"安全"区域选择"解除锁定"(Unblock);
- APK 文件权限问题:以管理员身份运行 BCV;
- 界面卡顿(UI Lagging):进入
View -> Visual Settings -> Window Theme选择System Theme(系统主题),README 明确建议更换为系统主题以解决卡顿。
从 BytecodeViewer.java 可以看出,BCV 在启动时会尝试设置 SecurityManager(SecurityMan.java),用于动态分析调试时的安全防护;如果在 Java 18+ 上遇到 "Cannot set security manager!" 提示,需要以 -Djava.security.manager=allow 参数允许。
3. 命令行接口(CLI):参数全解与执行逻辑
BCV 内置了基于 Apache Commons CLI 的命令行解析器,核心实现位于 BCVCommandLine.java,各子命令位于 cli/actions/commands 目录。README 中给出了完整的 CLI 参数清单:
-help Displays the help menu
-clean Deletes the BCV directory
-english Forces English language translations
-list Displays the available decompilers
-decompiler <decompiler> Selects the decompiler, procyon by default
-i <input file> Selects the input file (Jar, Class, APK, ZIP, DEX all work automatically)
-o <output file> Selects the output file (Java or Java-Bytecode)
-t <target classname> Must either be the fully qualified classname or "all" to decompile all as zip
-nowait Doesn't wait for the user to read the CLI messages
3.1 参数语义与源码印证
对照源码可以确认每个参数的真实行为:
-help:打印帮助菜单。对应 HelpCommand.java,它会输出 CLI 与 GUI 两段命令帮助。-clean/-cleanboot:-clean删除 BCV 目录;Help 输出中还列出了-cleanboot(删除 BCV 目录后继续启动 GUI),对应 CleanBootCommand.java 与 CleanCommand.java。-english/-language <language>:-english强制使用英语翻译;-language可指定具体语言并继续启动 GUI,对应 LanguageCommand.java。语言匹配支持严格匹配与松散匹配两种策略:先按枚举名、可读名称、语言代码严格匹配,未命中再按包含关系做松散匹配(源码中依次遍历Language.values()与lang.getLanguageCode())。-list:列出可用的反编译器,对应 ListCommand.java,输出为:Procyon、CFR、FernFlower、Krakatau、Krakatau-Bytecode、JD-GUI、Smali、ASMifier 共 8 项。-decompiler <decompiler>:选择反编译器,默认 procyon。在 BCVCommandLine.java 中,非法的反编译器名称会被校验拒绝,并提示Type -list for the list。-i <input file>:输入文件,JAR、Class、APK、ZIP、DEX 均可自动识别。CLI 中若缺失-i会输出Set the input with -i(BCVCommandLine.java)。-o <output file>:输出文件(Java 源码或 Java-Bytecode)。若输出文件已存在,CLI 会打印警告并删除旧文件(BCVCommandLine.java)。-t <target classname>:目标类名,必须是全限定类名,或传all表示将全部类反编译打包为 Zip。-nowait:跳过 5 秒等待。CLI 默认在开始反编译前SleepUtil.sleep(5 * 1000)等待 5 秒以让用户阅读输出(BCVCommandLine.java),加上-nowait可立即执行。
3.2 CLI 的实际执行流程
BCVCommandLine.java 的 init(String<a href="https://link.gitcode.com/i/e3c02c09b9012e2031f29ad4c70c3dcc" target="_blank">] args) 依次完成:注册选项(-i、-o、-t、-nowait)与 6 个 CLICommand(Clean、CleanBoot、Decompiler、Language、Help、List)→ 通过 containsCLICommand 判断是否进入纯 CLI 模式 → parseCommandLine 解析参数并执行对应命令。当检测到 CLI 命令时,BytecodeViewer.main 不会初始化 Swing GUI(见 [BytecodeViewer.java 中 if(!CLI.isCLI()) 的分支),从而实现无头模式运行。
一个完整的 CLI 反编译示例(将 myapp.jar 用 Procyon 反编译为 Java 源码):
java -jar Bytecode-Viewer-2.13.2.jar -i myapp.jar -o output -t all -decompiler procyon -nowait
4. GUI 使用指南:拖放、视图面板与资源导航
4.1 加载资源
README 建议的使用方式:将 JAR、Zip、ClassFile 或 Android 文件(APK、DEX、XAPK、APKM 等)直接拖入 BCV,它会自动开始解码流程。也可以在启动时把文件路径作为命令行参数传入,main 方法中的 openFilesFromLaunchArguments()(BytecodeViewer.java)会为每个非命令参数调用 openFiles 打开文件。
4.2 视图面板(View Panes)
加载完成后,通过 View Pane -> View 1、View 2、View 3 等 选择要使用的反编译器。视图面板用于并排显示最多 3 个反编译器/反汇编器的输出,并可在该处切换"可编辑"状态——这是 BCV 的核心交互之一:同一类可以在不同面板中分别展示 Java 反编译结果与字节码,并允许对任意面板进行编辑。
4.3 资源列表与导航
在左侧资源列表中选中要打开的资源,BCV 会尽力以最合适的方式展示(反编译、反汇编、十六进制等)。支持右键菜单操作(展开/折叠、删除、新建、快速编辑、快速打开等,见 gui/contextmenu/resourcelist 目录)。右下/左下角的搜索面板(searching/SearchPanel.java)支持方法调用搜索、LDC 字符串搜索、字段调用搜索、带注解成员搜索与正则搜索。
4.4 编辑与编译
BCV 内置 Java 编译器(compilers/impl/JavaCompiler.java)以及 Krakatau、Smali 两个汇编器。修改 Java 源码或字节码后,可通过 BytecodeViewer.compile(...)(BytecodeViewer.java)对打开的全部可编译面板统一编译;该方法的源码注释也提醒:所有修改代码的插件都应调用 BytecodeViewer.updateAllClassNodeByteArrays() 与 BytecodeViewer.refreshAllTabs() 才能让改动生效。
5. 反编译器与反汇编器机制
5.1 6 个内置 Java 反编译器
| 反编译器 | 源码实现 | 说明 |
|---|---|---|
| Krakatau | KrakatauDecompiler.java | 以 Krakatau 为核心的反编译/反汇编实现 |
| CFR | CFRDecompiler.java | 知名 Java 反编译器 CFR(0.152) |
| Procyon | ProcyonDecompiler.java | 默认反编译器,Procyon 0.6.0 |
| FernFlower | FernFlowerDecompiler.java | IntelliJ 同源反编译器 |
| JADX | JADXDecompiler.java | 常用于 APK/DEX |
| JD-GUI | JDGUIDecompiler.java | JD-GUI 1.6.6bcv |
所有反编译器统一继承 AbstractDecompiler,暴露两个核心抽象方法:decompileClassNode(ClassNode cn, byte[] bytes)(单类反编译)与 decompileToZip(String sourceJar, String zipName)(整包批量反编译导出),这正是"导出为 Zip / Decompile All As Zip"功能的底层实现。
以默认的 ProcyonDecompiler.java 为例,其单类反编译流程为:把 ClassNode 字节写入临时 .class 文件 → 初始化 DecompilerSettings、MetadataSystem、DecompilationOptions → 通过 lookupType 解析类型 → decompileType 输出为文本 → 最后用 EncodeUtils.unicodeToString 处理 Unicode 转义。decompileToZip 则遍历 Jar 条目,将每个 .class 条目反编译为同名 .java 条目写入 Zip 输出流,非 class 资源按原样复制(并对重复条目做去重处理)。
5.2 3 个字节码反汇编器与 2 个汇编器
除 Java 反编译器外,BCV 还内置了字节码层面的工具:
- 反汇编器:Krakatau(字节码)、Smali(DEX/APK 场景,SmaliDisassembler.java)、Javap(JavapDisassembler.java)以及 ASM 相关的 ASMDisassembler.java、ASMifierGenerator.java(
-list中的 Krakatau-Bytecode、Smali、ASMifier 即来自这些实现); - 汇编器:KrakatauAssembler(KrakatauAssembler.java)与 SmaliAssembler(SmaliAssembler.java)。
5.3 Procyon 的 GUI 可调选项
以 Procyon 为例,ProcyonDecompiler.java 的 getDecompilerSettings() 揭示了 GUI 中可供调节的反编译选项及其默认行为:
alwaysGenerateExceptionVars:始终为 catch 块生成异常变量;excludeNestedTypes:排除嵌套类型;showDebugLineNumbers/includeLineNumbersInBytecode:显示调试行号 / 字节码中包含行号;includeErrorDiagnostics:包含错误诊断信息;showSyntheticMembers:显示合成成员;simplifyMemberReferences:简化成员引用;mergeVariables:合并变量;forceExplicitTypeArguments/forceExplicitImports:强制显式类型参数 / 显式导入;flattenSwitchBlocks:扁平化 switch 块;retainPointlessSwitches/retainRedundantCasts:保留无意义 switch / 冗余强转;unicodeOutputEnabled:Unicode 输出。
这些开关与 MainViewerGUI.java 中的菜单项一一对应,理解它们有助于根据反编译目标调出更干净的输出。
6. 插件系统:原理、五种语言与实战示例
6.1 插件工作原理
README 对插件机制做了清晰说明:插件系统允许你与已加载的 class 文件交互,例如编写字符串去混淆器(String deobfuscator)、恶意代码搜索器,或任何你能想到的工具。插件激活后,BCV 会以"包含 BCV 中加载的每一个类的 ClassNode ArrayList"调用插件,让用户完全通过 ASM 来处理这些类。
从源码看,Plugin.java 继承自 Thread,execute(List<ClassNode> classNodeList) 是插件唯一需要实现的抽象方法。插件运行时:
run()先设置忙碌状态,若无已加载资源则直接返回;executeContainer()遍历每个 ResourceContainer,将当前容器设为activeContainer,再以该容器的全部resourceClasses为参数调用execute(...);- 插件执行完毕设置
finished = true并恢复忙碌状态。
PluginManager.java 负责插件的加载与调度,其静态初始化块注册了扩展名到启动策略的映射:
| 扩展名 | 启动策略 | 对应源码 |
|---|---|---|
| jar | CompiledJavaPluginLaunchStrategy | CompiledJavaPluginLaunchStrategy.java |
| java | JavaPluginLaunchStrategy | JavaPluginLaunchStrategy.java |
| js | JavascriptPluginLaunchStrategy | JavascriptPluginLaunchStrategy.java |
| gy / groovy | GroovyPluginLaunchStrategy | GroovyPluginLaunchStrategy.java |
| py / python | PythonPluginLaunchStrategy | PythonPluginLaunchStrategy.java |
| rb / ruby | RubyPluginLaunchStrategy | RubyPluginLaunchStrategy.java |
注意:README 中描述插件系统"supports java and javascript scripting",而当前仓库的 PluginManager.java 已扩展支持 groovy、python、ruby 等更多语言,这是比 README 更新进化的实现事实。PluginManager 同一时间只允许运行一个插件(ONE_PLUGIN_AT_A_TIME),插件输出的控制台与异常会以 Tab 形式聚合到标签式控制台(PluginConsole.java)。
6.2 预置插件清单
仓库自带一批可直接使用的预置插件(plugin/preinstalled),例如:
- AllatoriStringDecrypter:Allatori 混淆字符串解密;
- ZKMStringDecrypter / ZStringArrayDecrypter:ZKM 与 ZString 字符串解密;
- ChangeClassFileVersions:修改 class 文件版本;
- CodeSequenceDiagram:生成代码时序图;
- EZInjection:EZ 注入;
- MaliciousCodeScanner:恶意代码扫描;
- ReplaceStrings:批量替换字符串/字符串数组;
- ShowAllStrings / ShowMainMethods:展示全部字符串 / 展示所有 main 方法;
- StackFramesRemover:移除栈帧;
- ViewAPKAndroidPermissions / ViewManifest:查看 APK 权限与 Manifest。
以 ReplaceStrings.java 为例,它遍历每个 ClassNode 的字段,把值为 String 的字段内容替换为目标字符串;类名传 * 时作用于全部类,否则只作用于指定类——这就是一个典型的基于 ASM 的字符串批量处理插件。
6.3 编写你的第一个 Java 插件
仓库 plugins/java 目录提供了现成的插件模板与示例。一个最小 Java 插件只需继承 Plugin 并实现 execute:
import java.util.List;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.api.Plugin;
public class Skeleton extends Plugin {
@Override
public void execute(List<ClassNode> classNodeList) {
// 在此用 ASM 处理所有已加载的类
for (ClassNode cn : classNodeList) {
System.out.println(cn.name);
}
}
}
完整的字符串解密示例见 plugins/java/ExampleStringDecrypter.java:它通过 BCV.getClassNodeLoader().addClass(cn) 把 ClassNode 加载进类加载器,再反射读取字段 z 的值并输出到 PluginConsole 控制台。该示例还演示了一个重要安全实践:执行类初始化可能触发恶意代码,因此弹窗警告用户"IF THE FILE YOU'RE LOADING IS MALICIOUS, DO NOT CONTINUE"。
JavaScript 与 Python 的对应模板分别见 plugins/javascript/Skeleton.js 与 plugins/python/skeleton.py,Groovy 与 Ruby 模板见 plugins/groovy/Skeleton.gy、plugins/ruby/Skeleton.rb。各语言的具体插件写法可参考同名目录下的 ExampleStringDecrypter 与 ExamplePrintClassesPlugin 示例。
7. 常见问题排查汇总
将 README 的 FAQ 与源码信息整理如下:
| 症状 | 解决方案 |
|---|---|
| UI 卡顿(UI Lagging) | View -> Visual Settings -> Window Theme 中选择 System Theme |
| java.lang.OutOfMemoryError | 加大堆内存启动:java -Xmx3G -jar BCV.jar |
| java.io.FileNotFoundException(Windows) | 右键 jar 文件 → 属性 → 常规页底部安全区域点击"解除锁定"(Unblock) |
| java.io.FileNotFoundException(APK) | 以管理员身份运行 BCV |
| Java 18+ 无法设置 SecurityManager | 使用 -Djava.security.manager=allow 参数启动(源码 BytecodeViewer.java 的提示信息) |
| 插件修改代码后不生效 | 调用 BytecodeViewer.updateAllClassNodeByteArrays() 与 BytecodeViewer.refreshAllTabs()(源码 TODO 注释明确要求) |
8. 相关资源索引
- 主文档:README.md
- 贡献指南:CONTRIBUTING.md
- 致谢名单:CREDITS.md
- 安全说明:SECURITY.md
- 开源许可:LICENSE(README 标注为 Copyleft 许可)
- Maven 构建描述:pom.xml
- 插件示例目录:plugins/java、plugins/javascript、plugins/python、plugins/groovy、plugins/ruby
- 安装辅助脚本(Windows):install/_install BCV.bat 与 install/_uninstall BCV.bat
结语
Bytecode Viewer 把反编译、反汇编、编辑、编译、搜索、插件与恶意代码扫描整合进一个轻量级 Java 应用中,无论是逆向分析入门还是日常的 APK/JAR 审计,都能在拖放之间完成主要工作流。本文覆盖了 README 中的安装、CLI 参数、GUI 操作与插件机制,并深入 BCVCommandLine.java、AbstractDecompiler.java、Plugin.java 等核心源码,帮助你从"会用"进阶到"理解其内部运作"。上手验证的最快路径就是:mvn package 编译,或直接 java -jar 运行发布包,拖入一个 JAR 开始探索。