Java tutorial
/*************************************************************************** * Copyright (C) 2016 iObserve Project (https://www.iobserve-devops.net) * * 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 org.iobserve.mobile.instrument.core; import java.io.File; import java.io.IOException; import java.lang.ProcessBuilder.Redirect; import java.nio.file.Files; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.apache.commons.io.FileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import net.lingala.zip4j.core.ZipFile; import net.lingala.zip4j.exception.ZipException; /** * Proxy for the APK tool which allows us to modify the original manifest file. * * @see https://ibotpeaches.github.io/Apktool/ * @author Robert Heinrich * @author David Monschein * */ public class APKToolProxy { /** Logger. */ private static final Logger LOG = LogManager.getLogger(APKToolProxy.class); /** Path to the library. */ private static final String LIB_PATH = "lib\\apktool_2.2.1.jar"; /** Name of the Android manifest file. */ private static final String MANIFEST_FILE = "AndroidManifest.xml"; /** Input Android application. */ private File input; /** Name of the folder to where the application can be unzipped. */ private String folderName; /** Name of the package provided by the application. */ private String packageName; /** * Creates a proxy for the APKTool. * * @param inputAPK * input application */ public APKToolProxy(final File inputAPK) { this.input = inputAPK; this.folderName = null; this.packageName = null; } /** * Decodes the application to a specified folder. * * @param fName * the folder where the application files can be stored * temporarily. * @return true if success - false otherwise */ public boolean decodeAPK(final String fName) { this.folderName = fName; // apktool d bar.apk -o baz final ProcessBuilder pb = new ProcessBuilder("java", "-jar", LIB_PATH, "d", input.getAbsolutePath(), "-o", folderName); try { pb.start().waitFor(); return true; } catch (InterruptedException | IOException e) { LOG.error("Failed to execute APKTool to decode application."); return false; } } /** * Adjust the manifest file of the application. * * @param rights * the rights which are mandatory * @param modifiedManifest * the file where the adjusted manifest can be stored * @return true if success - false otherwise */ public boolean adjustManifest(final List<String> rights, final File modifiedManifest) { if (folderName == null) { return false; } // read all rights final File manifestFile = new File(folderName + "/" + MANIFEST_FILE); if (!manifestFile.exists()) { return false; } final Set<String> exRights = new HashSet<String>(); // PARSING XML final Document dom; // Make an instance of the DocumentBuilderFactory final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); try { final DocumentBuilder db = dbf.newDocumentBuilder(); dom = db.parse(manifestFile); } catch (ParserConfigurationException | SAXException | IOException e) { return false; } final Node manifestNode = dom.getElementsByTagName("manifest").item(0); this.packageName = manifestNode.getAttributes().getNamedItem("package").getTextContent(); final NodeList permissionNodes = manifestNode.getChildNodes(); for (int k = 0; k < permissionNodes.getLength(); k++) { final Node permNode = permissionNodes.item(k); if (permNode.getNodeName().equals("uses-permission")) { exRights.add(permNode.getAttributes().getNamedItem("android:name").getTextContent()); } } // determine which to add for (String right : rights) { if (!exRights.contains(right)) { final Element nNode = dom.createElement("uses-permission"); nNode.setAttribute("android:name", right); manifestNode.appendChild(nNode); } } // write content back to file try { final TransformerFactory transformerFactory = TransformerFactory.newInstance(); final Transformer transformer = transformerFactory.newTransformer(); final DOMSource source = new DOMSource(dom); final StreamResult result = new StreamResult(manifestFile); transformer.transform(source, result); } catch (TransformerException e) { return false; } // RECOMPILE // apktool b bar -o new_bar.apk final File rebuildFile = new File(folderName + "/rebuild.apk"); final ProcessBuilder pb = new ProcessBuilder("java", "-jar", LIB_PATH, "b", folderName, "-o", rebuildFile.getAbsolutePath()); pb.redirectOutput(Redirect.INHERIT); pb.redirectError(Redirect.INHERIT); try { pb.start().waitFor(); } catch (InterruptedException | IOException e) { LOG.error("Failed to rebuild apk with APKTool."); return false; } // UNZIP IT try { final ZipFile rebuildZip = new ZipFile(rebuildFile); rebuildZip.extractFile(MANIFEST_FILE, folderName + "/" + "manifest_new"); Files.copy(new File(folderName + "/" + "manifest_new" + "/" + MANIFEST_FILE).toPath(), modifiedManifest.toPath()); } catch (ZipException | IOException e) { LOG.error("Failed to extract the manifest from the rebuilt application."); return false; } return true; } /** * Cleans temporary files. * * @throws IOException * if not all temp files could be deleted. */ public void cleanup() throws IOException { FileUtils.deleteDirectory(new File(folderName)); } /** * Gets the package name for the application. * * @return the package name for the application. */ public String getPackageName() { return packageName; } }