Github每日精选(第38期):Java 8+ Jar 和 Android APK 逆向工程套件(反编译器、编辑器、调试器等)bytecode-viewer
bytecode-viewer
bytecode-viewer
是一个 轻量级用户友好的 Java/Android 字节码查看器、反编译器。
BCV 由几个开源工具提供支持,旨在帮助逆向过程。
BCV 带有 6 个反编译器、3 个反汇编器、2 个汇编器、2 个 APK 转换器、高级搜索、调试等。
它完全用 Java 编写,并且是开源的。它目前由 Konloch 维护和开发。
github 的地址在这里。
主要特征
- 只需拖放即可反编译和搜索 Java Jars 和 Android APK
- 文件格式支持:Class、Jar、XAPK、APK、DEX、WAR、JSP、图像资源、文本资源等
- 6 个内置 Java 反编译器:Krakatau、CFR、Procyon、FernFlower、JADX、JD-GUI
- 3 个内置字节码反汇编器,包括 2 个汇编器:Krakatau 和 Smali/BakSmali
- 来自 Dex2Jar 和 Enjarify 的 APK/DEX 支持
- 内置Java编译器
- 高级静态搜索功能
- 可定制的用户界面
- 插件+脚本引擎设计
- 恶意代码扫描API
- 翻译成 30 多种语言,包括:阿拉伯语、德语、日语、普通话、俄语、西班牙语)
- 将功能导出为 Runnable Jar、Zip、APK、反编译为 Zip 等。
命令行输入
-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
如何使用
- 从 Jar、Zip、ClassFile 或 Android 文件(APK、DEX、XAPK 等)开始,将其拖入 BCV。它将自动开始解码过程。
- 从这里您可以通过选择 View Pane>View 1、View 2、View 3 等来选择您想要使用的反编译器。
- 视图窗格用于并排显示最多 3 个反编译器,您也可以在此处切换可编辑性。
- 通过使用资源列表导航选择您要打开的资源,BCV 将尽力显示它(反编译、反汇编等)。
- 您可以使用插件来帮助您搜索以及使用左下角的搜索窗格。
代码分析
这里主要分析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("<init>")) {
sb.append(class_);
} else if (!m.name.equals("<clinit>")) {
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("<clinit>")) {
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<string>
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("<clinit>"))
sb.append(" // <clinit>");
else if (m.name.equals("<init>"))
sb.append(" // <init>");
}
sb.append(" //");
sb.append(m.desc);
sb.append(nl);
if (m.signature != null) {
sb.append(" <sig:").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;
}