From 0ca74eca67819ea69d7f79aaf987abb5e1056ef1 Mon Sep 17 00:00:00 2001 From: Connor Tumbleson Date: Sun, 31 Mar 2013 17:13:10 -0500 Subject: [PATCH] support for decoding unknown files, @todo add support for building unknown files --- .../src/main/java/brut/androlib/Androlib.java | 114 +++++++----------- .../main/java/brut/androlib/ApkDecoder.java | 64 +++++----- .../brut/androlib/res/AndrolibResources.java | 57 ++++++++- .../java/brut/androlib/res/data/ResTable.java | 9 ++ 4 files changed, 143 insertions(+), 101 deletions(-) diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java index 46213854..d900ba50 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java @@ -33,23 +33,12 @@ import java.util.logging.Logger; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerConfigurationException; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; - -import org.w3c.dom.Document; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.xml.sax.SAXException; +import org.apache.commons.io.FileUtils; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; +import org.apache.commons.io.FileUtils.*; + /** * @author Ryszard Wiśniewski */ @@ -152,10 +141,11 @@ public class Androlib { return false; } - public void decodeUnknownFiles(ExtFile apkFile, File outDir) + public void decodeUnknownFiles(ExtFile apkFile, File outDir, ResTable resTable) throws AndrolibException { LOGGER.info("Copying unknown files/dir..."); File unknownOut = new File(outDir, UNK_DIRNAME); + ZipEntry invZipFile; // have to use container of ZipFile to help identify compression type // with regular looping of apkFile for easy copy @@ -163,23 +153,27 @@ public class Androlib { Directory unk = apkFile.getDirectory(); ZipFile apkZipFile = new ZipFile(apkFile.getAbsolutePath()); - // loop all items in container, ignoring any that are pre-defined by aapt - Set files = unk.getFiles(); + // loop all items in container recursively, ignoring any that are pre-defined by aapt + Set files = unk.getFiles(true); for (String file : files) { if (!isAPKFileNames(file)) { + + // copy file out of archive into special "unknown" folder + // to be re-included on build unk.copyToDir(unknownOut,file); - System.out.println(apkZipFile.getEntry(file).getMethod()); + try { + invZipFile = apkZipFile.getEntry(file.toString()); + + // lets record the name of the file, and its compression type + // so that we may re-include it the same way + if (invZipFile != null) { + resTable.addUnknownFileInfo(invZipFile.getName(), String.valueOf(invZipFile.getMethod())); + } + } catch (NullPointerException ignored) { + + } } } - // for folders now. - Map dirs = unk.getDirs(); - for (String dir : dirs.keySet()) { - if (!isAPKFileNames(dir)) { - unk.copyToDir(unknownOut,dir); - System.out.println(apkZipFile.getEntry(dir).getMethod()); - } - // @todo add ability to loop through dir and pull those methods - } } catch (DirectoryException ex) { throw new AndrolibException(ex); @@ -187,8 +181,6 @@ public class Androlib { catch (IOException ex) { throw new AndrolibException(ex); } - - } public void writeOriginalFiles(ExtFile apkFile, File outDir) @@ -287,6 +279,10 @@ public class Androlib { buildLib(appDir, flags); buildCopyOriginalFiles(appDir, flags); buildApk(appDir, outFile, flags); + + // we must go after the Apk is built, and copy the files in via Zip + // this is because Aapt won't add files it doesn't know (ex unknown files) + buildUnknownFiles(appDir,outFile,meta); } public void buildSources(File appDir, HashMap flags) @@ -533,53 +529,31 @@ public class Androlib { } } - public void remove_manifest_versions(String filePath) - throws AndrolibException { + public void buildUnknownFiles(File appDir, File outFile, Map meta) + throws AndrolibException { - File f = new File(filePath); - - if (f.exists()) { - // remove versionCode and versionName - try { - DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); - DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); - Document doc = docBuilder.parse(filePath.toString()); + // confirm we have unknown files to inject + if (meta.containsKey("unknownFiles")) { + LOGGER.info("Copying unknown files/dir..."); - Node manifest = doc.getFirstChild(); + Map files = (Map)meta.get("unknownFiles"); - // load attr - NamedNodeMap attr = manifest.getAttributes(); - Node vCode = attr.getNamedItem("android:versionCode"); - Node vName = attr.getNamedItem("android:versionName"); + try { + ZipFile apkZipFile = new ZipFile(outFile.getAbsolutePath()); - // remove versionCode - if (vCode != null) { - attr.removeNamedItem("android:versionCode"); + // loop through files inside + for (Map.Entry entry : files.entrySet()) { + + // check if file exists + if (new File(appDir,entry.getKey()).isFile()) { + // apkZipFile. + } + } + } catch (IOException ex) { + throw new AndrolibException(ex); + } } - if (vName != null) { - attr.removeNamedItem("android:versionName"); - } - - // save manifest - TransformerFactory transformerFactory = TransformerFactory.newInstance(); - Transformer transformer = transformerFactory.newTransformer(); - DOMSource source = new DOMSource(doc); - StreamResult result = new StreamResult(new File(filePath)); - transformer.transform(source, result); - - } catch (ParserConfigurationException ex) { - throw new AndrolibException(ex); - } catch (SAXException ex) { - throw new AndrolibException(ex); - } catch (IOException ex) { - throw new AndrolibException(ex); - } catch (TransformerConfigurationException ex) { - throw new AndrolibException(ex); - } catch (TransformerException ex) { - throw new AndrolibException(ex); - } } - } public void buildApk(File appDir, File outApk, HashMap flags) throws AndrolibException { diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java index d049ff64..7d6ade68 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java @@ -133,11 +133,8 @@ public class ApkDecoder { } mAndrolib.decodeRawFiles(mApkFile, outDir); - mAndrolib.decodeUnknownFiles(mApkFile, outDir); + mAndrolib.decodeUnknownFiles(mApkFile, outDir, mResTable); mAndrolib.writeOriginalFiles(mApkFile, outDir); - - // remove version names in favour of aapt injection - mAndrolib.remove_manifest_versions(outDir.getAbsolutePath() + "/AndroidManifest.xml"); writeMetaFile(); } @@ -251,6 +248,7 @@ public class ApkDecoder { putPackageInfo(meta); putVersionInfo(meta); putCompressionInfo(meta); + putUnknownInfo(meta); //meta.put("packageId", getResTable().getPackageInfo().get("cur_package_id")); } @@ -281,32 +279,42 @@ public class ApkDecoder { meta.put("usesFramework", uses); } - private void putSdkInfo(Map meta) throws AndrolibException { - Map info = getResTable().getSdkInfo(); - if (info.size() > 0) { - meta.put("sdkInfo", info); - } - } - - private void putPackageInfo(Map meta) - throws AndrolibException { - Map info = getResTable().getPackageInfo(); - if (info.size() > 0) { - meta.put("packageInfo", info); - } - } - - private void putVersionInfo(Map meta) throws AndrolibException { - Map info = getResTable().getVersionInfo(); - if (info.size() > 0) { - meta.put("versionInfo", info); + private void putSdkInfo(Map meta) + throws AndrolibException { + Map info = getResTable().getSdkInfo(); + if (info.size() > 0) { + meta.put("sdkInfo", info); + } } - } - private void putCompressionInfo(Map meta) - throws AndrolibException { - meta.put("compressionType", getCompressionType()); - } + private void putPackageInfo(Map meta) + throws AndrolibException { + Map info = getResTable().getPackageInfo(); + if (info.size() > 0) { + meta.put("packageInfo", info); + } + } + + private void putVersionInfo(Map meta) + throws AndrolibException { + Map info = getResTable().getVersionInfo(); + if (info.size() > 0) { + meta.put("versionInfo", info); + } + } + + private void putUnknownInfo(Map meta) + throws AndrolibException { + Map info = getResTable().getUnknownFiles(); + if (info.size() > 0) { + meta.put("unknownFiles", info); + } + } + + private void putCompressionInfo(Map meta) + throws AndrolibException { + meta.put("compressionType", getCompressionType()); + } private boolean getCompressionType() { return mCompressResources; diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java index 7d680a53..c891a050 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java @@ -47,7 +47,6 @@ import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; -import org.w3c.dom.NodeList; import org.apache.commons.io.IOUtils; import org.xml.sax.SAXException; @@ -244,6 +243,54 @@ final public class AndrolibResources { } } + public void remove_manifest_versions(String filePath) + throws AndrolibException { + + File f = new File(filePath); + + if (f.exists()) { + // remove versionCode and versionName + try { + DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); + Document doc = docBuilder.parse(filePath.toString()); + + Node manifest = doc.getFirstChild(); + + // load attr + NamedNodeMap attr = manifest.getAttributes(); + Node vCode = attr.getNamedItem("android:versionCode"); + Node vName = attr.getNamedItem("android:versionName"); + + // remove versionCode + if (vCode != null) { + attr.removeNamedItem("android:versionCode"); + } + if (vName != null) { + attr.removeNamedItem("android:versionName"); + } + + // save manifest + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + DOMSource source = new DOMSource(doc); + StreamResult result = new StreamResult(new File(filePath)); + transformer.transform(source, result); + + } catch (ParserConfigurationException ex) { + throw new AndrolibException(ex); + } catch (SAXException ex) { + throw new AndrolibException(ex); + } catch (IOException ex) { + throw new AndrolibException(ex); + } catch (TransformerConfigurationException ex) { + throw new AndrolibException(ex); + } catch (TransformerException ex) { + throw new AndrolibException(ex); + } + } + } + public void decode(ResTable resTable, ExtFile apkFile, File outDir) throws AndrolibException { Duo duo = getResFileDecoder(); @@ -263,9 +310,13 @@ final public class AndrolibResources { fileDecoder.decodeManifest(inApk, "AndroidManifest.xml", out, "AndroidManifest.xml"); - // fix package if needed + // fix package (Android 4.2) adjust_package_manifest(resTable, outDir.getAbsolutePath() - + "/AndroidManifest.xml"); + + File.separator + "AndroidManifest.xml"); + + // Remove versionName / versionCode (aapt API 16) + remove_manifest_versions(outDir.getAbsolutePath() + + File.separator + "/AndroidManifest.xml"); if (inApk.containsDir("res")) { in = inApk.getDir("res"); diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java index 198083eb..305beb06 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java @@ -38,6 +38,7 @@ public class ResTable { private Map mSdkInfo = new LinkedHashMap(); private Map mPackageInfo = new LinkedHashMap(); private Map mVersionInfo = new LinkedHashMap(); + private Map mUnknownFiles = new LinkedHashMap(); public ResTable() { mAndRes = null; @@ -137,6 +138,10 @@ public class ResTable { mPackageInfo.put(key, value); } + public void addUnknownFileInfo(String file, String value) { + mUnknownFiles.put(file,value); + } + public Map getPackageInfo() { return mPackageInfo; } @@ -149,6 +154,10 @@ public class ResTable { return mSdkInfo; } + public Map getUnknownFiles() { + return mUnknownFiles; + } + public boolean isPackageInfoValueSet(String key) { return (mPackageInfo.containsKey(key)); }