/* 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.analysis.steps.extract; import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Calendar; import org.apache.log4j.Logger; import brut.androlib.AndrolibException; import brut.androlib.ApkDecoder; import brut.androlib.err.CantFindFrameworkResException; import brut.androlib.err.OutDirExistsException; import brut.androlib.res.AndrolibResources; import brut.androlib.res.util.ExtFile; import de.rub.syssec.saaf.misc.config.Config; import de.rub.syssec.saaf.misc.config.ConfigKeys; /** * Interface to the Android-APKtool * * @author Martin Ussath * @author Hanno Lemoine <hanno.lemoine@gdata.de> * */ public class ApkDecoderInterface { /** * What to do if the apktool installed on the users system is older than ours. * * @author Tilman Bender <tilman.bender@rub.de> * */ public enum Treatment{ DONT_TOUCH, RENAME, DELETE } private static final Logger LOGGER = Logger.getLogger(ApkDecoderInterface.class); /** * Decodes an APK file * * <h4>Handles following problems:</h4> * <p> * Problem 1: if SAAF uses an older version of APKTool than the user * ~/apktool/framework/1.apk * Handling: 3 Options for the user ({@link Config}) * - default: APKTool only decode the smali files, no Resources (incl. Manifest) * - mv: rename the framework file, and complete decode * - del: delete the framework file, and complete decode * </p> * * @param apk the apk * @param destination the destination dir (if already existing, it will be removed firstly) * @return false if the decoding crashes */ private static final Object MUTEX= new Object(); public static boolean decode(File apk, File destination) throws DecoderException { boolean decodeSucseccfull = false; synchronized(MUTEX){ ApkDecoder localApkDecoder = new ApkDecoder(); try { // HL: The ApkTool can delete the Destination Folder on his own: (also faster) localApkDecoder.setForceDelete(true); localApkDecoder.setOutDir(destination); localApkDecoder.setApkFile(apk); //disable resource decoding localApkDecoder.setDecodeResources((short) 0x0100); //apkdecoder constants // public final static short DECODE_SOURCES_NONE = 0x0000; // public final static short DECODE_SOURCES_SMALI = 0x0001; // public final static short DECODE_SOURCES_JAVA = 0x0002; // // public final static short DECODE_RESOURCES_NONE = 0x0100; // public final static short DECODE_RESOURCES_FULL = 0x0101; // localApkDecoder.setDecodeSources(ApkDecoder.DECODE_SOURCES_JAVA); //HL: Not yet implemented by APKTool localApkDecoder.decode(); //extract the manifest file, because disabling decoding resource also disables decoding of the manifest file AndrolibResources res = new AndrolibResources(); ExtFile apkFile = new ExtFile(apk); res.decodeManifest(res.getResTable(apkFile,true), apkFile, destination); decodeSucseccfull = true; } catch (OutDirExistsException ex) { // Should never occur, because setForceDelete(true) is called. LOGGER.error( "Destination directory (" + destination.getAbsolutePath() + ") " + "already exists.",ex); throw new DecoderException(ex); } catch (CantFindFrameworkResException ex) { LOGGER.warn( "Can't find framework resources for package of id: " + String.valueOf(ex.getPkgId()) + ". You must install proper " + "framework files, see Android-APKtool-project website for more info."); throw new DecoderException(ex); } catch (AndrolibException ex) { /** * Handle the special type of AndrolibException caused by an outdated * version of Android-APKTool (used by SAAF) in contrast to the version the user uses. */ if (ex.getMessage().startsWith("Multiple resources:")) { Treatment userOption = Treatment.valueOf(Config.getInstance().getConfigValue(ConfigKeys.APKTOOL_TREATMENT)); if ( (userOption == Treatment.DELETE) || (userOption == Treatment.RENAME)) { //1. Rename or Delete current framework file final String frameworkDir = System.getProperty("user.home") + File.separatorChar + "apktool" + File.separatorChar + "framework" + File.separatorChar; File apktool_framwork = new File(frameworkDir + "1.apk"); if (userOption == Treatment.DELETE) { apktool_framwork.delete(); } else { //case Config.RENAME_APKtool_FRAMEWORK_IF_TOO_OLD final String curDateTime = new SimpleDateFormat("yyyy-MM-dd_HHmm").format(Calendar.getInstance().getTime()); apktool_framwork.renameTo(new File(frameworkDir + "1_mv_by_SAAF_on_" + curDateTime + ".apk")); } //2. Next try to decode try { localApkDecoder.decode(); } catch (IOException e) { throw new DecoderException(e); } catch (AndrolibException e) { throw new DecoderException(e); } decodeSucseccfull = true; //3. Register created framework file to be deleted on the shutdown of SAAF File created_apktool_framwork = new File(frameworkDir + "1.apk"); if ( created_apktool_framwork.exists()) { created_apktool_framwork.deleteOnExit(); } } else { //case Config.DONT_TOUCH_APKtool_FRAMEWORK_IF_TOO_OLD //localApkDecoder.setDecodeResources(ApkDecoder.DECODE_RESOURCES_NONE); //HL: Alternative, if APKTool crashes by Resources try { //localApkDecoder.setDecodeResources((short)0x0100); //HL: Alternative, if APKTool crashes by Resources localApkDecoder.decode(); } catch (IOException e) { throw new DecoderException(e); } catch (AndrolibException e) { throw new DecoderException(e); } LOGGER.error("Could not decode app correctly with APKtool, " + "because SAAF version is older than your APKtool. \nYou can " + "solve this problem by allowing SAAF in the config file " + "to move (mv) or to delete (del) \nthe framework file " + "(~/apktool/framwork/1.apk)." + "e.g. 'PERMISSION_FOR_APKtool_FRAMEWORK_TO=mv'"); //TODO: Check if Logger crashes in headless mode } } else { throw new DecoderException(ex); } }catch(Exception e){ e.printStackTrace(); } } return decodeSucseccfull; } }