簡介
由于工作需要反編譯分析 java 源碼,于是需要反編譯器做些改動,所以就有了這篇文章。
這次要分析的反編譯器是 Femflower,是著名 IDE Idea 的反編譯器。源碼也是從 Idea 開源部分摳出來的。
[Github](https://github.com/JetBrains/intellij-community/tree/master/plugins/java-decompiler/engine)
數(shù)據(jù)結(jié)構(gòu)
CodeConstants
package org
.jetbrains .java .decompiler .code
代碼段,即字節(jié)碼中的關(guān)鍵字,是所有關(guān)鍵字的父類型,例如 ALOAD,NEW,RET 等。
其保存了所有字節(jié)碼中關(guān)鍵字對應(yīng)的 Constant,便于詞法解析對應(yīng)。
Instruction
package org.jetbrains.java.decompiler.code;
指令,CodeConstants 的子類型,對應(yīng)字節(jié)碼中的指令,同上例如 ALOAD,NEW,RET。眾多指令的父類:這些 Instructions 將被保存在一個 Class[] 中。方法 ConstantsUtil. getInstructionInstance 從字節(jié)碼數(shù)據(jù)中獲取指令對應(yīng)的 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 ; }
if (cl ==
null ) {instr =
new Instruction();}
else {instr = (Instruction)cl.newInstance();}}instr.opcode = opcode;
return instr;}
catch (Exception ex) {
return null ;}
}
InstructionSequence
指令片段,其實就是一段字節(jié)碼的 Instruction 集合。
IMatchable
package org.jetbrains.java.decompiler.struct.match;
可匹配接口,在字節(jié)碼中,除了上面的所說的指令一類的關(guān)鍵字,那些類似定義的類名,變量名,方法名被稱為 Matchable 字段,可結(jié)構(gòu)化的字段。
Matchable 主要有兩個實現(xiàn),一是 Statement 分支結(jié)構(gòu),對應(yīng)方法,Field,Var 的等聲明,二是 Exprent 表達(dá)式,類似等于,Field 引用,New Object,if,等等。簡單的說 Matchable 類似于指令后面的操作數(shù),即 ALOAD {Mathable}。
從字節(jié)碼中匹配對應(yīng)的 IMatchable 類型如下:
public IMatchable
findObject (MatchNode matchNode,
int index)
位于 Imatchbale 接口內(nèi)。
Statement
package org.jetbrains.java.decompiler.modules.decompiler.stats; 分支結(jié)構(gòu),類似于 if,Switch,Synchronize,Try Catch等含有分支的結(jié)構(gòu)。
Exprent
package org.jetbrains.java.decompiler.modules.decompiler.exps; 表達(dá)式,類似方法調(diào)用,變量引用,常量,賦值,New,return 等等都是表達(dá)式。
Struct
Struct 是比較重要的,描述了 Java 里面幾個比較重要的結(jié)構(gòu),類型,方法體,Field。除此之外還有一個 Context,上下文,和文件的路徑相關(guān)。
CosntantPool
常量池,常量池中除了保存了常量之外,還有 Field,Method 的一些關(guān)鍵信息,類成員的名稱,Modifers 信息都需要到常量池中讀取。
ControlFlowGraph
流程控制圖,CFG,程序中有關(guān)流程控制例如,循環(huán),if 判斷等等,在類似于匯編的字節(jié)碼中,程序順序執(zhí)行,流程控制也是以順序執(zhí)行 + 跳轉(zhuǎn)的方式實現(xiàn)。也就是說在字節(jié)碼中流程控制是一種扁平結(jié)構(gòu)的代碼段,而在 java 源碼中是類似于圖的立體分支結(jié)構(gòu)。
那么將字節(jié)碼中的流程控制代碼段轉(zhuǎn)化為 java 代碼的大致過程就是:
InstructionSequence 代碼段 —(構(gòu)建圖)—> ControlFlowGraph —> Statement 分支語句。。。
/*** 將方法結(jié)構(gòu)體 中的流程控制代碼段 轉(zhuǎn)化為分支語句* @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()); mt.expandData();InstructionSequence seq = mt.getInstructionSequence();ControlFlowGraph graph =
new ControlFlowGraph(seq);DeadCodeHelper.removeDeadBlocks(graph);graph.inlineJsr(mt);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)) {DeadCodeHelper.incorporateValueReturns(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);FinallyProcessor fProc =
new FinallyProcessor(md, varProc);
while (fProc.iterateGraph(mt, root, graph)) {root = DomHelper.parseGraph(graph);}DomHelper.removeSynchronizedHandler(root);SequenceHelper.condenseSequences(root);ClearStructHelper.clearStatements(root);ExprProcessor proc =
new ExprProcessor(md, varProc);proc.processStatement(root, cl);SequenceHelper.condenseSequences(root);
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 ;}
if (isInitializer || !ExitHelper.condenseExits(root)) {
break ;}}ExitHelper.removeRedundantReturns(root);SecondaryFunctionsHelper.identifySecondaryFunctions(root, varProc);varProc.setVarDefinitions(root);LabelHelper.replaceContinueWithBreak(root);mt.releaseResources();
return root;
}
BytecodeMappingTracer
BytecodeMappingTracer 是一個用于跟蹤所寫 java code 行數(shù)的追蹤器,每當(dāng) TextBuffer寫過一行時,tracer 內(nèi)部的行數(shù)都被手動 +1,本來行數(shù)是不包括 Class 體上面的 package 語句和 import 語句的。由于需要自行添加 headlines 變量。
TextBuffer
TextBuffer 其實沒什么好講的,java code 的字符串最后都被塞到了這里,類似于 StringBuffer,不過加上了一些函數(shù)。
需要注意的是對于表達(dá)式來說,表達(dá)式是一個樹形的集合,將表達(dá)式樹寫成 java code 就是對樹進(jìn)行遍歷,而每個表達(dá)式即樹節(jié)點都是一個單獨(dú)的 TextBuffer,最后由 append 拼接。所以想知道某個表達(dá)式在一行中具體的位置是比較困難的。
拿 ExitExprent 即 return 表達(dá)式來說:
@Override
public 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);}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
/*** 輸入分發(fā)*/
public void save () {
switch (type) {
case TYPE_FOLDER:resultSaver.saveFolder(filename);
for (String[] pair : otherEntries) {resultSaver.copyFile(pair[
0 ], filename, pair[
1 ]);}
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);
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 ;
case TYPE_JAR:
case TYPE_ZIP:resultSaver.saveFolder(archivePath);resultSaver.createArchive(archivePath, filename, manifest);
for (String dirEntry : dirEntries) {resultSaver.saveDirEntry(archivePath, filename, dirEntry);}
for (String[] pair : otherEntries) {
if (type != TYPE_JAR || !JarFile.MANIFEST_NAME.equalsIgnoreCase(pair[
1 ])) {resultSaver.copyEntry(pair[
0 ], archivePath, filename, pair[
1 ]);}}
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);}}resultSaver.closeArchive(archivePath, filename);}
}
反編譯一個類
對類基礎(chǔ)結(jié)構(gòu)的解析
入口
/*** 反編譯類文件* @param cl* @return */
@Override
public 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 函數(shù)邏輯比較清晰,先寫 package xxxxx, 然后是 import xxxxxx, 最后是類結(jié)構(gòu)。
/*** 寫類文件* @param cl* @param buffer* @throws IOException*/
public void writeClass (StructClass cl, TextBuffer buffer)
throws IOException {ClassNode root = mapRootClasses.get(cl.qualifiedName);
if (root.type != ClassNode.CLASS_ROOT) {
return ;}DecompilerContext.getLogger().startReadingClass(cl.qualifiedName);
try {ImportCollector importCollector =
new ImportCollector(root);DecompilerContext.setImportCollector(importCollector);DecompilerContext.setCounterContainer(
new CounterContainer());DecompilerContext.setBytecodeSourceMapper(
new BytecodeSourceMapper());
new LambdaProcessor().processClass(root);addClassnameToImport(root, importCollector);initWrappers(root,
null );
new NestedClassProcessor().processClass(root, root);
new NestedMemberAccess().propagateMemberAccess(root);
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();}
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);
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 數(shù)據(jù)結(jié)構(gòu)多了 Class 內(nèi)部,Method 外部的 Expert 表達(dá)式集合,以及 MethodWrapper 集合,而 MethodWrapper 與 MethodNode 的區(qū)別可以類推。
private final VBStyleCollection<Exprent, String> staticFieldInitializers
private final VBStyleCollection<Exprent, String> dynamicFieldInitializers
private final VBStyleCollection<MethodWrapper, String> methods
可以看到主要是 Field 定義右邊的初始化表達(dá)式。
/**** @param node 進(jìn)入是 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
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 ())// 參數(shù)名列表VarNamesCollector vc = new VarNamesCollector()DecompilerContext
.setVarNamesCollector (vc)// 引用計數(shù)器CounterContainer counter = new CounterContainer()DecompilerContext
.setCounterContainer (counter)// 方法描述, 根據(jù)方法全限定名MethodDescriptor md = MethodDescriptor
.parseDescriptor (mt
.getDescriptor ())VarProcessor varProc = new VarProcessor(mt, md)DecompilerContext
.setProperty (DecompilerContext
.CURRENT _VAR_PROCESSOR, varProc)RootStatement root = nullboolean isError = falsetry {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" )//看門狗,當(dāng)處理方法超過指定時間時,則認(rèn)為處理失敗超時,需要強(qiáng)行殺死線程.long stopAt = System
.currentTimeMillis () + maxSec *
1000 mtThread
.start ()while (!mtProc
.isFinished ()) {try {synchronized (mtProc
.lock ) {// 看門狗每
0.2 s 檢查一次時間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 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 = isErrormethods
.addWithKey (methodWrapper, InterpreterUtil
.makeUniqueKey (mt
.getName (), mt
.getDescriptor ()))// rename vars so that no one has the same name as a fieldvarProc
.refreshVarNames (new VarNamesCollector(setFieldNames))// if debug information present
and should be usedif (DecompilerContext
.getOption (IFernflowerPreferences
.USE _DEBUG_VAR_NAMES)) {StructLocalVariableTableAttribute attr = mt
.getLocalVariableAttr ()if (attr != null) {// only param names herevarProc
.setDebugVarNames (attr
.getMapParamNames ())// the rest is heremethodWrapper
.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)eString name = varExprent
.getDebugName (mt)if (name != null) {varProc
.setVarName (varExprent
.getVarVersionPair (), name)}})return
0 })}}DecompilerContext
.getLogger ()
.endMethod ()}DecompilerContext
.getLogger ()
.endClass ()
}
核心:ClassWritter
ClassWritter 主要解析了上面所說的類的最主要的直接成員,嵌套的內(nèi)部類,Field,和 Method。
有三個主要方法:1.classTojava:解析類結(jié)構(gòu)本身,2.fieldTojava:解析 Field 3.methodTojava:解析 Method。
classTojava classToJava 是 ClassWrite 類中其他函數(shù)的入口。其最后一個參數(shù) headLines 是自行添加的。
/**** @param node* @param buffer* @param indent 縮進(jìn) 在 Class 塊中就是 {}* @param tracer* @param headLines 頭部行數(shù)*/
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());invokeProcessors(node);ClassWrapper wrapper = node.getWrapper();StructClass cl = wrapper.getClassStruct();DecompilerContext.getLogger().startWriteClass(cl.qualifiedName);
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));
for (StructField fd : cl.getFields()) {
boolean hide = fd.isSynthetic() && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC) ||wrapper.getHiddenMembers().contains(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()));
if (hide)
continue ;
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 ;}fieldToJava(wrapper, cl, fd, buffer, indent +
1 , dummy_tracer); hasContent =
true ;}
if (enumFields) {buffer.append(
';' ).appendLineSeparator();dummy_tracer.incrementCurrentSourceLine();}startLine += buffer.countLines(start_class_def);
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;}}
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 )//寫廢棄 Annotationif (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 = nullif (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 = charStartposition
.end = charEndposition
.line = lineposition
.src = wrapper
.rootClass .qualifiedName +
".java" field
.setPosition (position)//寫初始化表達(dá)式 int a = {初始化表達(dá)式}Exprent initializerif (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)initializernexpr
.setEnumConst (true)//寫操作交由具體的表達(dá)式結(jié)構(gòu)體代理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))}}//結(jié)束if (!isEnum) {buffer
.append (
";" )
.appendLineSeparator ()tracer
.incrementCurrentSourceLine ()}if (visitor != null) {visitor
.fieldDefine (field)}
}
methodToJava 同樣的,Method 內(nèi)部也有很多表達(dá)式,同樣被代理給了 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; method.setStatic(
true );}
if (CodeConstants.CLINIT_NAME.equals(mt.getName())) {flags &= CodeConstants.ACC_STATIC; 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()) {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(
' ' );}method.getPosition().start = buffer.length();buffer.append(toValidJavaIdentifier(name));method.getPosition().end = buffer.length();method.getPosition().line = tracer.getLinesInJavaSource();buffer.append(
'(' );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); 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 ) {
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(
' ' );}buffer.append(
'{' ).appendLineSeparator();tracer.incrementCurrentSourceLine();RootStatement root = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root;
if (root !=
null && !methodWrapper.decompiledWithErrors) {
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);}
return !hideMethod;
}
表達(dá)式樹以及流程控制
前面相當(dāng)于 java code 的骨架,表達(dá)式相當(dāng)于 java code 的內(nèi)容了,表達(dá)式一般存在于 變量定義的 = 右邊初始化字段,以及 Method 內(nèi)部大量的表達(dá)式。
一組有關(guān)的表達(dá)式(一般是一行)是樹形結(jié)構(gòu),上面說過將表達(dá)式樹轉(zhuǎn)換為 java code 即是樹的遍歷。遍歷的同時調(diào)用 toJava 組裝 TextBuffer
public TextBuffer
toJava (
int indent, BytecodeMappingTracer tracer) {
throw new RuntimeException(
"not implemented" );
}
可見是交給具體的表達(dá)式實現(xiàn)。 至于 Statement 流程控制語句或者說是分支語句則差不多。
總結(jié)
以上是生活随笔 為你收集整理的java 反编译器源码分析 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔 推薦給好友。