diff --git a/jadx-core/src/main/java/jadx/api/JavaField.java b/jadx-core/src/main/java/jadx/api/JavaField.java index be09e3cd422c28a8769fd7367dae36111a146636..15c2f2ae4c25ea636153581f6233b2fd6c204b07 100644 --- a/jadx-core/src/main/java/jadx/api/JavaField.java +++ b/jadx-core/src/main/java/jadx/api/JavaField.java @@ -28,6 +28,10 @@ public final class JavaField implements JavaNode { return parent.getFullName() + '.' + getName(); } + public String getRawName() { + return field.getName(); + } + @Override public JavaClass getDeclaringClass() { return parent; diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeArea.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeArea.java index cd66042609884f5f754c0efeffd0d65df827a69f..4bcec51dea6f3a4a160b8cc7c386bac68ff28f1a 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeArea.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeArea.java @@ -105,6 +105,7 @@ public final class CodeArea extends AbstractCodeArea { popup.add(new CommentSearchAction(this)); popup.add(rename); popup.addPopupMenuListener(findUsage); + popup.addPopupMenuListener(frida); popup.addPopupMenuListener(goToDeclaration); popup.addPopupMenuListener(comment); popup.addPopupMenuListener(rename); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/FridaAction.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/FridaAction.java index 4003278e290ad7151c4bc16ec8330a5955558655..e02ad06a97c07afe294d672014b2899c10f4bf6e 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/FridaAction.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/FridaAction.java @@ -5,10 +5,7 @@ import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; import javax.swing.*; @@ -17,12 +14,16 @@ import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import jadx.api.JavaClass; +import jadx.api.JavaField; +import jadx.api.JavaMethod; import jadx.api.data.annotations.VarDeclareRef; import jadx.core.dex.info.MethodInfo; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.MethodNode; import jadx.gui.treemodel.JClass; +import jadx.gui.treemodel.JField; import jadx.gui.treemodel.JMethod; import jadx.gui.treemodel.JNode; import jadx.gui.utils.NLS; @@ -30,15 +31,13 @@ import jadx.gui.utils.NLS; import static javax.swing.KeyStroke.getKeyStroke; public final class FridaAction extends JNodeMenuAction { - private static final Logger LOG = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); + private static final Logger LOG = LoggerFactory.getLogger(FridaAction.class); private static final long serialVersionUID = 4692546569977976384L; - private Map isInitial = new HashMap<>(); - private String methodName; + private final Map isInitial = new HashMap<>(); public FridaAction(CodeArea codeArea) { super(NLS.str("popup.frida") + " (f)", codeArea); - LOG.info("triggered meee"); KeyStroke key = getKeyStroke(KeyEvent.VK_F, 0); codeArea.getInputMap().put(key, "trigger frida"); codeArea.getActionMap().put("trigger frida", new AbstractAction() { @@ -46,143 +45,130 @@ public final class FridaAction extends JNodeMenuAction { public void actionPerformed(ActionEvent e) { node = getNodeByOffset(codeArea.getWordStart(codeArea.getCaretPosition())); - copyFridaCode(); + + String fridaSnippet = generateFridaSnippet(); + + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + StringSelection selection = new StringSelection(fridaSnippet); + clipboard.setContents(selection, selection); } }); } - private void copyFridaCode() { - - if (node != null) { - if (node instanceof JMethod) { - JMethod n = (JMethod) node; - MethodNode methodNode = n.getJavaMethod().getMethodNode(); - MethodInfo mi = methodNode.getMethodInfo(); - methodName = mi.getName(); - if (methodName.equals("") || methodName.equals("onCreate")) { - methodName = "$init"; - } - String fullClassName = methodNode.getParentClass().getFullName(); - String className = methodNode.getParentClass().getShortName(); - LOG.debug("node is jmethod"); - ClassNode tmp = methodNode.getParentClass(); - while (true) { - if (!tmp.isTopClass()) { - fullClassName = fullClassName.substring(0, fullClassName.lastIndexOf(".")) + "$" - + fullClassName.substring(fullClassName.lastIndexOf(".") + 1, fullClassName.length()); - } else { - break; - } - tmp = tmp.getParentClass(); - } - JMethod jMth = (JMethod) node; - int mthLine = jMth.getLine(); - List argNames = jMth.getRootClass().getCodeInfo().getAnnotations().entrySet().stream() - .filter(e -> e.getKey().getLine() == mthLine && e.getValue() instanceof VarDeclareRef) + private String generateFridaSnippet() { + if (node instanceof JMethod) { + LOG.debug("node is jmethod"); + return generateMethodSnippet((JMethod) node); + } else if (node instanceof JClass) { + LOG.debug("node is jclass"); + return generateClassSnippet((JClass) node); + } else if (node instanceof JField) { + LOG.debug("node is jfield"); + return generateFieldSnippet((JField) node); + } + LOG.debug("cannot generate frida snippet from node"); + return ""; + + } + + private String generateMethodSnippet(JMethod jMth) { + JavaMethod javaMethod = jMth.getJavaMethod(); + MethodInfo methodInfo = javaMethod.getMethodNode().getMethodInfo(); + String methodName = methodInfo.getName(); + if (methodName.equals("") || methodName.equals("onCreate")) { + methodName = "$init"; + } + String rawClassName = javaMethod.getDeclaringClass().getRawName(); + String shortClassName = javaMethod.getDeclaringClass().getName(); + + String functionUntilImplementation; + if (isOverloaded(javaMethod.getMethodNode())) { + List methodArgs = methodInfo.getArgumentsTypes(); + String overloadStr = methodArgs.stream().map(this::parseArgType).collect(Collectors.joining(", ")); + functionUntilImplementation = String.format("%s.%s.overload(%s).implementation", shortClassName, methodName, overloadStr); + } else { + functionUntilImplementation = String.format("%s.%s.implementation", shortClassName, methodName); + } + + String functionParametersString = + Objects.requireNonNull(javaMethod.getTopParentClass().getCodeInfo()).getAnnotations().entrySet().stream() + .filter(e -> e.getKey().getLine() == jMth.getLine() && e.getValue() instanceof VarDeclareRef) .sorted(Comparator.comparingInt(e -> e.getKey().getPos())) .map(e -> ((VarDeclareRef) e.getValue()).getName()) - .collect(Collectors.toList()); - - StringBuilder functionParameters = new StringBuilder(); - for (String argName : argNames) { - functionParameters.append(argName + ", "); - } - if (functionParameters.toString().length() > 2) { - functionParameters.setLength(functionParameters.length() - 2); - } - - List methods = methodNode.getParentClass().getMethods(); - List filteredmethod = methods.stream().filter(m -> m.getName().equals(methodName)).collect(Collectors.toList()); - StringBuilder sb = new StringBuilder(); - String overloadStr = ""; - if (filteredmethod.size() > 1) { - List methodArgs = mi.getArgumentsTypes(); - for (ArgType argType : methodArgs) { - sb.append("'" + parseArgType(argType) + "', "); - } - if (sb.length() > 2) { - sb.setLength(sb.length() - 2); - } - overloadStr = sb.toString(); - - } - String functionUntilImplementation = ""; - if (!overloadStr.equals("")) { - functionUntilImplementation = String.format("%s.%s.overload(%s).implementation", className, methodName, overloadStr); - } else { - functionUntilImplementation = String.format("%s.%s.implementation", className, methodName); - } - String functionParameterAndBody = ""; - String functionParametersString = functionParameters.toString(); - if (!functionParametersString.equals("")) { - functionParameterAndBody = String.format( - "%s = function(%s){\n\tconsole.log('%s is called')\n\tlet ret = this.%s(%s)\n\tconsole.log('%s ret value is ' + ret)\n\treturn ret\n}", - functionUntilImplementation, functionParametersString, methodName, methodName, functionParametersString, - methodName); - } else { - functionParameterAndBody = String.format( - "%s = function(){\n\tconsole.log('%s is called')\n\tlet ret = this.%s()\n\tconsole.log('%s ret value is ' + ret)\n\treturn ret\n}", - functionUntilImplementation, methodName, methodName, methodName); - } - String finalFridaCode = ""; - if (isInitial.getOrDefault(fullClassName, true)) { - finalFridaCode = String.format("let %s = Java.use(\"%s\")\n%s", className, fullClassName, functionParameterAndBody); - isInitial.put(fullClassName, false); - } else { - finalFridaCode = functionParameterAndBody; - } - LOG.debug("frida code : " + finalFridaCode); - Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); - StringSelection selection = new StringSelection(finalFridaCode); - clipboard.setContents(selection, selection); + .collect(Collectors.joining(", ")); - } else if (node instanceof JClass) { - LOG.debug("node is jclass"); - JClass jc = (JClass) node; - String fullClassName = jc.getCls().getClassNode().getClassInfo().getFullName(); - String className = jc.getCls().getClassNode().getClassInfo().getShortName(); - ClassNode tmp = jc.getCls().getClassNode(); - while (true) { - if (!tmp.isTopClass()) { - fullClassName = fullClassName.substring(0, fullClassName.lastIndexOf(".")) + "$" - + fullClassName.substring(fullClassName.lastIndexOf(".") + 1, fullClassName.length()); - } else { - break; - } - tmp = tmp.getParentClass(); - } - String finalFridaCode = String.format("let %s = Java.use(\"%s\")", className, fullClassName); - Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); - StringSelection selection = new StringSelection(finalFridaCode); - clipboard.setContents(selection, selection); - LOG.debug("frida code : " + finalFridaCode); - isInitial.put(fullClassName, false); - } else { - LOG.debug("node is something else"); - } + String functionParameterAndBody = String.format( + "%s = function(%s){\n\tconsole.log('%s is called');\n\tlet ret = this.%s(%s);\n\tconsole.log('%s ret value is ' + ret);\n\treturn ret;\n}", + functionUntilImplementation, functionParametersString, methodName, methodName, functionParametersString, methodName); + + String finalFridaCode; + if (isInitial.getOrDefault(rawClassName, true)) { + String classSnippet = generateClassSnippet(jMth.getJParent()); + finalFridaCode = classSnippet + "\n" + functionParameterAndBody; + } else { + finalFridaCode = functionParameterAndBody; + } + LOG.debug("frida code : " + finalFridaCode); + return finalFridaCode; + } + private String generateClassSnippet(JClass jc) { + JavaClass javaClass = jc.getCls(); + String rawClassName = javaClass.getRawName(); + String shortClassName = javaClass.getName(); + String finalFridaCode = String.format("let %s = Java.use(\"%s\");", shortClassName, rawClassName); + LOG.debug("frida code : " + finalFridaCode); + isInitial.put(rawClassName, false); + return finalFridaCode; + } + + private String generateFieldSnippet(JField jf) { + JavaField javaField = jf.getJavaField(); + String rawFieldName = javaField.getRawName(); + String fieldName = javaField.getName(); + + List methodNodes = javaField.getFieldNode().getParentClass().getMethods(); + for (MethodNode methodNode : methodNodes) { + if (methodNode.getName().equals(rawFieldName)) { + rawFieldName = "_" + rawFieldName; + break; + } } + + JClass jc = jf.getRootClass(); + String classSnippet = generateClassSnippet(jc); + String finalFridaCode = String.format("%s\n%s = %s.%s.value;", classSnippet, fieldName, jc.getName(), rawFieldName); + LOG.debug("frida code : " + finalFridaCode); + return finalFridaCode; + } + + public Boolean isOverloaded(MethodNode methodNode) { + ClassNode parentClass = methodNode.getParentClass(); + List methods = parentClass.getMethods(); + return methods.stream() + .anyMatch(m -> m.getName().equals(methodNode.getName()) + && !Objects.equals(methodNode.getMethodInfo().getShortId(), m.getMethodInfo().getShortId())); } private String parseArgType(ArgType x) { - StringBuilder parsedArgType = new StringBuilder(); + StringBuilder parsedArgType = new StringBuilder("'"); if (x.isArray()) { parsedArgType.append(x.getPrimitiveType().getShortName()); parsedArgType.append(x.getArrayElement().getPrimitiveType().getShortName()); if (!x.getArrayElement().isPrimitive()) { - parsedArgType.append(x.getArrayElement().toString() + ";"); + parsedArgType.append(x.getArrayElement().toString()).append(";"); } } else { - parsedArgType.append(x.toString()); + parsedArgType.append(x); } - return parsedArgType.toString(); + return parsedArgType.append("'").toString(); } @Override public void actionPerformed(ActionEvent e) { node = codeArea.getNodeUnderCaret(); - copyFridaCode(); + generateFridaSnippet(); } @Nullable