From 0b8796e56fa454a1bc635d74ebeb5eff796d56b3 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Fri, 1 Apr 2016 13:19:29 +0200 Subject: [PATCH] add AndroidManifest parser for reading directly from APKs This will make our algorithm choices a lot more flexible both in terms of the Installer process and the swap repo process. --- .../fdroid/fdroid/AndroidXMLDecompress.java | 177 ++++++++++++++++++ app/src/test/assets/urzip.apk | Bin 0 -> 9969 bytes .../fdroid/AndroidXMLDecompressTest.java | 51 +++++ 3 files changed, 228 insertions(+) create mode 100644 app/src/main/java/org/fdroid/fdroid/AndroidXMLDecompress.java create mode 100644 app/src/test/assets/urzip.apk create mode 100644 app/src/test/java/org/fdroid/fdroid/AndroidXMLDecompressTest.java diff --git a/app/src/main/java/org/fdroid/fdroid/AndroidXMLDecompress.java b/app/src/main/java/org/fdroid/fdroid/AndroidXMLDecompress.java new file mode 100644 index 000000000..b8569f246 --- /dev/null +++ b/app/src/main/java/org/fdroid/fdroid/AndroidXMLDecompress.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2016 Hans-Christoph Steiner + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +/* + Copyright (c) 2016, Liu Dong + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of apk-parser nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.fdroid.fdroid; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +/** + * Parse the 'compressed' binary form of Android XML docs such as for + * {@code AndroidManifest.xml} in APK files. This is a very truncated + * version of apk-parser since currently, we only need the header from + * the binary XML AndroidManifest.xml. apk-parser provides full APK + * parsing, which is a lot more than what is needed. + * + * @see apk-parser + * @see Android Internals: Binary XML + * @see a binary XML parser + */ +public class AndroidXMLDecompress { + public static int startTag = 0x00100102; + + /** + * Just get the XML attributes from the {@code } element. + * + * @return A key value map of the attributes, with values as {@link Object}s + */ + public static Map getManifestHeaderAttributes(String filename) throws IOException { + byte[] binaryXml = getManifestFromFilename(filename); + int numbStrings = littleEndianWord(binaryXml, 4 * 4); + int stringIndexTableOffset = 0x24; + int stringTableOffset = stringIndexTableOffset + numbStrings * 4; + int xmlTagOffset = littleEndianWord(binaryXml, 3 * 4); + for (int i = xmlTagOffset; i < binaryXml.length - 4; i += 4) { + if (littleEndianWord(binaryXml, i) == startTag) { + xmlTagOffset = i; + break; + } + } + int offset = xmlTagOffset; + + while (offset < binaryXml.length) { + int tag0 = littleEndianWord(binaryXml, offset); + int nameStringIndex = littleEndianWord(binaryXml, offset + 5 * 4); + + if (tag0 == startTag) { + int numbAttrs = littleEndianWord(binaryXml, offset + 7 * 4); + offset += 9 * 4; + + HashMap attributes = new HashMap(3); + for (int i = 0; i < numbAttrs; i++) { + int attributeNameStringIndex = littleEndianWord(binaryXml, offset + 1 * 4); + int attributeValueStringIndex = littleEndianWord(binaryXml, offset + 2 * 4); + int attributeResourceId = littleEndianWord(binaryXml, offset + 4 * 4); + offset += 5 * 4; + + String attributeName = getString(binaryXml, stringIndexTableOffset, stringTableOffset, attributeNameStringIndex); + Object attributeValue; + if (attributeValueStringIndex != -1) { + attributeValue = getString(binaryXml, stringIndexTableOffset, stringTableOffset, attributeValueStringIndex); + } else { + attributeValue = attributeResourceId; + } + attributes.put(attributeName, attributeValue); + } + return attributes; + } else { + // we only need the first start tag + break; + } + } + return new HashMap(0); + } + + public static byte[] getManifestFromFilename(String filename) throws IOException { + InputStream is = null; + ZipFile zip = null; + int size = 0; + + if (filename.endsWith(".apk")) { + zip = new ZipFile(filename); + ZipEntry ze = zip.getEntry("AndroidManifest.xml"); + is = zip.getInputStream(ze); + size = (int) ze.getSize(); + } else { + throw new RuntimeException("This only works on APK files!"); + } + byte[] buf = new byte[size]; + is.read(buf); + + is.close(); + if (zip != null) { + zip.close(); + } + return buf; + } + + public static String getString(byte[] bytes, int stringIndexTableOffset, int stringTableOffset, int stringIndex) { + if (stringIndex < 0) { + return null; + } + int stringOffset = stringTableOffset + littleEndianWord(bytes, stringIndexTableOffset + stringIndex * 4); + return getStringAt(bytes, stringOffset); + } + + public static String getStringAt(byte[] bytes, int stringOffset) { + int length = bytes[stringOffset + 1] << 8 & 0xff00 | bytes[stringOffset] & 0xff; + byte[] chars = new byte[length]; + for (int i = 0; i < length; i++) { + chars[i] = bytes[stringOffset + 2 + i * 2]; + } + return new String(chars); + } + + /** + * Return the little endian 32-bit word from the byte array at offset + */ + public static int littleEndianWord(byte[] bytes, int offset) { + return bytes[offset + 3] + << 24 & 0xff000000 + | bytes[offset + 2] + << 16 & 0xff0000 + | bytes[offset + 1] + << 8 & 0xff00 + | bytes[offset] & 0xFF; + } +} diff --git a/app/src/test/assets/urzip.apk b/app/src/test/assets/urzip.apk new file mode 100644 index 0000000000000000000000000000000000000000..ee5e5cba868321f3eb85d1e3cc8b50a0a7fd560f GIT binary patch literal 9969 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 literal 0 HcmV?d00001 diff --git a/app/src/test/java/org/fdroid/fdroid/AndroidXMLDecompressTest.java b/app/src/test/java/org/fdroid/fdroid/AndroidXMLDecompressTest.java new file mode 100644 index 000000000..ead537730 --- /dev/null +++ b/app/src/test/java/org/fdroid/fdroid/AndroidXMLDecompressTest.java @@ -0,0 +1,51 @@ +package org.fdroid.fdroid; + +import org.junit.Test; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +public class AndroidXMLDecompressTest { + + String[] testDirNames = { + System.getProperty("user.dir") + "/src/test/assets", + System.getProperty("user.dir") + "/build/outputs/apk", + System.getenv("HOME") + "/fdroid/repo", + }; + + FilenameFilter apkFilter = new FilenameFilter() { + @Override + public boolean accept(File dir, String filename) { + return filename.endsWith(".apk"); + } + }; + + @Test + public void testParseVersionCode() throws IOException { + for (File f : getFilesToTest()) { + System.out.println("\n" + f); + Map map = AndroidXMLDecompress.getManifestHeaderAttributes(f.getAbsolutePath()); + for (String key : map.keySet()) { + System.out.println(key + "=\"" + map.get(key) + "\""); + } + } + } + + private List getFilesToTest() { + ArrayList apkFiles = new ArrayList(5); + for (String dirName : testDirNames) { + System.out.println("looking in " + dirName); + File dir = new File(dirName); + File[] files = dir.listFiles(apkFilter); + if (files != null) { + apkFiles.addAll(Arrays.asList(files)); + } + } + return apkFiles; + } +}