mirror of
https://github.com/revanced/Apktool.git
synced 2024-11-06 12:47:03 +01:00
Move build functions from Androlib to the ApkBuilder (#3103)
This commit is contained in:
parent
10495cbe96
commit
9c495cae29
@ -21,6 +21,7 @@ import brut.androlib.exceptions.AndrolibException;
|
||||
import brut.androlib.exceptions.CantFindFrameworkResException;
|
||||
import brut.androlib.exceptions.InFileNotFoundException;
|
||||
import brut.androlib.exceptions.OutDirExistsException;
|
||||
import brut.androlib.res.AndrolibResources;
|
||||
import brut.common.BrutException;
|
||||
import brut.directory.DirectoryException;
|
||||
import brut.directory.ExtFile;
|
||||
@ -32,6 +33,9 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.*;
|
||||
|
||||
/**
|
||||
* Main entry point of the apktool.
|
||||
*/
|
||||
public class Main {
|
||||
public static void main(String[] args) throws BrutException {
|
||||
|
||||
@ -174,10 +178,9 @@ public class Main {
|
||||
}
|
||||
|
||||
ApkDecoder decoder = new ApkDecoder(config, new ExtFile(apkName));
|
||||
decoder.setOutDir(outDir);
|
||||
|
||||
try {
|
||||
decoder.decode();
|
||||
decoder.decode(outDir);
|
||||
} catch (OutDirExistsException ex) {
|
||||
System.err
|
||||
.println("Destination directory ("
|
||||
@ -259,7 +262,7 @@ public class Main {
|
||||
if (cli.hasOption("a") || cli.hasOption("aapt")) {
|
||||
config.aaptVersion = AaptManager.getAaptVersion(cli.getOptionValue("a"));
|
||||
}
|
||||
new Androlib(config).build(new File(appDirName), outFile);
|
||||
new ApkBuilder(config, new ExtFile(appDirName)).build(outFile);
|
||||
} catch (BrutException ex) {
|
||||
System.err.println(ex.getMessage());
|
||||
System.exit(1);
|
||||
@ -268,23 +271,23 @@ public class Main {
|
||||
|
||||
private static void cmdInstallFramework(CommandLine cli, Config config) throws AndrolibException {
|
||||
String apkName = getLastArg(cli);
|
||||
new Androlib(config).installFramework(new File(apkName));
|
||||
new AndrolibResources(config).installFramework(new File(apkName));
|
||||
}
|
||||
|
||||
private static void cmdListFrameworks(CommandLine cli, Config config) throws AndrolibException {
|
||||
new Androlib(config).listFrameworks();
|
||||
new AndrolibResources(config).listFrameworkDirectory();
|
||||
}
|
||||
|
||||
private static void cmdPublicizeResources(CommandLine cli, Config config) throws AndrolibException {
|
||||
String apkName = getLastArg(cli);
|
||||
new Androlib(config).publicizeResources(new File(apkName));
|
||||
new AndrolibResources(config).publicizeResources(new File(apkName));
|
||||
}
|
||||
|
||||
private static void cmdEmptyFrameworkDirectory(CommandLine cli, Config config) throws AndrolibException {
|
||||
if (cli.hasOption("f") || cli.hasOption("force")) {
|
||||
config.forceDeleteFramework = true;
|
||||
}
|
||||
new Androlib(config).emptyFrameworkDirectory();
|
||||
new AndrolibResources(config).emptyFrameworkDirectory();
|
||||
}
|
||||
|
||||
private static String getLastArg(CommandLine cli) {
|
||||
@ -293,7 +296,7 @@ public class Main {
|
||||
}
|
||||
|
||||
private static void _version() {
|
||||
System.out.println(Androlib.getVersion());
|
||||
System.out.println(ApktoolProperties.getVersion());
|
||||
}
|
||||
|
||||
private static void _Options() {
|
||||
@ -549,7 +552,7 @@ public class Main {
|
||||
|
||||
// print out license info prior to formatter.
|
||||
System.out.println(
|
||||
"Apktool v" + Androlib.getVersion() + " - a tool for reengineering Android apk files\n" +
|
||||
"Apktool v" + ApktoolProperties.getVersion() + " - a tool for reengineering Android apk files\n" +
|
||||
"with smali v" + ApktoolProperties.get("smaliVersion") +
|
||||
" and baksmali v" + ApktoolProperties.get("baksmaliVersion") + "\n" +
|
||||
"Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>\n" +
|
||||
|
@ -21,281 +21,67 @@ import brut.androlib.meta.MetaInfo;
|
||||
import brut.androlib.meta.UsesFramework;
|
||||
import brut.androlib.res.AndrolibResources;
|
||||
import brut.androlib.res.data.ResConfigFlags;
|
||||
import brut.androlib.res.data.ResPackage;
|
||||
import brut.androlib.res.data.ResTable;
|
||||
import brut.androlib.res.data.ResUnknownFiles;
|
||||
import brut.androlib.res.xml.ResXmlPatcher;
|
||||
import brut.androlib.src.SmaliBuilder;
|
||||
import brut.common.BrutException;
|
||||
import brut.common.InvalidUnknownFileException;
|
||||
import brut.common.RootUnknownFileException;
|
||||
import brut.common.TraversalUnknownFileException;
|
||||
import brut.androlib.res.xml.ResXmlPatcher;
|
||||
import brut.androlib.src.SmaliBuilder;
|
||||
import brut.androlib.src.SmaliDecoder;
|
||||
import brut.common.BrutException;
|
||||
import brut.directory.*;
|
||||
import brut.util.*;
|
||||
import brut.directory.Directory;
|
||||
import brut.directory.DirectoryException;
|
||||
import brut.directory.ExtFile;
|
||||
import brut.util.BrutIO;
|
||||
import brut.util.OS;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import com.android.tools.smali.dexlib2.iface.DexFile;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import java.io.*;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.*;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.CRC32;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
public class Androlib {
|
||||
public class ApkBuilder {
|
||||
private final static Logger LOGGER = Logger.getLogger(ApkBuilder.class.getName());
|
||||
|
||||
private final AndrolibResources mAndRes;
|
||||
protected final ResUnknownFiles mResUnknownFiles = new ResUnknownFiles();
|
||||
private final Config config;
|
||||
private int mMinSdkVersion = 0;
|
||||
private final ExtFile mApkDir;
|
||||
|
||||
public Androlib() {
|
||||
this(Config.getDefaultConfig());
|
||||
private final static String APK_DIRNAME = "build/apk";
|
||||
private final static String UNK_DIRNAME = "unknown";
|
||||
private final static String[] APK_RESOURCES_FILENAMES = new String[] {
|
||||
"resources.arsc", "AndroidManifest.xml", "res", "r", "R" };
|
||||
private final static String[] APK_RESOURCES_WITHOUT_RES_FILENAMES = new String[] {
|
||||
"resources.arsc", "AndroidManifest.xml" };
|
||||
private final static String[] APP_RESOURCES_FILENAMES = new String[] {
|
||||
"AndroidManifest.xml", "res" };
|
||||
private final static String[] APK_MANIFEST_FILENAMES = new String[] {
|
||||
"AndroidManifest.xml" };
|
||||
|
||||
public ApkBuilder(ExtFile apkDir) {
|
||||
this(Config.getDefaultConfig(), apkDir);
|
||||
}
|
||||
|
||||
public Androlib(Config config) {
|
||||
public ApkBuilder(Config config, ExtFile apkDir) {
|
||||
this.config = config;
|
||||
mAndRes = new AndrolibResources(config);
|
||||
mApkDir = apkDir;
|
||||
}
|
||||
|
||||
public ResTable getResTable(ExtFile apkFile)
|
||||
throws AndrolibException {
|
||||
return mAndRes.getResTable(apkFile, true);
|
||||
}
|
||||
|
||||
public ResTable getResTable(ExtFile apkFile, boolean loadMainPkg)
|
||||
throws AndrolibException {
|
||||
return mAndRes.getResTable(apkFile, loadMainPkg);
|
||||
}
|
||||
|
||||
public int getMinSdkVersion() {
|
||||
return mMinSdkVersion;
|
||||
}
|
||||
|
||||
public void decodeSourcesRaw(ExtFile apkFile, File outDir, String filename)
|
||||
throws AndrolibException {
|
||||
try {
|
||||
LOGGER.info("Copying raw " + filename + " file...");
|
||||
apkFile.getDirectory().copyToDir(outDir, filename);
|
||||
} catch (DirectoryException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void decodeSourcesSmali(File apkFile, File outDir, String filename)
|
||||
throws AndrolibException {
|
||||
try {
|
||||
File smaliDir;
|
||||
if (filename.equalsIgnoreCase("classes.dex")) {
|
||||
smaliDir = new File(outDir, SMALI_DIRNAME);
|
||||
} else {
|
||||
smaliDir = new File(outDir, SMALI_DIRNAME + "_" + filename.substring(0, filename.indexOf(".")));
|
||||
}
|
||||
OS.rmdir(smaliDir);
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
smaliDir.mkdirs();
|
||||
LOGGER.info("Baksmaling " + filename + "...");
|
||||
DexFile dexFile = SmaliDecoder.decode(apkFile, smaliDir, filename,
|
||||
config.baksmaliDebugMode, config.apiLevel);
|
||||
int minSdkVersion = dexFile.getOpcodes().api;
|
||||
if (mMinSdkVersion == 0 || mMinSdkVersion > minSdkVersion) {
|
||||
mMinSdkVersion = minSdkVersion;
|
||||
}
|
||||
} catch (BrutException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void decodeManifestRaw(ExtFile apkFile, File outDir)
|
||||
throws AndrolibException {
|
||||
try {
|
||||
LOGGER.info("Copying raw manifest...");
|
||||
apkFile.getDirectory().copyToDir(outDir, APK_MANIFEST_FILENAMES);
|
||||
} catch (DirectoryException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void decodeManifestFull(ExtFile apkFile, File outDir, ResTable resTable)
|
||||
throws AndrolibException {
|
||||
mAndRes.decodeManifest(resTable, apkFile, outDir);
|
||||
}
|
||||
|
||||
public void decodeResourcesRaw(ExtFile apkFile, File outDir)
|
||||
throws AndrolibException {
|
||||
try {
|
||||
LOGGER.info("Copying raw resources...");
|
||||
apkFile.getDirectory().copyToDir(outDir, APK_RESOURCES_FILENAMES);
|
||||
} catch (DirectoryException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void decodeResourcesFull(ExtFile apkFile, File outDir, ResTable resTable)
|
||||
throws AndrolibException {
|
||||
mAndRes.decode(resTable, apkFile, outDir);
|
||||
}
|
||||
|
||||
public void decodeManifestWithResources(ExtFile apkFile, File outDir, ResTable resTable)
|
||||
throws AndrolibException {
|
||||
mAndRes.decodeManifestWithResources(resTable, apkFile, outDir);
|
||||
}
|
||||
|
||||
public void decodeRawFiles(ExtFile apkFile, File outDir)
|
||||
throws AndrolibException {
|
||||
LOGGER.info("Copying assets and libs...");
|
||||
try {
|
||||
Directory in = apkFile.getDirectory();
|
||||
|
||||
if (config.decodeAssets == Config.DECODE_ASSETS_FULL) {
|
||||
if (in.containsDir("assets")) {
|
||||
in.copyToDir(outDir, "assets");
|
||||
}
|
||||
}
|
||||
if (in.containsDir("lib")) {
|
||||
in.copyToDir(outDir, "lib");
|
||||
}
|
||||
if (in.containsDir("libs")) {
|
||||
in.copyToDir(outDir, "libs");
|
||||
}
|
||||
if (in.containsDir("kotlin")) {
|
||||
in.copyToDir(outDir, "kotlin");
|
||||
}
|
||||
} catch (DirectoryException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void recordUncompressedFiles(ExtFile apkFile, Collection<String> uncompressedFilesOrExts) throws AndrolibException {
|
||||
try {
|
||||
Directory unk = apkFile.getDirectory();
|
||||
Set<String> files = unk.getFiles(true);
|
||||
|
||||
for (String file : files) {
|
||||
if (isAPKFileNames(file) && unk.getCompressionLevel(file) == 0) {
|
||||
String extOrFile = "";
|
||||
if (unk.getSize(file) != 0) {
|
||||
extOrFile = FilenameUtils.getExtension(file);
|
||||
}
|
||||
|
||||
if (extOrFile.isEmpty() || !NO_COMPRESS_PATTERN.matcher(extOrFile).find()) {
|
||||
extOrFile = file;
|
||||
if (mAndRes.mResFileMapping.containsKey(extOrFile)) {
|
||||
extOrFile = mAndRes.mResFileMapping.get(extOrFile);
|
||||
}
|
||||
}
|
||||
if (!uncompressedFilesOrExts.contains(extOrFile)) {
|
||||
uncompressedFilesOrExts.add(extOrFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (DirectoryException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAPKFileNames(String file) {
|
||||
for (String apkFile : APK_STANDARD_ALL_FILENAMES) {
|
||||
if (apkFile.equals(file) || file.startsWith(apkFile + "/")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void decodeUnknownFiles(ExtFile apkFile, File outDir)
|
||||
throws AndrolibException {
|
||||
LOGGER.info("Copying unknown files...");
|
||||
File unknownOut = new File(outDir, UNK_DIRNAME);
|
||||
try {
|
||||
Directory unk = apkFile.getDirectory();
|
||||
|
||||
// loop all items in container recursively, ignoring any that are pre-defined by aapt
|
||||
Set<String> files = unk.getFiles(true);
|
||||
for (String file : files) {
|
||||
if (!isAPKFileNames(file) && !file.endsWith(".dex")) {
|
||||
|
||||
// copy file out of archive into special "unknown" folder
|
||||
unk.copyToDir(unknownOut, file);
|
||||
// lets record the name of the file, and its compression type
|
||||
// so that we may re-include it the same way
|
||||
mResUnknownFiles.addUnknownFileInfo(file, String.valueOf(unk.getCompressionLevel(file)));
|
||||
}
|
||||
}
|
||||
} catch (DirectoryException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeOriginalFiles(ExtFile apkFile, File outDir)
|
||||
throws AndrolibException {
|
||||
LOGGER.info("Copying original files...");
|
||||
File originalDir = new File(outDir, "original");
|
||||
if (!originalDir.exists()) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
originalDir.mkdirs();
|
||||
}
|
||||
|
||||
try {
|
||||
Directory in = apkFile.getDirectory();
|
||||
if (in.containsFile("AndroidManifest.xml")) {
|
||||
in.copyToDir(originalDir, "AndroidManifest.xml");
|
||||
}
|
||||
if (in.containsFile("stamp-cert-sha256")) {
|
||||
in.copyToDir(originalDir, "stamp-cert-sha256");
|
||||
}
|
||||
if (in.containsDir("META-INF")) {
|
||||
in.copyToDir(originalDir, "META-INF");
|
||||
|
||||
if (in.containsDir("META-INF/services")) {
|
||||
// If the original APK contains the folder META-INF/services folder
|
||||
// that is used for service locators (like coroutines on android),
|
||||
// copy it to the destination folder so it does not get dropped.
|
||||
LOGGER.info("Copying META-INF/services directory");
|
||||
in.copyToDir(outDir, "META-INF/services");
|
||||
}
|
||||
}
|
||||
} catch (DirectoryException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeMetaFile(File mOutDir, MetaInfo meta)
|
||||
throws AndrolibException {
|
||||
try {
|
||||
meta.save(new File(mOutDir, "apktool.yml"));
|
||||
} catch (IOException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public MetaInfo readMetaFile(ExtFile appDir)
|
||||
throws AndrolibException {
|
||||
try(
|
||||
InputStream in = appDir.getDirectory().getFileInput("apktool.yml")
|
||||
) {
|
||||
return MetaInfo.load(in);
|
||||
} catch (DirectoryException | IOException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void build(File appDir, File outFile) throws BrutException {
|
||||
build(new ExtFile(appDir), outFile);
|
||||
}
|
||||
|
||||
public void build(ExtFile appDir, File outFile)
|
||||
public void build(File outFile)
|
||||
throws BrutException {
|
||||
LOGGER.info("Using Apktool " + Androlib.getVersion());
|
||||
LOGGER.info("Using Apktool " + ApktoolProperties.getVersion());
|
||||
|
||||
MetaInfo meta = readMetaFile(appDir);
|
||||
MetaInfo meta = MetaInfo.readMetaFile(mApkDir);
|
||||
config.isFramework = meta.isFrameworkApk;
|
||||
config.resourcesAreCompressed = meta.compressionType;
|
||||
config.doNotCompress = meta.doNotCompress;
|
||||
@ -314,31 +100,31 @@ public class Androlib {
|
||||
|
||||
if (outFile == null) {
|
||||
String outFileName = meta.apkFileName;
|
||||
outFile = new File(appDir, "dist" + File.separator + (outFileName == null ? "out.apk" : outFileName));
|
||||
outFile = new File(mApkDir, "dist" + File.separator + (outFileName == null ? "out.apk" : outFileName));
|
||||
}
|
||||
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
new File(appDir, APK_DIRNAME).mkdirs();
|
||||
File manifest = new File(appDir, "AndroidManifest.xml");
|
||||
File manifestOriginal = new File(appDir, "AndroidManifest.xml.orig");
|
||||
new File(mApkDir, APK_DIRNAME).mkdirs();
|
||||
File manifest = new File(mApkDir, "AndroidManifest.xml");
|
||||
File manifestOriginal = new File(mApkDir, "AndroidManifest.xml.orig");
|
||||
|
||||
buildSources(appDir);
|
||||
buildNonDefaultSources(appDir);
|
||||
buildManifestFile(appDir, manifest, manifestOriginal);
|
||||
buildResources(appDir, meta.usesFramework);
|
||||
buildLibs(appDir);
|
||||
buildCopyOriginalFiles(appDir);
|
||||
buildApk(appDir, outFile);
|
||||
buildSources(mApkDir);
|
||||
buildNonDefaultSources(mApkDir);
|
||||
buildManifestFile(mApkDir, manifest, manifestOriginal);
|
||||
buildResources(mApkDir, meta.usesFramework);
|
||||
buildLibs(mApkDir);
|
||||
buildCopyOriginalFiles(mApkDir);
|
||||
buildApk(mApkDir, outFile);
|
||||
|
||||
// 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);
|
||||
buildUnknownFiles(mApkDir, outFile, meta);
|
||||
|
||||
// we copied the AndroidManifest.xml to AndroidManifest.xml.orig so we can edit it
|
||||
// lets restore the unedited one, to not change the original
|
||||
if (manifest.isFile() && manifest.exists() && manifestOriginal.isFile()) {
|
||||
try {
|
||||
if (new File(appDir, "AndroidManifest.xml").delete()) {
|
||||
if (new File(mApkDir, "AndroidManifest.xml").delete()) {
|
||||
FileUtils.moveFile(manifestOriginal, manifest);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
@ -501,7 +287,7 @@ public class Androlib {
|
||||
}
|
||||
|
||||
if (config.netSecConf) {
|
||||
MetaInfo meta = readMetaFile(new ExtFile(appDir));
|
||||
MetaInfo meta = MetaInfo.readMetaFile(new ExtFile(appDir));
|
||||
if (meta.sdkInfo != null && meta.sdkInfo.get("targetSdkVersion") != null) {
|
||||
if (Integer.parseInt(meta.sdkInfo.get("targetSdkVersion")) < ResConfigFlags.SDK_NOUGAT) {
|
||||
LOGGER.warning("Target SDK version is lower than 24! Network Security Configuration might be ignored!");
|
||||
@ -773,36 +559,6 @@ public class Androlib {
|
||||
mAndRes.zipPackage(outApk, new File(appDir, APK_DIRNAME), assetDir);
|
||||
}
|
||||
|
||||
public void publicizeResources(File arscFile) throws AndrolibException {
|
||||
mAndRes.publicizeResources(arscFile);
|
||||
}
|
||||
|
||||
public void installFramework(File frameFile)
|
||||
throws AndrolibException {
|
||||
mAndRes.installFramework(frameFile);
|
||||
}
|
||||
|
||||
public void listFrameworks() throws AndrolibException {
|
||||
mAndRes.listFrameworkDirectory();
|
||||
}
|
||||
|
||||
public void emptyFrameworkDirectory() throws AndrolibException {
|
||||
mAndRes.emptyFrameworkDirectory();
|
||||
}
|
||||
|
||||
public boolean isFrameworkApk(ResTable resTable) {
|
||||
for (ResPackage pkg : resTable.listMainPackages()) {
|
||||
if (pkg.getId() > 0 && pkg.getId() < 64) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String getVersion() {
|
||||
return ApktoolProperties.get("application.version");
|
||||
}
|
||||
|
||||
private File[] parseUsesFramework(UsesFramework usesFramework)
|
||||
throws AndrolibException {
|
||||
if (usesFramework == null) {
|
||||
@ -851,24 +607,4 @@ public class Androlib {
|
||||
public void close() throws IOException {
|
||||
mAndRes.close();
|
||||
}
|
||||
|
||||
private final static Logger LOGGER = Logger.getLogger(Androlib.class.getName());
|
||||
|
||||
private final static String SMALI_DIRNAME = "smali";
|
||||
private final static String APK_DIRNAME = "build/apk";
|
||||
private final static String UNK_DIRNAME = "unknown";
|
||||
private final static String[] APK_RESOURCES_FILENAMES = new String[] {
|
||||
"resources.arsc", "AndroidManifest.xml", "res", "r", "R" };
|
||||
private final static String[] APK_RESOURCES_WITHOUT_RES_FILENAMES = new String[] {
|
||||
"resources.arsc", "AndroidManifest.xml" };
|
||||
private final static String[] APP_RESOURCES_FILENAMES = new String[] {
|
||||
"AndroidManifest.xml", "res" };
|
||||
private final static String[] APK_MANIFEST_FILENAMES = new String[] {
|
||||
"AndroidManifest.xml" };
|
||||
private final static String[] APK_STANDARD_ALL_FILENAMES = new String[] {
|
||||
"classes.dex", "AndroidManifest.xml", "resources.arsc", "res", "r", "R",
|
||||
"lib", "libs", "assets", "META-INF", "kotlin" };
|
||||
private final static Pattern NO_COMPRESS_PATTERN = Pattern.compile("(" +
|
||||
"jpg|jpeg|png|gif|wav|mp2|mp3|ogg|aac|mpg|mpeg|mid|midi|smf|jet|rtttl|imy|xmf|mp4|" +
|
||||
"m4a|m4v|3gp|3gpp|3g2|3gpp2|amr|awb|wma|wmv|webm|webp|mkv)$");
|
||||
}
|
@ -27,27 +27,48 @@ import brut.androlib.meta.VersionInfo;
|
||||
import brut.androlib.res.AndrolibResources;
|
||||
import brut.androlib.res.data.ResPackage;
|
||||
import brut.androlib.res.data.ResTable;
|
||||
import brut.androlib.res.data.ResUnknownFiles;
|
||||
import brut.androlib.src.SmaliDecoder;
|
||||
import brut.directory.Directory;
|
||||
import brut.directory.ExtFile;
|
||||
import brut.androlib.res.xml.ResXmlPatcher;
|
||||
import brut.common.BrutException;
|
||||
import brut.directory.DirectoryException;
|
||||
import brut.util.OS;
|
||||
import com.android.tools.smali.dexlib2.iface.DexFile;
|
||||
import com.google.common.base.Strings;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ApkDecoder {
|
||||
|
||||
private final static Logger LOGGER = Logger.getLogger(ApkDecoder.class.getName());
|
||||
|
||||
private final Config config;
|
||||
private final Androlib mAndrolib;
|
||||
private final AndrolibResources mAndRes;
|
||||
private final ExtFile mApkFile;
|
||||
private File mOutDir;
|
||||
private ResTable mResTable;
|
||||
protected final ResUnknownFiles mResUnknownFiles;
|
||||
private Collection<String> mUncompressedFiles;
|
||||
private int mMinSdkVersion = 0;
|
||||
|
||||
private final static String SMALI_DIRNAME = "smali";
|
||||
private final static String UNK_DIRNAME = "unknown";
|
||||
private final static String[] APK_RESOURCES_FILENAMES = new String[] {
|
||||
"resources.arsc", "AndroidManifest.xml", "res", "r", "R" };
|
||||
private final static String[] APK_MANIFEST_FILENAMES = new String[] {
|
||||
"AndroidManifest.xml" };
|
||||
private final static String[] APK_STANDARD_ALL_FILENAMES = new String[] {
|
||||
"classes.dex", "AndroidManifest.xml", "resources.arsc", "res", "r", "R",
|
||||
"lib", "libs", "assets", "META-INF", "kotlin" };
|
||||
private final static Pattern NO_COMPRESS_PATTERN = Pattern.compile("(" +
|
||||
"jpg|jpeg|png|gif|wav|mp2|mp3|ogg|aac|mpg|mpeg|mid|midi|smf|jet|rtttl|imy|xmf|mp4|" +
|
||||
"m4a|m4v|3gp|3gpp|3g2|3gpp2|amr|awb|wma|wmv|webm|webp|mkv)$");
|
||||
|
||||
public ApkDecoder(ExtFile apkFile) {
|
||||
this(Config.getDefaultConfig(), apkFile);
|
||||
@ -55,7 +76,8 @@ public class ApkDecoder {
|
||||
|
||||
public ApkDecoder(Config config, ExtFile apkFile) {
|
||||
this.config = config;
|
||||
mAndrolib = new Androlib(config);
|
||||
mAndRes = new AndrolibResources(config);
|
||||
mResUnknownFiles = new ResUnknownFiles();
|
||||
mApkFile = apkFile;
|
||||
}
|
||||
|
||||
@ -67,14 +89,8 @@ public class ApkDecoder {
|
||||
this(config, new ExtFile(apkFile));
|
||||
}
|
||||
|
||||
public void setOutDir(File outDir) {
|
||||
mOutDir = outDir;
|
||||
}
|
||||
|
||||
public void decode() throws AndrolibException, IOException, DirectoryException {
|
||||
public void decode(File outDir) throws AndrolibException, IOException, DirectoryException {
|
||||
try {
|
||||
File outDir = getOutDir();
|
||||
|
||||
if (!config.forceDelete && outDir.exists()) {
|
||||
throw new OutDirExistsException();
|
||||
}
|
||||
@ -90,24 +106,24 @@ public class ApkDecoder {
|
||||
}
|
||||
outDir.mkdirs();
|
||||
|
||||
LOGGER.info("Using Apktool " + Androlib.getVersion() + " on " + mApkFile.getName());
|
||||
LOGGER.info("Using Apktool " + ApktoolProperties.getVersion() + " on " + mApkFile.getName());
|
||||
|
||||
if (hasResources()) {
|
||||
switch (config.decodeResources) {
|
||||
case Config.DECODE_RESOURCES_NONE:
|
||||
mAndrolib.decodeResourcesRaw(mApkFile, outDir);
|
||||
decodeResourcesRaw(mApkFile, outDir);
|
||||
if (config.forceDecodeManifest == Config.FORCE_DECODE_MANIFEST_FULL) {
|
||||
// done after raw decoding of resources because copyToDir overwrites dest files
|
||||
if (hasManifest()) {
|
||||
mAndrolib.decodeManifestWithResources(mApkFile, outDir, getResTable());
|
||||
decodeManifestWithResources(mApkFile, outDir, getResTable());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Config.DECODE_RESOURCES_FULL:
|
||||
if (hasManifest()) {
|
||||
mAndrolib.decodeManifestWithResources(mApkFile, outDir, getResTable());
|
||||
decodeManifestWithResources(mApkFile, outDir, getResTable());
|
||||
}
|
||||
mAndrolib.decodeResourcesFull(mApkFile, outDir, getResTable());
|
||||
decodeResourcesFull(mApkFile, outDir, getResTable());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@ -116,10 +132,10 @@ public class ApkDecoder {
|
||||
if (hasManifest()) {
|
||||
if (config.decodeResources == Config.DECODE_RESOURCES_FULL
|
||||
|| config.forceDecodeManifest == Config.FORCE_DECODE_MANIFEST_FULL) {
|
||||
mAndrolib.decodeManifestFull(mApkFile, outDir, getResTable());
|
||||
decodeManifestFull(mApkFile, outDir, getResTable());
|
||||
}
|
||||
else {
|
||||
mAndrolib.decodeManifestRaw(mApkFile, outDir);
|
||||
decodeManifestRaw(mApkFile, outDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -127,11 +143,11 @@ public class ApkDecoder {
|
||||
if (hasSources()) {
|
||||
switch (config.decodeSources) {
|
||||
case Config.DECODE_SOURCES_NONE:
|
||||
mAndrolib.decodeSourcesRaw(mApkFile, outDir, "classes.dex");
|
||||
decodeSourcesRaw(mApkFile, outDir, "classes.dex");
|
||||
break;
|
||||
case Config.DECODE_SOURCES_SMALI:
|
||||
case Config.DECODE_SOURCES_SMALI_ONLY_MAIN_CLASSES:
|
||||
mAndrolib.decodeSourcesSmali(mApkFile, outDir, "classes.dex");
|
||||
decodeSourcesSmali(mApkFile, outDir, "classes.dex");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -144,16 +160,16 @@ public class ApkDecoder {
|
||||
if (! file.equalsIgnoreCase("classes.dex")) {
|
||||
switch(config.decodeSources) {
|
||||
case Config.DECODE_SOURCES_NONE:
|
||||
mAndrolib.decodeSourcesRaw(mApkFile, outDir, file);
|
||||
decodeSourcesRaw(mApkFile, outDir, file);
|
||||
break;
|
||||
case Config.DECODE_SOURCES_SMALI:
|
||||
mAndrolib.decodeSourcesSmali(mApkFile, outDir, file);
|
||||
decodeSourcesSmali(mApkFile, outDir, file);
|
||||
break;
|
||||
case Config.DECODE_SOURCES_SMALI_ONLY_MAIN_CLASSES:
|
||||
if (file.startsWith("classes") && file.endsWith(".dex")) {
|
||||
mAndrolib.decodeSourcesSmali(mApkFile, outDir, file);
|
||||
decodeSourcesSmali(mApkFile, outDir, file);
|
||||
} else {
|
||||
mAndrolib.decodeSourcesRaw(mApkFile, outDir, file);
|
||||
decodeSourcesRaw(mApkFile, outDir, file);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -162,12 +178,12 @@ public class ApkDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
mAndrolib.decodeRawFiles(mApkFile, outDir);
|
||||
mAndrolib.decodeUnknownFiles(mApkFile, outDir);
|
||||
decodeRawFiles(mApkFile, outDir);
|
||||
decodeUnknownFiles(mApkFile, outDir);
|
||||
mUncompressedFiles = new ArrayList<>();
|
||||
mAndrolib.recordUncompressedFiles(mApkFile, mUncompressedFiles);
|
||||
mAndrolib.writeOriginalFiles(mApkFile, outDir);
|
||||
writeMetaFile();
|
||||
recordUncompressedFiles(mApkFile, mUncompressedFiles);
|
||||
writeOriginalFiles(mApkFile, outDir);
|
||||
writeMetaFile(outDir);
|
||||
} finally {
|
||||
try {
|
||||
mApkFile.close();
|
||||
@ -183,7 +199,7 @@ public class ApkDecoder {
|
||||
throw new AndrolibException(
|
||||
"Apk doesn't contain either AndroidManifest.xml file or resources.arsc file");
|
||||
}
|
||||
mResTable = mAndrolib.getResTable(mApkFile, hasResources);
|
||||
mResTable = mAndRes.getResTable(mApkFile, hasResources);
|
||||
mResTable.setAnalysisMode(config.analysisMode);
|
||||
}
|
||||
return mResTable;
|
||||
@ -231,29 +247,21 @@ public class ApkDecoder {
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (mAndrolib != null) {
|
||||
mAndrolib.close();
|
||||
}
|
||||
mAndRes.close();
|
||||
}
|
||||
|
||||
private File getOutDir() throws AndrolibException {
|
||||
if (mOutDir == null) {
|
||||
throw new AndrolibException("Out dir not set");
|
||||
}
|
||||
return mOutDir;
|
||||
}
|
||||
|
||||
private void writeMetaFile() throws AndrolibException {
|
||||
public void writeMetaFile(File outDir)
|
||||
throws AndrolibException {
|
||||
MetaInfo meta = new MetaInfo();
|
||||
meta.version = Androlib.getVersion();
|
||||
meta.version = ApktoolProperties.getVersion();
|
||||
meta.apkFileName = mApkFile.getName();
|
||||
|
||||
if (mResTable != null) {
|
||||
meta.isFrameworkApk = mAndrolib.isFrameworkApk(mResTable);
|
||||
meta.isFrameworkApk = mResTable.isFrameworkApk();
|
||||
putUsesFramework(meta);
|
||||
putSdkInfo(meta);
|
||||
putSdkInfo(outDir, meta);
|
||||
putPackageInfo(meta);
|
||||
putVersionInfo(meta);
|
||||
putVersionInfo(outDir, meta);
|
||||
putSharedLibraryInfo(meta);
|
||||
putSparseResourcesInfo(meta);
|
||||
} else {
|
||||
@ -262,7 +270,11 @@ public class ApkDecoder {
|
||||
putUnknownInfo(meta);
|
||||
putFileCompressionInfo(meta);
|
||||
|
||||
mAndrolib.writeMetaFile(mOutDir, meta);
|
||||
try {
|
||||
meta.save(new File(outDir, "apktool.yml"));
|
||||
} catch (IOException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void putUsesFramework(MetaInfo meta) {
|
||||
@ -286,24 +298,24 @@ public class ApkDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
private void putSdkInfo(MetaInfo meta) {
|
||||
private void putSdkInfo(File outDir, MetaInfo meta) {
|
||||
Map<String, String> info = mResTable.getSdkInfo();
|
||||
if (info.size() > 0) {
|
||||
String refValue;
|
||||
if (info.get("minSdkVersion") != null) {
|
||||
refValue = ResXmlPatcher.pullValueFromIntegers(mOutDir, info.get("minSdkVersion"));
|
||||
refValue = ResXmlPatcher.pullValueFromIntegers(outDir, info.get("minSdkVersion"));
|
||||
if (refValue != null) {
|
||||
info.put("minSdkVersion", refValue);
|
||||
}
|
||||
}
|
||||
if (info.get("targetSdkVersion") != null) {
|
||||
refValue = ResXmlPatcher.pullValueFromIntegers(mOutDir, info.get("targetSdkVersion"));
|
||||
refValue = ResXmlPatcher.pullValueFromIntegers(outDir, info.get("targetSdkVersion"));
|
||||
if (refValue != null) {
|
||||
info.put("targetSdkVersion", refValue);
|
||||
}
|
||||
}
|
||||
if (info.get("maxSdkVersion") != null) {
|
||||
refValue = ResXmlPatcher.pullValueFromIntegers(mOutDir, info.get("maxSdkVersion"));
|
||||
refValue = ResXmlPatcher.pullValueFromIntegers(outDir, info.get("maxSdkVersion"));
|
||||
if (refValue != null) {
|
||||
info.put("maxSdkVersion", refValue);
|
||||
}
|
||||
@ -313,10 +325,9 @@ public class ApkDecoder {
|
||||
}
|
||||
|
||||
private void putMinSdkInfo(MetaInfo meta) {
|
||||
int minSdkVersion = mAndrolib.getMinSdkVersion();
|
||||
if (minSdkVersion > 0) {
|
||||
if (mMinSdkVersion > 0) {
|
||||
Map<String, String> sdkInfo = new LinkedHashMap<>();
|
||||
sdkInfo.put("minSdkVersion", Integer.toString(minSdkVersion));
|
||||
sdkInfo.put("minSdkVersion", Integer.toString(mMinSdkVersion));
|
||||
meta.sdkInfo = sdkInfo;
|
||||
}
|
||||
}
|
||||
@ -343,9 +354,9 @@ public class ApkDecoder {
|
||||
meta.packageInfo.forcedPackageId = String.valueOf(id);
|
||||
}
|
||||
|
||||
private void putVersionInfo(MetaInfo meta) {
|
||||
private void putVersionInfo(File outDir, MetaInfo meta) {
|
||||
VersionInfo info = mResTable.getVersionInfo();
|
||||
String refValue = ResXmlPatcher.pullValueFromStrings(mOutDir, info.versionName);
|
||||
String refValue = ResXmlPatcher.pullValueFromStrings(outDir, info.versionName);
|
||||
if (refValue != null) {
|
||||
info.versionName = refValue;
|
||||
}
|
||||
@ -361,7 +372,7 @@ public class ApkDecoder {
|
||||
}
|
||||
|
||||
private void putUnknownInfo(MetaInfo meta) {
|
||||
meta.unknownFiles = mAndrolib.mResUnknownFiles.getUnknownFiles();
|
||||
meta.unknownFiles = mResUnknownFiles.getUnknownFiles();
|
||||
}
|
||||
|
||||
private void putFileCompressionInfo(MetaInfo meta) {
|
||||
@ -369,4 +380,192 @@ public class ApkDecoder {
|
||||
meta.doNotCompress = mUncompressedFiles;
|
||||
}
|
||||
}
|
||||
|
||||
public void decodeSourcesRaw(ExtFile apkFile, File outDir, String filename)
|
||||
throws AndrolibException {
|
||||
try {
|
||||
LOGGER.info("Copying raw " + filename + " file...");
|
||||
apkFile.getDirectory().copyToDir(outDir, filename);
|
||||
} catch (DirectoryException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void decodeSourcesSmali(File apkFile, File outDir, String filename)
|
||||
throws AndrolibException {
|
||||
try {
|
||||
File smaliDir;
|
||||
if (filename.equalsIgnoreCase("classes.dex")) {
|
||||
smaliDir = new File(outDir, SMALI_DIRNAME);
|
||||
} else {
|
||||
smaliDir = new File(outDir, SMALI_DIRNAME + "_" + filename.substring(0, filename.indexOf(".")));
|
||||
}
|
||||
OS.rmdir(smaliDir);
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
smaliDir.mkdirs();
|
||||
LOGGER.info("Baksmaling " + filename + "...");
|
||||
DexFile dexFile = SmaliDecoder.decode(apkFile, smaliDir, filename,
|
||||
config.baksmaliDebugMode, config.apiLevel);
|
||||
int minSdkVersion = dexFile.getOpcodes().api;
|
||||
if (mMinSdkVersion == 0 || mMinSdkVersion > minSdkVersion) {
|
||||
mMinSdkVersion = minSdkVersion;
|
||||
}
|
||||
} catch (BrutException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void decodeManifestRaw(ExtFile apkFile, File outDir)
|
||||
throws AndrolibException {
|
||||
try {
|
||||
LOGGER.info("Copying raw manifest...");
|
||||
apkFile.getDirectory().copyToDir(outDir, APK_MANIFEST_FILENAMES);
|
||||
} catch (DirectoryException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void decodeManifestFull(ExtFile apkFile, File outDir, ResTable resTable)
|
||||
throws AndrolibException {
|
||||
mAndRes.decodeManifest(resTable, apkFile, outDir);
|
||||
}
|
||||
|
||||
public void decodeResourcesRaw(ExtFile apkFile, File outDir)
|
||||
throws AndrolibException {
|
||||
try {
|
||||
LOGGER.info("Copying raw resources...");
|
||||
apkFile.getDirectory().copyToDir(outDir, APK_RESOURCES_FILENAMES);
|
||||
} catch (DirectoryException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void decodeResourcesFull(ExtFile apkFile, File outDir, ResTable resTable)
|
||||
throws AndrolibException {
|
||||
mAndRes.decode(resTable, apkFile, outDir);
|
||||
}
|
||||
|
||||
public void decodeManifestWithResources(ExtFile apkFile, File outDir, ResTable resTable)
|
||||
throws AndrolibException {
|
||||
mAndRes.decodeManifestWithResources(resTable, apkFile, outDir);
|
||||
}
|
||||
|
||||
public void decodeRawFiles(ExtFile apkFile, File outDir)
|
||||
throws AndrolibException {
|
||||
LOGGER.info("Copying assets and libs...");
|
||||
try {
|
||||
Directory in = apkFile.getDirectory();
|
||||
|
||||
if (config.decodeAssets == Config.DECODE_ASSETS_FULL) {
|
||||
if (in.containsDir("assets")) {
|
||||
in.copyToDir(outDir, "assets");
|
||||
}
|
||||
}
|
||||
if (in.containsDir("lib")) {
|
||||
in.copyToDir(outDir, "lib");
|
||||
}
|
||||
if (in.containsDir("libs")) {
|
||||
in.copyToDir(outDir, "libs");
|
||||
}
|
||||
if (in.containsDir("kotlin")) {
|
||||
in.copyToDir(outDir, "kotlin");
|
||||
}
|
||||
} catch (DirectoryException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAPKFileNames(String file) {
|
||||
for (String apkFile : APK_STANDARD_ALL_FILENAMES) {
|
||||
if (apkFile.equals(file) || file.startsWith(apkFile + "/")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void decodeUnknownFiles(ExtFile apkFile, File outDir)
|
||||
throws AndrolibException {
|
||||
LOGGER.info("Copying unknown files...");
|
||||
File unknownOut = new File(outDir, UNK_DIRNAME);
|
||||
try {
|
||||
Directory unk = apkFile.getDirectory();
|
||||
|
||||
// loop all items in container recursively, ignoring any that are pre-defined by aapt
|
||||
Set<String> files = unk.getFiles(true);
|
||||
for (String file : files) {
|
||||
if (!isAPKFileNames(file) && !file.endsWith(".dex")) {
|
||||
|
||||
// copy file out of archive into special "unknown" folder
|
||||
unk.copyToDir(unknownOut, file);
|
||||
// let's record the name of the file, and its compression type
|
||||
// so that we may re-include it the same way
|
||||
mResUnknownFiles.addUnknownFileInfo(file, String.valueOf(unk.getCompressionLevel(file)));
|
||||
}
|
||||
}
|
||||
} catch (DirectoryException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeOriginalFiles(ExtFile apkFile, File outDir)
|
||||
throws AndrolibException {
|
||||
LOGGER.info("Copying original files...");
|
||||
File originalDir = new File(outDir, "original");
|
||||
if (!originalDir.exists()) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
originalDir.mkdirs();
|
||||
}
|
||||
|
||||
try {
|
||||
Directory in = apkFile.getDirectory();
|
||||
if (in.containsFile("AndroidManifest.xml")) {
|
||||
in.copyToDir(originalDir, "AndroidManifest.xml");
|
||||
}
|
||||
if (in.containsFile("stamp-cert-sha256")) {
|
||||
in.copyToDir(originalDir, "stamp-cert-sha256");
|
||||
}
|
||||
if (in.containsDir("META-INF")) {
|
||||
in.copyToDir(originalDir, "META-INF");
|
||||
|
||||
if (in.containsDir("META-INF/services")) {
|
||||
// If the original APK contains the folder META-INF/services folder
|
||||
// that is used for service locators (like coroutines on android),
|
||||
// copy it to the destination folder so it does not get dropped.
|
||||
LOGGER.info("Copying META-INF/services directory");
|
||||
in.copyToDir(outDir, "META-INF/services");
|
||||
}
|
||||
}
|
||||
} catch (DirectoryException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void recordUncompressedFiles(ExtFile apkFile, Collection<String> uncompressedFilesOrExts) throws AndrolibException {
|
||||
try {
|
||||
Directory unk = apkFile.getDirectory();
|
||||
Set<String> files = unk.getFiles(true);
|
||||
|
||||
for (String file : files) {
|
||||
if (isAPKFileNames(file) && unk.getCompressionLevel(file) == 0) {
|
||||
String extOrFile = "";
|
||||
if (unk.getSize(file) != 0) {
|
||||
extOrFile = FilenameUtils.getExtension(file);
|
||||
}
|
||||
|
||||
if (extOrFile.isEmpty() || !NO_COMPRESS_PATTERN.matcher(extOrFile).find()) {
|
||||
extOrFile = file;
|
||||
if (mAndRes.mResFileMapping.containsKey(extOrFile)) {
|
||||
extOrFile = mAndRes.mResFileMapping.get(extOrFile);
|
||||
}
|
||||
}
|
||||
if (!uncompressedFilesOrExts.contains(extOrFile)) {
|
||||
uncompressedFilesOrExts.add(extOrFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (DirectoryException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,10 @@ public class ApktoolProperties {
|
||||
return sProps;
|
||||
}
|
||||
|
||||
public static String getVersion() {
|
||||
return get("application.version");
|
||||
}
|
||||
|
||||
private static void loadProps() {
|
||||
InputStream in = ApktoolProperties.class.getResourceAsStream("/properties/apktool.properties");
|
||||
sProps = new Properties();
|
||||
|
@ -16,6 +16,9 @@
|
||||
*/
|
||||
package brut.androlib.meta;
|
||||
|
||||
import brut.androlib.exceptions.AndrolibException;
|
||||
import brut.directory.DirectoryException;
|
||||
import brut.directory.ExtFile;
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.LoaderOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
@ -73,4 +76,15 @@ public class MetaInfo {
|
||||
public static MetaInfo load(InputStream is) {
|
||||
return getYaml().loadAs(is, MetaInfo.class);
|
||||
}
|
||||
|
||||
public static MetaInfo readMetaFile(ExtFile appDir)
|
||||
throws AndrolibException {
|
||||
try(
|
||||
InputStream in = appDir.getDirectory().getFileInput("apktool.yml")
|
||||
) {
|
||||
return MetaInfo.load(in);
|
||||
} catch (DirectoryException | IOException ex) {
|
||||
throw new AndrolibException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -211,4 +211,13 @@ public class ResTable {
|
||||
public boolean getSparseResources() {
|
||||
return mSparseResources;
|
||||
}
|
||||
|
||||
public boolean isFrameworkApk() {
|
||||
for (ResPackage pkg : listMainPackages()) {
|
||||
if (pkg.getId() > 0 && pkg.getId() < 64) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -34,13 +34,13 @@ import java.util.logging.Logger;
|
||||
/**
|
||||
* Binary xml files parser.
|
||||
*
|
||||
* Parser has only two states: (1) Operational state, which parser
|
||||
* <p>Parser has only two states: (1) Operational state, which parser
|
||||
* obtains after first successful call to next() and retains until
|
||||
* open(), close(), or failed call to next(). (2) Closed state, which
|
||||
* parser obtains after open(), close(), or failed call to next(). In
|
||||
* this state methods return invalid values or throw exceptions.
|
||||
*
|
||||
* TODO: * check all methods in closed state
|
||||
* <p>TODO: * check all methods in closed state
|
||||
*/
|
||||
public class AXmlResourceParser implements XmlResourceParser {
|
||||
|
||||
|
@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
* Copyright (C) 2010 Connor Tumbleson <connor.tumbleson@gmail.com>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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 brut.androlib.res.util;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
||||
public class ExtFile extends brut.directory.ExtFile {
|
||||
public ExtFile(File file) {
|
||||
super(file.getPath());
|
||||
}
|
||||
}
|
@ -118,7 +118,7 @@ public final class ResXmlEncoders {
|
||||
default:
|
||||
if (!isPrintableChar(c)) {
|
||||
|
||||
// lets not write trailing \u0000 if we are at end of string
|
||||
// let's not write trailing \u0000 if we are at end of string
|
||||
if ((out.length() + 1) == str.length() && c == '\u0000') {
|
||||
continue;
|
||||
}
|
||||
@ -162,7 +162,7 @@ public final class ResXmlEncoders {
|
||||
|
||||
/**
|
||||
* It returns a tuple of:
|
||||
* - a list of offsets of non positional substitutions. non-pos is defined as any "%" which isn't "%%" nor "%\d+\$"
|
||||
* - a list of offsets of non-positional substitutions. non-pos is defined as any "%" which isn't "%%" nor "%\d+\$"
|
||||
* - a list of offsets of positional substitutions
|
||||
*/
|
||||
private static Duo<List<Integer>, List<Integer>> findSubstitutions(String str, int nonPosMax) {
|
||||
|
@ -37,8 +37,8 @@ import static org.junit.Assert.*;
|
||||
public class BaseTest {
|
||||
|
||||
protected void compareUnknownFiles() throws BrutException {
|
||||
MetaInfo control = new Androlib().readMetaFile(sTestOrigDir);
|
||||
MetaInfo test = new Androlib().readMetaFile(sTestNewDir);
|
||||
MetaInfo control = MetaInfo.readMetaFile(sTestOrigDir);
|
||||
MetaInfo test = MetaInfo.readMetaFile(sTestNewDir);
|
||||
assertNotNull(control.unknownFiles);
|
||||
assertNotNull(test.unknownFiles);
|
||||
|
||||
|
@ -43,12 +43,11 @@ public class AndroidOreoNotSparseTest extends BaseTest {
|
||||
|
||||
LOGGER.info("Decoding not_sparse.apk...");
|
||||
ApkDecoder apkDecoder = new ApkDecoder(testApk);
|
||||
apkDecoder.setOutDir(sTestNewDir);
|
||||
apkDecoder.decode();
|
||||
apkDecoder.decode(sTestNewDir);
|
||||
|
||||
LOGGER.info("Building not_sparse.apk...");
|
||||
Config config = Config.getDefaultConfig();
|
||||
new Androlib(config).build(sTestNewDir, testApk);
|
||||
new ApkBuilder(config, sTestNewDir).build(testApk);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
@ -43,12 +43,11 @@ public class AndroidOreoSparseTest extends BaseTest {
|
||||
|
||||
LOGGER.info("Decoding sparse.apk...");
|
||||
ApkDecoder apkDecoder = new ApkDecoder(testApk);
|
||||
apkDecoder.setOutDir(sTestNewDir);
|
||||
apkDecoder.decode();
|
||||
apkDecoder.decode(sTestNewDir);
|
||||
|
||||
LOGGER.info("Building sparse.apk...");
|
||||
Config config = Config.getDefaultConfig();
|
||||
new Androlib(config).build(sTestNewDir, testApk);
|
||||
new ApkBuilder(config, sTestNewDir).build(testApk);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
package brut.androlib.aapt1;
|
||||
|
||||
import brut.androlib.Androlib;
|
||||
import brut.androlib.ApkBuilder;
|
||||
import brut.androlib.ApkDecoder;
|
||||
import brut.androlib.BaseTest;
|
||||
import brut.androlib.TestUtils;
|
||||
@ -44,12 +44,11 @@ public class BuildAndDecodeJarTest extends BaseTest {
|
||||
|
||||
LOGGER.info("Building testjar.jar...");
|
||||
File testJar = new File(sTmpDir, "testjar.jar");
|
||||
new Androlib().build(sTestOrigDir, testJar);
|
||||
new ApkBuilder(sTestOrigDir).build(testJar);
|
||||
|
||||
LOGGER.info("Decoding testjar.jar...");
|
||||
ApkDecoder apkDecoder = new ApkDecoder(testJar);
|
||||
apkDecoder.setOutDir(sTestNewDir);
|
||||
apkDecoder.decode();
|
||||
apkDecoder.decode(sTestNewDir);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
package brut.androlib.aapt1;
|
||||
|
||||
import brut.androlib.Androlib;
|
||||
import brut.androlib.ApkBuilder;
|
||||
import brut.androlib.ApkDecoder;
|
||||
import brut.androlib.BaseTest;
|
||||
import brut.androlib.TestUtils;
|
||||
@ -52,12 +52,11 @@ public class BuildAndDecodeTest extends BaseTest {
|
||||
|
||||
LOGGER.info("Building testapp.apk...");
|
||||
File testApk = new File(sTmpDir, "testapp.apk");
|
||||
new Androlib().build(sTestOrigDir, testApk);
|
||||
new ApkBuilder(sTestOrigDir).build(testApk);
|
||||
|
||||
LOGGER.info("Decoding testapp.apk...");
|
||||
ApkDecoder apkDecoder = new ApkDecoder(testApk);
|
||||
apkDecoder.setOutDir(sTestNewDir);
|
||||
apkDecoder.decode();
|
||||
apkDecoder.decode(sTestNewDir);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@ -492,7 +491,7 @@ public class BuildAndDecodeTest extends BaseTest {
|
||||
|
||||
@Test
|
||||
public void confirmZeroByteFileExtensionIsNotStored() throws BrutException {
|
||||
MetaInfo metaInfo = new Androlib().readMetaFile(sTestNewDir);
|
||||
MetaInfo metaInfo = MetaInfo.readMetaFile(sTestNewDir);
|
||||
|
||||
for (String item : metaInfo.doNotCompress) {
|
||||
assertNotEquals("jpg", item);
|
||||
@ -501,7 +500,7 @@ public class BuildAndDecodeTest extends BaseTest {
|
||||
|
||||
@Test
|
||||
public void confirmZeroByteFileIsStored() throws BrutException {
|
||||
MetaInfo metaInfo = new Androlib().readMetaFile(sTestNewDir);
|
||||
MetaInfo metaInfo = MetaInfo.readMetaFile(sTestNewDir);
|
||||
assertTrue(metaInfo.doNotCompress.contains("assets/0byte_file.jpg"));
|
||||
}
|
||||
|
||||
|
@ -51,12 +51,11 @@ public class DebugTagRetainedTest extends BaseTest {
|
||||
config.debugMode = true;
|
||||
|
||||
File testApk = new File(sTmpDir, "issue1235.apk");
|
||||
new Androlib(config).build(sTestOrigDir, testApk);
|
||||
new ApkBuilder(config, sTestOrigDir).build(testApk);
|
||||
|
||||
LOGGER.info("Decoding issue1235.apk...");
|
||||
ApkDecoder apkDecoder = new ApkDecoder(testApk);
|
||||
apkDecoder.setOutDir(sTestNewDir);
|
||||
apkDecoder.decode();
|
||||
apkDecoder.decode(sTestNewDir);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
package brut.androlib.aapt1;
|
||||
|
||||
import brut.androlib.Androlib;
|
||||
import brut.androlib.ApkBuilder;
|
||||
import brut.androlib.ApkDecoder;
|
||||
import brut.androlib.BaseTest;
|
||||
import brut.androlib.TestUtils;
|
||||
@ -46,12 +46,11 @@ public class DefaultBaksmaliVariableTest extends BaseTest {
|
||||
|
||||
LOGGER.info("Building issue1481.jar...");
|
||||
File testJar = new File(sTmpDir, "issue1481.jar");
|
||||
new Androlib().build(sTestOrigDir, testJar);
|
||||
new ApkBuilder(sTestOrigDir).build(testJar);
|
||||
|
||||
LOGGER.info("Decoding issue1481.jar...");
|
||||
ApkDecoder apkDecoder = new ApkDecoder(testJar);
|
||||
apkDecoder.setOutDir(sTestNewDir);
|
||||
apkDecoder.decode();
|
||||
apkDecoder.decode(sTestNewDir);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
package brut.androlib.aapt1;
|
||||
|
||||
import brut.androlib.Androlib;
|
||||
import brut.androlib.ApkBuilder;
|
||||
import brut.androlib.ApkDecoder;
|
||||
import brut.androlib.Config;
|
||||
import brut.androlib.TestUtils;
|
||||
@ -46,12 +46,11 @@ public class EmptyResourcesArscTest {
|
||||
|
||||
LOGGER.info("Decoding issue1730.apk...");
|
||||
ApkDecoder apkDecoder = new ApkDecoder(testApk);
|
||||
apkDecoder.setOutDir(sTestNewDir);
|
||||
apkDecoder.decode();
|
||||
apkDecoder.decode(sTestNewDir);
|
||||
|
||||
LOGGER.info("Building issue1730.apk...");
|
||||
Config config = Config.getDefaultConfig();
|
||||
new Androlib(config).build(sTestNewDir, testApk);
|
||||
new ApkBuilder(config, sTestNewDir).build(testApk);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
package brut.androlib.aapt1;
|
||||
|
||||
import brut.androlib.Androlib;
|
||||
import brut.androlib.ApkBuilder;
|
||||
import brut.androlib.ApkDecoder;
|
||||
import brut.androlib.BaseTest;
|
||||
import brut.androlib.TestUtils;
|
||||
@ -50,20 +50,20 @@ public class LargeIntsInManifestTest extends BaseTest {
|
||||
ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk));
|
||||
sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + ".out");
|
||||
|
||||
apkDecoder.setOutDir(new File(sTmpDir + File.separator + apk + ".out"));
|
||||
apkDecoder.decode();
|
||||
File outDir = new File(sTmpDir + File.separator + apk + ".out");
|
||||
apkDecoder.decode(outDir);
|
||||
|
||||
// build issue767
|
||||
ExtFile testApk = new ExtFile(sTmpDir, apk + ".out");
|
||||
new Androlib().build(testApk, null);
|
||||
new ApkBuilder(testApk).build(null);
|
||||
String newApk = apk + ".out" + File.separator + "dist" + File.separator + apk;
|
||||
|
||||
// decode issue767 again
|
||||
apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + newApk));
|
||||
sTestNewDir = new ExtFile(sTmpDir + File.separator + apk + ".out.two");
|
||||
|
||||
apkDecoder.setOutDir(new File(sTmpDir + File.separator + apk + ".out.two"));
|
||||
apkDecoder.decode();
|
||||
outDir = new File(sTmpDir + File.separator + apk + ".out.two");
|
||||
apkDecoder.decode(outDir);
|
||||
|
||||
compareXmlFiles("AndroidManifest.xml");
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
package brut.androlib.aapt1;
|
||||
|
||||
import brut.androlib.Androlib;
|
||||
import brut.androlib.ApkBuilder;
|
||||
import brut.androlib.ApkDecoder;
|
||||
import brut.androlib.BaseTest;
|
||||
import brut.androlib.TestUtils;
|
||||
@ -57,19 +57,19 @@ public class ProviderAttributeTest extends BaseTest {
|
||||
|
||||
// decode issue636.apk
|
||||
ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk));
|
||||
apkDecoder.setOutDir(new File(sTmpDir + File.separator + apk + ".out"));
|
||||
apkDecoder.decode();
|
||||
File outDir = new File(sTmpDir + File.separator + apk + ".out");
|
||||
apkDecoder.decode(outDir);
|
||||
|
||||
// build issue636
|
||||
ExtFile testApk = new ExtFile(sTmpDir, apk + ".out");
|
||||
new Androlib().build(testApk, null);
|
||||
new ApkBuilder(testApk).build(null);
|
||||
String newApk = apk + ".out" + File.separator + "dist" + File.separator + apk;
|
||||
assertTrue(fileExists(newApk));
|
||||
|
||||
// decode issues636 again
|
||||
apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + newApk));
|
||||
apkDecoder.setOutDir(new File(sTmpDir + File.separator + apk + ".out.two"));
|
||||
apkDecoder.decode();
|
||||
outDir = new File(sTmpDir + File.separator + apk + ".out.two");
|
||||
apkDecoder.decode(outDir);
|
||||
|
||||
String expected = TestUtils.replaceNewlines("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>\n" +
|
||||
"<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" android:compileSdkVersion=\"23\" android:compileSdkVersionCodename=\"6.0-2438415\" package=\"com.ibotpeaches.issue636\" platformBuildVersionCode=\"22\" platformBuildVersionName=\"5.1-1756733\">\n" +
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
package brut.androlib.aapt1;
|
||||
|
||||
import brut.androlib.Androlib;
|
||||
import brut.androlib.ApkBuilder;
|
||||
import brut.androlib.ApkDecoder;
|
||||
import brut.androlib.BaseTest;
|
||||
import brut.androlib.TestUtils;
|
||||
@ -54,10 +54,10 @@ public class ReferenceVersionCodeTest extends BaseTest {
|
||||
// decode issue1234.apk
|
||||
ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + apk));
|
||||
ExtFile decodedApk = new ExtFile(sTmpDir + File.separator + apk + ".out");
|
||||
apkDecoder.setOutDir(new File(sTmpDir + File.separator + apk + ".out"));
|
||||
apkDecoder.decode();
|
||||
File outDir = new File(sTmpDir + File.separator + apk + ".out");
|
||||
apkDecoder.decode(outDir);
|
||||
|
||||
MetaInfo metaInfo = new Androlib().readMetaFile(decodedApk);
|
||||
MetaInfo metaInfo = MetaInfo.readMetaFile(decodedApk);
|
||||
assertEquals("v1.0.0", metaInfo.versionInfo.versionName);
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ package brut.androlib.aapt1;
|
||||
|
||||
import brut.androlib.*;
|
||||
import brut.androlib.exceptions.AndrolibException;
|
||||
import brut.androlib.res.AndrolibResources;
|
||||
import brut.directory.ExtFile;
|
||||
import brut.common.BrutException;
|
||||
import brut.util.OS;
|
||||
@ -54,7 +55,7 @@ public class SharedLibraryTest extends BaseTest {
|
||||
config.frameworkDirectory = sTmpDir.getAbsolutePath();
|
||||
config.frameworkTag = "building";
|
||||
|
||||
new Androlib(config).installFramework(new File(sTmpDir + File.separator + apkName));
|
||||
new AndrolibResources(config).installFramework(new File(sTmpDir + File.separator + apkName));
|
||||
|
||||
assertTrue(fileExists("2-building.apk"));
|
||||
}
|
||||
@ -66,7 +67,7 @@ public class SharedLibraryTest extends BaseTest {
|
||||
Config config = Config.getDefaultConfig();
|
||||
config.frameworkDirectory = sTmpDir.getAbsolutePath();
|
||||
|
||||
new Androlib(config).installFramework(new File(sTmpDir + File.separator + apkName));
|
||||
new AndrolibResources(config).installFramework(new File(sTmpDir + File.separator + apkName));
|
||||
|
||||
assertTrue(fileExists("2.apk"));
|
||||
}
|
||||
@ -82,27 +83,27 @@ public class SharedLibraryTest extends BaseTest {
|
||||
config.frameworkTag = "shared";
|
||||
|
||||
// install library/framework
|
||||
new Androlib(config).installFramework(new File(sTmpDir + File.separator + library));
|
||||
new AndrolibResources(config).installFramework(new File(sTmpDir + File.separator + library));
|
||||
assertTrue(fileExists("2-shared.apk"));
|
||||
|
||||
// decode client.apk
|
||||
ApkDecoder apkDecoder = new ApkDecoder(config, new ExtFile(sTmpDir + File.separator + client));
|
||||
apkDecoder.setOutDir(new File(sTmpDir + File.separator + client + ".out"));
|
||||
apkDecoder.decode();
|
||||
File outDir = new File(sTmpDir + File.separator + client + ".out");
|
||||
apkDecoder.decode(outDir);
|
||||
|
||||
// decode library.apk
|
||||
ApkDecoder libraryDecoder = new ApkDecoder(config, new ExtFile(sTmpDir + File.separator + library));
|
||||
libraryDecoder.setOutDir(new File(sTmpDir + File.separator + library + ".out"));
|
||||
libraryDecoder.decode();
|
||||
outDir = new File(sTmpDir + File.separator + library + ".out");
|
||||
libraryDecoder.decode(outDir);
|
||||
|
||||
// build client.apk
|
||||
ExtFile clientApk = new ExtFile(sTmpDir, client + ".out");
|
||||
new Androlib(config).build(clientApk, null);
|
||||
new ApkBuilder(config, clientApk).build(null);
|
||||
assertTrue(fileExists(client + ".out" + File.separator + "dist" + File.separator + client));
|
||||
|
||||
// build library.apk (shared library)
|
||||
ExtFile libraryApk = new ExtFile(sTmpDir, library + ".out");
|
||||
new Androlib(config).build(libraryApk, null);
|
||||
new ApkBuilder(config, libraryApk).build(null);
|
||||
assertTrue(fileExists(library + ".out" + File.separator + "dist" + File.separator + library));
|
||||
}
|
||||
|
||||
|
@ -54,9 +54,7 @@ public class SkipAssetTest extends BaseTest {
|
||||
// decode issue1605.apk
|
||||
ApkDecoder apkDecoder = new ApkDecoder(config, new ExtFile(sTmpDir + File.separator + apk));
|
||||
sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + ".out");
|
||||
|
||||
apkDecoder.setOutDir(sTestOrigDir);
|
||||
apkDecoder.decode();
|
||||
apkDecoder.decode(sTestOrigDir);
|
||||
|
||||
checkFileDoesNotExist("assets" + File.separator + "kotlin.kotlin_builtins");
|
||||
checkFileDoesNotExist("assets" + File.separator + "ranges" + File.separator + "ranges.kotlin_builtins");
|
||||
@ -73,9 +71,7 @@ public class SkipAssetTest extends BaseTest {
|
||||
// decode issue1605.apk
|
||||
ApkDecoder apkDecoder = new ApkDecoder(config, new ExtFile(sTmpDir + File.separator + apk));
|
||||
sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + ".out");
|
||||
|
||||
apkDecoder.setOutDir(sTestOrigDir);
|
||||
apkDecoder.decode();
|
||||
apkDecoder.decode(sTestOrigDir);
|
||||
|
||||
checkFileDoesExist("assets" + File.separator + "kotlin.kotlin_builtins");
|
||||
checkFileDoesExist("assets" + File.separator + "ranges" + File.separator + "ranges.kotlin_builtins");
|
||||
|
@ -48,12 +48,12 @@ public class UnknownCompressionTest extends BaseTest {
|
||||
// decode deflated_unknowns.apk
|
||||
// need new ExtFile because closed in decode()
|
||||
ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTestOrigDir));
|
||||
apkDecoder.setOutDir(new File(sTestOrigDir.getAbsolutePath() + ".out"));
|
||||
apkDecoder.decode();
|
||||
File outDir = new File(sTestOrigDir.getAbsolutePath() + ".out");
|
||||
apkDecoder.decode(outDir);
|
||||
|
||||
// build deflated_unknowns
|
||||
ExtFile clientApkFolder = new ExtFile(sTestOrigDir.getAbsolutePath() + ".out");
|
||||
new Androlib(config).build(clientApkFolder, null);
|
||||
new ApkBuilder(config, clientApkFolder).build(null);
|
||||
sTestNewDir = new ExtFile(clientApkFolder, "dist" + File.separator + apk);
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ package brut.androlib.aapt2;
|
||||
import brut.androlib.*;
|
||||
import brut.androlib.meta.MetaInfo;
|
||||
import brut.androlib.Config;
|
||||
import brut.androlib.res.AndrolibResources;
|
||||
import brut.common.BrutException;
|
||||
import brut.directory.ExtFile;
|
||||
import brut.util.OS;
|
||||
@ -49,12 +50,11 @@ public class BuildAndDecodeTest extends BaseTest {
|
||||
|
||||
LOGGER.info("Building testapp.apk...");
|
||||
File testApk = new File(sTmpDir, "testapp.apk");
|
||||
new Androlib(config).build(sTestOrigDir, testApk);
|
||||
new ApkBuilder(config, sTestOrigDir).build(testApk);
|
||||
|
||||
LOGGER.info("Decoding testapp.apk...");
|
||||
ApkDecoder apkDecoder = new ApkDecoder(testApk);
|
||||
apkDecoder.setOutDir(sTestNewDir);
|
||||
apkDecoder.decode();
|
||||
apkDecoder.decode(sTestNewDir);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@ -79,13 +79,13 @@ public class BuildAndDecodeTest extends BaseTest {
|
||||
|
||||
@Test
|
||||
public void confirmZeroByteFileExtensionIsNotStored() throws BrutException {
|
||||
MetaInfo metaInfo = new Androlib().readMetaFile(sTestNewDir);
|
||||
MetaInfo metaInfo = MetaInfo.readMetaFile(sTestNewDir);
|
||||
assertFalse(metaInfo.doNotCompress.contains("jpg"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void confirmZeroByteFileIsStored() throws BrutException {
|
||||
MetaInfo metaInfo = new Androlib().readMetaFile(sTestNewDir);
|
||||
MetaInfo metaInfo = MetaInfo.readMetaFile(sTestNewDir);
|
||||
assertTrue(metaInfo.doNotCompress.contains("assets/0byte_file.jpg"));
|
||||
}
|
||||
|
||||
|
@ -53,12 +53,11 @@ public class DebuggableFalseChangeToTrueTest extends BaseTest {
|
||||
config.verbose = true;
|
||||
|
||||
File testApk = new File(sTmpDir, "issue2328-debuggable-flase.apk");
|
||||
new Androlib(config).build(sTestOrigDir, testApk);
|
||||
new ApkBuilder(config, sTestOrigDir).build(testApk);
|
||||
|
||||
LOGGER.info("Decoding issue2328-debuggable-flase.apk...");
|
||||
ApkDecoder apkDecoder = new ApkDecoder(testApk);
|
||||
apkDecoder.setOutDir(sTestNewDir);
|
||||
apkDecoder.decode();
|
||||
apkDecoder.decode(sTestNewDir);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
@ -53,12 +53,11 @@ public class DebuggableTrueAddedTest extends BaseTest {
|
||||
config.verbose = true;
|
||||
|
||||
File testApk = new File(sTmpDir, "issue2328-debuggable-missing.apk");
|
||||
new Androlib(config).build(sTestOrigDir, testApk);
|
||||
new ApkBuilder(config, sTestOrigDir).build(testApk);
|
||||
|
||||
LOGGER.info("Decoding issue2328-debuggable-missing.apk...");
|
||||
ApkDecoder apkDecoder = new ApkDecoder(testApk);
|
||||
apkDecoder.setOutDir(sTestNewDir);
|
||||
apkDecoder.decode();
|
||||
apkDecoder.decode(sTestNewDir);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
@ -53,12 +53,11 @@ public class DebuggableTrueRetainedTest extends BaseTest {
|
||||
config.verbose = true;
|
||||
|
||||
File testApk = new File(sTmpDir, "issue2328-debuggable-true.apk");
|
||||
new Androlib(config).build(sTestOrigDir, testApk);
|
||||
new ApkBuilder(config, sTestOrigDir).build(testApk);
|
||||
|
||||
LOGGER.info("Decoding issue2328-debuggable-true.apk...");
|
||||
ApkDecoder apkDecoder = new ApkDecoder(testApk);
|
||||
apkDecoder.setOutDir(sTestNewDir);
|
||||
apkDecoder.decode();
|
||||
apkDecoder.decode(sTestNewDir);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
@ -57,12 +57,11 @@ public class NetworkConfigTest extends BaseTest {
|
||||
config.netSecConf = true;
|
||||
config.useAapt2 = true;
|
||||
File testApk = new File(sTmpDir, "testapp.apk");
|
||||
new Androlib(config).build(sTestOrigDir, testApk);
|
||||
new ApkBuilder(config, sTestOrigDir).build(testApk);
|
||||
|
||||
LOGGER.info("Decoding testapp.apk...");
|
||||
ApkDecoder apkDecoder = new ApkDecoder(testApk);
|
||||
apkDecoder.setOutDir(sTestNewDir);
|
||||
apkDecoder.decode();
|
||||
apkDecoder.decode(sTestNewDir);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
@ -60,12 +60,11 @@ public class NoNetworkConfigTest extends BaseTest {
|
||||
config.netSecConf = true;
|
||||
config.useAapt2 = true;
|
||||
File testApk = new File(sTmpDir, "testapp.apk");
|
||||
new Androlib(config).build(sTestOrigDir, testApk);
|
||||
new ApkBuilder(config, sTestOrigDir).build(testApk);
|
||||
|
||||
LOGGER.info("Decoding testapp.apk...");
|
||||
ApkDecoder apkDecoder = new ApkDecoder(testApk);
|
||||
apkDecoder.setOutDir(sTestNewDir);
|
||||
apkDecoder.decode();
|
||||
apkDecoder.decode(sTestNewDir);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
@ -50,12 +50,11 @@ public class NonStandardPkgIdTest extends BaseTest {
|
||||
|
||||
LOGGER.info("Building pkgid8.apk...");
|
||||
File testApk = new File(sTmpDir, "pkgid8.apk");
|
||||
new Androlib(config).build(sTestOrigDir, testApk);
|
||||
new ApkBuilder(config, sTestOrigDir).build(testApk);
|
||||
|
||||
LOGGER.info("Decoding pkgid8.apk...");
|
||||
ApkDecoder apkDecoder = new ApkDecoder(testApk);
|
||||
apkDecoder.setOutDir(sTestNewDir);
|
||||
apkDecoder.decode();
|
||||
apkDecoder.decode(sTestNewDir);
|
||||
mResTable = apkDecoder.getResTable();
|
||||
}
|
||||
|
||||
|
@ -51,8 +51,8 @@ public class AndResGuardTest extends BaseTest {
|
||||
ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk));
|
||||
sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + ".out");
|
||||
|
||||
apkDecoder.setOutDir(new File(sTmpDir + File.separator + apk + ".out"));
|
||||
apkDecoder.decode();
|
||||
File outDir = new File(sTmpDir + File.separator + apk + ".out");
|
||||
apkDecoder.decode(outDir);
|
||||
|
||||
File aPng = new File(sTestOrigDir,"res/mipmap-hdpi-v4/a.png");
|
||||
assertTrue(aPng.isFile());
|
||||
@ -67,8 +67,8 @@ public class AndResGuardTest extends BaseTest {
|
||||
String apk = "issue1170.apk";
|
||||
ApkDecoder apkDecoder = new ApkDecoder(config, new File(sTmpDir + File.separator + apk));
|
||||
sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + ".raw.out");
|
||||
apkDecoder.setOutDir(new File(sTmpDir + File.separator + apk + ".raw.out"));
|
||||
apkDecoder.decode();
|
||||
File outDir = new File(sTmpDir + File.separator + apk + ".raw.out");
|
||||
apkDecoder.decode(outDir);
|
||||
|
||||
File aPng = new File(sTestOrigDir,"r/a/a.png");
|
||||
assertTrue(aPng.isFile());
|
||||
|
@ -56,8 +56,8 @@ public class DecodeKotlinCoroutinesTest extends BaseTest {
|
||||
config.forceDelete = true;
|
||||
// decode kotlin coroutines
|
||||
ApkDecoder apkDecoder = new ApkDecoder(config, new File(sTmpDir + File.separator + apk));
|
||||
apkDecoder.setOutDir(new File(sTmpDir + File.separator + apk + ".out"));
|
||||
apkDecoder.decode();
|
||||
File outDir = new File(sTmpDir + File.separator + apk + ".out");
|
||||
apkDecoder.decode(outDir);
|
||||
File coroutinesExceptionHandler = new File(sTmpDir + File.separator + apk + ".out" + File.separator + "META-INF" + File.separator + "services", "kotlinx.coroutines.CoroutineExceptionHandler");
|
||||
File coroutinesMainDispatcherHandler = new File(sTmpDir + File.separator + apk + ".out" + File.separator + "META-INF" + File.separator + "services", "kotlinx.coroutines.internal.MainDispatcherFactory");
|
||||
|
||||
@ -72,19 +72,19 @@ public class DecodeKotlinCoroutinesTest extends BaseTest {
|
||||
config.forceDelete = true;
|
||||
// decode kotlin coroutines
|
||||
ApkDecoder apkDecoder = new ApkDecoder(config, new File(sTmpDir + File.separator + apk));
|
||||
apkDecoder.setOutDir(new File(sTmpDir + File.separator + apk + ".out"));
|
||||
apkDecoder.decode();
|
||||
File outDir = new File(sTmpDir + File.separator + apk + ".out");
|
||||
apkDecoder.decode(outDir);
|
||||
|
||||
// build kotlin coroutines
|
||||
ExtFile testApk = new ExtFile(sTmpDir, apk + ".out");
|
||||
new Androlib(config).build(testApk, null);
|
||||
new ApkBuilder(config, testApk).build(null);
|
||||
String newApk = apk + ".out" + File.separator + "dist" + File.separator + apk;
|
||||
assertTrue(fileExists(newApk));
|
||||
|
||||
// decode kotlin coroutines again
|
||||
apkDecoder = new ApkDecoder(config, new File(sTmpDir + File.separator + newApk));
|
||||
apkDecoder.setOutDir(new File(sTmpDir + File.separator + apk + ".out.two"));
|
||||
apkDecoder.decode();
|
||||
outDir = new File(sTmpDir + File.separator + apk + ".out.two");
|
||||
apkDecoder.decode(outDir);
|
||||
|
||||
Files.readAllBytes(Paths.get(sTmpDir + File.separator + apk + ".out.two" + File.separator + "AndroidManifest.xml"));
|
||||
File coroutinesExceptionHandler = new File(sTmpDir + File.separator + apk + ".out.two" + File.separator + "META-INF" + File.separator + "services", "kotlinx.coroutines.CoroutineExceptionHandler");
|
||||
|
@ -46,8 +46,8 @@ public class DecodeKotlinTest extends BaseTest {
|
||||
ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk));
|
||||
sTestNewDir = new ExtFile(sTmpDir + File.separator + apk + ".out");
|
||||
|
||||
apkDecoder.setOutDir(new File(sTmpDir + File.separator + apk + ".out"));
|
||||
apkDecoder.decode();
|
||||
File outDir = new File(sTmpDir + File.separator + apk + ".out");
|
||||
apkDecoder.decode(outDir);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
package brut.androlib.decode;
|
||||
|
||||
import brut.androlib.Androlib;
|
||||
import brut.androlib.ApkBuilder;
|
||||
import brut.androlib.ApkDecoder;
|
||||
import brut.androlib.BaseTest;
|
||||
import brut.androlib.TestUtils;
|
||||
@ -55,10 +55,10 @@ public class DoubleExtensionUnknownFileTest extends BaseTest {
|
||||
// decode issue1244.apk
|
||||
ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk));
|
||||
ExtFile decodedApk = new ExtFile(sTmpDir + File.separator + apk + ".out");
|
||||
apkDecoder.setOutDir(new File(sTmpDir + File.separator + apk + ".out"));
|
||||
apkDecoder.decode();
|
||||
File outDir = new File(sTmpDir + File.separator + apk + ".out");
|
||||
apkDecoder.decode(outDir);
|
||||
|
||||
MetaInfo metaInfo = new Androlib().readMetaFile(decodedApk);
|
||||
MetaInfo metaInfo = MetaInfo.readMetaFile(decodedApk);
|
||||
for (String string : metaInfo.doNotCompress) {
|
||||
if (StringUtils.countMatches(string, ".") > 1) {
|
||||
assertTrue(string.equalsIgnoreCase("assets/bin/Data/sharedassets1.assets.split0"));
|
||||
|
@ -50,12 +50,11 @@ public class DuplicateDexTest extends BaseTest {
|
||||
|
||||
LOGGER.info("Decoding duplicatedex.apk...");
|
||||
ApkDecoder apkDecoder = new ApkDecoder(testApk);
|
||||
apkDecoder.setOutDir(sTestNewDir);
|
||||
apkDecoder.decode();
|
||||
apkDecoder.decode(sTestNewDir);
|
||||
|
||||
LOGGER.info("Building duplicatedex.apk...");
|
||||
Config config = Config.getDefaultConfig();
|
||||
new Androlib(config).build(sTestNewDir, testApk);
|
||||
new ApkBuilder(config, sTestNewDir).build(testApk);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -67,11 +66,10 @@ public class DuplicateDexTest extends BaseTest {
|
||||
config.decodeSources = Config.DECODE_SOURCES_SMALI_ONLY_MAIN_CLASSES;
|
||||
|
||||
ApkDecoder apkDecoder = new ApkDecoder(config, testApk);
|
||||
apkDecoder.setOutDir(sTestNewDir);
|
||||
apkDecoder.decode();
|
||||
apkDecoder.decode(sTestNewDir);
|
||||
|
||||
LOGGER.info("Building duplicatedex.apk...");
|
||||
new Androlib(config).build(sTestNewDir, testApk);
|
||||
new ApkBuilder(config, sTestNewDir).build(testApk);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -54,8 +54,8 @@ public class Empty9PatchTest extends BaseTest {
|
||||
ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk));
|
||||
sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + ".out");
|
||||
|
||||
apkDecoder.setOutDir(new File(sTmpDir + File.separator + apk + ".out"));
|
||||
apkDecoder.decode();
|
||||
File outDir = new File(sTmpDir + File.separator + apk + ".out");
|
||||
apkDecoder.decode(outDir);
|
||||
|
||||
File aPng = new File(sTestOrigDir,"res/drawable-xhdpi/empty.9.png");
|
||||
assertTrue(aPng.isFile());
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
package brut.androlib.decode;
|
||||
|
||||
import brut.androlib.Androlib;
|
||||
import brut.androlib.ApkBuilder;
|
||||
import brut.androlib.ApkDecoder;
|
||||
import brut.androlib.BaseTest;
|
||||
import brut.androlib.TestUtils;
|
||||
@ -43,12 +43,12 @@ public class ExternalEntityTest extends BaseTest {
|
||||
|
||||
LOGGER.info("Building doctype.apk...");
|
||||
File testApk = new File(sTestOrigDir, "doctype.apk");
|
||||
new Androlib().build(sTestOrigDir, testApk);
|
||||
new ApkBuilder(sTestOrigDir).build(testApk);
|
||||
|
||||
LOGGER.info("Decoding doctype.apk...");
|
||||
ApkDecoder apkDecoder = new ApkDecoder(testApk);
|
||||
apkDecoder.setOutDir(new File(sTestOrigDir + File.separator + "output"));
|
||||
apkDecoder.decode();
|
||||
File outDir = new File(sTestOrigDir + File.separator + "output");
|
||||
apkDecoder.decode(outDir);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
@ -140,7 +140,6 @@ public class ForceManifestDecodeNoResourcesTest extends BaseTest {
|
||||
config.decodeResources = decodeResources;
|
||||
config.forceDecodeManifest = decodeManifest;
|
||||
ApkDecoder apkDecoder = new ApkDecoder(config, new File(apk));
|
||||
apkDecoder.setOutDir(new File(output));
|
||||
apkDecoder.decode();
|
||||
apkDecoder.decode(new File(output));
|
||||
}
|
||||
}
|
||||
|
@ -49,10 +49,8 @@ public class MinifiedArscTest extends BaseTest {
|
||||
config.forceDelete = true;
|
||||
// decode issue1157.apk
|
||||
ApkDecoder apkDecoder = new ApkDecoder(config, new ExtFile(sTmpDir, apk));
|
||||
apkDecoder.setOutDir(sTestNewDir);
|
||||
|
||||
// this should not raise an exception:
|
||||
apkDecoder.decode();
|
||||
apkDecoder.decode(sTestNewDir);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
package brut.androlib.decode;
|
||||
|
||||
import brut.androlib.Androlib;
|
||||
import brut.androlib.ApkBuilder;
|
||||
import brut.androlib.ApkDecoder;
|
||||
import brut.androlib.BaseTest;
|
||||
import brut.androlib.TestUtils;
|
||||
@ -54,10 +54,10 @@ public class MissingVersionManifestTest extends BaseTest {
|
||||
// decode issue1264.apk
|
||||
ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk));
|
||||
ExtFile decodedApk = new ExtFile(sTmpDir + File.separator + apk + ".out");
|
||||
apkDecoder.setOutDir(new File(sTmpDir + File.separator + apk + ".out"));
|
||||
apkDecoder.decode();
|
||||
File outDir = new File(sTmpDir + File.separator + apk + ".out");
|
||||
apkDecoder.decode(outDir);
|
||||
|
||||
MetaInfo metaInfo = new Androlib().readMetaFile(decodedApk);
|
||||
MetaInfo metaInfo = MetaInfo.readMetaFile(decodedApk);
|
||||
assertNull(metaInfo.versionInfo.versionName);
|
||||
}
|
||||
}
|
||||
|
@ -45,8 +45,8 @@ public class OutsideOfDirectoryEntryTest extends BaseTest {
|
||||
ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk));
|
||||
sTestNewDir = new ExtFile(sTmpDir + File.separator + apk + ".out");
|
||||
|
||||
apkDecoder.setOutDir(new File(sTmpDir + File.separator + apk + ".out"));
|
||||
apkDecoder.decode();
|
||||
File outDir = new File(sTmpDir + File.separator + apk + ".out");
|
||||
apkDecoder.decode(outDir);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
@ -53,9 +53,8 @@ public class ParentDirectoryTraversalTest extends BaseTest {
|
||||
config.decodeResources = Config.DECODE_RESOURCES_NONE;
|
||||
// decode issue1498.apk
|
||||
ApkDecoder apkDecoder = new ApkDecoder(config, new File(sTmpDir + File.separator + apk));
|
||||
apkDecoder.setOutDir(new File(sTmpDir + File.separator + apk + ".out"));
|
||||
|
||||
File outDir = new File(sTmpDir + File.separator + apk + ".out");
|
||||
// this should not raise an exception:
|
||||
apkDecoder.decode();
|
||||
apkDecoder.decode(outDir);
|
||||
}
|
||||
}
|
||||
|
@ -53,8 +53,8 @@ public class VectorDrawableTest extends BaseTest {
|
||||
ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk));
|
||||
sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + ".out");
|
||||
|
||||
apkDecoder.setOutDir(new File(sTmpDir + File.separator + apk + ".out"));
|
||||
apkDecoder.decode();
|
||||
File outDir = new File(sTmpDir + File.separator + apk + ".out");
|
||||
apkDecoder.decode(outDir);
|
||||
|
||||
checkFileExists("res/drawable/ic_arrow_drop_down_black_24dp.xml");
|
||||
checkFileExists("res/drawable/ic_android_black_24dp.xml");
|
||||
|
@ -49,13 +49,12 @@ public class DexStaticFieldValueTest extends BaseTest {
|
||||
|
||||
LOGGER.info("Building issue2543.apk...");
|
||||
File testApk = new File(sTmpDir, "issue2543.apk");
|
||||
new Androlib(config).build(sTestOrigDir, testApk);
|
||||
new ApkBuilder(config, sTestOrigDir).build(testApk);
|
||||
|
||||
LOGGER.info("Decoding issue2543.apk...");
|
||||
config.baksmaliDebugMode = false;
|
||||
ApkDecoder apkDecoder = new ApkDecoder(config, new ExtFile(testApk));
|
||||
apkDecoder.setOutDir(sTestNewDir);
|
||||
apkDecoder.decode();
|
||||
apkDecoder.decode(sTestNewDir);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
package brut.androlib.yaml;
|
||||
|
||||
import brut.androlib.Androlib;
|
||||
import brut.androlib.ApkBuilder;
|
||||
import brut.androlib.BaseTest;
|
||||
import brut.androlib.TestUtils;
|
||||
import brut.androlib.Config;
|
||||
@ -45,6 +45,6 @@ public class MaliciousYamlTest extends BaseTest {
|
||||
public void testMaliciousYamlNotLoaded() throws BrutException {
|
||||
Config config = Config.getDefaultConfig();
|
||||
File testApk = new File(sTmpDir, "cve20220476.apk");
|
||||
new Androlib(config).build(sTestNewDir, testApk);
|
||||
new ApkBuilder(config, sTestNewDir).build(testApk);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user