Bytecode Viewer 2.13 完整指南:Java/Android 逆向工程套件的安装、CLI 反编译与插件开发实战

原创2026-10-02 21:50:111,151 阅读
文章标签:逆向工程开发工具

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 还内置了字节码层面的工具:

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) 是插件唯一需要实现的抽象方法。插件运行时:

  1. run() 先设置忙碌状态,若无已加载资源则直接返回;
  2. executeContainer() 遍历每个 ResourceContainer,将当前容器设为 activeContainer,再以该容器的全部 resourceClasses 为参数调用 execute(...);
  3. 插件执行完毕设置 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. 相关资源索引

结语

Bytecode Viewer 把反编译、反汇编、编辑、编译、搜索、插件与恶意代码扫描整合进一个轻量级 Java 应用中,无论是逆向分析入门还是日常的 APK/JAR 审计,都能在拖放之间完成主要工作流。本文覆盖了 README 中的安装、CLI 参数、GUI 操作与插件机制,并深入 BCVCommandLine.java、AbstractDecompiler.java、Plugin.java 等核心源码,帮助你从"会用"进阶到"理解其内部运作"。上手验证的最快路径就是:mvn package 编译,或直接 java -jar 运行发布包,拖入一个 JAR 开始探索。

登录后查看全文
bytecode-viewer