de.rub.syssec.saaf.Headless.java Source code

Java tutorial

Introduction

Here is the source code for de.rub.syssec.saaf.Headless.java

Source

/* SAAF: A static analyzer for APK files.
 * Copyright (C) 2013  syssec.rub.de
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package de.rub.syssec.saaf;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Collection;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;

import de.rub.syssec.saaf.misc.config.Config;
import de.rub.syssec.saaf.misc.config.ConfigKeys;

/**
 * This class handles the non GUI mode and starts the analysis.
 * 
 * @author Hanno Lemoine <hanno.lemoine@gdata.de>
 * @author Johannes Hoffmann <johannes.hoffmann@rub.de>
 */
public class Headless {
    private static final Logger LOGGER = Logger.getLogger(Headless.class);
    private static Config CONFIG = Config.getInstance();

    private Headless() {
        /* no instance */}

    /**
     * Analyze one or multiple APKs.
     * If you use MultiThreading(default) be aware, sometimes if an error occurs,
     * no Exception and no StackTrace is shown.
     *
     * important command parameter:
     * <default>              APKs in given directory are analyzed.
     * --recursive, -r        APKs in subdirectories are also analyzed.
     * --fileList, -fl        APKs found at given file location are analyzed.
     * --single-threaded, -st to deactivate MultiThreading
     *
     * @param path      {fileLocation of an APK,
     *                  dir with several APKs in it,
     *                  file with several file paths to APKs}
     *                  depending on cmd-parameter.
     *
     */
    public static void startAnalysis(File path) {
        if (path == null) {
            LOGGER.error("Please specify a file or directory to analyze!");
            return;
        } else if (!path.exists()) {
            LOGGER.error("File or directory does not exist. Inserted path: " + path);
            return;
        }

        LinkedList<File> apks = new LinkedList<File>();
        if (path.isFile() && !CONFIG.getBooleanConfigValue(ConfigKeys.USE_FILE_LIST)) {
            apks = gatherApksFromPath(path);
        } else if (path.isFile() && CONFIG.getBooleanConfigValue(ConfigKeys.USE_FILE_LIST)) {
            apks = gatherApksFromFileList(path);
        } else if (path.isDirectory() && !CONFIG.getBooleanConfigValue(ConfigKeys.RECURSIVE_DIR_ANALYSIS)) {
            apks = gatherApksFromPath(path);
        } else if (path.isDirectory() && CONFIG.getBooleanConfigValue(ConfigKeys.RECURSIVE_DIR_ANALYSIS)) {
            apks = gatherApksFromPath(path);
        }

        if (apks.size() <= 0) {
            LOGGER.error("Found no APK to analyze!");
            return;
        }

        performAnalysis(apks);
    }

    /**
     * Gather APKs for option-cases file, directory and recursive directory.
     *
     * @param path      APK file or directory of multiple APKs
     * @return          LinkedList of APK-Files
     * 
     */
    private static LinkedList<File> gatherApksFromPath(File path) {
        LinkedList<File> apks = new LinkedList<File>();
        if (path.isDirectory()) {
            Collection<File> fc = FileUtils.listFiles(path, null,
                    CONFIG.getBooleanConfigValue(ConfigKeys.RECURSIVE_DIR_ANALYSIS));
            apks = new LinkedList<File>(fc);
            if (apks.size() <= 0) {
                LOGGER.info("No files found in directory. Forgot a -r?");
            }
            LOGGER.info("Read " + apks.size() + " files from Directory " + path);
        } else if (path.isFile()) {
            apks.add(path);
        }
        return apks;
    }

    /**
     * Gather APKs for option-case fileList.
     * You can configure a optional fileList_prefix in saaf.conf.
     *
     * @param fileList  ASCI-file with multiple paths to APK-files
     * @return          LinkedList of APK-Files
     */
    private static LinkedList<File> gatherApksFromFileList(File fileList) {
        LinkedList<File> apks = new LinkedList<File>();
        String pathFileListPrefix = CONFIG.getConfigValue(ConfigKeys.FILE_LIST_PREFIX);
        if (pathFileListPrefix == null) {
            LOGGER.warn("No 'prefix' directory configured for fileList! If"
                    + "you want to use it, add 'path_fileList_prefix' to" + "saaf.conf.");
            pathFileListPrefix = "";
        }
        BufferedReader in = null;
        try {
            in = new BufferedReader(new FileReader(fileList));
            String line = null;
            while ((line = in.readLine()) != null) {
                /**
                 * HACK: Every File produced by Microsoft SQL ... Studio,
                 * starts with the non printable Bytes EF BB BF. This is
                 * only a BugFix to destroy these bytes.
                 * 
                 * FIXME: Is this sequence on EACH line or only at the first?
                 */
                byte[] a = new byte[] { (byte) 0xEF, (byte) 0xBB, (byte) 0xBF };
                line = line.replaceAll(new String(a), "");

                File pathFileList = new File(pathFileListPrefix + File.separator
                        + line.replace("\\\\", "").replace("\\", File.separator));
                if (!pathFileList.isFile() || !pathFileList.canRead()) {
                    LOGGER.error("Skipping non valid file: " + pathFileListPrefix);
                } else {
                    apks.add(pathFileList);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (Exception ignored) {
                }
            }
        }
        LOGGER.info("Read " + apks.size() + " files from FileList " + fileList);
        return apks;
    }

    /**
     * Analyze one or multiple APKs. Depending on the configuration this may
     * happen single- or multithreaded.
     *
     * @param apks the apks to analyze
     */
    private static void performAnalysis(LinkedList<File> apks) {
        // Initialize MultiThreading and queue
        int corePoolSize = Runtime.getRuntime().availableProcessors();
        if (corePoolSize > 1)
            corePoolSize--;
        int numThreads = CONFIG.getIntConfigValue(ConfigKeys.MULTITHREADING_THREADS, corePoolSize);
        if (!CONFIG.getBooleanConfigValue(ConfigKeys.MULTITHREADING_ENABLED)) { // multithreading is disabled
            numThreads = 1;
        }
        // Create executor and submit jobs
        SAAFThreadPoolExecutor executor = new SAAFThreadPoolExecutor(apks, numThreads, numThreads, 5,
                TimeUnit.SECONDS);
        executor.allowCoreThreadTimeOut(true);

        // Tell the executor to shutdown afterwards
        executor.shutdown();
        boolean b = true;
        try {
            b = executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); // timeout should not occur
        } catch (InterruptedException e) {
            LOGGER.error("Got interrupted while waiting for analyses to finish, this should not happen.", e);
        }
        if (!b) {
            LOGGER.error("Got a timeout while waiting for analyses to finish, this should not happen.");
        }
        executor.printStatistic();
    }
}