From ba7a354efae7d49833c887cf147ac940c975a1fa Mon Sep 17 00:00:00 2001 From: Gegy Date: Wed, 30 Jan 2019 21:05:32 +0200 Subject: Remap sources (#106) * Source remapping beginnings * Fix navigation to remapped classes * Translate identifier info reference * Remap local variables with default names in source * Caching translator * Fix lack of highlighting for first opened class * Fix unicode variable names * Unicode checker shouldn't be checking just alphanumeric * Fix package tree being built from obf names * Don't index `this` as method call for method::reference * Apply proposed names * Fix source export issues * Replace unicode var names at bytecode level uniquely * Drop imports from editor source * Class selector fixes * Delta keep track of base mappings to enable lookup of old names * Optimize source remapping by remapping source with a StringBuffer instead of copying * Bump version --- .../cuchaz/enigma/gui/DecompiledClassSource.java | 129 +++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java (limited to 'src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java') diff --git a/src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java b/src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java new file mode 100644 index 0000000..03f76c9 --- /dev/null +++ b/src/main/java/cuchaz/enigma/gui/DecompiledClassSource.java @@ -0,0 +1,129 @@ +package cuchaz.enigma.gui; + +import cuchaz.enigma.Deobfuscator; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.SourceIndex; +import cuchaz.enigma.analysis.Token; +import cuchaz.enigma.api.EnigmaPlugin; +import cuchaz.enigma.gui.highlight.TokenHighlightType; +import cuchaz.enigma.translation.LocalNameGenerator; +import cuchaz.enigma.translation.Translator; +import cuchaz.enigma.translation.representation.TypeDescriptor; +import cuchaz.enigma.translation.representation.entry.ClassEntry; +import cuchaz.enigma.translation.representation.entry.Entry; +import cuchaz.enigma.translation.representation.entry.FieldEntry; +import cuchaz.enigma.translation.representation.entry.LocalVariableDefEntry; + +import javax.annotation.Nullable; +import java.util.*; + +public class DecompiledClassSource { + private final ClassEntry classEntry; + private final Deobfuscator deobfuscator; + + private final SourceIndex obfuscatedIndex; + private SourceIndex remappedIndex; + + private final Map> highlightedTokens = new EnumMap<>(TokenHighlightType.class); + + public DecompiledClassSource(ClassEntry classEntry, Deobfuscator deobfuscator, SourceIndex index) { + this.classEntry = classEntry; + this.deobfuscator = deobfuscator; + this.obfuscatedIndex = index; + this.remappedIndex = index; + } + + public void remapSource(Translator translator) { + highlightedTokens.clear(); + + SourceRemapper remapper = new SourceRemapper(obfuscatedIndex.getSource(), obfuscatedIndex.referenceTokens()); + + SourceRemapper.Result remapResult = remapper.remap((token, movedToken) -> remapToken(token, movedToken, translator)); + remappedIndex = obfuscatedIndex.remapTo(remapResult); + } + + private String remapToken(Token token, Token movedToken, Translator translator) { + EntryReference, Entry> reference = obfuscatedIndex.getReference(token); + + if (deobfuscator.isRenamable(reference)) { + Entry entry = reference.getNameableEntry(); + Entry translatedEntry = translator.translate(entry); + + if (isDeobfuscated(entry, translatedEntry)) { + highlightToken(movedToken, TokenHighlightType.DEOBFUSCATED); + return translatedEntry.getSourceRemapName(); + } else { + String proposedName = proposeName(entry); + if (proposedName != null) { + highlightToken(movedToken, TokenHighlightType.PROPOSED); + return proposedName; + } + + highlightToken(movedToken, TokenHighlightType.OBFUSCATED); + + String defaultName = generateDefaultName(translatedEntry); + if (defaultName != null) { + return defaultName; + } + } + } + + return null; + } + + @Nullable + private String proposeName(Entry entry) { + if (entry instanceof FieldEntry) { + for (EnigmaPlugin plugin : deobfuscator.getPlugins()) { + String owner = entry.getContainingClass().getFullName(); + String proposal = plugin.proposeFieldName(owner, entry.getName(), ((FieldEntry) entry).getDesc().toString()); + if (proposal != null) { + return proposal; + } + } + } + return null; + } + + @Nullable + private String generateDefaultName(Entry entry) { + if (entry instanceof LocalVariableDefEntry) { + LocalVariableDefEntry localVariable = (LocalVariableDefEntry) entry; + + int index = localVariable.getIndex(); + if (localVariable.isArgument()) { + List arguments = localVariable.getParent().getDesc().getArgumentDescs(); + return LocalNameGenerator.generateArgumentName(index, localVariable.getDesc(), arguments); + } else { + return LocalNameGenerator.generateLocalVariableName(index, localVariable.getDesc()); + } + } + + return null; + } + + private boolean isDeobfuscated(Entry entry, Entry translatedEntry) { + return !entry.getName().equals(translatedEntry.getName()); + } + + public ClassEntry getEntry() { + return classEntry; + } + + public SourceIndex getIndex() { + return remappedIndex; + } + + public Map> getHighlightedTokens() { + return highlightedTokens; + } + + private void highlightToken(Token token, TokenHighlightType highlightType) { + highlightedTokens.computeIfAbsent(highlightType, t -> new ArrayList<>()).add(token); + } + + @Override + public String toString() { + return remappedIndex.getSource(); + } +} -- cgit v1.2.3