/* 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.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
public class ApkUnzipper {
private static final Logger LOGGER = Logger.getLogger(ApkUnzipper.class);
/**
* Extracts the given archive into the given destination directory
*
* @param archive
* - the file to extract
* @param dest
* - the destination directory
* @throws Exception
*/
public static void extractArchive(File archive, File destDir)
throws IOException {
if (!destDir.exists()) {
destDir.mkdir();
}
ZipFile zipFile = new ZipFile(archive);
Enumeration<? extends ZipEntry> entries = zipFile.entries();
byte[] buffer = new byte[16384];
int len;
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String entryFileName = entry.getName();
File dir = buildDirectoryHierarchyFor(entryFileName, destDir);
if (!dir.exists()) {
dir.mkdirs();
}
if (!entry.isDirectory()) {
File file = new File(destDir, entryFileName);
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(file));
BufferedInputStream bis = new BufferedInputStream(
zipFile.getInputStream(entry));
while ((len = bis.read(buffer)) > 0) {
bos.write(buffer, 0, len);
}
bos.flush();
bos.close();
bis.close();
}
}
zipFile.close();
}
/**
* Copies the given file into the given destination directory
*
* @param archive
* - the file to copy
* @param dest
* - the destination directory
* @throws IOException
*/
public static void copyApk(File archive, File dest) throws IOException {
File destination = new File(dest, archive.getName());
FileUtils.copyFile(archive, destination);
}
/**
* Extracts the given apk into the given destination directory
*
* @param archive
* - the file to extract
* @param dest
* - the destination directory
* @throws IOException
*/
public static void extractApk(File archive, File dest) throws IOException {
@SuppressWarnings("resource") // Closing it later results in an error
ZipFile zipFile = new ZipFile(archive);
Enumeration<? extends ZipEntry> entries = zipFile.entries();
BufferedOutputStream bos = null;
BufferedInputStream bis = null;
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String entryFileName = entry.getName();
byte[] buffer = new byte[16384];
int len;
File dir = buildDirectoryHierarchyFor(entryFileName, dest);// destDir
if (!dir.exists()) {
dir.mkdirs();
}
if (!entry.isDirectory()) {
if (entry.getSize() == 0) {
LOGGER.warn("Found ZipEntry \'" + entry.getName()
+ "\' with size 0 in " + archive.getName()
+ ". Looks corrupted.");
continue;
}
try {
bos = new BufferedOutputStream(new FileOutputStream(
new File(dest, entryFileName)));// destDir,...
bis = new BufferedInputStream(zipFile.getInputStream(entry));
while ((len = bis.read(buffer)) > 0) {
bos.write(buffer, 0, len);
}
bos.flush();
} catch (IOException ioe) {
LOGGER.warn("Failed to extract entry \'" + entry.getName()
+ "\' from archive. Results for "
+ archive.getName() + " may not be accurate");
} finally {
if (bos != null) bos.close();
if (bis != null) bis.close();
// if (zipFile != null) zipFile.close();
}
}
}
}
private static File buildDirectoryHierarchyFor(String entryName,
File destDir) {
int lastIndex = entryName.lastIndexOf('/');
String internalPathToEntry = entryName.substring(0, lastIndex + 1);
return new File(destDir, internalPathToEntry);
}
}