Skip to content

Commit

Permalink
fix: avoid trying to parse raw AndroidManifest (#3720)
Browse files Browse the repository at this point in the history
Recording feature flags moved to ResourcesDecoder to fix
an issue where raw AndroidManifest is attempted to be
parsed even when decoding without resources.

Replaced remaining usages of FileInputStream/FileInputStream
with their NIO equivalents for consistency with rest of code.

Minor redundancy and format tweaks.
  • Loading branch information
IgorEisberg authored Nov 8, 2024
1 parent b49e770 commit e065b26
Show file tree
Hide file tree
Showing 9 changed files with 56 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import brut.androlib.exceptions.OutDirExistsException;
import brut.androlib.apk.ApkInfo;
import brut.androlib.res.ResourcesDecoder;
import brut.androlib.res.xml.ResXmlPatcher;
import brut.androlib.src.SmaliDecoder;
import brut.directory.Directory;
import brut.directory.ExtFile;
Expand Down Expand Up @@ -322,15 +321,6 @@ private void writeApkInfo(File outDir) throws AndrolibException {
mApkInfo.setMinSdkVersion(Integer.toString(mMinSdkVersion));
}

// record feature flags
File manifest = new File(outDir, "AndroidManifest.xml");
List<String> featureFlags = ResXmlPatcher.pullManifestFeatureFlags(manifest);
if (featureFlags != null) {
for (String flag : featureFlags) {
mApkInfo.addFeatureFlag(flag, true);
}
}

// record uncompressed files
try {
Map<String, String> resFileMapping = mResDecoder.getResFileMapping();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import brut.directory.FileDirectory;

import java.io.*;
import java.nio.file.Files;
import java.util.*;
import java.util.regex.Pattern;

Expand Down Expand Up @@ -192,7 +193,10 @@ public void addFeatureFlag(String flag, boolean value) {
}

public void save(File file) throws AndrolibException {
try (YamlWriter writer = new YamlWriter(new FileOutputStream(file))) {
try (
OutputStream out = Files.newOutputStream(file.toPath());
YamlWriter writer = new YamlWriter(out)
) {
write(writer);
} catch (FileNotFoundException ex) {
throw new AndrolibException("File not found");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,21 +100,28 @@ public void decodeManifest(File outDir) throws AndrolibException {
IOUtils.closeQuietly(outputStream);
}

if (mApkInfo.hasResources()) {
if (!mConfig.analysisMode) {
// Remove versionName / versionCode (aapt API 16)
//
// check for a mismatch between resources.arsc package and the package listed in AndroidManifest
// also remove the android::versionCode / versionName from manifest for rebuild
// this is a required change to prevent aapt warning about conflicting versions
// it will be passed as a parameter to aapt like "--min-sdk-version" via apktool.yml
adjustPackageManifest(outDir.getAbsolutePath() + File.separator + "AndroidManifest.xml");

ResXmlPatcher.removeManifestVersions(new File(
outDir.getAbsolutePath() + File.separator + "AndroidManifest.xml"));

// update apk info
mApkInfo.packageInfo.forcedPackageId = String.valueOf(mResTable.getPackageId());
File manifest = new File(outDir, "AndroidManifest.xml");

if (mApkInfo.hasResources() && !mConfig.analysisMode) {
// Remove versionName / versionCode (aapt API 16)
//
// check for a mismatch between resources.arsc package and the package listed in AndroidManifest
// also remove the android::versionCode / versionName from manifest for rebuild
// this is a required change to prevent aapt warning about conflicting versions
// it will be passed as a parameter to aapt like "--min-sdk-version" via apktool.yml
adjustPackageManifest(manifest);

ResXmlPatcher.removeManifestVersions(manifest);

// update apk info
mApkInfo.packageInfo.forcedPackageId = String.valueOf(mResTable.getPackageId());
}

// record feature flags
List<String> featureFlags = ResXmlPatcher.pullManifestFeatureFlags(manifest);
if (featureFlags != null) {
for (String flag : featureFlags) {
mApkInfo.addFeatureFlag(flag, true);
}
}
}
Expand All @@ -123,7 +130,7 @@ public void updateApkInfo(File outDir) throws AndrolibException {
mResTable.initApkInfo(mApkInfo, outDir);
}

private void adjustPackageManifest(String filePath) throws AndrolibException {
private void adjustPackageManifest(File manifest) throws AndrolibException {
// compare resources.arsc package name to the one present in AndroidManifest
ResPackage resPackage = mResTable.getCurrentResPackage();
String pkgOriginal = resPackage.getName();
Expand All @@ -141,7 +148,7 @@ private void adjustPackageManifest(String filePath) throws AndrolibException {
LOGGER.info("Regular manifest package...");
} else {
LOGGER.info("Renamed manifest package found! Replacing " + pkgRenamed + " with " + pkgOriginal);
ResXmlPatcher.renameManifestPackage(new File(filePath), pkgOriginal);
ResXmlPatcher.renameManifestPackage(manifest, pkgOriginal);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,16 +260,10 @@ public void setSharedLibrary(boolean flag) {
}

public void setSparseResources(boolean flag) {
if (mApkInfo.sparseResources != flag) {
LOGGER.info("Sparsely packed resources detected.");
}
mApkInfo.sparseResources = flag;
}

public void setCompactEntries(boolean flag) {
if (mApkInfo.compactEntries != flag) {
LOGGER.info("Compactly packed resource entries detected.");
}
mApkInfo.compactEntries = flag;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -501,8 +501,8 @@ private ResConfigFlags readConfigFlags() throws IOException, AndrolibException {
char[] language = new char[0];
char[] country = new char[0];
if (size >= 12) {
language = this.unpackLanguageOrRegion(mIn.readByte(), mIn.readByte(), 'a');
country = this.unpackLanguageOrRegion(mIn.readByte(), mIn.readByte(), '0');
language = unpackLanguageOrRegion(mIn.readByte(), mIn.readByte(), 'a');
country = unpackLanguageOrRegion(mIn.readByte(), mIn.readByte(), '0');
read = 12;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public class ResFileDecoder {
private final ResStreamDecoderContainer mDecoders;

public ResFileDecoder(ResStreamDecoderContainer decoders) {
this.mDecoders = decoders;
mDecoders = decoders;
}

public void decode(ResResource res, Directory inDir, Directory outDir, Map<String, String> resFileMapping)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ public class StyledString {
private final List<Span> mSpans;

public StyledString(String text, List<Span> spans) {
this.mText = text;
this.mSpans = spans;
mText = text;
mSpans = spans;
}

String getText() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.w3c.dom.*;
import org.xml.sax.SAXException;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
Expand All @@ -29,10 +30,14 @@
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.*;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.io.*;
import java.util.*;
import java.util.logging.Logger;

Expand Down Expand Up @@ -413,8 +418,8 @@ public static Document loadDocument(File file)
docFactory.setFeature(FEATURE_LOAD_DTD, false);

try {
docFactory.setAttribute(ACCESS_EXTERNAL_DTD, " ");
docFactory.setAttribute(ACCESS_EXTERNAL_SCHEMA, " ");
docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, " ");
docFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, " ");
} catch (IllegalArgumentException ex) {
LOGGER.warning("JAXP 1.5 Support is required to validate XML");
}
Expand Down Expand Up @@ -454,8 +459,6 @@ private static void saveDocument(File file, Document doc)
}
}

private static final String ACCESS_EXTERNAL_DTD = "http://javax.xml.XMLConstants/property/accessExternalDTD";
private static final String ACCESS_EXTERNAL_SCHEMA = "http://javax.xml.XMLConstants/property/accessExternalSchema";
private static final String FEATURE_LOAD_DTD = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
private static final String FEATURE_DISABLE_DOCTYPE_DECL = "http://apache.org/xml/features/disallow-doctype-decl";

Expand Down
24 changes: 13 additions & 11 deletions brut.j.dir/src/main/java/brut/directory/FileDirectory.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import java.io.*;
import java.net.URLDecoder;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedHashMap;
Expand All @@ -36,25 +37,23 @@ public FileDirectory(String dir) throws DirectoryException, UnsupportedEncodingE

public FileDirectory(File dir) throws DirectoryException {
super();
if (! dir.isDirectory()) {
if (!dir.isDirectory()) {
throw new DirectoryException("file must be a directory: " + dir);
}
mDir = dir;
}

@Override
public long getSize(String fileName)
throws DirectoryException {
public long getSize(String fileName) throws DirectoryException {
File file = new File(generatePath(fileName));
if (! file.isFile()) {
if (!file.isFile()) {
throw new DirectoryException("file must be a file: " + file);
}
return file.length();
}

@Override
public long getCompressedSize(String fileName)
throws DirectoryException {
public long getCompressedSize(String fileName) throws DirectoryException {
return getSize(fileName);
}

Expand All @@ -69,17 +68,19 @@ protected AbstractDirectory createDirLocal(String name) throws DirectoryExceptio
@Override
protected InputStream getFileInputLocal(String name) throws DirectoryException {
try {
return new FileInputStream(generatePath(name));
} catch (FileNotFoundException ex) {
File file = new File(generatePath(name));
return Files.newInputStream(file.toPath());
} catch (IOException ex) {
throw new DirectoryException(ex);
}
}

@Override
protected OutputStream getFileOutputLocal(String name) throws DirectoryException {
try {
return new FileOutputStream(generatePath(name));
} catch (FileNotFoundException ex) {
File file = new File(generatePath(name));
return Files.newOutputStream(file.toPath());
} catch (IOException ex) {
throw new DirectoryException(ex);
}
}
Expand All @@ -96,8 +97,9 @@ protected void loadFiles() {

@Override
protected void removeFileLocal(String name) {
File file = new File(generatePath(name));
//noinspection ResultOfMethodCallIgnored
new File(generatePath(name)).delete();
file.delete();
}

private String generatePath(String name) {
Expand Down

0 comments on commit e065b26

Please sign in to comment.