Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 80 additions & 109 deletions brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -123,27 +123,26 @@ public void build(File outApk) throws AndrolibException {
}

private void buildSources(File outDir) throws AndrolibException {
if (!copySourcesRaw(outDir, "classes.dex")) {
buildSourcesSmali(outDir, "smali", "classes.dex");
}

try {
Directory in = mApkDir.getDirectory();

// Loop through any smali_ directories for multi-dex APKs.
// Process smali dirs.
for (String dirName : in.getDirs().keySet()) {
if (dirName.startsWith("smali_")) {
String fileName = dirName.substring(dirName.indexOf("_") + 1) + ".dex";
if (!copySourcesRaw(outDir, fileName)) {
buildSourcesSmali(outDir, dirName, fileName);
}
String fileName;
if (dirName.equals("smali")) {
fileName = "classes.dex";
} else if (dirName.startsWith("smali_")) {
fileName = dirName.substring(dirName.indexOf('_') + 1) + ".dex";
} else {
continue;
}

buildSourcesSmali(outDir, dirName, fileName);
}

// Loop through any classes#.dex files for multi-dex APKs.
// Process dex files.
for (String fileName : in.getFiles()) {
// Skip classes.dex because we have handled it.
if (fileName.endsWith(".dex") && !fileName.equals("classes.dex")) {
if (fileName.endsWith(".dex")) {
copySourcesRaw(outDir, fileName);
}
}
Expand All @@ -152,26 +151,6 @@ private void buildSources(File outDir) throws AndrolibException {
}
}

private boolean copySourcesRaw(File outDir, String fileName) throws AndrolibException {
File working = new File(mApkDir, fileName);
if (!working.isFile()) {
return false;
}

File stored = new File(outDir, fileName);
if (!mConfig.isForced() && !isModified(working, stored)) {
return true;
}

LOGGER.info("Copying raw " + fileName + " file...");
try {
BrutIO.copyAndClose(Files.newInputStream(working.toPath()), Files.newOutputStream(stored.toPath()));
} catch (IOException ex) {
throw new AndrolibException(ex);
}
return true;
}

private void buildSourcesSmali(File outDir, String dirName, String fileName) throws AndrolibException {
if (mWorker != null) {
mWorker.submit(() -> {
Expand All @@ -195,11 +174,9 @@ private void buildSourcesSmaliJob(File outDir, String dirName, String fileName)
}

File dexFile = new File(outDir, fileName);
if (!mConfig.isForced()) {
LOGGER.info("Checking whether sources have changed...");
if (!isModified(smaliDir, dexFile)) {
return;
}
if (!mConfig.isForced() && !isFileNewer(smaliDir, dexFile)) {
LOGGER.info("Sources in " + dirName + " have not changed.");
return;
}
OS.rmfile(dexFile);

Expand All @@ -208,6 +185,26 @@ private void buildSourcesSmaliJob(File outDir, String dirName, String fileName)
builder.build(dexFile);
}

private void copySourcesRaw(File outDir, String fileName) throws AndrolibException {
File inFile = new File(mApkDir, fileName);
if (!inFile.isFile()) {
return;
}

File outFile = new File(outDir, fileName);
if (!mConfig.isForced() && !isFileNewer(inFile, outFile)) {
LOGGER.info("File " + fileName + " has not changed.");
return;
}

LOGGER.info("Copying raw " + fileName + " file...");
try {
BrutIO.copyAndClose(Files.newInputStream(inFile.toPath()), Files.newOutputStream(outFile.toPath()));
} catch (IOException ex) {
throw new AndrolibException(ex);
}
}

private void backupManifestFile(File manifest, File manifestOrig) throws AndrolibException {
// We cannot patch AndroidManifest.xml if it was not decoded.
if (new File(mApkDir, "resources.arsc").isFile()) {
Expand All @@ -230,55 +227,34 @@ private void backupManifestFile(File manifest, File manifestOrig) throws Androli

private void buildResources(File outDir, File manifest) throws AndrolibException {
if (!manifest.isFile()) {
LOGGER.fine("Could not find AndroidManifest.xml");
return;
}

if (new File(mApkDir, "resources.arsc").isFile()) {
copyResourcesRaw(outDir, manifest);
} else if (new File(mApkDir, "res").isDirectory()) {
buildResourcesFull(outDir, manifest);
} else {
LOGGER.fine("Could not find resources");
buildManifest(outDir, manifest);
File resDir = new File(mApkDir, "res");
if (resDir.isDirectory()) {
buildResourcesFull(outDir, manifest, resDir);
return;
}
}

private void copyResourcesRaw(File outDir, File manifest) throws AndrolibException {
if (!mConfig.isForced()) {
LOGGER.info("Checking whether resources have changed...");
if (!isModified(manifest, new File(outDir, "AndroidManifest.xml"))
&& !isModified(new File(mApkDir, "resources.arsc"), new File(outDir, "resources.arsc"))
&& !isModified(newFiles(mApkDir, ApkInfo.RESOURCES_DIRNAMES),
newFiles(outDir, ApkInfo.RESOURCES_DIRNAMES))) {
return;
}
File arscFile = new File(mApkDir, "resources.arsc");
if (arscFile.isFile()) {
copyResourcesRaw(outDir, manifest, arscFile);
return;
}

LOGGER.info("Copying raw resources...");
try {
Directory in = mApkDir.getDirectory();

in.copyToDir(outDir, "AndroidManifest.xml");
in.copyToDir(outDir, "resources.arsc");
in.copyToDir(outDir, ApkInfo.RESOURCES_DIRNAMES);
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
LOGGER.fine("Could not find resources.");
buildManifest(outDir, manifest);
}

private void buildResourcesFull(File outDir, File manifest) throws AndrolibException {
File resourcesFile = new File(outDir.getParentFile(), "resources.zip");
if (!mConfig.isForced()) {
LOGGER.info("Checking whether resources have changed...");
if (!isModified(manifest, new File(outDir, "AndroidManifest.xml"))
&& !isModified(newFiles(mApkDir, ApkInfo.RESOURCES_DIRNAMES),
newFiles(outDir, ApkInfo.RESOURCES_DIRNAMES))
&& resourcesFile.isFile()) {
return;
}
private void buildResourcesFull(File outDir, File manifest, File resDir) throws AndrolibException {
File resZip = new File(outDir.getParentFile(), "resources.zip");
if (!mConfig.isForced() && resZip.isFile()
&& !isFileNewer(manifest, new File(outDir, "AndroidManifest.xml"))
&& !isFileNewer(resDir, new File(outDir, "res"))) {
LOGGER.info("Resources have not changed.");
return;
}
OS.rmfile(resourcesFile);
OS.rmfile(resZip);

if (mConfig.isDebuggable()) {
LOGGER.info("Setting 'debuggable' attribute to 'true' in AndroidManifest.xml");
Expand All @@ -287,10 +263,8 @@ private void buildResourcesFull(File outDir, File manifest) throws AndrolibExcep

if (mConfig.isNetSecConf()) {
String targetSdkVersion = mApkInfo.getSdkInfo().getTargetSdkVersion();
if (targetSdkVersion != null) {
if (SdkInfo.parseSdkInt(targetSdkVersion) < ResConfig.SDK_NOUGAT) {
LOGGER.warning("Target SDK version is lower than 24! Network Security Configuration might be ignored!");
}
if (targetSdkVersion != null && SdkInfo.parseSdkInt(targetSdkVersion) < ResConfig.SDK_NOUGAT) {
LOGGER.warning("Target SDK version is lower than 24! Network Security Configuration might be ignored!");
}

File netSecConfOrig = new File(mApkDir, "res/xml/network_security_config.xml");
Expand All @@ -308,7 +282,6 @@ private void buildResourcesFull(File outDir, File manifest) throws AndrolibExcep
}
OS.rmfile(tmpFile);

File resDir = new File(mApkDir, "res");
File npDir = new File(mApkDir, "9patch");
if (!npDir.isDirectory()) {
npDir = null;
Expand All @@ -320,22 +293,37 @@ private void buildResourcesFull(File outDir, File manifest) throws AndrolibExcep
invoker.invoke(tmpFile, manifest, resDir, npDir, null, getIncludeFiles());

Directory tmpDir = tmpFile.getDirectory();
tmpDir.copyToDir(outDir, "AndroidManifest.xml");
tmpDir.copyToDir(outDir, "resources.arsc");
tmpDir.copyToDir(outDir, ApkInfo.RESOURCES_DIRNAMES);
tmpDir.copyToDir(outDir, "AndroidManifest.xml", "resources.arsc", "res");
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
} finally {
OS.rmfile(tmpFile);
}
}

private void copyResourcesRaw(File outDir, File manifest, File arscFile) throws AndrolibException {
if (!mConfig.isForced()
&& !isFileNewer(manifest, new File(outDir, "AndroidManifest.xml"))
&& !isFileNewer(arscFile, new File(outDir, "resources.arsc"))) {
LOGGER.info("Resources have not changed.");
return;
}

LOGGER.info("Copying raw resources...");
try {
Directory in = mApkDir.getDirectory();

in.copyToDir(outDir, "AndroidManifest.xml", "resources.arsc");
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
}

private void buildManifest(File outDir, File manifest) throws AndrolibException {
if (!mConfig.isForced()) {
LOGGER.info("Checking whether AndroidManifest.xml has changed...");
if (!isModified(manifest, new File(outDir, "AndroidManifest.xml"))) {
return;
}
if (!mConfig.isForced()
&& !isFileNewer(manifest, new File(outDir, "AndroidManifest.xml"))) {
LOGGER.info("AndroidManifest.xml has not changed.");
return;
}

ExtFile tmpFile;
Expand Down Expand Up @@ -480,24 +468,7 @@ private File[] getIncludeFiles() throws AndrolibException {
return files.toArray(new File[0]);
}

private boolean isModified(File working, File stored) {
return !stored.exists() || BrutIO.recursiveModifiedTime(working) > BrutIO.recursiveModifiedTime(stored);
}

private boolean isModified(File[] working, File[] stored) {
for (File file : stored) {
if (!file.exists()) {
return true;
}
}
return BrutIO.recursiveModifiedTime(working) > BrutIO.recursiveModifiedTime(stored);
}

private File[] newFiles(File dir, String[] names) {
File[] files = new File[names.length];
for (int i = 0; i < names.length; i++) {
files[i] = new File(dir, names[i]);
}
return files;
private boolean isFileNewer(File file, File reference) {
return !reference.exists() || BrutIO.recursiveModifiedTime(file) > BrutIO.recursiveModifiedTime(reference);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,21 +111,11 @@ private void decodeSources(File outDir) throws AndrolibException {
return;
}

switch (mConfig.getDecodeSources()) {
case NONE:
copySourcesRaw(outDir, "classes.dex");
break;
case FULL:
case ONLY_MAIN_CLASSES:
decodeSourcesSmali(outDir, "classes.dex");
break;
}

try {
Directory in = mApkFile.getDirectory();

for (String fileName : in.getFiles(true)) {
if (!fileName.endsWith(".dex") || fileName.equals("classes.dex")) {
if (!fileName.endsWith(".dex")) {
continue;
}

Expand All @@ -150,17 +140,6 @@ private void decodeSources(File outDir) throws AndrolibException {
}
}

private void copySourcesRaw(File outDir, String fileName) throws AndrolibException {
LOGGER.info("Copying raw " + fileName + " file...");
try {
Directory in = mApkFile.getDirectory();

in.copyToDir(outDir, fileName);
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
}

private void decodeSourcesSmali(File outDir, String fileName) throws AndrolibException {
if (mWorker != null) {
mWorker.submit(() -> {
Expand All @@ -178,12 +157,8 @@ private void decodeSourcesSmali(File outDir, String fileName) throws AndrolibExc
}

private void decodeSourcesSmaliJob(File outDir, String fileName) throws AndrolibException {
File smaliDir;
if (fileName.equals("classes.dex")) {
smaliDir = new File(outDir, "smali");
} else {
smaliDir = new File(outDir, "smali_" + fileName.substring(0, fileName.indexOf('.')));
}
File smaliDir = new File(outDir, "smali" + (!fileName.equals("classes.dex")
? "_" + fileName.substring(0, fileName.indexOf('.')) : ""));

OS.mkdir(smaliDir);

Expand All @@ -198,6 +173,17 @@ private void decodeSourcesSmaliJob(File outDir, String fileName) throws Androlib
}
}

private void copySourcesRaw(File outDir, String fileName) throws AndrolibException {
LOGGER.info("Copying raw " + fileName + " file...");
try {
Directory in = mApkFile.getDirectory();

in.copyToDir(outDir, fileName);
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
}

private void decodeResources(File outDir) throws AndrolibException {
if (!mApkInfo.hasResources()) {
return;
Expand All @@ -220,7 +206,6 @@ private void copyResourcesRaw(File outDir) throws AndrolibException {
Directory in = mApkFile.getDirectory();

in.copyToDir(outDir, "resources.arsc");
in.copyToDir(outDir, ApkInfo.RESOURCES_DIRNAMES);
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,14 @@
import java.util.regex.Pattern;

public class ApkInfo implements YamlSerializable {
public static final String[] RESOURCES_DIRNAMES = { "res", "r", "R" };
public static final String[] RAW_DIRNAMES = { "assets", "lib" };

public static final Pattern ORIGINAL_FILENAMES_PATTERN = Pattern.compile(
"AndroidManifest\\.xml|META-INF/[^/]+\\.(RSA|SF|MF)|stamp-cert-sha256");

public static final Pattern STANDARD_FILENAMES_PATTERN = Pattern.compile(
"[^/]+\\.dex|resources\\.arsc|(" + String.join("|", RESOURCES_DIRNAMES) + "|"
+ String.join("|", RAW_DIRNAMES) + ")/.*|" + ORIGINAL_FILENAMES_PATTERN.pattern());
"[^/]+\\.dex|resources\\.arsc|(" + String.join("|", RAW_DIRNAMES) + ")/.*|"
+ ORIGINAL_FILENAMES_PATTERN.pattern());

private String mVersion;
private String mApkFileName;
Expand Down
Loading
Loading