Java tutorial
/* 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(); } }