mirror of
https://github.com/revanced/Apktool.git
synced 2025-05-03 15:24:26 +02:00
support for decoding unknown files, @todo add support for building unknown files
This commit is contained in:
parent
2e44e3a856
commit
0ca74eca67
@ -33,23 +33,12 @@ import java.util.logging.Logger;
|
|||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
import javax.xml.parsers.DocumentBuilder;
|
import org.apache.commons.io.FileUtils;
|
||||||
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.yaml.snakeyaml.DumperOptions;
|
import org.yaml.snakeyaml.DumperOptions;
|
||||||
import org.yaml.snakeyaml.Yaml;
|
import org.yaml.snakeyaml.Yaml;
|
||||||
|
|
||||||
|
import org.apache.commons.io.FileUtils.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
@ -152,10 +141,11 @@ public class Androlib {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void decodeUnknownFiles(ExtFile apkFile, File outDir)
|
public void decodeUnknownFiles(ExtFile apkFile, File outDir, ResTable resTable)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
LOGGER.info("Copying unknown files/dir...");
|
LOGGER.info("Copying unknown files/dir...");
|
||||||
File unknownOut = new File(outDir, UNK_DIRNAME);
|
File unknownOut = new File(outDir, UNK_DIRNAME);
|
||||||
|
ZipEntry invZipFile;
|
||||||
|
|
||||||
// have to use container of ZipFile to help identify compression type
|
// have to use container of ZipFile to help identify compression type
|
||||||
// with regular looping of apkFile for easy copy
|
// with regular looping of apkFile for easy copy
|
||||||
@ -163,23 +153,27 @@ public class Androlib {
|
|||||||
Directory unk = apkFile.getDirectory();
|
Directory unk = apkFile.getDirectory();
|
||||||
ZipFile apkZipFile = new ZipFile(apkFile.getAbsolutePath());
|
ZipFile apkZipFile = new ZipFile(apkFile.getAbsolutePath());
|
||||||
|
|
||||||
// loop all items in container, ignoring any that are pre-defined by aapt
|
// loop all items in container recursively, ignoring any that are pre-defined by aapt
|
||||||
Set<String> files = unk.getFiles();
|
Set<String> files = unk.getFiles(true);
|
||||||
for (String file : files) {
|
for (String file : files) {
|
||||||
if (!isAPKFileNames(file)) {
|
if (!isAPKFileNames(file)) {
|
||||||
|
|
||||||
|
// copy file out of archive into special "unknown" folder
|
||||||
|
// to be re-included on build
|
||||||
unk.copyToDir(unknownOut,file);
|
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<String, Directory> 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) {
|
catch (DirectoryException ex) {
|
||||||
throw new AndrolibException(ex);
|
throw new AndrolibException(ex);
|
||||||
@ -187,8 +181,6 @@ public class Androlib {
|
|||||||
catch (IOException ex) {
|
catch (IOException ex) {
|
||||||
throw new AndrolibException(ex);
|
throw new AndrolibException(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeOriginalFiles(ExtFile apkFile, File outDir)
|
public void writeOriginalFiles(ExtFile apkFile, File outDir)
|
||||||
@ -287,6 +279,10 @@ public class Androlib {
|
|||||||
buildLib(appDir, flags);
|
buildLib(appDir, flags);
|
||||||
buildCopyOriginalFiles(appDir, flags);
|
buildCopyOriginalFiles(appDir, flags);
|
||||||
buildApk(appDir, outFile, 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<String, Boolean> flags)
|
public void buildSources(File appDir, HashMap<String, Boolean> flags)
|
||||||
@ -533,53 +529,31 @@ public class Androlib {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void remove_manifest_versions(String filePath)
|
public void buildUnknownFiles(File appDir, File outFile, Map<String, Object> meta)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
|
|
||||||
File f = new File(filePath);
|
// confirm we have unknown files to inject
|
||||||
|
if (meta.containsKey("unknownFiles")) {
|
||||||
if (f.exists()) {
|
LOGGER.info("Copying unknown files/dir...");
|
||||||
// remove versionCode and versionName
|
|
||||||
try {
|
|
||||||
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
|
|
||||||
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
|
|
||||||
Document doc = docBuilder.parse(filePath.toString());
|
|
||||||
|
|
||||||
Node manifest = doc.getFirstChild();
|
Map<String, String> files = (Map<String, String>)meta.get("unknownFiles");
|
||||||
|
|
||||||
// load attr
|
try {
|
||||||
NamedNodeMap attr = manifest.getAttributes();
|
ZipFile apkZipFile = new ZipFile(outFile.getAbsolutePath());
|
||||||
Node vCode = attr.getNamedItem("android:versionCode");
|
|
||||||
Node vName = attr.getNamedItem("android:versionName");
|
|
||||||
|
|
||||||
// remove versionCode
|
// loop through files inside
|
||||||
if (vCode != null) {
|
for (Map.Entry<String,String> entry : files.entrySet()) {
|
||||||
attr.removeNamedItem("android:versionCode");
|
|
||||||
|
// 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,
|
public void buildApk(File appDir, File outApk,
|
||||||
HashMap<String, Boolean> flags) throws AndrolibException {
|
HashMap<String, Boolean> flags) throws AndrolibException {
|
||||||
|
@ -133,11 +133,8 @@ public class ApkDecoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mAndrolib.decodeRawFiles(mApkFile, outDir);
|
mAndrolib.decodeRawFiles(mApkFile, outDir);
|
||||||
mAndrolib.decodeUnknownFiles(mApkFile, outDir);
|
mAndrolib.decodeUnknownFiles(mApkFile, outDir, mResTable);
|
||||||
mAndrolib.writeOriginalFiles(mApkFile, outDir);
|
mAndrolib.writeOriginalFiles(mApkFile, outDir);
|
||||||
|
|
||||||
// remove version names in favour of aapt injection
|
|
||||||
mAndrolib.remove_manifest_versions(outDir.getAbsolutePath() + "/AndroidManifest.xml");
|
|
||||||
writeMetaFile();
|
writeMetaFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,6 +248,7 @@ public class ApkDecoder {
|
|||||||
putPackageInfo(meta);
|
putPackageInfo(meta);
|
||||||
putVersionInfo(meta);
|
putVersionInfo(meta);
|
||||||
putCompressionInfo(meta);
|
putCompressionInfo(meta);
|
||||||
|
putUnknownInfo(meta);
|
||||||
//meta.put("packageId", getResTable().getPackageInfo().get("cur_package_id"));
|
//meta.put("packageId", getResTable().getPackageInfo().get("cur_package_id"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,32 +279,42 @@ public class ApkDecoder {
|
|||||||
meta.put("usesFramework", uses);
|
meta.put("usesFramework", uses);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void putSdkInfo(Map<String, Object> meta) throws AndrolibException {
|
private void putSdkInfo(Map<String, Object> meta)
|
||||||
Map<String, String> info = getResTable().getSdkInfo();
|
throws AndrolibException {
|
||||||
if (info.size() > 0) {
|
Map<String, String> info = getResTable().getSdkInfo();
|
||||||
meta.put("sdkInfo", info);
|
if (info.size() > 0) {
|
||||||
}
|
meta.put("sdkInfo", info);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void putPackageInfo(Map<String, Object> meta)
|
|
||||||
throws AndrolibException {
|
|
||||||
Map<String, String> info = getResTable().getPackageInfo();
|
|
||||||
if (info.size() > 0) {
|
|
||||||
meta.put("packageInfo", info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void putVersionInfo(Map<String, Object> meta) throws AndrolibException {
|
|
||||||
Map<String, String> info = getResTable().getVersionInfo();
|
|
||||||
if (info.size() > 0) {
|
|
||||||
meta.put("versionInfo", info);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void putCompressionInfo(Map<String, Object> meta)
|
private void putPackageInfo(Map<String, Object> meta)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
meta.put("compressionType", getCompressionType());
|
Map<String, String> info = getResTable().getPackageInfo();
|
||||||
}
|
if (info.size() > 0) {
|
||||||
|
meta.put("packageInfo", info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putVersionInfo(Map<String, Object> meta)
|
||||||
|
throws AndrolibException {
|
||||||
|
Map<String, String> info = getResTable().getVersionInfo();
|
||||||
|
if (info.size() > 0) {
|
||||||
|
meta.put("versionInfo", info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putUnknownInfo(Map<String, Object> meta)
|
||||||
|
throws AndrolibException {
|
||||||
|
Map<String,String> info = getResTable().getUnknownFiles();
|
||||||
|
if (info.size() > 0) {
|
||||||
|
meta.put("unknownFiles", info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putCompressionInfo(Map<String, Object> meta)
|
||||||
|
throws AndrolibException {
|
||||||
|
meta.put("compressionType", getCompressionType());
|
||||||
|
}
|
||||||
|
|
||||||
private boolean getCompressionType() {
|
private boolean getCompressionType() {
|
||||||
return mCompressResources;
|
return mCompressResources;
|
||||||
|
@ -47,7 +47,6 @@ import javax.xml.transform.stream.StreamResult;
|
|||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.NamedNodeMap;
|
import org.w3c.dom.NamedNodeMap;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
import org.w3c.dom.NodeList;
|
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.xml.sax.SAXException;
|
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)
|
public void decode(ResTable resTable, ExtFile apkFile, File outDir)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
Duo<ResFileDecoder, AXmlResourceParser> duo = getResFileDecoder();
|
Duo<ResFileDecoder, AXmlResourceParser> duo = getResFileDecoder();
|
||||||
@ -263,9 +310,13 @@ final public class AndrolibResources {
|
|||||||
fileDecoder.decodeManifest(inApk, "AndroidManifest.xml", out,
|
fileDecoder.decodeManifest(inApk, "AndroidManifest.xml", out,
|
||||||
"AndroidManifest.xml");
|
"AndroidManifest.xml");
|
||||||
|
|
||||||
// fix package if needed
|
// fix package (Android 4.2)
|
||||||
adjust_package_manifest(resTable, outDir.getAbsolutePath()
|
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")) {
|
if (inApk.containsDir("res")) {
|
||||||
in = inApk.getDir("res");
|
in = inApk.getDir("res");
|
||||||
|
@ -38,6 +38,7 @@ public class ResTable {
|
|||||||
private Map<String, String> mSdkInfo = new LinkedHashMap<String, String>();
|
private Map<String, String> mSdkInfo = new LinkedHashMap<String, String>();
|
||||||
private Map<String, String> mPackageInfo = new LinkedHashMap<String, String>();
|
private Map<String, String> mPackageInfo = new LinkedHashMap<String, String>();
|
||||||
private Map<String, String> mVersionInfo = new LinkedHashMap<String, String>();
|
private Map<String, String> mVersionInfo = new LinkedHashMap<String, String>();
|
||||||
|
private Map<String, String> mUnknownFiles = new LinkedHashMap<String, String>();
|
||||||
|
|
||||||
public ResTable() {
|
public ResTable() {
|
||||||
mAndRes = null;
|
mAndRes = null;
|
||||||
@ -137,6 +138,10 @@ public class ResTable {
|
|||||||
mPackageInfo.put(key, value);
|
mPackageInfo.put(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addUnknownFileInfo(String file, String value) {
|
||||||
|
mUnknownFiles.put(file,value);
|
||||||
|
}
|
||||||
|
|
||||||
public Map<String, String> getPackageInfo() {
|
public Map<String, String> getPackageInfo() {
|
||||||
return mPackageInfo;
|
return mPackageInfo;
|
||||||
}
|
}
|
||||||
@ -149,6 +154,10 @@ public class ResTable {
|
|||||||
return mSdkInfo;
|
return mSdkInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getUnknownFiles() {
|
||||||
|
return mUnknownFiles;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isPackageInfoValueSet(String key) {
|
public boolean isPackageInfoValueSet(String key) {
|
||||||
return (mPackageInfo.containsKey(key));
|
return (mPackageInfo.containsKey(key));
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user