mirror of
https://github.com/skylot/jadx.git
synced 2024-11-26 22:20:50 +00:00
feat(api): add JadxArgs property to adjust xml security checks (#2291)
This commit is contained in:
parent
2d10537050
commit
063af8cd62
@ -18,7 +18,6 @@ dependencies {
|
||||
|
||||
testImplementation("ch.qos.logback:logback-classic:1.5.9")
|
||||
testImplementation("org.assertj:assertj-core:3.26.3")
|
||||
testImplementation("org.mockito:mockito-core:5.14.1")
|
||||
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.11.2")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
|
@ -1,5 +1,7 @@
|
||||
package jadx.cli;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -8,8 +10,11 @@ import jadx.api.JadxDecompiler;
|
||||
import jadx.api.impl.AnnotatedCodeWriter;
|
||||
import jadx.api.impl.NoOpCodeCache;
|
||||
import jadx.api.impl.SimpleCodeWriter;
|
||||
import jadx.api.security.JadxSecurityFlag;
|
||||
import jadx.api.security.impl.JadxSecurity;
|
||||
import jadx.cli.LogHelper.LogLevelEnum;
|
||||
import jadx.cli.plugins.JadxFilesGetter;
|
||||
import jadx.commons.app.JadxCommonEnv;
|
||||
import jadx.core.utils.exceptions.JadxArgsValidateException;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.plugins.tools.JadxExternalPluginsLoader;
|
||||
@ -49,6 +54,7 @@ public class JadxCLI {
|
||||
jadxArgs.setPluginLoader(new JadxExternalPluginsLoader());
|
||||
jadxArgs.setFilesGetter(JadxFilesGetter.INSTANCE);
|
||||
initCodeWriterProvider(jadxArgs);
|
||||
applyEnvVars(jadxArgs);
|
||||
try (JadxDecompiler jadx = new JadxDecompiler(jadxArgs)) {
|
||||
jadx.load();
|
||||
if (checkForErrors(jadx)) {
|
||||
@ -81,6 +87,22 @@ public class JadxCLI {
|
||||
}
|
||||
}
|
||||
|
||||
private static void applyEnvVars(JadxArgs jadxArgs) {
|
||||
Set<JadxSecurityFlag> flags = JadxSecurityFlag.all();
|
||||
boolean modified = false;
|
||||
boolean disableXmlSecurity = JadxCommonEnv.getBool("JADX_DISABLE_XML_SECURITY", false);
|
||||
if (disableXmlSecurity) {
|
||||
flags.remove(JadxSecurityFlag.SECURE_XML_PARSER);
|
||||
// TODO: not related to 'xml security', but kept for compatibility
|
||||
flags.remove(JadxSecurityFlag.VERIFY_APP_PACKAGE);
|
||||
modified = true;
|
||||
}
|
||||
// TODO: migrate 'ZipSecurity'
|
||||
if (modified) {
|
||||
jadxArgs.setSecurity(new JadxSecurity(flags));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean checkForErrors(JadxDecompiler jadx) {
|
||||
if (jadx.getRoot().getClasses().isEmpty()) {
|
||||
if (jadx.getArgs().isSkipResources()) {
|
||||
|
@ -30,6 +30,9 @@ import jadx.api.impl.AnnotatedCodeWriter;
|
||||
import jadx.api.impl.InMemoryCodeCache;
|
||||
import jadx.api.plugins.loader.JadxBasePluginLoader;
|
||||
import jadx.api.plugins.loader.JadxPluginLoader;
|
||||
import jadx.api.security.IJadxSecurity;
|
||||
import jadx.api.security.JadxSecurityFlag;
|
||||
import jadx.api.security.impl.JadxSecurity;
|
||||
import jadx.api.usage.IUsageInfoCache;
|
||||
import jadx.api.usage.impl.InMemoryUsageInfoCache;
|
||||
import jadx.core.deobf.DeobfAliasProvider;
|
||||
@ -174,6 +177,11 @@ public class JadxArgs implements Closeable {
|
||||
*/
|
||||
private IJadxFilesGetter filesGetter = TempFilesGetter.INSTANCE;
|
||||
|
||||
/**
|
||||
* Additional data validation and security checks
|
||||
*/
|
||||
private IJadxSecurity security = new JadxSecurity(JadxSecurityFlag.all());
|
||||
|
||||
/**
|
||||
* Don't save files (can be using for performance testing)
|
||||
*/
|
||||
@ -726,6 +734,14 @@ public class JadxArgs implements Closeable {
|
||||
this.filesGetter = filesGetter;
|
||||
}
|
||||
|
||||
public IJadxSecurity getSecurity() {
|
||||
return security;
|
||||
}
|
||||
|
||||
public void setSecurity(IJadxSecurity security) {
|
||||
this.security = security;
|
||||
}
|
||||
|
||||
public boolean isSkipFilesSave() {
|
||||
return skipFilesSave;
|
||||
}
|
||||
|
@ -51,7 +51,6 @@ import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.core.utils.tasks.TaskExecutor;
|
||||
import jadx.core.xmlgen.BinaryXMLParser;
|
||||
import jadx.core.xmlgen.ResourcesSaver;
|
||||
|
||||
/**
|
||||
@ -92,8 +91,6 @@ public final class JadxDecompiler implements Closeable {
|
||||
private List<JavaClass> classes;
|
||||
private List<ResourceFile> resources;
|
||||
|
||||
private BinaryXMLParser binaryXmlParser;
|
||||
|
||||
private final IDecompileScheduler decompileScheduler = new DecompilerScheduler();
|
||||
private final JadxEventsImpl events = new JadxEventsImpl();
|
||||
private final ResourcesLoader resourcesLoader = new ResourcesLoader(this);
|
||||
@ -168,7 +165,6 @@ public final class JadxDecompiler implements Closeable {
|
||||
root = null;
|
||||
classes = null;
|
||||
resources = null;
|
||||
binaryXmlParser = null;
|
||||
events.reset();
|
||||
}
|
||||
|
||||
@ -467,13 +463,6 @@ public final class JadxDecompiler implements Closeable {
|
||||
return root;
|
||||
}
|
||||
|
||||
synchronized BinaryXMLParser getBinaryXmlParser() {
|
||||
if (binaryXmlParser == null) {
|
||||
binaryXmlParser = new BinaryXMLParser(root);
|
||||
}
|
||||
return binaryXmlParser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get JavaClass by ClassNode without loading and decompilation
|
||||
*/
|
||||
|
20
jadx-core/src/main/java/jadx/api/security/IJadxSecurity.java
Normal file
20
jadx-core/src/main/java/jadx/api/security/IJadxSecurity.java
Normal file
@ -0,0 +1,20 @@
|
||||
package jadx.api.security;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
public interface IJadxSecurity {
|
||||
|
||||
/**
|
||||
* Check if application package is safe
|
||||
*
|
||||
* @return normalized/sanitized string or same string if safe
|
||||
*/
|
||||
String verifyAppPackage(String appPackage);
|
||||
|
||||
/**
|
||||
* XML document parser
|
||||
*/
|
||||
Document parseXml(InputStream in);
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package jadx.api.security;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
public enum JadxSecurityFlag {
|
||||
|
||||
VERIFY_APP_PACKAGE,
|
||||
SECURE_XML_PARSER;
|
||||
|
||||
public static Set<JadxSecurityFlag> all() {
|
||||
return EnumSet.allOf(JadxSecurityFlag.class);
|
||||
}
|
||||
|
||||
public static Set<JadxSecurityFlag> none() {
|
||||
return EnumSet.noneOf(JadxSecurityFlag.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package jadx.api.security.impl;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import jadx.api.security.IJadxSecurity;
|
||||
import jadx.api.security.JadxSecurityFlag;
|
||||
import jadx.core.deobf.NameMapper;
|
||||
|
||||
public class JadxSecurity implements IJadxSecurity {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JadxSecurity.class);
|
||||
|
||||
private final Set<JadxSecurityFlag> flags;
|
||||
|
||||
public JadxSecurity(Set<JadxSecurityFlag> flags) {
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String verifyAppPackage(String appPackage) {
|
||||
if (flags.contains(JadxSecurityFlag.VERIFY_APP_PACKAGE)
|
||||
&& !NameMapper.isValidFullIdentifier(appPackage)) {
|
||||
LOG.warn("App package '{}' has invalid format and will be ignored", appPackage);
|
||||
return "INVALID_PACKAGE";
|
||||
}
|
||||
return appPackage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Document parseXml(InputStream in) {
|
||||
DocumentBuilderFactory dbf;
|
||||
if (flags.contains(JadxSecurityFlag.SECURE_XML_PARSER)) {
|
||||
dbf = SecureDBFHolder.INSTANCE;
|
||||
} else {
|
||||
dbf = SimpleDBFHolder.INSTANCE;
|
||||
}
|
||||
try {
|
||||
return dbf.newDocumentBuilder().parse(in);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to parse xml", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SimpleDBFHolder {
|
||||
private static final DocumentBuilderFactory INSTANCE = DocumentBuilderFactory.newInstance();
|
||||
}
|
||||
|
||||
private static final class SecureDBFHolder {
|
||||
private static final DocumentBuilderFactory INSTANCE = buildSecureDBF();
|
||||
|
||||
private static DocumentBuilderFactory buildSecureDBF() {
|
||||
try {
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
|
||||
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
||||
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
||||
dbf.setFeature("http://apache.org/xml/features/dom/create-entity-ref-nodes", false);
|
||||
dbf.setXIncludeAware(false);
|
||||
dbf.setExpandEntityReferences(false);
|
||||
return dbf;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Fail to build secure XML DocumentBuilderFactory", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -98,6 +98,8 @@ public class RootNode {
|
||||
*/
|
||||
private @Nullable JadxDecompiler decompiler;
|
||||
|
||||
private @Nullable ManifestAttributes manifestAttributes;
|
||||
|
||||
public RootNode(JadxArgs args) {
|
||||
this.args = args;
|
||||
this.preDecompilePasses = Jadx.getPreDecompilePassesList();
|
||||
@ -211,7 +213,7 @@ public class RootNode {
|
||||
if (parser != null) {
|
||||
processResources(parser.getResStorage());
|
||||
updateObfuscatedFiles(parser, resources);
|
||||
ManifestAttributes.getInstance().updateAttributes(parser);
|
||||
initManifestAttributes().updateAttributes(parser);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to parse 'resources.pb'/'.arsc' file", e);
|
||||
@ -721,4 +723,13 @@ public class RootNode {
|
||||
public GradleInfoStorage getGradleInfoStorage() {
|
||||
return gradleInfoStorage;
|
||||
}
|
||||
|
||||
public synchronized ManifestAttributes initManifestAttributes() {
|
||||
ManifestAttributes attrs = manifestAttributes;
|
||||
if (attrs == null) {
|
||||
attrs = new ManifestAttributes(args.getSecurity());
|
||||
manifestAttributes = attrs;
|
||||
}
|
||||
return attrs;
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +95,8 @@ public class ExportGradleProject {
|
||||
AppAttribute.MIN_SDK_VERSION,
|
||||
AppAttribute.TARGET_SDK_VERSION,
|
||||
AppAttribute.VERSION_CODE,
|
||||
AppAttribute.VERSION_NAME));
|
||||
AppAttribute.VERSION_NAME),
|
||||
root.getArgs().getSecurity());
|
||||
return parser.parse();
|
||||
}
|
||||
}
|
||||
|
@ -495,6 +495,11 @@ public class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated env vars shouldn't be used in core modules.
|
||||
* Prefer to parse in `app` (use JadxCommonEnv from 'app-commons') and set in jadx args.
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean getEnvVarBool(String varName, boolean defValue) {
|
||||
String strValue = System.getenv(varName);
|
||||
if (strValue == null) {
|
||||
@ -503,6 +508,11 @@ public class Utils {
|
||||
return strValue.equalsIgnoreCase("true");
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated env vars shouldn't be used in core modules.
|
||||
* Prefer to parse in `app` (use JadxCommonEnv from 'app-commons') and set in jadx args.
|
||||
*/
|
||||
@Deprecated
|
||||
public static int getEnvVarInt(String varName, int defValue) {
|
||||
String strValue = System.getenv(varName);
|
||||
if (strValue == null) {
|
||||
|
@ -1,38 +1,37 @@
|
||||
package jadx.core.utils.android;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.api.ResourceType;
|
||||
import jadx.api.security.IJadxSecurity;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
import jadx.core.xmlgen.XmlSecurity;
|
||||
|
||||
public class AndroidManifestParser {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AndroidManifestParser.class);
|
||||
|
||||
private final Document androidManifest;
|
||||
private final Document appStrings;
|
||||
private final EnumSet<AppAttribute> parseAttrs;
|
||||
private final IJadxSecurity security;
|
||||
|
||||
public AndroidManifestParser(ResourceFile androidManifestRes, EnumSet<AppAttribute> parseAttrs) {
|
||||
this(androidManifestRes, null, parseAttrs);
|
||||
public AndroidManifestParser(ResourceFile androidManifestRes, EnumSet<AppAttribute> parseAttrs, IJadxSecurity security) {
|
||||
this(androidManifestRes, null, parseAttrs, security);
|
||||
}
|
||||
|
||||
public AndroidManifestParser(ResourceFile androidManifestRes, ResContainer appStrings, EnumSet<AppAttribute> parseAttrs) {
|
||||
public AndroidManifestParser(ResourceFile androidManifestRes, ResContainer appStrings,
|
||||
EnumSet<AppAttribute> parseAttrs, IJadxSecurity security) {
|
||||
this.parseAttrs = parseAttrs;
|
||||
this.security = Objects.requireNonNull(security);
|
||||
|
||||
this.androidManifest = parseAndroidManifest(androidManifestRes);
|
||||
this.appStrings = parseAppStrings(appStrings);
|
||||
@ -206,10 +205,9 @@ public class AndroidManifestParser {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Document parseXml(String xmlContent) {
|
||||
try {
|
||||
DocumentBuilder builder = XmlSecurity.getDBF().newDocumentBuilder();
|
||||
Document document = builder.parse(new InputSource(new StringReader(xmlContent)));
|
||||
private Document parseXml(String xmlContent) {
|
||||
try (InputStream xmlStream = new ByteArrayInputStream(xmlContent.getBytes(StandardCharsets.UTF_8))) {
|
||||
Document document = security.parseXml(xmlStream);
|
||||
document.getDocumentElement().normalize();
|
||||
return document;
|
||||
} catch (Exception e) {
|
||||
@ -217,7 +215,7 @@ public class AndroidManifestParser {
|
||||
}
|
||||
}
|
||||
|
||||
private static Document parseAppStrings(ResContainer appStrings) {
|
||||
private Document parseAppStrings(ResContainer appStrings) {
|
||||
if (appStrings == null) {
|
||||
return null;
|
||||
}
|
||||
@ -225,7 +223,7 @@ public class AndroidManifestParser {
|
||||
return parseXml(content);
|
||||
}
|
||||
|
||||
private static Document parseAndroidManifest(ResourceFile androidManifest) {
|
||||
private Document parseAndroidManifest(ResourceFile androidManifest) {
|
||||
if (androidManifest == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -23,21 +23,13 @@ import jadx.core.utils.android.AndroidResourcesMap;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.xmlgen.entry.ValuesParser;
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* Don't die when error occurs
|
||||
* Check error cases, maybe checked const values are not always the same
|
||||
* Better error messages
|
||||
* What to do, when Binary XML Manifest is > size(int)?
|
||||
* Check for missing chunk size types
|
||||
* Implement missing data types
|
||||
* Use line numbers to recreate EXACT AndroidManifest
|
||||
* Check Element chunk size
|
||||
*/
|
||||
|
||||
public class BinaryXMLParser extends CommonBinaryParser {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BinaryXMLParser.class);
|
||||
|
||||
private final RootNode rootNode;
|
||||
private final ManifestAttributes manifestAttributes;
|
||||
private final boolean attrNewLine;
|
||||
|
||||
private final Map<Integer, String> resNames;
|
||||
private Map<String, String> nsMap;
|
||||
private Set<String> nsMapGenerated;
|
||||
@ -53,16 +45,13 @@ public class BinaryXMLParser extends CommonBinaryParser {
|
||||
private boolean isOneLine = true;
|
||||
private int namespaceDepth = 0;
|
||||
private @Nullable int[] resourceIds;
|
||||
|
||||
private final RootNode rootNode;
|
||||
private String appPackageName;
|
||||
|
||||
private Map<String, ClassNode> classNameCache;
|
||||
|
||||
private final boolean attrNewLine;
|
||||
|
||||
public BinaryXMLParser(RootNode rootNode) {
|
||||
this.rootNode = rootNode;
|
||||
this.manifestAttributes = rootNode.initManifestAttributes();
|
||||
this.attrNewLine = !rootNode.getArgs().isSkipXmlPrettyPrint();
|
||||
try {
|
||||
ConstStorage constStorage = rootNode.getConstValues();
|
||||
@ -325,7 +314,7 @@ public class BinaryXMLParser extends CommonBinaryParser {
|
||||
writer.add(' ');
|
||||
}
|
||||
writer.add(attrFullName).add("=\"");
|
||||
String decodedAttr = ManifestAttributes.getInstance().decode(attrName, attrValData);
|
||||
String decodedAttr = manifestAttributes.decode(attrName, attrValData);
|
||||
if (decodedAttr != null) {
|
||||
memorizePackageName(attrName, decodedAttr);
|
||||
if (isDeobfCandidateAttr(attrFullName)) {
|
||||
@ -419,8 +408,7 @@ public class BinaryXMLParser extends CommonBinaryParser {
|
||||
return "NOT_FOUND_STR_0x" + Integer.toHexString(strId);
|
||||
}
|
||||
|
||||
private void decodeAttribute(int attributeNS, int attrValDataType, int attrValData,
|
||||
String attrFullName) {
|
||||
private void decodeAttribute(int attributeNS, int attrValDataType, int attrValData, String attrFullName) {
|
||||
if (attrValDataType == TYPE_REFERENCE) {
|
||||
// reference custom processing
|
||||
String resName = resNames.get(attrValData);
|
||||
|
@ -7,8 +7,6 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.w3c.dom.Document;
|
||||
@ -16,11 +14,17 @@ import org.w3c.dom.NamedNodeMap;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import jadx.api.security.IJadxSecurity;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.xmlgen.entry.RawNamedValue;
|
||||
import jadx.core.xmlgen.entry.ResourceEntry;
|
||||
import jadx.core.xmlgen.entry.ValuesParser;
|
||||
|
||||
// TODO: move to Android specific module!
|
||||
|
||||
/**
|
||||
* Load and store Android Manifest attributes specification
|
||||
*/
|
||||
public class ManifestAttributes {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ManifestAttributes.class);
|
||||
|
||||
@ -53,24 +57,12 @@ public class ManifestAttributes {
|
||||
}
|
||||
}
|
||||
|
||||
private final IJadxSecurity security;
|
||||
private final Map<String, MAttr> attrMap = new HashMap<>();
|
||||
|
||||
private final Map<String, MAttr> appAttrMap = new HashMap<>();
|
||||
|
||||
private static ManifestAttributes instance;
|
||||
|
||||
public static ManifestAttributes getInstance() {
|
||||
if (instance == null) {
|
||||
try {
|
||||
instance = new ManifestAttributes();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to create ManifestAttributes", e);
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private ManifestAttributes() {
|
||||
public ManifestAttributes(IJadxSecurity security) {
|
||||
this.security = security;
|
||||
parseAll();
|
||||
}
|
||||
|
||||
@ -86,8 +78,7 @@ public class ManifestAttributes {
|
||||
if (xmlStream == null) {
|
||||
throw new JadxRuntimeException(xml + " not found in classpath");
|
||||
}
|
||||
DocumentBuilder dBuilder = XmlSecurity.getDBF().newDocumentBuilder();
|
||||
doc = dBuilder.parse(xmlStream);
|
||||
doc = security.parseXml(xmlStream);
|
||||
} catch (Exception e) {
|
||||
throw new JadxRuntimeException("Xml load error, file: " + xml, e);
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ public class ResTableBinaryParser extends CommonBinaryParser implements IResTabl
|
||||
public void decode(InputStream inputStream) throws IOException {
|
||||
long start = System.currentTimeMillis();
|
||||
is = new ParserStream(inputStream);
|
||||
resStorage = new ResourceStorage();
|
||||
resStorage = new ResourceStorage(root.getArgs().getSecurity());
|
||||
decodeTableChunk();
|
||||
resStorage.finish();
|
||||
if (LOG.isDebugEnabled()) {
|
||||
@ -99,7 +99,7 @@ public class ResTableBinaryParser extends CommonBinaryParser implements IResTabl
|
||||
@Override
|
||||
public ResContainer decodeFiles() {
|
||||
ValuesParser vp = new ValuesParser(strings, resStorage.getResourcesNames());
|
||||
ResXmlGen resGen = new ResXmlGen(resStorage, vp);
|
||||
ResXmlGen resGen = new ResXmlGen(resStorage, vp, root.initManifestAttributes());
|
||||
|
||||
ICodeInfo content = XmlGenUtils.makeXmlDump(root.makeCodeWriter(), resStorage);
|
||||
List<ResContainer> xmlFiles = resGen.makeResourcesXml(root.getArgs());
|
||||
|
@ -43,10 +43,12 @@ public class ResXmlGen {
|
||||
|
||||
private final ResourceStorage resStorage;
|
||||
private final ValuesParser vp;
|
||||
private final ManifestAttributes manifestAttributes;
|
||||
|
||||
public ResXmlGen(ResourceStorage resStorage, ValuesParser vp) {
|
||||
public ResXmlGen(ResourceStorage resStorage, ValuesParser vp, ManifestAttributes manifestAttributes) {
|
||||
this.resStorage = resStorage;
|
||||
this.vp = vp;
|
||||
this.manifestAttributes = manifestAttributes;
|
||||
}
|
||||
|
||||
public List<ResContainer> makeResourcesXml(JadxArgs args) {
|
||||
@ -191,7 +193,7 @@ public class ResXmlGen {
|
||||
if (dataType == ParserConstants.TYPE_INT_DEC && nameStr != null) {
|
||||
try {
|
||||
int intVal = Integer.parseInt(valueStr);
|
||||
String newVal = ManifestAttributes.getInstance().decode(nameStr.replace("android:", "").replace("attr.", ""), intVal);
|
||||
String newVal = manifestAttributes.decode(nameStr.replace("android:", "").replace("attr.", ""), intVal);
|
||||
if (newVal != null) {
|
||||
valueStr = newVal;
|
||||
}
|
||||
@ -202,7 +204,7 @@ public class ResXmlGen {
|
||||
if (dataType == ParserConstants.TYPE_INT_HEX && nameStr != null) {
|
||||
try {
|
||||
int intVal = Integer.decode(valueStr);
|
||||
String newVal = ManifestAttributes.getInstance().decode(nameStr.replace("android:", "").replace("attr.", ""), intVal);
|
||||
String newVal = manifestAttributes.decode(nameStr.replace("android:", "").replace("attr.", ""), intVal);
|
||||
if (newVal != null) {
|
||||
valueStr = newVal;
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import jadx.api.security.IJadxSecurity;
|
||||
import jadx.core.xmlgen.entry.ResourceEntry;
|
||||
|
||||
public class ResourceStorage {
|
||||
@ -16,6 +17,8 @@ public class ResourceStorage {
|
||||
.thenComparing(ResourceEntry::getKeyName);
|
||||
|
||||
private final List<ResourceEntry> list = new ArrayList<>();
|
||||
private final IJadxSecurity security;
|
||||
|
||||
private String appPackage;
|
||||
|
||||
/**
|
||||
@ -28,6 +31,10 @@ public class ResourceStorage {
|
||||
*/
|
||||
private final Map<Integer, String> renames = new HashMap<>();
|
||||
|
||||
public ResourceStorage(IJadxSecurity security) {
|
||||
this.security = security;
|
||||
}
|
||||
|
||||
public void add(ResourceEntry resEntry) {
|
||||
list.add(resEntry);
|
||||
uniqNameEntries.put(resEntry, resEntry);
|
||||
@ -76,7 +83,7 @@ public class ResourceStorage {
|
||||
}
|
||||
|
||||
public void setAppPackage(String appPackage) {
|
||||
this.appPackage = XmlSecurity.verifyAppPackage(appPackage);
|
||||
this.appPackage = security.verifyAppPackage(appPackage);
|
||||
}
|
||||
|
||||
public Map<Integer, String> getResourcesNames() {
|
||||
|
@ -1,54 +0,0 @@
|
||||
package jadx.core.xmlgen;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.deobf.NameMapper;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
public class XmlSecurity {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(XmlSecurity.class);
|
||||
|
||||
private static final boolean DISABLE_CHECKS = Utils.getEnvVarBool("JADX_DISABLE_XML_SECURITY", false);
|
||||
|
||||
private static final DocumentBuilderFactory DBF_INSTANCE = buildDBF();
|
||||
|
||||
private XmlSecurity() {
|
||||
}
|
||||
|
||||
public static DocumentBuilderFactory getDBF() {
|
||||
return DBF_INSTANCE;
|
||||
}
|
||||
|
||||
public static String verifyAppPackage(String appPackage) {
|
||||
if (DISABLE_CHECKS) {
|
||||
return appPackage;
|
||||
}
|
||||
if (NameMapper.isValidFullIdentifier(appPackage)) {
|
||||
return appPackage;
|
||||
}
|
||||
LOG.warn("App package '{}' has invalid format and will be ignored", appPackage);
|
||||
return "INVALID_PACKAGE";
|
||||
}
|
||||
|
||||
private static DocumentBuilderFactory buildDBF() {
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
if (DISABLE_CHECKS) {
|
||||
return dbf;
|
||||
}
|
||||
try {
|
||||
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
|
||||
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
||||
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
||||
dbf.setFeature("http://apache.org/xml/features/dom/create-entity-ref-nodes", false);
|
||||
dbf.setXIncludeAware(false);
|
||||
dbf.setExpandEntityReferences(false);
|
||||
return dbf;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Fail to build secure XML DocumentBuilderFactory", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package jadx.api;
|
||||
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.xmlgen.BinaryXMLParser;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class JadxDecompilerTestUtils {
|
||||
public static JadxDecompiler getMockDecompiler() {
|
||||
JadxDecompiler decompiler = mock(JadxDecompiler.class);
|
||||
RootNode rootNode = new RootNode(new JadxArgs());
|
||||
when(decompiler.getRoot()).thenReturn(rootNode);
|
||||
when(decompiler.getBinaryXmlParser()).thenReturn(new BinaryXMLParser(rootNode));
|
||||
return decompiler;
|
||||
}
|
||||
}
|
@ -8,6 +8,9 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.api.security.IJadxSecurity;
|
||||
import jadx.api.security.JadxSecurityFlag;
|
||||
import jadx.api.security.impl.JadxSecurity;
|
||||
import jadx.core.xmlgen.entry.RawNamedValue;
|
||||
import jadx.core.xmlgen.entry.RawValue;
|
||||
import jadx.core.xmlgen.entry.ResourceEntry;
|
||||
@ -17,6 +20,8 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class ResXmlGenTest {
|
||||
private final JadxArgs args = new JadxArgs();
|
||||
private final IJadxSecurity security = new JadxSecurity(JadxSecurityFlag.all());
|
||||
private final ManifestAttributes manifestAttributes = new ManifestAttributes(security);
|
||||
|
||||
@BeforeEach
|
||||
void init() {
|
||||
@ -25,13 +30,13 @@ class ResXmlGenTest {
|
||||
|
||||
@Test
|
||||
void testSimpleAttr() {
|
||||
ResourceStorage resStorage = new ResourceStorage();
|
||||
ResourceStorage resStorage = new ResourceStorage(security);
|
||||
ResourceEntry re = new ResourceEntry(2130903103, "jadx.gui.app", "attr", "size", "");
|
||||
re.setNamedValues(Lists.list(new RawNamedValue(16777216, new RawValue(16, 64))));
|
||||
resStorage.add(re);
|
||||
|
||||
ValuesParser vp = new ValuesParser(null, resStorage.getResourcesNames());
|
||||
ResXmlGen resXmlGen = new ResXmlGen(resStorage, vp);
|
||||
ResXmlGen resXmlGen = new ResXmlGen(resStorage, vp, manifestAttributes);
|
||||
List<ResContainer> files = resXmlGen.makeResourcesXml(args);
|
||||
|
||||
assertThat(files).hasSize(1);
|
||||
@ -46,14 +51,14 @@ class ResXmlGenTest {
|
||||
|
||||
@Test
|
||||
void testAttrEnum() {
|
||||
ResourceStorage resStorage = new ResourceStorage();
|
||||
ResourceStorage resStorage = new ResourceStorage(security);
|
||||
ResourceEntry re = new ResourceEntry(2130903103, "jadx.gui.app", "attr", "size", "");
|
||||
re.setNamedValues(
|
||||
Lists.list(new RawNamedValue(16777216, new RawValue(16, 65536)), new RawNamedValue(17039620, new RawValue(16, 1))));
|
||||
resStorage.add(re);
|
||||
|
||||
ValuesParser vp = new ValuesParser(null, resStorage.getResourcesNames());
|
||||
ResXmlGen resXmlGen = new ResXmlGen(resStorage, vp);
|
||||
ResXmlGen resXmlGen = new ResXmlGen(resStorage, vp, manifestAttributes);
|
||||
List<ResContainer> files = resXmlGen.makeResourcesXml(args);
|
||||
|
||||
assertThat(files).hasSize(1);
|
||||
@ -69,14 +74,14 @@ class ResXmlGenTest {
|
||||
|
||||
@Test
|
||||
void testAttrFlag() {
|
||||
ResourceStorage resStorage = new ResourceStorage();
|
||||
ResourceStorage resStorage = new ResourceStorage(security);
|
||||
ResourceEntry re = new ResourceEntry(2130903103, "jadx.gui.app", "attr", "size", "");
|
||||
re.setNamedValues(
|
||||
Lists.list(new RawNamedValue(16777216, new RawValue(16, 131072)), new RawNamedValue(17039620, new RawValue(16, 1))));
|
||||
resStorage.add(re);
|
||||
|
||||
ValuesParser vp = new ValuesParser(null, resStorage.getResourcesNames());
|
||||
ResXmlGen resXmlGen = new ResXmlGen(resStorage, vp);
|
||||
ResXmlGen resXmlGen = new ResXmlGen(resStorage, vp, manifestAttributes);
|
||||
List<ResContainer> files = resXmlGen.makeResourcesXml(args);
|
||||
|
||||
assertThat(files).hasSize(1);
|
||||
@ -92,14 +97,14 @@ class ResXmlGenTest {
|
||||
|
||||
@Test
|
||||
void testAttrMin() {
|
||||
ResourceStorage resStorage = new ResourceStorage();
|
||||
ResourceStorage resStorage = new ResourceStorage(security);
|
||||
ResourceEntry re = new ResourceEntry(2130903103, "jadx.gui.app", "attr", "size", "");
|
||||
re.setNamedValues(
|
||||
Lists.list(new RawNamedValue(16777216, new RawValue(16, 4)), new RawNamedValue(16777217, new RawValue(16, 1))));
|
||||
resStorage.add(re);
|
||||
|
||||
ValuesParser vp = new ValuesParser(null, resStorage.getResourcesNames());
|
||||
ResXmlGen resXmlGen = new ResXmlGen(resStorage, vp);
|
||||
ResXmlGen resXmlGen = new ResXmlGen(resStorage, vp, manifestAttributes);
|
||||
List<ResContainer> files = resXmlGen.makeResourcesXml(args);
|
||||
|
||||
assertThat(files).hasSize(1);
|
||||
@ -114,7 +119,7 @@ class ResXmlGenTest {
|
||||
|
||||
@Test
|
||||
void testStyle() {
|
||||
ResourceStorage resStorage = new ResourceStorage();
|
||||
ResourceStorage resStorage = new ResourceStorage(security);
|
||||
ResourceEntry re = new ResourceEntry(2130903103, "jadx.gui.app", "style", "JadxGui", "");
|
||||
re.setNamedValues(Lists.list(new RawNamedValue(16842836, new RawValue(1, 17170445))));
|
||||
resStorage.add(re);
|
||||
@ -124,7 +129,7 @@ class ResXmlGenTest {
|
||||
re.setNamedValues(new ArrayList<>());
|
||||
resStorage.add(re);
|
||||
ValuesParser vp = new ValuesParser(null, resStorage.getResourcesNames());
|
||||
ResXmlGen resXmlGen = new ResXmlGen(resStorage, vp);
|
||||
ResXmlGen resXmlGen = new ResXmlGen(resStorage, vp, manifestAttributes);
|
||||
List<ResContainer> files = resXmlGen.makeResourcesXml(args);
|
||||
|
||||
assertThat(files).hasSize(1);
|
||||
@ -142,7 +147,7 @@ class ResXmlGenTest {
|
||||
|
||||
@Test
|
||||
void testString() {
|
||||
ResourceStorage resStorage = new ResourceStorage();
|
||||
ResourceStorage resStorage = new ResourceStorage(security);
|
||||
ResourceEntry re = new ResourceEntry(2130903103, "jadx.gui.app", "string", "app_name", "");
|
||||
re.setSimpleValue(new RawValue(3, 0));
|
||||
re.setNamedValues(Lists.list());
|
||||
@ -151,7 +156,7 @@ class ResXmlGenTest {
|
||||
BinaryXMLStrings strings = new BinaryXMLStrings();
|
||||
strings.put(0, "Jadx Decompiler App");
|
||||
ValuesParser vp = new ValuesParser(strings, resStorage.getResourcesNames());
|
||||
ResXmlGen resXmlGen = new ResXmlGen(resStorage, vp);
|
||||
ResXmlGen resXmlGen = new ResXmlGen(resStorage, vp, manifestAttributes);
|
||||
List<ResContainer> files = resXmlGen.makeResourcesXml(args);
|
||||
|
||||
assertThat(files).hasSize(1);
|
||||
@ -165,7 +170,7 @@ class ResXmlGenTest {
|
||||
|
||||
@Test
|
||||
void testStringFormattedFalse() {
|
||||
ResourceStorage resStorage = new ResourceStorage();
|
||||
ResourceStorage resStorage = new ResourceStorage(security);
|
||||
ResourceEntry re = new ResourceEntry(2130903103, "jadx.gui.app", "string", "app_name", "");
|
||||
re.setSimpleValue(new RawValue(3, 0));
|
||||
re.setNamedValues(Lists.list());
|
||||
@ -174,7 +179,7 @@ class ResXmlGenTest {
|
||||
BinaryXMLStrings strings = new BinaryXMLStrings();
|
||||
strings.put(0, "%s at %s");
|
||||
ValuesParser vp = new ValuesParser(strings, resStorage.getResourcesNames());
|
||||
ResXmlGen resXmlGen = new ResXmlGen(resStorage, vp);
|
||||
ResXmlGen resXmlGen = new ResXmlGen(resStorage, vp, manifestAttributes);
|
||||
List<ResContainer> files = resXmlGen.makeResourcesXml(args);
|
||||
|
||||
assertThat(files).hasSize(1);
|
||||
@ -188,7 +193,7 @@ class ResXmlGenTest {
|
||||
|
||||
@Test
|
||||
void testArrayEscape() {
|
||||
ResourceStorage resStorage = new ResourceStorage();
|
||||
ResourceStorage resStorage = new ResourceStorage(security);
|
||||
ResourceEntry re = new ResourceEntry(2130903103, "jadx.gui.app", "array", "single_quote_escape_sample", "");
|
||||
re.setNamedValues(
|
||||
Lists.list(new RawNamedValue(16777216, new RawValue(3, 0))));
|
||||
@ -197,7 +202,7 @@ class ResXmlGenTest {
|
||||
BinaryXMLStrings strings = new BinaryXMLStrings();
|
||||
strings.put(0, "Let's go");
|
||||
ValuesParser vp = new ValuesParser(strings, resStorage.getResourcesNames());
|
||||
ResXmlGen resXmlGen = new ResXmlGen(resStorage, vp);
|
||||
ResXmlGen resXmlGen = new ResXmlGen(resStorage, vp, manifestAttributes);
|
||||
List<ResContainer> files = resXmlGen.makeResourcesXml(args);
|
||||
|
||||
assertThat(files).hasSize(1);
|
||||
|
@ -2,58 +2,44 @@ package jadx.tests.api;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import jadx.api.ICodeInfo;
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.api.JadxDecompilerTestUtils;
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.api.ResourceFileContent;
|
||||
import jadx.api.ResourceType;
|
||||
import jadx.api.impl.SimpleCodeInfo;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.export.ExportGradleProject;
|
||||
import jadx.core.export.ExportGradleTask;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
|
||||
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public abstract class ExportGradleTest {
|
||||
|
||||
private final RootNode root;
|
||||
|
||||
public ExportGradleTest() {
|
||||
final JadxDecompiler decompiler = JadxDecompilerTestUtils.getMockDecompiler();
|
||||
root = decompiler.getRoot();
|
||||
}
|
||||
|
||||
private static final String MANIFEST_TESTS_DIR = "src/test/manifest";
|
||||
|
||||
private final RootNode root = new RootNode(new JadxArgs());
|
||||
|
||||
@TempDir
|
||||
private File exportDir;
|
||||
|
||||
protected ResContainer createResourceContainer(String filename) {
|
||||
final ResContainer container = mock(ResContainer.class);
|
||||
ICodeInfo codeInfo = mock(ICodeInfo.class);
|
||||
when(codeInfo.getCodeStr()).thenReturn(loadFileContent(new File(MANIFEST_TESTS_DIR, filename)));
|
||||
when(container.getText()).thenReturn(codeInfo);
|
||||
return container;
|
||||
protected ICodeInfo loadResource(String filename) {
|
||||
return new SimpleCodeInfo(loadFileContent(new File(MANIFEST_TESTS_DIR, filename)));
|
||||
}
|
||||
|
||||
private static String loadFileContent(File filePath) {
|
||||
StringBuilder contentBuilder = new StringBuilder();
|
||||
|
||||
try (Stream<String> stream = Files.lines(filePath.toPath(), StandardCharsets.UTF_8)) {
|
||||
stream.forEach(s -> contentBuilder.append(s).append("\n"));
|
||||
try {
|
||||
return Files.readString(filePath.toPath());
|
||||
} catch (IOException e) {
|
||||
fail("Loading file failed: %s", e.getMessage());
|
||||
fail("Loading file failed", e);
|
||||
return "";
|
||||
}
|
||||
return contentBuilder.toString();
|
||||
}
|
||||
|
||||
protected RootNode getRootNode() {
|
||||
@ -61,30 +47,24 @@ public abstract class ExportGradleTest {
|
||||
}
|
||||
|
||||
protected void exportGradle(String manifestFilename, String stringsFileName) {
|
||||
ResourceFile androidManifest = mock(ResourceFile.class);
|
||||
final ResContainer androidManifestContainer = createResourceContainer(manifestFilename);
|
||||
when(androidManifest.loadContent()).thenReturn(androidManifestContainer);
|
||||
final ResContainer strings = createResourceContainer(stringsFileName);
|
||||
ResourceFile androidManifest = new ResourceFileContent(manifestFilename,
|
||||
ResourceType.XML, loadResource(manifestFilename));
|
||||
ResContainer strings = ResContainer.textResource(stringsFileName, loadResource(stringsFileName));
|
||||
|
||||
final ExportGradleTask exportGradleTask = new ExportGradleTask(List.of(androidManifest), root, exportDir);
|
||||
ExportGradleTask exportGradleTask = new ExportGradleTask(List.of(androidManifest), root, exportDir);
|
||||
exportGradleTask.init();
|
||||
assertThat(exportGradleTask.getSrcOutDir()).exists();
|
||||
assertThat(exportGradleTask.getResOutDir()).exists();
|
||||
|
||||
final ExportGradleProject export =
|
||||
new ExportGradleProject(root, exportDir, androidManifest, strings);
|
||||
ExportGradleProject export = new ExportGradleProject(root, exportDir, androidManifest, strings);
|
||||
export.generateGradleFiles();
|
||||
}
|
||||
|
||||
protected String getAppGradleBuild() {
|
||||
File appBuildGradle = new File(exportDir, "app/build.gradle");
|
||||
assertThat(appBuildGradle).exists();
|
||||
return loadFileContent(appBuildGradle);
|
||||
return loadFileContent(new File(exportDir, "app/build.gradle"));
|
||||
}
|
||||
|
||||
protected String getSettingsGradle() {
|
||||
File settingsGradle = new File(exportDir, "settings.gradle");
|
||||
assertThat(settingsGradle).exists();
|
||||
return loadFileContent(settingsGradle);
|
||||
return loadFileContent(new File(exportDir, "settings.gradle"));
|
||||
}
|
||||
}
|
||||
|
@ -342,7 +342,7 @@ public abstract class IntegrationTest extends TestUtils {
|
||||
if (resMap.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
ResourceStorage resStorage = new ResourceStorage();
|
||||
ResourceStorage resStorage = new ResourceStorage(root.getArgs().getSecurity());
|
||||
for (Map.Entry<Integer, String> entry : resMap.entrySet()) {
|
||||
Integer id = entry.getKey();
|
||||
String name = entry.getValue();
|
||||
|
@ -148,7 +148,8 @@ public class DbgUtils {
|
||||
}
|
||||
AndroidManifestParser parser = new AndroidManifestParser(
|
||||
AndroidManifestParser.getAndroidManifest(decompiler.getResources()),
|
||||
EnumSet.of(AppAttribute.MAIN_ACTIVITY));
|
||||
EnumSet.of(AppAttribute.MAIN_ACTIVITY),
|
||||
decompiler.getArgs().getSecurity());
|
||||
if (!parser.isManifestFound()) {
|
||||
UiUtils.errorMessage(mw, NLS.str("error_dialog.not_found", "AndroidManifest.xml"));
|
||||
return null;
|
||||
|
@ -952,7 +952,8 @@ public class MainWindow extends JFrame {
|
||||
public void goToMainActivity() {
|
||||
AndroidManifestParser parser = new AndroidManifestParser(
|
||||
AndroidManifestParser.getAndroidManifest(getWrapper().getResources()),
|
||||
EnumSet.of(AppAttribute.MAIN_ACTIVITY));
|
||||
EnumSet.of(AppAttribute.MAIN_ACTIVITY),
|
||||
getWrapper().getArgs().getSecurity());
|
||||
if (!parser.isManifestFound()) {
|
||||
JOptionPane.showMessageDialog(MainWindow.this,
|
||||
NLS.str("error_dialog.not_found", "AndroidManifest.xml"),
|
||||
@ -982,7 +983,8 @@ public class MainWindow extends JFrame {
|
||||
public void goToApplication() {
|
||||
AndroidManifestParser parser = new AndroidManifestParser(
|
||||
AndroidManifestParser.getAndroidManifest(getWrapper().getResources()),
|
||||
EnumSet.of(AppAttribute.APPLICATION));
|
||||
EnumSet.of(AppAttribute.APPLICATION),
|
||||
getWrapper().getArgs().getSecurity());
|
||||
if (!parser.isManifestFound()) {
|
||||
JOptionPane.showMessageDialog(MainWindow.this,
|
||||
NLS.str("error_dialog.not_found", "AndroidManifest.xml"),
|
||||
|
@ -34,7 +34,7 @@ public class ResTableProtoParser extends CommonProtoParser implements IResTableP
|
||||
|
||||
@Override
|
||||
public void decode(InputStream inputStream) throws IOException {
|
||||
resStorage = new ResourceStorage();
|
||||
resStorage = new ResourceStorage(root.getArgs().getSecurity());
|
||||
ResourceTable table = ResourceTable.parseFrom(FileUtils.streamToByteArray(inputStream));
|
||||
for (Package p : table.getPackageList()) {
|
||||
parse(p);
|
||||
@ -45,7 +45,7 @@ public class ResTableProtoParser extends CommonProtoParser implements IResTableP
|
||||
@Override
|
||||
public synchronized ResContainer decodeFiles() {
|
||||
ValuesParser vp = new ValuesParser(new BinaryXMLStrings(), resStorage.getResourcesNames());
|
||||
ResXmlGen resGen = new ResXmlGen(resStorage, vp);
|
||||
ResXmlGen resGen = new ResXmlGen(resStorage, vp, root.initManifestAttributes());
|
||||
ICodeInfo content = XmlGenUtils.makeXmlDump(root.makeCodeWriter(), resStorage);
|
||||
List<ResContainer> xmlFiles = resGen.makeResourcesXml(root.getArgs());
|
||||
return ResContainer.resourceTable("res", xmlFiles, content);
|
||||
|
Loading…
Reference in New Issue
Block a user