bytecode-viewer 是一个 轻量级用户友好的 Java/Android 字节码查看器、反编译器。
BCV 由几个开源工具提供支持,旨在帮助逆向过程。
BCV 带有 6 个反编译器、3 个反汇编器、2 个汇编器、2 个 APK 转换器、高级搜索、调试等。
它完全用 Java 编写,并且是开源的。它目前由 Konloch 维护和开发。

-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
这里主要分析MethodNodeDecompiler.java 下的反编译代码,反编译代码也是这个项目的核心,通过反编译代码可以看看这种逆向工具是如何做到对编译的代码进行逆向的。
public static PrefixedStringBuilder decompile(PrefixedStringBuilder sb,
MethodNode m, ClassNode cn) {
String class_;
if (cn.name.contains("/")) {
class_ = cn.name.substring(cn.name.lastIndexOf("/") + 1);
} else {
class_ = cn.name;
}
String s = getAccessString(m.access);
sb.append(" ");
sb.append(s);
if (s.length() > 0)
sb.append(" ");
if (m.name.equals("" )) {
sb.append(class_);
} else if (!m.name.equals("" )) {
Type returnType = Type.getReturnType(m.desc);
sb.append(returnType.getClassName());
sb.append(" ");
sb.append(m.name);
}
TypeAndName[] args = new TypeAndName[0];
if (!m.name.equals("" )) {
sb.append("(");
final Type[] argTypes = Type.getArgumentTypes(m.desc);
args = new TypeAndName[argTypes.length];
for (int i = 0; i < argTypes.length; i++) {
final Type type = argTypes[i];
final TypeAndName tan = new TypeAndName();
final String argName = "arg" + i;
tan.name = argName;
tan.type = type;
args[i] = tan;
sb.append(type.getClassName() + " " + argName
+ (i < argTypes.length - 1 ? ", " : ""));
}
sb.append(")");
}
int amountOfThrows = m.exceptions.size();
if (amountOfThrows > 0) {
sb.append(" throws ");
sb.append(m.exceptions.get(0));// exceptions is list
for (int i = 1; i < amountOfThrows; i++) {
sb.append(", ");
sb.append(m.exceptions.get(i));
}
}
if (s.contains("abstract")) {
sb.append(" {}");
sb.append(" //");
sb.append(m.desc);
sb.append(nl);
} else {
sb.append(" {");
if (BytecodeViewer.viewer.debugHelpers.isSelected()) {
if (m.name.equals("" ))
sb.append(" // " );
else if (m.name.equals("" ))
sb.append(" // " );
}
sb.append(" //");
sb.append(m.desc);
sb.append(nl);
if (m.signature != null) {
sb.append(" ).append(m.signature).append(">");
}
if (m.annotationDefault != null) {
sb.append(m.annotationDefault);
sb.append("\n");
}
InstructionPrinter insnPrinter = new InstructionPrinter(m, args);
addAttrList(m.attrs, "attr", sb, insnPrinter);
addAttrList(m.invisibleAnnotations, "invisAnno", sb, insnPrinter);
addAttrList(m.invisibleAnnotations, "invisLocalVarAnno", sb,
insnPrinter);
addAttrList(m.invisibleTypeAnnotations, "invisTypeAnno", sb,
insnPrinter);
addAttrList(m.localVariables, "localVar", sb, insnPrinter);
addAttrList(m.visibleAnnotations, "visAnno", sb, insnPrinter);
addAttrList(m.visibleLocalVariableAnnotations, "visLocalVarAnno",
sb, insnPrinter);
addAttrList(m.visibleTypeAnnotations, "visTypeAnno", sb,
insnPrinter);
List<TryCatchBlockNode> tryCatchBlocks = m.tryCatchBlocks;
for (int i = 0; i < tryCatchBlocks.size(); i++) {
TryCatchBlockNode o = tryCatchBlocks.get(i);
sb.append(" ");
sb.append("TryCatch").append(i).append(": L");
sb.append(insnPrinter.resolveLabel(o.start));
sb.append(" to L");
sb.append(insnPrinter.resolveLabel(o.end));
sb.append(" handled by L");
sb.append(insnPrinter.resolveLabel(o.handler));
sb.append(": ");
if (o.type != null)
sb.append(o.type);
else
sb.append("Type is null.");
sb.append(nl);
}
for (String insn : insnPrinter.createPrint()) {
sb.append(" ");
sb.append(insn);
sb.append(nl);
}
sb.append(" }" + nl);
}
return sb;
}