From c61cb80a8b0ea1bd7160e0bc69c6af83c28306fe Mon Sep 17 00:00:00 2001 From: LBJ-the-GOAT <66319139+LBJ-the-GOAT@users.noreply.github.com> Date: Wed, 27 Jan 2021 19:58:57 +0800 Subject: [PATCH] feat(gui): rename local variables (#1023) (#1084) (PR #1098) Co-authored-by: tobias --- .../main/java/jadx/api/JadxDecompiler.java | 9 +- .../src/main/java/jadx/api/JavaVariable.java | 60 ++++++++++ .../main/java/jadx/core/codegen/InsnGen.java | 43 ++++--- .../java/jadx/core/codegen/MethodGen.java | 16 ++- .../java/jadx/core/codegen/RegionGen.java | 35 ++++-- .../java/jadx/core/deobf/DeobfPresets.java | 43 ++++++- .../java/jadx/core/deobf/Deobfuscator.java | 12 +- .../java/jadx/core/dex/info/MethodInfo.java | 28 ++++- .../core/dex/instructions/args/CodeVar.java | 17 ++- .../core/dex/instructions/args/NamedArg.java | 15 ++- .../dex/instructions/args/VisibleVar.java | 14 +++ .../java/jadx/core/dex/nodes/MethodNode.java | 66 +++++++++-- .../jadx/core/dex/nodes/VariableNode.java | 109 ++++++++++++++++++ .../java/jadx/core/utils/CodeGenUtils.java | 11 ++ .../java/jadx/gui/treemodel/JVariable.java | 51 ++++++++ .../main/java/jadx/gui/ui/RenameDialog.java | 21 ++-- .../main/java/jadx/gui/utils/JNodeCache.java | 14 +-- .../java/jadx/gui/utils/JumpPosition.java | 11 +- 18 files changed, 497 insertions(+), 78 deletions(-) create mode 100644 jadx-core/src/main/java/jadx/api/JavaVariable.java create mode 100644 jadx-core/src/main/java/jadx/core/dex/instructions/args/VisibleVar.java create mode 100644 jadx-core/src/main/java/jadx/core/dex/nodes/VariableNode.java create mode 100644 jadx-gui/src/main/java/jadx/gui/treemodel/JVariable.java diff --git a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java index cfd072b85..a56479ca4 100644 --- a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java +++ b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java @@ -30,10 +30,7 @@ import jadx.api.plugins.input.data.ILoadResult; import jadx.core.Jadx; import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.nodes.LineAttrNode; -import jadx.core.dex.nodes.ClassNode; -import jadx.core.dex.nodes.FieldNode; -import jadx.core.dex.nodes.MethodNode; -import jadx.core.dex.nodes.RootNode; +import jadx.core.dex.nodes.*; import jadx.core.dex.visitors.SaveCode; import jadx.core.export.ExportGradleProject; import jadx.core.utils.Utils; @@ -449,6 +446,10 @@ public final class JadxDecompiler implements Closeable { if (obj instanceof FieldNode) { return getJavaFieldByNode((FieldNode) obj); } + if (obj instanceof VariableNode) { + VariableNode varNode = (VariableNode) obj; + return new JavaVariable(getJavaClassByNode(varNode.getClassNode()), varNode); + } throw new JadxRuntimeException("Unexpected node type: " + obj); } diff --git a/jadx-core/src/main/java/jadx/api/JavaVariable.java b/jadx-core/src/main/java/jadx/api/JavaVariable.java new file mode 100644 index 000000000..476a8609f --- /dev/null +++ b/jadx-core/src/main/java/jadx/api/JavaVariable.java @@ -0,0 +1,60 @@ +package jadx.api; + +import java.util.Collections; +import java.util.List; + +import jadx.core.dex.nodes.VariableNode; + +public class JavaVariable implements JavaNode { + JavaClass cls; + VariableNode node; + + public JavaVariable(JavaClass cls, VariableNode node) { + this.cls = cls; + this.node = node; + } + + public VariableNode getVariableNode() { + return node; + } + + @Override + public String getName() { + return node.getName(); + } + + @Override + public String getFullName() { + return node.getName(); + } + + @Override + public JavaClass getDeclaringClass() { + return cls; + } + + @Override + public JavaClass getTopParentClass() { + return cls.getTopParentClass(); + } + + @Override + public int getDecompiledLine() { + return node.getDecompiledLine(); + } + + @Override + public List getUseIn() { + return Collections.emptyList(); + } + + @Override + public int hashCode() { + return node.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return node.equals(obj); + } +} diff --git a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java index 54137789d..71bd15a73 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -35,25 +35,16 @@ import jadx.core.dex.instructions.InvokeNode; import jadx.core.dex.instructions.InvokeType; import jadx.core.dex.instructions.NewArrayNode; import jadx.core.dex.instructions.SwitchInsn; -import jadx.core.dex.instructions.args.ArgType; -import jadx.core.dex.instructions.args.CodeVar; -import jadx.core.dex.instructions.args.InsnArg; -import jadx.core.dex.instructions.args.InsnWrapArg; -import jadx.core.dex.instructions.args.LiteralArg; -import jadx.core.dex.instructions.args.Named; -import jadx.core.dex.instructions.args.RegisterArg; -import jadx.core.dex.instructions.args.SSAVar; +import jadx.core.dex.instructions.args.*; import jadx.core.dex.instructions.mods.ConstructorInsn; import jadx.core.dex.instructions.mods.TernaryInsn; -import jadx.core.dex.nodes.ClassNode; -import jadx.core.dex.nodes.FieldNode; -import jadx.core.dex.nodes.InsnNode; -import jadx.core.dex.nodes.MethodNode; -import jadx.core.dex.nodes.RootNode; +import jadx.core.dex.nodes.*; +import jadx.core.utils.CodeGenUtils; import jadx.core.utils.RegionUtils; import jadx.core.utils.exceptions.CodegenException; import jadx.core.utils.exceptions.JadxRuntimeException; +import static jadx.core.dex.nodes.VariableNode.*; import static jadx.core.utils.android.AndroidResourcesUtils.handleAppResField; public class InsnGen { @@ -97,12 +88,26 @@ public class InsnGen { public void addArg(CodeWriter code, InsnArg arg, boolean wrap) throws CodegenException { if (arg.isRegister()) { + CodeVar codeVar = CodeGenUtils.getCodeVar((RegisterArg) arg); + if (codeVar != null) { + VariableNode node = mth.getVariable(codeVar.getIndex()); + if (node != null) { + node.useVar(code, codeVar); + code.attachAnnotation(node); + } + } code.add(mgen.getNameGen().useArg((RegisterArg) arg)); } else if (arg.isLiteral()) { code.add(lit((LiteralArg) arg)); } else if (arg.isInsnWrap()) { addWrappedArg(code, (InsnWrapArg) arg, wrap); } else if (arg.isNamed()) { + if (arg instanceof NamedArg) { + VariableNode node = mth.getVariable(((NamedArg) arg).getIndex()); + if (node != null) { + code.attachAnnotation(node); + } + } code.add(((Named) arg).getName()); } else { throw new CodegenException("Unknown arg type " + arg); @@ -140,7 +145,16 @@ public class InsnGen { } useType(code, codeVar.getType()); code.add(' '); - code.add(mgen.getNameGen().assignArg(codeVar)); + VariableNode node = mth.declareVar(codeVar, mgen.getNameGen(), VarKind.VAR); + String name; + if (node != null) { + code.attachDefinition(node); + name = node.getName(); + codeVar.setName(name); + } else { + name = mgen.getNameGen().assignArg(codeVar); + } + code.add(name); } private String lit(LiteralArg arg) { @@ -639,6 +653,7 @@ public class InsnGen { if (insn.isSuper()) { code.add("super"); } else if (insn.isThis()) { + code.attachAnnotation(mth.getParentClass()); code.add("this"); } else { code.add("new "); diff --git a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java index 9306edd0d..3e5466d8a 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java @@ -29,6 +29,7 @@ import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.instructions.args.SSAVar; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; +import jadx.core.dex.nodes.VariableNode; import jadx.core.dex.trycatch.CatchAttr; import jadx.core.dex.visitors.DepthTraversal; import jadx.core.dex.visitors.IDexTreeVisitor; @@ -42,6 +43,7 @@ import jadx.core.utils.exceptions.JadxOverflowException; import static jadx.core.codegen.MethodGen.FallbackOption.BLOCK_DUMP; import static jadx.core.codegen.MethodGen.FallbackOption.COMMENTED_DUMP; import static jadx.core.codegen.MethodGen.FallbackOption.FALLBACK_MODE; +import static jadx.core.dex.nodes.VariableNode.*; public class MethodGen { private static final Logger LOG = LoggerFactory.getLogger(MethodGen.class); @@ -220,7 +222,19 @@ public class MethodGen { classGen.useType(code, argType); } code.add(' '); - code.add(nameGen.assignArg(var)); + VariableNode node = mth.declareVar(var, nameGen, VarKind.ARG); + String name; + if (node != null) { + code.attachDefinition(node); + name = node.getName(); + var.setName(name); + } else { + name = nameGen.assignArg(var); + } + if (var.isThis()) { + code.attachDefinition(mth.getParentClass()); + } + code.add(name); i++; if (it.hasNext()) { diff --git a/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java b/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java index f20249458..e6906d53d 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java @@ -21,12 +21,7 @@ import jadx.core.dex.instructions.args.CodeVar; import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.NamedArg; import jadx.core.dex.instructions.args.RegisterArg; -import jadx.core.dex.nodes.BlockNode; -import jadx.core.dex.nodes.FieldNode; -import jadx.core.dex.nodes.IBlock; -import jadx.core.dex.nodes.IContainer; -import jadx.core.dex.nodes.IRegion; -import jadx.core.dex.nodes.InsnNode; +import jadx.core.dex.nodes.*; import jadx.core.dex.regions.Region; import jadx.core.dex.regions.SwitchRegion; import jadx.core.dex.regions.SwitchRegion.CaseInfo; @@ -40,10 +35,13 @@ import jadx.core.dex.regions.loops.LoopRegion; import jadx.core.dex.regions.loops.LoopType; import jadx.core.dex.trycatch.ExceptionHandler; import jadx.core.utils.BlockUtils; +import jadx.core.utils.CodeGenUtils; import jadx.core.utils.RegionUtils; import jadx.core.utils.exceptions.CodegenException; import jadx.core.utils.exceptions.JadxRuntimeException; +import static jadx.core.dex.nodes.VariableNode.*; + public class RegionGen extends InsnGen { private static final Logger LOG = LoggerFactory.getLogger(RegionGen.class); @@ -351,10 +349,29 @@ public class RegionGen extends InsnGen { if (arg == null) { code.add("unknown"); // throwing exception is too late at this point } else if (arg instanceof RegisterArg) { - RegisterArg reg = (RegisterArg) arg; - code.add(mgen.getNameGen().assignArg(reg.getSVar().getCodeVar())); + String name; + CodeVar codeVar = CodeGenUtils.getCodeVar((RegisterArg) arg); + if (codeVar != null) { + VariableNode node = mth.declareVar(codeVar, mgen.getNameGen(), VarKind.CATCH_ARG); + if (node != null) { + code.attachDefinition(node); + name = node.getName(); + codeVar.setName(name); + } else { + name = mgen.getNameGen().assignArg(codeVar); + } + } else { + RegisterArg reg = (RegisterArg) arg; + name = mgen.getNameGen().assignArg(reg.getSVar().getCodeVar()); + } + code.add(name); } else if (arg instanceof NamedArg) { - code.add(mgen.getNameGen().assignNamedArg((NamedArg) arg)); + VariableNode node = mth.declareVar((NamedArg) arg, mgen.getNameGen(), VarKind.CATCH_ARG); + if (node != null) { + code.add(node.getName()); + } else { + code.add(mgen.getNameGen().assignNamedArg((NamedArg) arg)); + } } else { throw new JadxRuntimeException("Unexpected arg type in catch block: " + arg + ", class: " + arg.getClass().getSimpleName()); } diff --git a/jadx-core/src/main/java/jadx/core/deobf/DeobfPresets.java b/jadx-core/src/main/java/jadx/core/deobf/DeobfPresets.java index f88a75d84..1d312c536 100644 --- a/jadx-core/src/main/java/jadx/core/deobf/DeobfPresets.java +++ b/jadx-core/src/main/java/jadx/core/deobf/DeobfPresets.java @@ -6,11 +6,7 @@ import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -20,6 +16,7 @@ import jadx.core.dex.info.ClassInfo; import jadx.core.dex.info.FieldInfo; import jadx.core.dex.info.MethodInfo; import jadx.core.dex.nodes.RootNode; +import jadx.core.dex.nodes.VariableNode; import jadx.core.utils.files.FileUtils; import static java.nio.charset.StandardCharsets.UTF_8; @@ -35,6 +32,7 @@ public class DeobfPresets { private final Map clsPresetMap = new HashMap<>(); private final Map fldPresetMap = new HashMap<>(); private final Map mthPresetMap = new HashMap<>(); + private final Map> varPresetMap = new HashMap<>(); @Nullable public static DeobfPresets build(RootNode root) { @@ -94,6 +92,13 @@ public class DeobfPresets { case 'm': mthPresetMap.put(origName, alias); break; + case 'v': + String[] mthIDAndVarIndex = origName.split(VariableNode.VAR_SEPARATOR); + if (mthIDAndVarIndex.length == 2) { + Set nameList = varPresetMap.computeIfAbsent(mthIDAndVarIndex[0], k -> new HashSet<>()); + nameList.add(makeVarSecIndex(mthIDAndVarIndex[1], alias)); + } + break; } } } catch (Exception e) { @@ -101,6 +106,10 @@ public class DeobfPresets { } } + public static String makeVarSecIndex(String indexes, String name) { + return indexes + VariableNode.VAR_SEPARATOR + name; + } + private static String[] splitAndTrim(String str) { String[] v = str.substring(2).split("="); for (int i = 0; i < v.length; i++) { @@ -123,6 +132,15 @@ public class DeobfPresets { for (Map.Entry mthEntry : mthPresetMap.entrySet()) { list.add(String.format("m %s = %s", mthEntry.getKey(), mthEntry.getValue())); } + for (Map.Entry> varEntry : varPresetMap.entrySet()) { + for (String val : varEntry.getValue()) { + String[] indexAndName = val.split(VariableNode.VAR_SEPARATOR); + if (indexAndName.length == 2) { + list.add(String.format("v %s%s%s = %s", + varEntry.getKey(), VariableNode.VAR_SEPARATOR, indexAndName[0], indexAndName[1])); + } + } + } Collections.sort(list); Files.write(deobfMapFile, list, MAP_FILE_CHARSET, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); @@ -147,6 +165,7 @@ public class DeobfPresets { clsPresetMap.clear(); fldPresetMap.clear(); mthPresetMap.clear(); + varPresetMap.clear(); } public Path getDeobfMapFile() { @@ -168,4 +187,18 @@ public class DeobfPresets { public Map getMthPresetMap() { return mthPresetMap; } + + public Map> getVarPresetMap() { + return varPresetMap; + } + + public void updateVariableName(VariableNode node, String name) { + String key = node.getRenameKey(); + key = key.substring(0, key.indexOf(VariableNode.VAR_SEPARATOR)); + String newIndex = makeVarSecIndex(node.makeVarIndex(), name); + String oldIndex = makeVarSecIndex(node.makeVarIndex(), node.getName()); + Set indexSet = varPresetMap.computeIfAbsent(key, k -> new HashSet<>()); + indexSet.remove(oldIndex); + indexSet.add(newIndex); + } } diff --git a/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java b/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java index 6289f0d1f..a7b7fca7f 100644 --- a/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java +++ b/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java @@ -2,13 +2,7 @@ package jadx.core.deobf; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; +import java.util.*; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -209,6 +203,10 @@ public class Deobfuscator { } private void renameMethod(MethodNode mth) { + Set names = deobfPresets.getVarPresetMap().get(mth.getMethodInfo().getRawFullId()); + if (names != null) { + mth.getMethodInfo().setVarNameMap(names); + } String alias = getMethodAlias(mth); if (alias != null) { applyMethodAlias(mth, alias); diff --git a/jadx-core/src/main/java/jadx/core/dex/info/MethodInfo.java b/jadx-core/src/main/java/jadx/core/dex/info/MethodInfo.java index c635087ff..69855ba48 100644 --- a/jadx-core/src/main/java/jadx/core/dex/info/MethodInfo.java +++ b/jadx-core/src/main/java/jadx/core/dex/info/MethodInfo.java @@ -1,7 +1,6 @@ package jadx.core.dex.info; -import java.util.List; -import java.util.Objects; +import java.util.*; import org.jetbrains.annotations.Nullable; @@ -10,6 +9,7 @@ import jadx.api.plugins.input.data.IMethodRef; import jadx.core.codegen.TypeGen; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.nodes.RootNode; +import jadx.core.dex.nodes.VariableNode; import jadx.core.utils.Utils; public final class MethodInfo implements Comparable { @@ -20,6 +20,7 @@ public final class MethodInfo implements Comparable { private final ClassInfo declClass; private final String shortId; private String alias; + private Map varNameMap; private MethodInfo(ClassInfo declClass, String name, List args, ArgType retType) { this.name = name; @@ -148,6 +149,29 @@ public final class MethodInfo implements Comparable { return !name.equals(alias); } + public synchronized void setVarNameMap(Set names) { + if (varNameMap == null) { + varNameMap = new HashMap<>(); + } + for (String name : names) { + String[] indexesAndName = name.split(VariableNode.VAR_SEPARATOR); + if (indexesAndName.length == 2) { + varNameMap.put(indexesAndName[0], indexesAndName[1]); + } + } + } + + public String getVariableName(String indexes) { + if (varNameMap != null) { + return varNameMap.get(indexes); + } + return null; + } + + public boolean hasVarNameMap() { + return varNameMap != null && varNameMap.size() > 0; + } + @Override public int hashCode() { return shortId.hashCode() + 31 * declClass.hashCode(); diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/CodeVar.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/CodeVar.java index 000416ffc..6705eb0ac 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/CodeVar.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/CodeVar.java @@ -4,7 +4,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -public class CodeVar { +public class CodeVar implements VisibleVar { private String name; private ArgType type; // before type inference can be null and set only for immutable types private List ssaVars = Collections.emptyList(); @@ -13,6 +13,18 @@ public class CodeVar { private boolean isThis; private boolean isDeclared; + private int index = -1; + + @Override + public int getIndex() { + return index; + } + + @Override + public void setIndex(int index) { + this.index = index; + } + public static CodeVar fromMthArg(RegisterArg mthArg, boolean linkRegister) { CodeVar var = new CodeVar(); var.setType(mthArg.getInitType()); @@ -26,14 +38,17 @@ public class CodeVar { return var; } + @Override public String getName() { return name; } + @Override public void setName(String name) { this.name = name; } + @Override public ArgType getType() { return type; } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/NamedArg.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/NamedArg.java index 9197ceb30..aa35f8977 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/NamedArg.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/NamedArg.java @@ -2,16 +2,28 @@ package jadx.core.dex.instructions.args; import org.jetbrains.annotations.NotNull; -public final class NamedArg extends InsnArg implements Named { +public final class NamedArg extends InsnArg implements Named, VisibleVar { @NotNull private String name; + private int index = -1; + public NamedArg(@NotNull String name, @NotNull ArgType type) { this.name = name; this.type = type; } + @Override + public int getIndex() { + return index; + } + + @Override + public void setIndex(int index) { + this.index = index; + } + @NotNull public String getName() { return name; @@ -22,6 +34,7 @@ public final class NamedArg extends InsnArg implements Named { return true; } + @Override public void setName(@NotNull String name) { this.name = name; } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/VisibleVar.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/VisibleVar.java new file mode 100644 index 000000000..21eed0664 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/VisibleVar.java @@ -0,0 +1,14 @@ +package jadx.core.dex.instructions.args; + +public interface VisibleVar { + int getIndex(); + + void setIndex(int index); + + String getName(); + + void setName(String name); + + ArgType getType(); + +} diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java index b39753f97..979ae1edf 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java @@ -1,9 +1,6 @@ package jadx.core.dex.nodes; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; +import java.util.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -16,6 +13,7 @@ import jadx.api.plugins.input.data.IMethodData; import jadx.api.plugins.input.data.annotations.EncodedValue; import jadx.api.plugins.input.data.annotations.IAnnotation; import jadx.core.Consts; +import jadx.core.codegen.NameGen; import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.annotations.AnnotationsList; import jadx.core.dex.attributes.annotations.MethodParameters; @@ -26,10 +24,8 @@ import jadx.core.dex.info.AccessInfo.AFType; import jadx.core.dex.info.ClassInfo; import jadx.core.dex.info.MethodInfo; import jadx.core.dex.instructions.InsnDecoder; -import jadx.core.dex.instructions.args.ArgType; -import jadx.core.dex.instructions.args.InsnArg; -import jadx.core.dex.instructions.args.RegisterArg; -import jadx.core.dex.instructions.args.SSAVar; +import jadx.core.dex.instructions.args.*; +import jadx.core.dex.nodes.VariableNode.VarKind; import jadx.core.dex.nodes.utils.TypeUtils; import jadx.core.dex.regions.Region; import jadx.core.dex.trycatch.ExceptionHandler; @@ -73,6 +69,7 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails, private Region region; private List useIn = Collections.emptyList(); + private List variables = new ArrayList<>(); public static MethodNode build(ClassNode classNode, IMethodData methodData) { MethodNode methodNode = new MethodNode(classNode, methodData); @@ -102,6 +99,59 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails, unload(); } + public List getVars() { + return new ArrayList<>(variables); + } + + public VariableNode getVariable(int index) { + if (index >= 0 && index < variables.size()) { + return variables.get(index); + } + return null; + } + + public VariableNode getVariable(int index, VarKind varType) { + for (VariableNode variable : variables) { + if (variable.getVarKind() == varType && variable.getIndex() == index) { + return variable; + } + } + return null; + } + + public VariableNode declareVar(VisibleVar var, NameGen nameGen, VarKind varKind) { + if (var instanceof CodeVar) { + if (((CodeVar) var).isThis()) { + return null; + } + } + VariableNode varNode; + int index = var.getIndex(); + if (index == -1) { + index = variables.size(); + var.setIndex(index); + varNode = null; + } else { + varNode = getVariable(var.getIndex()); + } + if (varNode == null) { + String name = mthInfo.getVariableName(VariableNode.makeVarIndex(index, varKind)); + if (name != null) { + var.setName(name); // set name with user renamed previously. + } + if (var instanceof CodeVar) { // let NameGen record this name or gen an valid name. + name = nameGen.assignArg((CodeVar) var); + } else if (var instanceof NamedArg) { + name = nameGen.assignNamedArg((NamedArg) var); + } else { + throw new JadxRuntimeException("Unexpected var type: " + var); + } + varNode = new VariableNode(this, name, var.getType(), varKind, index); + this.variables.add(varNode); + } + return varNode; + } + @Override public void unload() { loaded = false; diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/VariableNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/VariableNode.java new file mode 100644 index 000000000..5fcc82198 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/VariableNode.java @@ -0,0 +1,109 @@ +package jadx.core.dex.nodes; + +import jadx.core.codegen.CodeWriter; +import jadx.core.dex.attributes.nodes.LineAttrNode; +import jadx.core.dex.instructions.args.ArgType; +import jadx.core.dex.instructions.args.CodeVar; +import jadx.core.utils.exceptions.JadxRuntimeException; + +public class VariableNode extends LineAttrNode { + public enum VarKind { + // note: better not change the order of these fields, + // they are also used for variable renaming + VAR, ARG, CATCH_ARG + } + + public static final String VAR_SEPARATOR = "--->>"; // do not contain '=' + private VarKind varKind = VarKind.VAR; + private ArgType type; + private String name; + private int index; + MethodNode mth; + + public VariableNode(MethodNode mth, String name, ArgType type, VarKind varKind, int index) { + this.mth = mth; + this.name = name; + this.type = type; + this.index = index; + this.varKind = varKind; + } + + public MethodNode getMethodNode() { + return mth; + } + + public ClassNode getClassNode() { + return mth.getParentClass(); + } + + public VarKind getVarKind() { + return this.varKind; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public ArgType getType() { + return type; + } + + public int getIndex() { + return index; + } + + public void addUsage(int line, int offset, int codeOffset) { + } + + public void useVar(CodeWriter code, CodeVar codeVar) { + if (!codeVar.isThis()) { // TODO: add usage + // IdentifierVisitor.VariableNode node = codeVar.getVariableNode(); + // node.addUsage(code.getLine(), code.getOffset(), code.bufLength()); + // code.attachAnnotation(node); + } + } + + public String getRenameKey() { + return mth.getMethodInfo().getRawFullId() + VAR_SEPARATOR + makeVarIndex(index, varKind); + } + + public String makeVarIndex() { + return makeVarIndex(index, varKind); + } + + public static String makeVarIndex(int index, VarKind kind) { + return kindToStr(kind) + "@" + kind.ordinal() + "_" + index; + } + + private static String kindToStr(VarKind varKind) { + String kind; + switch (varKind) { + case VAR: + kind = "var"; + break; + case ARG: + kind = "param"; + break; + case CATCH_ARG: + kind = "catch"; + break; + default: + throw new JadxRuntimeException("Unexpected variable type " + varKind); + } + return kind; + } + + @Override + public int hashCode() { + return mth.hashCode() + 31 * getDefPosition() + 31 * makeVarIndex().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return this == obj; + } +} diff --git a/jadx-core/src/main/java/jadx/core/utils/CodeGenUtils.java b/jadx-core/src/main/java/jadx/core/utils/CodeGenUtils.java index 688aa0613..acc0c7747 100644 --- a/jadx-core/src/main/java/jadx/core/utils/CodeGenUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/CodeGenUtils.java @@ -7,6 +7,9 @@ import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.AttrNode; import jadx.core.dex.attributes.nodes.RenameReasonAttr; import jadx.core.dex.attributes.nodes.SourceFileAttr; +import jadx.core.dex.instructions.args.CodeVar; +import jadx.core.dex.instructions.args.RegisterArg; +import jadx.core.dex.instructions.args.SSAVar; import jadx.core.dex.nodes.ClassNode; public class CodeGenUtils { @@ -36,6 +39,14 @@ public class CodeGenUtils { } } + public static CodeVar getCodeVar(RegisterArg arg) { + SSAVar svar = arg.getSVar(); + if (svar != null) { + return svar.getCodeVar(); + } + return null; + } + private CodeGenUtils() { } } diff --git a/jadx-gui/src/main/java/jadx/gui/treemodel/JVariable.java b/jadx-gui/src/main/java/jadx/gui/treemodel/JVariable.java new file mode 100644 index 000000000..357bda908 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/treemodel/JVariable.java @@ -0,0 +1,51 @@ +package jadx.gui.treemodel; + +import javax.swing.*; + +import jadx.api.JavaNode; +import jadx.api.JavaVariable; + +public class JVariable extends JNode { + JClass cls; + JavaVariable var; + + public JVariable(JavaVariable var, JClass cls) { + this.cls = cls; + this.var = var; + } + + public JavaVariable getJavaVarNode() { + return (JavaVariable) getJavaNode(); + } + + @Override + public JClass getRootClass() { + return cls; + } + + @Override + public JavaNode getJavaNode() { + return var; + } + + @Override + public JClass getJParent() { + return cls; + } + + @Override + public Icon getIcon() { + return null; + } + + @Override + public String makeString() { + return var.getName(); + } + + @Override + public boolean canRename() { + return true; + } + +} diff --git a/jadx-gui/src/main/java/jadx/gui/ui/RenameDialog.java b/jadx-gui/src/main/java/jadx/gui/ui/RenameDialog.java index d961df64a..cc69da8dc 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/RenameDialog.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/RenameDialog.java @@ -4,11 +4,7 @@ import java.awt.BorderLayout; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import javax.swing.BorderFactory; @@ -27,26 +23,20 @@ import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import jadx.api.JavaClass; -import jadx.api.JavaField; -import jadx.api.JavaMethod; -import jadx.api.JavaNode; +import jadx.api.*; import jadx.core.codegen.CodeWriter; import jadx.core.deobf.DeobfPresets; import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.nodes.MethodOverrideAttr; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.RootNode; +import jadx.core.dex.nodes.VariableNode; import jadx.core.dex.visitors.RenameVisitor; import jadx.core.utils.Utils; import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.gui.jobs.IndexJob; import jadx.gui.settings.JadxSettings; -import jadx.gui.treemodel.JClass; -import jadx.gui.treemodel.JField; -import jadx.gui.treemodel.JMethod; -import jadx.gui.treemodel.JNode; -import jadx.gui.treemodel.JPackage; +import jadx.gui.treemodel.*; import jadx.gui.ui.codearea.ClassCodeContentPanel; import jadx.gui.ui.codearea.CodePanel; import jadx.gui.utils.*; @@ -126,6 +116,9 @@ public class RenameDialog extends JDialog { deobfPresets.getClsPresetMap().put(javaClass.getRawName(), renameText); } else if (node instanceof JPackage) { deobfPresets.getPkgPresetMap().put(((JPackage) node).getFullName(), renameText); + } else if (node instanceof JVariable) { + VariableNode varNode = ((JVariable) node).getJavaVarNode().getVariableNode(); + deobfPresets.updateVariableName(varNode, renameText); } } diff --git a/jadx-gui/src/main/java/jadx/gui/utils/JNodeCache.java b/jadx-gui/src/main/java/jadx/gui/utils/JNodeCache.java index e16ac30bf..5a022c252 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/JNodeCache.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/JNodeCache.java @@ -3,15 +3,9 @@ package jadx.gui.utils; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import jadx.api.JavaClass; -import jadx.api.JavaField; -import jadx.api.JavaMethod; -import jadx.api.JavaNode; +import jadx.api.*; import jadx.core.utils.exceptions.JadxRuntimeException; -import jadx.gui.treemodel.JClass; -import jadx.gui.treemodel.JField; -import jadx.gui.treemodel.JMethod; -import jadx.gui.treemodel.JNode; +import jadx.gui.treemodel.*; public class JNodeCache { @@ -45,6 +39,10 @@ public class JNodeCache { JavaField fld = (JavaField) node; return new JField(fld, (JClass) makeFrom(fld.getDeclaringClass())); } + if (node instanceof JavaVariable) { + JavaVariable var = (JavaVariable) node; + return new JVariable(var, (JClass) makeFrom(var.getDeclaringClass())); + } throw new JadxRuntimeException("Unknown type for JavaNode: " + node.getClass()); } } diff --git a/jadx-gui/src/main/java/jadx/gui/utils/JumpPosition.java b/jadx-gui/src/main/java/jadx/gui/utils/JumpPosition.java index a14fcba6b..dd87d1606 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/JumpPosition.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/JumpPosition.java @@ -1,9 +1,6 @@ package jadx.gui.utils; -import jadx.api.JavaClass; -import jadx.api.JavaField; -import jadx.api.JavaMethod; -import jadx.api.JavaNode; +import jadx.api.*; import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.gui.treemodel.*; @@ -55,6 +52,9 @@ public class JumpPosition { if (node instanceof JField) { return ((JField) node).getJavaField().getFieldNode().getDefPosition(); } + if (node instanceof JVariable) { + return ((JVariable) node).getJavaVarNode().getVariableNode().getDefPosition(); + } throw new JadxRuntimeException("Unexpected node " + node); } @@ -68,6 +68,9 @@ public class JumpPosition { if (node instanceof JavaField) { return ((JavaField) node).getFieldNode().getDefPosition(); } + if (node instanceof JavaVariable) { + return ((JavaVariable) node).getVariableNode().getDefPosition(); + } throw new JadxRuntimeException("Unexpected node " + node); }