java 反编译器源码分析
来源:互联网 发布:cnc编程学徒招聘 编辑:程序博客网 时间:2024/05/19 18:15
简介
由于工作需要反编译分析 java 源码,于是需要反编译器做些改动,所以就有了这篇文章。这次要分析的反编译器是 Femflower,是著名 IDE Idea 的反编译器。源码也是从 Idea 开源部分抠出来的。[Github](https://github.com/JetBrains/intellij-community/tree/master/plugins/java-decompiler/engine)
数据结构
CodeConstants
package org.jetbrains.java.decompiler.code;
代码段,即字节码中的关键字,是所有关键字的父类型,例如 ALOAD,NEW,RET 等。其保存了所有字节码中关键字对应的 Constant,便于词法解析对应。
Instruction
package org.jetbrains.java.decompiler.code;
指令,CodeConstants 的子类型,对应字节码中的指令,同上例如 ALOAD,NEW,RET。众多指令的父类:这些 Instructions 将被保存在一个 Class[] 中。方法 ConstantsUtil. getInstructionInstance 从字节码数据中获取指令对应的 Instruction 类型。
private static Instruction getInstructionInstance(int opcode, int bytecode_version) { try { Instruction instr; if ((opcode >= CodeConstants.opc_ifeq && opcode <= CodeConstants.opc_if_acmpne) || opcode == CodeConstants.opc_ifnull || opcode == CodeConstants.opc_ifnonnull) { instr = new IfInstruction(); } else { Class cl = opcodeClasses[opcode]; if (opcode == CodeConstants.opc_invokedynamic && bytecode_version < CodeConstants.BYTECODE_JAVA_7) { cl = null; // instruction unused in Java 6 and before } if (cl == null) { instr = new Instruction(); } else { instr = (Instruction)cl.newInstance(); } } instr.opcode = opcode; return instr; } catch (Exception ex) { return null; }}
InstructionSequence
指令片段,其实就是一段字节码的 Instruction 集合。
IMatchable
package org.jetbrains.java.decompiler.struct.match;
可匹配接口,在字节码中,除了上面的所说的指令一类的关键字,那些类似定义的类名,变量名,方法名被称为 Matchable 字段,可结构化的字段。Matchable 主要有两个实现,一是 Statement 分支结构,对应方法,Field,Var 的等声明,二是 Exprent 表达式,类似等于,Field 引用,New Object,if,等等。简单的说 Matchable 类似于指令后面的操作数,即 ALOAD {Mathable}。从字节码中匹配对应的 IMatchable 类型如下:
public IMatchable findObject(MatchNode matchNode, int index)
位于 Imatchbale 接口内。
Statement
package org.jetbrains.java.decompiler.modules.decompiler.stats;
分支结构,类似于 if,Switch,Synchronize,Try Catch等含有分支的结构。
Exprent
package org.jetbrains.java.decompiler.modules.decompiler.exps;
表达式,类似方法调用,变量引用,常量,赋值,New,return 等等都是表达式。
Struct
Struct 是比较重要的,描述了 Java 里面几个比较重要的结构,类型,方法体,Field。除此之外还有一个 Context,上下文,和文件的路径相关。
CosntantPool
常量池,常量池中除了保存了常量之外,还有 Field,Method 的一些关键信息,类成员的名称,Modifers 信息都需要到常量池中读取。
ControlFlowGraph
流程控制图,CFG,程序中有关流程控制例如,循环,if 判断等等,在类似于汇编的字节码中,程序顺序执行,流程控制也是以顺序执行 + 跳转的方式实现。也就是说在字节码中流程控制是一种扁平结构的代码段,而在 java 源码中是类似于图的立体分支结构。那么将字节码中的流程控制代码段转化为 java 代码的大致过程就是:
InstructionSequence 代码段 —(构建图)—> ControlFlowGraph —> Statement 分支语句。。。
/** * 将方法结构体 中的流程控制代码段 转化为分支语句 * @param mt * @param md * @param varProc * @return * @throws IOException */public static RootStatement codeToJava(StructMethod mt, MethodDescriptor md, VarProcessor varProc) throws IOException { StructClass cl = mt.getClassStruct(); boolean isInitializer = CodeConstants.CLINIT_NAME.equals(mt.getName()); // for now static initializer only mt.expandData(); //获取方法体的指令片段 InstructionSequence seq = mt.getInstructionSequence(); //控制流程图 ControlFlowGraph graph = new ControlFlowGraph(seq); //移除死循环的流程块 DeadCodeHelper.removeDeadBlocks(graph); //内联方法程序跳转处理 graph.inlineJsr(mt); // TODO: move to the start, before jsr inlining DeadCodeHelper.connectDummyExitBlock(graph); DeadCodeHelper.removeGotos(graph); ExceptionDeobfuscator.removeCircularRanges(graph); ExceptionDeobfuscator.restorePopRanges(graph); if (DecompilerContext.getOption(IFernflowerPreferences.REMOVE_EMPTY_RANGES)) { ExceptionDeobfuscator.removeEmptyRanges(graph); } if (DecompilerContext.getOption(IFernflowerPreferences.NO_EXCEPTIONS_RETURN)) { // special case: single return instruction outside of a protected range DeadCodeHelper.incorporateValueReturns(graph); } // ExceptionDeobfuscator.restorePopRanges(graph); ExceptionDeobfuscator.insertEmptyExceptionHandlerBlocks(graph); DeadCodeHelper.mergeBasicBlocks(graph); DecompilerContext.getCounterContainer().setCounter(CounterContainer.VAR_COUNTER, mt.getLocalVariables()); if (ExceptionDeobfuscator.hasObfuscatedExceptions(graph)) { DecompilerContext.getLogger().writeMessage("Heavily obfuscated exception ranges found!", IFernflowerLogger.Severity.WARN); } //流程控制快 -> 代码块 RootStatement root = DomHelper.parseGraph(graph); //处理 finally 块 FinallyProcessor fProc = new FinallyProcessor(md, varProc); while (fProc.iterateGraph(mt, root, graph)) { root = DomHelper.parseGraph(graph); } // remove synchronized exception handler // not until now because of comparison between synchronized statements in the finally cycle DomHelper.removeSynchronizedHandler(root); // LabelHelper.lowContinueLabels(root, new HashSet<StatEdge>()); SequenceHelper.condenseSequences(root); ClearStructHelper.clearStatements(root); //表达式处理 ExprProcessor proc = new ExprProcessor(md, varProc); proc.processStatement(root, cl); SequenceHelper.condenseSequences(root); //参数栈 v1 v2 v3 什么的 while (true) { StackVarsProcessor stackProc = new StackVarsProcessor(); stackProc.simplifyStackVars(root, mt, cl); varProc.setVarVersions(root); if (!new PPandMMHelper().findPPandMM(root)) { break; } } while (true) { LabelHelper.cleanUpEdges(root); while (true) { MergeHelper.enhanceLoops(root); if (LoopExtractHelper.extractLoops(root)) { continue; } if (!IfHelper.mergeAllIfs(root)) { break; } } if (DecompilerContext.getOption(IFernflowerPreferences.IDEA_NOT_NULL_ANNOTATION)) { if (IdeaNotNullHelper.removeHardcodedChecks(root, mt)) { SequenceHelper.condenseSequences(root); StackVarsProcessor stackProc = new StackVarsProcessor(); stackProc.simplifyStackVars(root, mt, cl); varProc.setVarVersions(root); } } LabelHelper.identifyLabels(root); if (InlineSingleBlockHelper.inlineSingleBlocks(root)) { continue; } // initializer may have at most one return point, so no transformation of method exits permitted if (isInitializer || !ExitHelper.condenseExits(root)) { break; } // FIXME: !! // if(!EliminateLoopsHelper.eliminateLoops(root)) { // break; // } } ExitHelper.removeRedundantReturns(root); SecondaryFunctionsHelper.identifySecondaryFunctions(root, varProc); varProc.setVarDefinitions(root); // must be the last invocation, because it makes the statement structure inconsistent // FIXME: new edge type needed LabelHelper.replaceContinueWithBreak(root); mt.releaseResources(); return root;}
BytecodeMappingTracer
BytecodeMappingTracer 是一个用于跟踪所写 java code 行数的追踪器,每当 TextBuffer写过一行时,tracer 内部的行数都被手动 +1,本来行数是不包括 Class 体上面的 package 语句和 import 语句的。由于需要自行添加 headlines 变量。
TextBuffer
TextBuffer 其实没什么好讲的,java code 的字符串最后都被塞到了这里,类似于 StringBuffer,不过加上了一些函数。需要注意的是对于表达式来说,表达式是一个树形的集合,将表达式树写成 java code 就是对树进行遍历,而每个表达式即树节点都是一个单独的 TextBuffer,最后由 append 拼接。所以想知道某个表达式在一行中具体的位置是比较困难的。 拿 ExitExprent 即 return 表达式来说:
@Overridepublic TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { tracer.addMapping(bytecode); if (exitType == EXIT_RETURN) { TextBuffer buffer = new TextBuffer("return"); if (retType.type != CodeConstants.TYPE_VOID) { buffer.append(' '); ExprProcessor.getCastedExprent(value, retType, buffer, indent, false, tracer); }………………………….. return buffer; }
过程
遍历 .class 文件
- Femflower.decmpileContext
//开始反编译
public void decompileContext() { if (DecompilerContext.getOption(IFernflowerPreferences.RENAME_ENTITIES)) { new IdentifierConverter().rename(structContext); } //从 Class 节点开始反编译 classesProcessor = new ClassesProcessor(structContext); classesProcessor.visitor = visitor; DecompilerContext.setClassProcessor(classesProcessor); DecompilerContext.setStructContext(structContext); structContext.saveContext();}
- StructContext.saveContext
public void saveContext() { for (ContextUnit unit : units.values()) { if (unit.isOwn()) { unit.save(); } }}
- ContextUnit.save
/** * 输入分发 */public void save() { switch (type) { //文件夹 case TYPE_FOLDER: // create folder resultSaver.saveFolder(filename); // non-class files 无内容的文件,直接拷贝 for (String[] pair : otherEntries) { resultSaver.copyFile(pair[0], filename, pair[1]); } // classes 类文件,需要解析 for (int i = 0; i < classes.size(); i++) { StructClass cl = classes.get(i); String entryName = decompiledData.getClassEntryName(cl, classEntries.get(i)); if (entryName != null) { //反编译这个类 得到 java String String content = decompiledData.getClassContent(cl); if (content != null) { int[] mapping = null; if (DecompilerContext.getOption(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING)) { mapping = DecompilerContext.getBytecodeSourceMapper().getOriginalLinesMapping(); } //保存到本地 resultSaver.saveClassFile(filename, cl.qualifiedName, entryName, content, mapping); } } } break; //jar 文件同下 zip 文件 case TYPE_JAR: case TYPE_ZIP: // create archive file resultSaver.saveFolder(archivePath); resultSaver.createArchive(archivePath, filename, manifest); // directory entries 生成文件夹结构 for (String dirEntry : dirEntries) { resultSaver.saveDirEntry(archivePath, filename, dirEntry); } // non-class entries 无内容文件 copy for (String[] pair : otherEntries) { if (type != TYPE_JAR || !JarFile.MANIFEST_NAME.equalsIgnoreCase(pair[1])) { resultSaver.copyEntry(pair[0], archivePath, filename, pair[1]); } } // classes 类文件 for (int i = 0; i < classes.size(); i++) { StructClass cl = classes.get(i); String entryName = decompiledData.getClassEntryName(cl, classEntries.get(i)); if (entryName != null) { //反编译 String content = decompiledData.getClassContent(cl); //保存 resultSaver.saveClassEntry(archivePath, filename, cl.qualifiedName, entryName, content); } } //关闭 zip 文件 resultSaver.closeArchive(archivePath, filename); }}
反编译一个类
对类基础结构的解析
- 入口
/** * 反编译类文件 * @param cl * @return */@Overridepublic String getClassContent(StructClass cl) { try { TextBuffer buffer = new TextBuffer(ClassesProcessor.AVERAGE_CLASS_SIZE); buffer.append(DecompilerContext.getProperty(IFernflowerPreferences.BANNER).toString()); classesProcessor.writeClass(cl, buffer); return buffer.toString(); } catch (Throwable ex) { DecompilerContext.getLogger().writeMessage("Class " + cl.qualifiedName + " couldn't be fully decompiled.", ex); return null; }}
- ClassesProcessor.writeClass
writeClass 函数逻辑比较清晰,先写 package xxxxx, 然后是 import xxxxxx, 最后是类结构。
/** * 写类文件 * @param cl * @param buffer * @throws IOException */public void writeClass(StructClass cl, TextBuffer buffer) throws IOException { //class 树,包含父类,内部类外部类等等 ClassNode root = mapRootClasses.get(cl.qualifiedName); if (root.type != ClassNode.CLASS_ROOT) { return; } DecompilerContext.getLogger().startReadingClass(cl.qualifiedName); try { //import 语句的集合 ImportCollector importCollector = new ImportCollector(root); DecompilerContext.setImportCollector(importCollector); DecompilerContext.setCounterContainer(new CounterContainer()); DecompilerContext.setBytecodeSourceMapper(new BytecodeSourceMapper()); new LambdaProcessor().processClass(root); // add simple class names to implicit import addClassnameToImport(root, importCollector); // build wrappers for all nested classes (that's where actual processing takes place) 实际拼装类代码的地方 initWrappers(root, null); // build 内部类 new NestedClassProcessor().processClass(root, root); // build 内部类 meber 引用 new NestedMemberAccess().propagateMemberAccess(root); // 写 package 语句 int index = cl.qualifiedName.lastIndexOf("/"); if (index >= 0) { String packageName = cl.qualifiedName.substring(0, index).replace('/', '.'); buffer.append("package "); buffer.append(packageName); buffer.append(";"); buffer.appendLineSeparator(); buffer.appendLineSeparator(); } // 写 import 语句 int import_lines_written = importCollector.writeImports(buffer); if (import_lines_written > 0) { buffer.appendLineSeparator(); } int offsetLines = buffer.countLines(); TextBuffer classBuffer = new TextBuffer(AVERAGE_CLASS_SIZE); // 重点,解析 class 结构 new ClassWriter(visitor).classToJava(root, classBuffer, 0, null, offsetLines); buffer.append(classBuffer); if (DecompilerContext.getOption(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING)) { BytecodeSourceMapper mapper = DecompilerContext.getBytecodeSourceMapper(); mapper.addTotalOffset(offsetLines); if (DecompilerContext.getOption(IFernflowerPreferences.DUMP_ORIGINAL_LINES)) { buffer.dumpOriginalLineNumbers(mapper.getOriginalLinesMapping()); } if (DecompilerContext.getOption(IFernflowerPreferences.UNIT_TEST_MODE)) { buffer.appendLineSeparator(); mapper.dumpMapping(buffer, true); } } } finally { destroyWrappers(root); DecompilerContext.getLogger().endReadingClass(); }}
- initWrappers
ClassWrapper 对比 ClassNode 这个已知的 Class 数据结构多了 Class 内部,Method 外部的 Expert 表达式集合,以及 MethodWrapper 集合,而 MethodWrapper 与 MethodNode 的区别可以类推。
private final VBStyleCollection<Exprent, String> staticFieldInitializersprivate final VBStyleCollection<Exprent, String> dynamicFieldInitializersprivate final VBStyleCollection<MethodWrapper, String> methods
可以看到主要是 Field 定义右边的初始化表达式。
/** * * @param node 进入是 root class * @throws IOException */private static void initWrappers(ClassNode node, ClassNode root) throws IOException { if (node.type == ClassNode.CLASS_LAMBDA) { return; } ClassWrapper wrapper = new ClassWrapper(node.classStruct); // 手动添加根类型 if (root == null) { wrapper.rootClass = node.classStruct; root = node; } else { wrapper.rootClass = root.classStruct; } //结构解析入口 wrapper.init(); node.wrapper = wrapper; // 递归解析内部类 for (ClassNode nd : node.nested) { initWrappers(nd, root); }}
- ClassWrapper.init
/** * 从字节码中解析类结构 * @throws IOException */public void init() throws IOException { DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS, classStruct); DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_WRAPPER, this); DecompilerContext.getLogger().startClass(classStruct.qualifiedName); // collect field names Set<String> setFieldNames = new HashSet<>(); for (StructField fd : classStruct.getFields()) { setFieldNames.add(fd.getName()); } int maxSec = Integer.parseInt(DecompilerContext.getProperty(IFernflowerPreferences.MAX_PROCESSING_METHOD).toString()); boolean testMode = DecompilerContext.getOption(IFernflowerPreferences.UNIT_TEST_MODE); // 拼装方法 for (StructMethod mt : classStruct.getMethods()) { DecompilerContext.getLogger().startMethod(mt.getName() + " " + mt.getDescriptor()); // 参数名列表 VarNamesCollector vc = new VarNamesCollector(); DecompilerContext.setVarNamesCollector(vc); // 引用计数器 CounterContainer counter = new CounterContainer(); DecompilerContext.setCounterContainer(counter); // 方法描述, 根据方法全限定名 MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); VarProcessor varProc = new VarProcessor(mt, md); DecompilerContext.setProperty(DecompilerContext.CURRENT_VAR_PROCESSOR, varProc); RootStatement root = null; boolean isError = false; try { if (mt.containsCode()) { if (maxSec == 0 || testMode) { root = MethodProcessorRunnable.codeToJava(mt, md, varProc); } else { MethodProcessorRunnable mtProc = new MethodProcessorRunnable(mt, md, varProc, DecompilerContext.getCurrentContext()); Thread mtThread = new Thread(mtProc, "Java decompiler"); //看门狗,当处理方法超过指定时间时,则认为处理失败超时,需要强行杀死线程. long stopAt = System.currentTimeMillis() + maxSec * 1000; mtThread.start(); while (!mtProc.isFinished()) { try { synchronized (mtProc.lock) { // 看门狗每 0.2s 检查一次时间 mtProc.lock.wait(200); } } catch (InterruptedException e) { killThread(mtThread); throw e; } //同上 if (System.currentTimeMillis() >= stopAt) { String message = "Processing time limit exceeded for method " + mt.getName() + ", execution interrupted."; DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.ERROR); killThread(mtThread); isError = true; break; } } if (!isError) { root = mtProc.getResult(); } } } else { boolean thisVar = !mt.hasModifier(CodeConstants.ACC_STATIC); int paramCount = 0; if (thisVar) { varProc.getThisVars().put(new VarVersionPair(0, 0), classStruct.qualifiedName); paramCount = 1; } paramCount += md.params.length; int varIndex = 0; for (int i = 0; i < paramCount; i++) { varProc.setVarName(new VarVersionPair(varIndex, 0), vc.getFreeName(varIndex)); if (thisVar) { if (i == 0) { varIndex++; } else { varIndex += md.params[i - 1].stackSize; } } else { varIndex += md.params[i].stackSize; } } } } catch (Throwable ex) { DecompilerContext.getLogger().writeMessage("Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be decompiled.", ex); isError = true; } MethodWrapper methodWrapper = new MethodWrapper(root, varProc, mt, counter); methodWrapper.decompiledWithErrors = isError; methods.addWithKey(methodWrapper, InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor())); // rename vars so that no one has the same name as a field varProc.refreshVarNames(new VarNamesCollector(setFieldNames)); // if debug information present and should be used if (DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_VAR_NAMES)) { StructLocalVariableTableAttribute attr = mt.getLocalVariableAttr(); if (attr != null) { // only param names here varProc.setDebugVarNames(attr.getMapParamNames()); // the rest is here methodWrapper.getOrBuildGraph().iterateExprents(exprent -> { List<Exprent> lst = exprent.getAllExprents(true); lst.add(exprent); lst.stream() .filter(e -> e.type == Exprent.EXPRENT_VAR) .forEach(e -> { VarExprent varExprent = (VarExprent)e; String name = varExprent.getDebugName(mt); if (name != null) { varProc.setVarName(varExprent.getVarVersionPair(), name); } }); return 0; }); } } DecompilerContext.getLogger().endMethod(); } DecompilerContext.getLogger().endClass();}
核心:ClassWritter
ClassWritter 主要解析了上面所说的类的最主要的直接成员,嵌套的内部类,Field,和 Method。有三个主要方法:1.classTojava:解析类结构本身,2.fieldTojava:解析 Field 3.methodTojava:解析 Method。
classTojava
classToJava 是 ClassWrite 类中其他函数的入口。其最后一个参数 headLines 是自行添加的。
/** * * @param node * @param buffer * @param indent 缩进 在 Class 块中就是 {} * @param tracer * @param headLines 头部行数 */public void classToJava(ClassNode node, TextBuffer buffer, int indent, BytecodeMappingTracer tracer, int headLines) { ClassNode outerNode = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE); DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, node); int startLine = tracer != null ? tracer.getCurrentSourceLine() : 0; BytecodeMappingTracer dummy_tracer = new BytecodeMappingTracer(startLine, headLines); dummy_tracer.visitor = visitor; dummy_tracer.srcPath = node.getWrapper().rootClass.qualifiedName + ".java"; dummy_tracer.classKey = node.classStruct.qualifiedName; dummy_tracer.methodKey = ""; try { Type type = new Type(); type.setName(node.simpleName); type.setKey(node.classStruct.qualifiedName); type.setFullName(node.classStruct.qualifiedName.replaceAll("/", ".")); type.setPosition(new Position()); // last minute processing invokeProcessors(node); ClassWrapper wrapper = node.getWrapper(); StructClass cl = wrapper.getClassStruct(); DecompilerContext.getLogger().startWriteClass(cl.qualifiedName); // write class definition // 写方法描述 这里插入 TypeDefineNode int start_class_def = buffer.length(); writeClassDefinition(node, buffer, indent, type); type.getPosition().line = startLine + headLines; boolean hasContent = false; boolean enumFields = false; dummy_tracer.incrementCurrentSourceLine(buffer.countLines(start_class_def)); // 写 Fields for (StructField fd : cl.getFields()) { // 是否是隐藏 field 如 this$0 boolean hide = fd.isSynthetic() && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC) || wrapper.getHiddenMembers().contains(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); if (hide) continue; // enum field boolean isEnum = fd.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); if (isEnum) { if (enumFields) { buffer.append(',').appendLineSeparator(); dummy_tracer.incrementCurrentSourceLine(); } enumFields = true; } else if (enumFields) { buffer.append(';'); buffer.appendLineSeparator(); buffer.appendLineSeparator(); dummy_tracer.incrementCurrentSourceLine(2); enumFields = false; } // 写 Fields fieldToJava(wrapper, cl, fd, buffer, indent + 1, dummy_tracer); // FIXME: insert real tracer hasContent = true; } if (enumFields) { buffer.append(';').appendLineSeparator(); dummy_tracer.incrementCurrentSourceLine(); } // FIXME: fields don't matter at the moment startLine += buffer.countLines(start_class_def); // methods for (StructMethod mt : cl.getMethods()) { boolean hide = mt.isSynthetic() && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC) || mt.hasModifier(CodeConstants.ACC_BRIDGE) && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_BRIDGE) || wrapper.getHiddenMembers().contains(InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor())); if (hide) continue; int position = buffer.length(); int storedLine = startLine; if (hasContent) { buffer.appendLineSeparator(); startLine++; } BytecodeMappingTracer method_tracer = new BytecodeMappingTracer(startLine, headLines); method_tracer.visitor = visitor; method_tracer.srcPath = node.getWrapper().rootClass.qualifiedName + ".java"; method_tracer.classKey = node.classStruct.qualifiedName; method_tracer.methodKey = "." + mt.getName() + mt.getDescriptor(); boolean methodSkipped = !methodToJava(node, mt, buffer, indent + 1, method_tracer); if (!methodSkipped) { hasContent = true; addTracer(cl, mt, method_tracer); startLine = method_tracer.getCurrentSourceLine(); } else { buffer.setLength(position); startLine = storedLine; } } // member classes 内部类 for (ClassNode inner : node.nested) { if (inner.type == ClassNode.CLASS_MEMBER) { StructClass innerCl = inner.classStruct; boolean isSynthetic = (inner.access & CodeConstants.ACC_SYNTHETIC) != 0 || innerCl.isSynthetic() || inner.namelessConstructorStub; boolean hide = isSynthetic && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC) || wrapper.getHiddenMembers().contains(innerCl.qualifiedName); if (hide) continue; if (hasContent) { buffer.appendLineSeparator(); startLine++; } BytecodeMappingTracer class_tracer = new BytecodeMappingTracer(startLine, headLines); class_tracer.visitor = visitor; class_tracer.srcPath = node.getWrapper().rootClass.qualifiedName + ".java"; class_tracer.classKey = inner.classStruct.qualifiedName; class_tracer.methodKey = ""; // 递归调用 classToJava(inner, buffer, indent + 1, class_tracer, headLines); startLine = buffer.countLines(); hasContent = true; } } buffer.appendIndent(indent).append('}'); if (node.type != ClassNode.CLASS_ANONYMOUS) { buffer.appendLineSeparator(); } if (visitor != null) { visitor.typeDefine(type); } } finally { DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, outerNode); } DecompilerContext.getLogger().endWriteClass();}
classToField
写 Field ,需要注意的是 Field 的初始化即 = 右边的写则代理给了 Exprent.toJava 方法。
//写 Field 重点 ReferenceNode 切入点private void fieldToJava(ClassWrapper wrapper, StructClass cl, StructField fd, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) { Field field = new Field(); int start = buffer.length(); boolean isInterface = cl.hasModifier(CodeConstants.ACC_INTERFACE); boolean isDeprecated = fd.getAttributes().containsKey("Deprecated"); boolean isEnum = fd.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); field.setName(fd.getName()); field.setInterface(isInterface); field.setEnum(isEnum); field.setKey(cl.qualifiedName + "." + fd.getName()); field.setFullName(cl.qualifiedName.replaceAll("/", ".") + "." + fd.getName()); field.setMemberClass(tracer.classKey); //写废弃 Annotation if (isDeprecated) { appendDeprecation(buffer, indent); } if (interceptor != null) { String oldName = interceptor.getOldName(cl.qualifiedName + " " + fd.getName() + " " + fd.getDescriptor()); appendRenameComment(buffer, oldName, MType.FIELD, indent); } //匿名字段 多为编译器生成字段 if (fd.isSynthetic()) { appendComment(buffer, "synthetic field", indent); } //写注解 插入点 appendAnnotations(buffer, indent, fd, TypeAnnotation.FIELD); buffer.appendIndent(indent); if (!isEnum) { appendModifiers(buffer, fd.getAccessFlags(), FIELD_ALLOWED, isInterface, FIELD_EXCLUDED); } VarType fieldType = new VarType(fd.getDescriptor(), false); field.setTypeKey(fieldType.value); // field 全限定名 GenericFieldDescriptor descriptor = null; if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)fd.getAttributes().getWithKey("Signature"); if (attr != null) { descriptor = GenericMain.parseFieldSignature(attr.getSignature()); } } if (!isEnum) { Position typePosition = new Position(); typePosition.line = tracer.getLinesInJavaSource(); field.setTypePosition(typePosition); if (descriptor != null) { //由 field 全限定名获取 field 类型的名称 typePosition.start = buffer.length(); String typeKey = GenericMain.getGenericCastTypeName(descriptor.type); buffer.append(typeKey); typePosition.end = buffer.length(); } else { typePosition.start = buffer.length(); String typeKey = ExprProcessor.getCastTypeName(fieldType); buffer.append(typeKey); typePosition.end = buffer.length(); } buffer.append(' '); } int charStart = buffer.length(); //重点 写 field 的名字 buffer.append(fd.getName()); int charEnd = buffer.length(); int line = tracer.getLinesInJavaSource(); //行号增加 tracer.incrementCurrentSourceLine(buffer.countLines(start)); Position position = new Position(); position.start = charStart; position.end = charEnd; position.line = line; position.src = wrapper.rootClass.qualifiedName + ".java"; field.setPosition(position); //写初始化表达式 int a = {初始化表达式}; Exprent initializer; if (fd.hasModifier(CodeConstants.ACC_STATIC)) { field.setStatic(true); initializer = wrapper.getStaticFieldInitializers().getWithKey(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); } else { initializer = wrapper.getDynamicFieldInitializers().getWithKey(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); } if (initializer != null) { if (isEnum && initializer.type == Exprent.EXPRENT_NEW) { NewExprent nexpr = (NewExprent)initializer; nexpr.setEnumConst(true); //写操作交由具体的表达式结构体代理 buffer.append(nexpr.toJava(indent, tracer)); } else { buffer.append(" = "); // FIXME: special case field initializer. Can map to more than one method (constructor) and bytecode intruction. buffer.append(initializer.toJava(indent, tracer)); } } else if (fd.hasModifier(CodeConstants.ACC_FINAL) && fd.hasModifier(CodeConstants.ACC_STATIC)) { StructConstantValueAttribute attr = (StructConstantValueAttribute)fd.getAttributes().getWithKey(StructGeneralAttribute.ATTRIBUTE_CONSTANT_VALUE); if (attr != null) { PrimitiveConstant constant = cl.getPool().getPrimitiveConstant(attr.getIndex()); buffer.append(" = "); buffer.append(new ConstExprent(fieldType, constant.value, null).toJava(indent, tracer)); } } //结束 if (!isEnum) { buffer.append(";").appendLineSeparator(); tracer.incrementCurrentSourceLine(); } if (visitor != null) { visitor.fieldDefine(field); }}
methodToJava
同样的,Method 内部也有很多表达式,同样被代理给了 Exprent.toJava
/** * 写 method * @param node * @param mt * @param buffer * @param indent * @param tracer * @return */private boolean methodToJava(ClassNode node, StructMethod mt, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) { Method method = new Method(); method.setName(mt.getName()); method.setPosition(new Position()); method.getPosition().src = node.getWrapper().rootClass.qualifiedName + ".java"; ClassWrapper wrapper = node.getWrapper(); StructClass cl = wrapper.getClassStruct(); MethodWrapper methodWrapper = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()); method.setDefineClass(cl.qualifiedName); method.setKey(cl.qualifiedName + "." + mt.getName() + mt.getDescriptor()); method.setFullName(cl.qualifiedName.replaceAll("/", ".") + "." + mt.getName()); boolean hideMethod = false; int start_index_method = buffer.length(); MethodWrapper outerWrapper = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER); DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, methodWrapper); try { boolean isInterface = cl.hasModifier(CodeConstants.ACC_INTERFACE); boolean isAnnotation = cl.hasModifier(CodeConstants.ACC_ANNOTATION); boolean isEnum = cl.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM); boolean isDeprecated = mt.getAttributes().containsKey("Deprecated"); boolean clinit = false, init = false, dinit = false; MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); method.setRetType(md.ret.value); if (md.params != null && md.params.length > 0) { List<String> parsKey = new ArrayList<>(); for (VarType parType:md.params) { parsKey.add(parType.value); } } int flags = mt.getAccessFlags(); method.setStatic(mt.hasModifier(CodeConstants.ACC_STATIC)); if ((flags & CodeConstants.ACC_NATIVE) != 0) { flags &= ~CodeConstants.ACC_STRICT; // compiler bug: a strictfp class sets all methods to strictfp method.setStatic(true); } if (CodeConstants.CLINIT_NAME.equals(mt.getName())) { flags &= CodeConstants.ACC_STATIC; // ignore all modifiers except 'static' in a static initializer method.setStatic(true); } if (isDeprecated) { appendDeprecation(buffer, indent); } if (interceptor != null) { String oldName = interceptor.getOldName(cl.qualifiedName + " " + mt.getName() + " " + mt.getDescriptor()); appendRenameComment(buffer, oldName, MType.METHOD, indent); } boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || mt.getAttributes().containsKey("Synthetic"); boolean isBridge = (flags & CodeConstants.ACC_BRIDGE) != 0; if (isSynthetic) { appendComment(buffer, "synthetic method", indent); } if (isBridge) { appendComment(buffer, "bridge method", indent); } //写注解 appendAnnotations(buffer, indent, mt, TypeAnnotation.METHOD_RETURN_TYPE); buffer.appendIndent(indent); appendModifiers(buffer, flags, METHOD_ALLOWED, isInterface, METHOD_EXCLUDED); method.setAbstract(mt.hasModifier(CodeConstants.ACC_ABSTRACT)); method.setPublic(mt.hasModifier(CodeConstants.ACC_PUBLIC)); method.setModifiers(flags); if (isInterface) { method.setAbstract(true); } if (isInterface && mt.containsCode()) { // 'default' modifier (Java 8) buffer.append("default "); method.setAbstract(false); } String name = mt.getName(); if (CodeConstants.INIT_NAME.equals(name)) { if (node.type == ClassNode.CLASS_ANONYMOUS) { name = ""; dinit = true; } else { name = node.simpleName; init = true; } } else if (CodeConstants.CLINIT_NAME.equals(name)) { name = ""; clinit = true; } //方法名,方法描述 GenericMethodDescriptor descriptor = null; if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) { StructGenericSignatureAttribute attr = (StructGenericSignatureAttribute)mt.getAttributes().getWithKey("Signature"); if (attr != null) { descriptor = GenericMain.parseMethodSignature(attr.getSignature()); if (descriptor != null) { long actualParams = md.params.length; List<VarVersionPair> sigFields = methodWrapper.signatureFields; if (sigFields != null) { actualParams = sigFields.stream().filter(Objects::isNull).count(); } else if (isEnum && init) actualParams -= 2; if (actualParams != descriptor.params.size()) { String message = "Inconsistent generic signature in method " + mt.getName() + " " + mt.getDescriptor() + " in " + cl.qualifiedName; DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN); descriptor = null; } } } } boolean throwsExceptions = false; int paramCount = 0; if (!clinit && !dinit) { boolean thisVar = !mt.hasModifier(CodeConstants.ACC_STATIC); // 方法返回值 if (descriptor != null && !descriptor.fparameters.isEmpty()) { appendTypeParameters(buffer, descriptor.fparameters, descriptor.fbounds); buffer.append(' '); } if (!init) { if (descriptor != null) { buffer.append(GenericMain.getGenericCastTypeName(descriptor.ret)); } else { buffer.append(ExprProcessor.getCastTypeName(md.ret)); } buffer.append(' '); } //append 方法名 method.getPosition().start = buffer.length(); buffer.append(toValidJavaIdentifier(name)); method.getPosition().end = buffer.length(); method.getPosition().line = tracer.getLinesInJavaSource(); buffer.append('('); // parameters 方法参数 List<VarVersionPair> signFields = methodWrapper.signatureFields; int lastVisibleParameterIndex = -1; for (int i = 0; i < md.params.length; i++) { if (signFields == null || signFields.get(i) == null) { lastVisibleParameterIndex = i; } } boolean firstParameter = true; int index = isEnum && init ? 3 : thisVar ? 1 : 0; boolean hasDescriptor = descriptor != null; int start = isEnum && init && !hasDescriptor ? 2 : 0; int params = hasDescriptor ? descriptor.params.size() : md.params.length; for (int i = start; i < params; i++) { if (hasDescriptor || (signFields == null || signFields.get(i) == null)) { if (!firstParameter) { buffer.append(", "); } // 参数上的注解 appendParameterAnnotations(buffer, mt, paramCount); if (methodWrapper.varproc.getVarFinal(new VarVersionPair(index, 0)) == VarTypeProcessor.VAR_EXPLICIT_FINAL) { buffer.append("final "); } if (descriptor != null) { GenericType parameterType = descriptor.params.get(i); boolean isVarArg = (i == lastVisibleParameterIndex && mt.hasModifier(CodeConstants.ACC_VARARGS) && parameterType.arrayDim > 0); if (isVarArg) { parameterType = parameterType.decreaseArrayDim(); } String typeName = GenericMain.getGenericCastTypeName(parameterType); if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) && DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); } buffer.append(typeName); if (isVarArg) { buffer.append("..."); } } else { VarType parameterType = md.params[i]; boolean isVarArg = (i == lastVisibleParameterIndex && mt.hasModifier(CodeConstants.ACC_VARARGS) && parameterType.arrayDim > 0); if (isVarArg) { parameterType = parameterType.decreaseArrayDim(); } String typeName = ExprProcessor.getCastTypeName(parameterType); if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) && DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) { typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT); } buffer.append(typeName); if (isVarArg) { buffer.append("..."); } } buffer.append(' '); String parameterName = methodWrapper.varproc.getVarName(new VarVersionPair(index, 0)); // 写参数名字 buffer.append(parameterName == null ? "param" + index : parameterName); // null iff decompiled with errors firstParameter = false; paramCount++; } index += md.params[i].stackSize; } buffer.append(')'); // 异常列表 StructExceptionsAttribute attr = (StructExceptionsAttribute)mt.getAttributes().getWithKey("Exceptions"); if ((descriptor != null && !descriptor.exceptions.isEmpty()) || attr != null) { throwsExceptions = true; buffer.append(" throws "); List<String> exceptions = new ArrayList<>(); for (int i = 0; i < attr.getThrowsExceptions().size(); i++) { if (i > 0) { buffer.append(", "); } if (descriptor != null && !descriptor.exceptions.isEmpty()) { GenericType type = descriptor.exceptions.get(i); buffer.append(GenericMain.getGenericCastTypeName(type)); exceptions.add(descriptor.exceptions.get(i).value); } else { VarType type = new VarType(attr.getExcClassname(i, cl.getPool()), true); buffer.append(ExprProcessor.getCastTypeName(type)); } } method.setExceptions(exceptions); } } tracer.incrementCurrentSourceLine(buffer.countLines(start_index_method)); if ((flags & (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_NATIVE)) != 0) { // native or abstract method (explicit or interface) if (isAnnotation) { StructAnnDefaultAttribute attr = (StructAnnDefaultAttribute)mt.getAttributes().getWithKey("AnnotationDefault"); if (attr != null) { buffer.append(" default "); buffer.append(attr.getDefaultValue().toJava(0, BytecodeMappingTracer.DUMMY)); } } buffer.append(';'); buffer.appendLineSeparator(); tracer.incrementCurrentSourceLine(); } else { if (!clinit && !dinit) { buffer.append(' '); } // We do not have line information for method start, lets have it here for now buffer.append('{').appendLineSeparator(); tracer.incrementCurrentSourceLine(); // 写方法内部众多的表达式 RootStatement root = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root; if (root != null && !methodWrapper.decompiledWithErrors) { // check for existence try { TextBuffer code = root.toJava(indent + 1, tracer); hideMethod = (clinit || dinit || hideConstructor(wrapper, init, throwsExceptions, paramCount)) && code.length() == 0; buffer.append(code); } catch (Throwable ex) { DecompilerContext.getLogger().writeMessage("Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be written.", ex); methodWrapper.decompiledWithErrors = true; } } if (methodWrapper.decompiledWithErrors) { buffer.appendIndent(indent + 1); buffer.append("// $FF: Couldn't be decompiled"); buffer.appendLineSeparator(); tracer.incrementCurrentSourceLine(); } if (root != null) { tracer.addMapping(root.getDummyExit().bytecode); } buffer.appendIndent(indent).append('}').appendLineSeparator(); tracer.incrementCurrentSourceLine(); } } finally { DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, outerWrapper); } if (visitor != null) { visitor.methodDefine(method); } // save total lines // TODO: optimize //tracer.setCurrentSourceLine(buffer.countLines(start_index_method)); return !hideMethod;}
表达式树以及流程控制
前面相当于 java code 的骨架,表达式相当于 java code 的内容了,表达式一般存在于 变量定义的 = 右边初始化字段,以及 Method 内部大量的表达式。一组有关的表达式(一般是一行)是树形结构,上面说过将表达式树转换为 java code 即是树的遍历。遍历的同时调用 toJava 组装 TextBuffer
public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) { throw new RuntimeException("not implemented");}
可见是交给具体的表达式实现。
至于 Statement 流程控制语句或者说是分支语句则差不多。
- java 反编译器源码分析
- 反编译器看源码
- java反编译器
- Java反编译器比较
- Java反编译器剖析
- 反编译器 java
- Java反编译器JD
- Java反编译器
- java的反编译器
- java的反编译器
- Java反编译器JD
- Java反编译器:Java Decompiler
- Java反编译器 Java Decompiler
- Java反编译器的问题
- 推荐一款Java反编译器
- JAVA反编译器使用记
- 推荐一款JAVA反编译器
- JCavaj Java Decompiler 免费java反编译器
- 加班到底有什么用
- dragger2
- HDOJ-- 2051 Bitset
- php实现的简单问卷调查系统
- 虚函数 C++(三)
- java 反编译器源码分析
- PHP 操作 Redis
- 关于ostream_iterator<int>(cout," ")的理解
- 用python实现数组
- C++bind
- 多态 C++(四)
- 关于函数返回值的判断
- 虚继承执行顺序
- 数组和指针