com.jakev.dexdumpsql.App.java Source code

Java tutorial

Introduction

Here is the source code for com.jakev.dexdumpsql.App.java

Source

/*
 * Android DEX to Sqlite3 DB utility
 * Copyright 2013-2014 Jake Valletta (@jake_valletta)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jakev.dexdumpsql;

import com.jakev.dexdumpsql.DexDbHelper;

import java.io.File;
import java.io.IOException;

import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;

import org.jf.dexlib2.iface.ClassDef;
import org.jf.dexlib2.iface.DexFile;
import org.jf.dexlib2.DexFileFactory;

public class App {

    private static final String gProgramName = "DexDumpSql";
    private static final String gCmdName = "dexdumpsql";
    private static final String gProgramVersion = "1.1";

    private static DexFile gDexFile = null;
    private static DexDbHelper gDexDb = null;

    private static final int METHOD_TYPE_DIRECT = 0;
    private static final int METHOD_TYPE_VIRTUAL = 1;

    private static Options gOptions = new Options();
    private static boolean gDebug = false;

    private static void usage() {

        HelpFormatter formatter = new HelpFormatter();

        System.out.println(gProgramName + " v" + gProgramVersion + " Command Line Utility");
        formatter.printHelp(gCmdName, gOptions);
    }

    /* From AOSP : dalvik/tools/dexdeps/src/com/android/dexdeps/Output.java */
    static String primitiveTypeLabel(char typeChar) {
        /* primitive type; substitute human-readable name in */
        switch (typeChar) {
        case 'B':
            return "byte";
        case 'C':
            return "char";
        case 'D':
            return "double";
        case 'F':
            return "float";
        case 'I':
            return "int";
        case 'J':
            return "long";
        case 'S':
            return "short";
        case 'V':
            return "void";
        case 'Z':
            return "boolean";
        default:
            /* huh? */
            System.err.println("Unexpected class char " + typeChar);
            assert false;
            return "UNKNOWN";
        }
    }

    static String descriptorToDot(String descr) {
        int targetLen = descr.length();
        int offset = 0;
        int arrayDepth = 0;

        if (descr == null) {
            return null;
        }

        /* strip leading [s; will be added to end */
        while (targetLen > 1 && descr.charAt(offset) == '[') {
            offset++;
            targetLen--;
        }
        arrayDepth = offset;

        if (targetLen == 1) {
            descr = primitiveTypeLabel(descr.charAt(offset));
            offset = 0;
            targetLen = descr.length();
        } else {
            /* account for leading 'L' and trailing ';' */
            if (targetLen >= 2 && descr.charAt(offset) == 'L' && descr.charAt(offset + targetLen - 1) == ';') {
                targetLen -= 2; /* two fewer chars to copy */
                offset++; /* skip the 'L' */
            }
        }

        char[] buf = new char[targetLen + arrayDepth * 2];

        /* copy class name over */
        int i;
        for (i = 0; i < targetLen; i++) {
            char ch = descr.charAt(offset + i);
            buf[i] = (ch == '/' || ch == '$') ? '.' : ch;
        }

        /* add the appopriate number of brackets for arrays */
        while (arrayDepth-- > 0) {
            buf[i++] = '[';
            buf[i++] = ']';
        }
        assert i == buf.length;

        return new String(buf);
    }
    /* End from AOSP */

    private static boolean isFile(String filePathString) {

        File f = new File(filePathString);
        if (f.exists() && !f.isDirectory()) {
            return true;
        } else {
            return false;
        }
    }

    private static int processDex() {

        int rtn = 0;
        int i = 0;

        /* Process each class */
        for (ClassDef classDef : gDexFile.getClasses()) {

            rtn |= processClass(i, classDef);
            i++;
        }

        return rtn;
    }

    private static int processClass(int classIdx, ClassDef classDef) {

        String superclassDescriptor = "";
        String classDescriptor = "";
        String superclassName = "";
        int accessFlags = 0;
        int rtn = 0;

        superclassName = classDef.getSuperclass();

        if (superclassName == null) {
            superclassDescriptor = "None";
        } else {
            superclassDescriptor = descriptorToDot(superclassName);
        }

        classDescriptor = descriptorToDot(classDef.getType());
        accessFlags = classDef.getAccessFlags();

        if (gDebug) {
            System.out.println("Adding class " + classDescriptor);
        }
        /* Add this class */
        rtn = gDexDb.addClass(classIdx, classDescriptor, accessFlags, superclassDescriptor);
        if (rtn != 0) {

            System.err.println(
                    "[ERROR] Unable to add class '" + classDescriptor + "' (" + Integer.toString(rtn) + ")");
            return rtn;
        }

        /* Process the static fields */
        rtn = gDexDb.addStaticFields(classDef, classIdx);

        /* Process the instance fields */
        rtn = gDexDb.addInstanceFields(classDef, classIdx);

        /* Process virtual methods */
        rtn = gDexDb.addVirtualMethods(classDef, classIdx, METHOD_TYPE_VIRTUAL);

        /* Process direct methods */
        rtn = gDexDb.addDirectMethods(classDef, classIdx, METHOD_TYPE_DIRECT);

        return rtn;
    }

    public static void main(String[] args) {

        int rtn = 0;

        String dexFileName = "";
        String dexDbName = "";
        int sdkVersion = 0;

        CommandLineParser parser = new BasicParser();
        CommandLine cmd = null;

        gOptions.addOption("a", true, "Android API level to use.");
        gOptions.addOption("d", false, "Show debugging information.");
        gOptions.addOption("h", false, "Show help screen.");
        gOptions.addOption("i", true, "Input DEX/ODEX file.");
        gOptions.addOption("o", true, "Output DB file.");

        try {
            cmd = parser.parse(gOptions, args);

            if (cmd.hasOption("h")) {
                usage();
                System.exit(0);
            }

            if (cmd.hasOption("d"))
                gDebug = true;

            if (!cmd.hasOption("i") || !cmd.hasOption("o") || !cmd.hasOption("a")) {
                System.err.println("[ERROR] Input, output, and API level parameters are required!");
                usage();
                System.exit(-1);
            }

        } catch (ParseException e) {
            System.err.println("[ERROR] Unable to parse command line properties: " + e);
            System.exit(-1);
        }

        dexFileName = cmd.getOptionValue("i");
        dexDbName = cmd.getOptionValue("o");

        try {
            sdkVersion = Integer.parseInt(cmd.getOptionValue("a"));
        } catch (NumberFormatException e) {
            System.err.println("[ERROR] Numeric API level required!");
            System.exit(-2);
        }

        if (!isFile(dexFileName)) {
            System.err.println("[ERROR] File '" + dexFileName + "' does not exist!");
            System.exit(-3);
        }

        if (gDebug) {
            System.out.println("Loading DEX into object.");
        }
        try {
            gDexFile = DexFileFactory.loadDexFile(dexFileName, sdkVersion);
        } catch (IOException e) {
            System.err.println("[ERROR] Unable to load DEX file!");
            System.exit(-4);
        }

        if (gDebug) {
            System.out.println("Creating DexDbHelper.");
        }
        gDexDb = new DexDbHelper(dexDbName);

        if (gDebug) {
            System.out.println("Droping data from DB (if exists).");
        }
        rtn = gDexDb.dropTables();
        if (rtn != 0) {
            System.err.println("[ERROR] Error dropping tables!");
            System.exit(rtn);
        }

        if (gDebug) {
            System.out.println("About to create tables...");
        }
        rtn = gDexDb.createTables();
        if (rtn != 0) {
            System.err.println("[ERROR] Error creating tables!");
            System.exit(rtn);
        }

        if (gDebug) {
            System.out.println("About to process DEX...");
        }
        rtn = processDex();
        if (rtn != 0) {
            System.err.println("[ERROR] Error processing dex!");
        }

        /* Close it down. */
        if (gDebug) {
            System.out.println("Closing database.");
        }
        rtn = gDexDb.closeDatabase();
        if (rtn != 0) {
            System.err.println("[ERROR] Could not close database!");
            System.exit(rtn);
        }
        System.exit(rtn);
    }
}