CalebFenton/dex-oracle

View on GitHub
driver/src/main/java/org/cf/oracle/options/TargetParser.java

Summary

Maintainability
B
6 hrs
Test Coverage
package org.cf.oracle.options;

import com.google.gson.*;
import org.cf.oracle.FileUtils;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

public class TargetParser {

    private static void parseTarget(Gson gson, String className, String methodName, String... args) {
        InvocationTarget target = new InvocationTarget("none", className, methodName, args);
        parseTargetExceptionally(gson, target);
    }

    private static void parseTargetExceptionally(Gson gson, InvocationTarget target) {
        try {
            parseTarget(gson, target);
        } catch (ClassNotFoundException | NoSuchMethodException | ExceptionInInitializerError e) {
            target.setParseException(e);
        }
    }

    private static void parseTarget(Gson gson, InvocationTarget target) throws ClassNotFoundException, NoSuchMethodException {
        String[] args = target.getArgumentStrings();
        Class<?>[] parameterTypes = new Class[args.length];
        Object[] methodArguments = new Object[parameterTypes.length];
        for (int i = 0; i < parameterTypes.length; i++) {
            String[] parts = args[i].split(":", 2);
            parameterTypes[i] = smaliToJavaClass(parts[0]);
            if (parts.length == 1) {
                methodArguments[i] = null;
            } else {
                String jsonValue = parts[1];
                if (parameterTypes[i] == String.class) {
                    try {
                        // Normalizing strings to byte[] avoids escaping ruby, bash, adb shell, and java
                        byte[] stringBytes = gson.fromJson(jsonValue, byte[].class);
                        methodArguments[i] = new String(stringBytes);
                    } catch (JsonSyntaxException ex) {
                        // Possibly not using byte array format for string (good luck)
                        methodArguments[i] = jsonValue;
                    }
                } else {
                    // System.out.println("Parsing: " + itemJson + " as " + paramTypes[i]);
                    methodArguments[i] = gson.fromJson(jsonValue, parameterTypes[i]);
                }
            }
        }
        target.setArguments(methodArguments);

        Class<?> methodClass = Class.forName(target.getClassName());
        Method method = methodClass.getDeclaredMethod(target.getMethodName(), parameterTypes);
        target.setMethod(method);
    }

    private static List<InvocationTarget> loadTargetsFromFile(Gson gson, String fileName) throws IOException {
        String targetJson = FileUtils.readFile(fileName);
        JsonArray targetItems = new JsonParser().parse(targetJson).getAsJsonArray();
        // JsonArray targetItems = json.getAsJsonArray();
        List<InvocationTarget> targets = new LinkedList<InvocationTarget>();
        for (JsonElement element : targetItems) {
            JsonObject targetItem = element.getAsJsonObject();
            String id = targetItem.get("id").getAsString();
            String className = targetItem.get("className").getAsString();
            String methodName = targetItem.get("methodName").getAsString();
            JsonArray argumentsJson = targetItem.get("arguments").getAsJsonArray();
            String[] arguments = new String[argumentsJson.size()];
            for (int i = 0; i < arguments.length; i++) {
                arguments[i] = argumentsJson.get(i).getAsString();
            }

            InvocationTarget target = new InvocationTarget(id, className, methodName, arguments);
            parseTargetExceptionally(gson, target);
            targets.add(target);
        }

        return targets;
    }

    private static Class<?> smaliToJavaClass(String className) throws ClassNotFoundException {
        if (className.equals("I")) {
            return int.class;
        } else if (className.equals("V")) {
            return void.class;
        } else if (className.equals("Z")) {
            return boolean.class;
        } else if (className.equals("B")) {
            return byte.class;
        } else if (className.equals("S")) {
            return short.class;
        } else if (className.equals("J")) {
            return long.class;
        } else if (className.equals("C")) {
            return char.class;
        } else if (className.equals("F")) {
            return float.class;
        } else if (className.equals("D")) {
            return double.class;
        } else {
            return Class.forName(className);
        }
    }

    public static List<InvocationTarget> parse(String[] args, Gson gson) throws IOException {
        if (args[0].startsWith("@")) {
            String fileName = args[0].substring(1);

            return loadTargetsFromFile(gson, fileName);
        } else {
            String className = args[0];
            String methodName = args[1];
            String[] argumentStrings = Arrays.copyOfRange(args, 2, args.length);
            InvocationTarget target = new InvocationTarget("none", className, methodName, args);
            parseTargetExceptionally(gson, target);

            List<InvocationTarget> targets = new LinkedList<InvocationTarget>();
            targets.add(target);

            return targets;
        }
    }

}