* Use raw_name instead of full_name for the names of class in generated frida snippet. Also cleaned the code a bit * Fixed getting method parameters from inlined methods * fixed generating code for constructor overloads, more cleaning * Fixed getting method parameters from inlined methods for real this time * made the option for a frida snippet only appear if clicked on a relevant node * added support for generating a frida snippet for fields * apply spotless * Update jadx-gui/src/main/java/jadx/gui/ui/codearea/FridaAction.java Co-authored-by: skylot <118523+skylot@users.noreply.github.com> * moved the overload check from NodeMethod to FridaAction * added semicolons in the end of lines of the generated frida snippet * fix code formatting
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<JNode> {
|
||||
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<String, Boolean> isInitial = new HashMap<>();
|
||||
private String methodName;
|
||||
private final Map<String, Boolean> 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<JNode> {
|
||||
|
||||
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() {
|
||||
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 "";
|
||||
|
||||
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("<init>") || 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<String> argNames = jMth.getRootClass().getCodeInfo().getAnnotations().entrySet().stream()
|
||||
.filter(e -> e.getKey().getLine() == mthLine && e.getValue() instanceof VarDeclareRef)
|
||||
}
|
||||
|
||||
private String generateMethodSnippet(JMethod jMth) {
|
||||
JavaMethod javaMethod = jMth.getJavaMethod();
|
||||
MethodInfo methodInfo = javaMethod.getMethodNode().getMethodInfo();
|
||||
String methodName = methodInfo.getName();
|
||||
if (methodName.equals("<init>") || methodName.equals("onCreate")) {
|
||||
methodName = "$init";
|
||||
}
|
||||
String rawClassName = javaMethod.getDeclaringClass().getRawName();
|
||||
String shortClassName = javaMethod.getDeclaringClass().getName();
|
||||
|
||||
String functionUntilImplementation;
|
||||
if (isOverloaded(javaMethod.getMethodNode())) {
|
||||
List<ArgType> 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());
|
||||
.collect(Collectors.joining(", "));
|
||||
|
||||
StringBuilder functionParameters = new StringBuilder();
|
||||
for (String argName : argNames) {
|
||||
functionParameters.append(argName + ", ");
|
||||
}
|
||||
if (functionParameters.toString().length() > 2) {
|
||||
functionParameters.setLength(functionParameters.length() - 2);
|
||||
}
|
||||
|
||||
List<MethodNode> methods = methodNode.getParentClass().getMethods();
|
||||
List<MethodNode> filteredmethod = methods.stream().filter(m -> m.getName().equals(methodName)).collect(Collectors.toList());
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String overloadStr = "";
|
||||
if (filteredmethod.size() > 1) {
|
||||
List<ArgType> 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);
|
||||
|
||||
} 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<MethodNode> 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<MethodNode> 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
|
||||
|
||||
Reference in New Issue
Block a user