Commit 716db8b9 authored by Skylot's avatar Skylot

manifest: restore application references and Android values (enums, flags)

parent b55975a3
......@@ -203,20 +203,21 @@ public final class JadxDecompiler {
reset();
root = new RootNode();
LOG.info("loading ...");
root.load(inputFiles);
if (this.args.isXMLTest()) {
InputFile inf = inputFiles.get(0);
try {
byte[] buffer = InputFile.loadXMLBuffer(inf.getFile());
if (buffer != null) {
File out = new File(args.getOutDir(), "AndroidManifest.xml");
BinaryXMLParser bxp = new BinaryXMLParser();
BinaryXMLParser bxp = new BinaryXMLParser(root);
bxp.parse(buffer, out);
}
} catch (Exception e) {
LOG.info("Decompiling AndroidManifest.xml failed!", e);
}
}
root.load(inputFiles);
}
void processClass(ClassNode cls) {
......
......@@ -174,6 +174,10 @@ public class CodeWriter {
updateIndent();
}
public int getIndent() {
return indent;
}
public int getLine() {
return line;
}
......
......@@ -104,6 +104,10 @@ public class RootNode {
return searchClassByName(fullName);
}
public List<DexNode> getDexNodes() {
return dexNodes;
}
public ErrorsCounter getErrorsCounter() {
return errorsCounter;
}
......
package jadx.core.xmlgen;
import jadx.core.codegen.CodeWriter;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.DexNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.exceptions.JadxRuntimeException;
import java.io.File;
......@@ -40,16 +44,28 @@ public class BinaryXMLParser {
private boolean wasOneLiner = false;
private CodeWriter writer;
private Map<Integer, String> styleMap = null;
private Map<Integer, String> styleMap = new HashMap<Integer, String>();
private Map<Integer, FieldNode> localStyleMap = new HashMap<Integer, FieldNode>();
private final ManifestAttributes attributes;
public BinaryXMLParser() {
styleMap = new HashMap<Integer, String>();
public BinaryXMLParser(RootNode root) {
try {
for (Field f : AndroidR.style.class.getFields()) {
styleMap.put(f.getInt(f.getType()), f.getName());
}
//TODO: also add application constant fields
} catch (IllegalAccessException e) {
// add application constants
for (DexNode dexNode : root.getDexNodes()) {
for (Map.Entry<Object, FieldNode> entry : dexNode.getConstFields().entrySet()) {
Object key = entry.getKey();
FieldNode field = entry.getValue();
if (field.getType().equals(ArgType.INT) && key instanceof Integer) {
localStyleMap.put((Integer) key, field);
}
}
}
attributes = new ManifestAttributes();
attributes.parse();
} catch (Exception e) {
throw new JadxRuntimeException("BinaryXMLParser init error", e);
}
}
......@@ -194,7 +210,7 @@ public class BinaryXMLParser {
}
wasOneLiner = false;
currentTag = strings[startNSName];
writer.startLine("<" + strings[startNSName]);
writer.startLine("<").add(strings[startNSName]);
int attributeStart = cInt16(bytes, count);
if (attributeStart != 0x14) {
die("startNS's attributeStart is not 0x14");
......@@ -208,7 +224,7 @@ public class BinaryXMLParser {
int classIndex = cInt16(bytes, count);
int styleIndex = cInt16(bytes, count);
if ("manifest".equals(strings[startNSName])) {
writer.add(" xmlns:\"" + nsURI + "\"");
writer.add(" xmlns:\"").add(nsURI).add("\"");
}
if (attributeCount > 0) {
writer.add(" ");
......@@ -227,14 +243,24 @@ public class BinaryXMLParser {
int attrValDataType = cInt8(bytes, count);
int attrValData = cInt32(bytes, count);
if (attributeNS != -1) {
writer.add(nsPrefix + ":");
writer.add(nsPrefix).add(':');
}
writer.add(strings[attributeName] + "=\"");
if (attrValDataType == 0x3) {
String attrName = strings[attributeName];
writer.add(attrName).add("=\"");
String decodedAttr = attributes.decode(attrName, attrValData);
if (decodedAttr != null) {
writer.add(decodedAttr);
} else {
switch (attrValDataType) {
case 0x3:
writer.add(strings[attrValData]);
} else if (attrValDataType == 0x10) {
break;
case 0x10:
writer.add(String.valueOf(attrValData));
} else if (attrValDataType == 0x12) {
break;
case 0x12:
// FIXME: What to do, when data is always -1?
if (attrValData == 0) {
writer.add("false");
......@@ -243,37 +269,33 @@ public class BinaryXMLParser {
} else {
writer.add("UNKNOWN_BOOLEAN_TYPE");
}
} else if (attrValDataType == 0x1) {
break;
case 0x1:
String name = styleMap.get(attrValData);
if (name != null) {
writer.add("@*");
if (attributeNS != -1) {
writer.add(nsPrefix + ":");
writer.add(nsPrefix).add(':');
}
writer.add("style/" + name.replaceAll("_", "."));
writer.add("style/").add(name.replaceAll("_", "."));
} else {
writer.add("0x" + Integer.toHexString(attrValData));
}
} else {
// TODO: extract values names from here:
// https://github.com/android/platform_frameworks_base/blob/master/core/res/res/values/attrs_manifest.xml
if ("configChanges".equals(strings[attributeName])) {
if (attrValData == 1152) {
writer.add("orientation");
} else if (attrValData == 4016) {
writer.add("keyboard|keyboardHidden|orientation|screenLayout|uiMode");
} else if (attrValData == 176) {
writer.add("keyboard|keyboardHidden|orientation");
} else if (attrValData == 160) {
writer.add("keyboardHidden|orientation");
FieldNode field = localStyleMap.get(attrValData);
if (field != null) {
String cls = field.getParentClass().getShortName().toLowerCase();
writer.add("@").add(cls).add("/").add(field.getName());
} else {
writer.add("UNKNOWN_DATA_" + Integer.toHexString(attrValData));
writer.add("0x").add(Integer.toHexString(attrValData));
}
} else {
}
break;
default:
writer.add("UNKNOWN_DATA_TYPE_" + attrValDataType);
break;
}
}
writer.add("\"");
writer.add('"');
if ((i + 1) < attributeCount) {
writer.add(" ");
}
......@@ -292,17 +314,19 @@ public class BinaryXMLParser {
int elementNS = cInt32(bytes, count);
int elementName = cInt32(bytes, count);
if (currentTag.equals(strings[elementName])) {
writer.add("/>");
writer.add(" />");
wasOneLiner = true;
} else {
writer.startLine("</");
if (elementNS != -1) {
writer.add(strings[elementNS] + ":");
writer.add(strings[elementNS]).add(':');
}
writer.add(strings[elementName] + ">");
writer.add(strings[elementName]).add(">");
}
if (writer.getIndent() != 0) {
writer.decIndent();
}
}
private int cInt8(byte[] bytes, int offset) {
byte[] tmp = new byte[4];
......
package jadx.core.xmlgen;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.InputStream;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class ManifestAttributes {
private static final Logger LOG = LoggerFactory.getLogger(ManifestAttributes.class);
private static final String MANIFEST_ATTR_XML = "/attrs_manifest.xml";
private enum MAttrType {
ENUM, FLAG
}
private static class MAttr {
private final MAttrType type;
private final Map<Integer, String> values = new LinkedHashMap<Integer, String>();
public MAttr(MAttrType type) {
this.type = type;
}
public MAttrType getType() {
return type;
}
public Map<Integer, String> getValues() {
return values;
}
@Override
public String toString() {
return "[" + type + ", " + values + "]";
}
}
private final Document doc;
private final Map<String, MAttr> attrMap = new HashMap<String, MAttr>();
public ManifestAttributes() throws Exception {
InputStream xmlStream = ManifestAttributes.class.getResourceAsStream(MANIFEST_ATTR_XML);
DocumentBuilder dBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
doc = dBuilder.parse(xmlStream);
}
public void parse() {
NodeList nodeList = doc.getChildNodes();
for (int count = 0; count < nodeList.getLength(); count++) {
Node node = nodeList.item(count);
if (node.getNodeType() == Node.ELEMENT_NODE) {
if (node.hasChildNodes()) {
parseAttrList(node.getChildNodes());
}
}
}
}
private void parseAttrList(NodeList nodeList) {
for (int count = 0; count < nodeList.getLength(); count++) {
Node tempNode = nodeList.item(count);
if (tempNode.getNodeType() == Node.ELEMENT_NODE
&& tempNode.hasAttributes()
&& tempNode.hasChildNodes()) {
String name = null;
NamedNodeMap nodeMap = tempNode.getAttributes();
for (int i = 0; i < nodeMap.getLength(); i++) {
Node node = nodeMap.item(i);
if (node.getNodeName().equals("name")) {
name = node.getNodeValue();
break;
}
}
if (name != null && tempNode.getNodeName().equals("attr")) {
parseValues(name, tempNode.getChildNodes());
} else {
parseAttrList(tempNode.getChildNodes());
}
}
}
}
private void parseValues(String name, NodeList nodeList) {
MAttr attr = null;
for (int count = 0; count < nodeList.getLength(); count++) {
Node tempNode = nodeList.item(count);
if (tempNode.getNodeType() == Node.ELEMENT_NODE
&& tempNode.hasAttributes()) {
if (attr == null) {
if (tempNode.getNodeName().equals("enum")) {
attr = new MAttr(MAttrType.ENUM);
} else if (tempNode.getNodeName().equals("flag")) {
attr = new MAttr(MAttrType.FLAG);
}
if (attr == null) {
return;
}
attrMap.put(name, attr);
}
NamedNodeMap attributes = tempNode.getAttributes();
Node nameNode = attributes.getNamedItem("name");
if (nameNode != null) {
Node valueNode = attributes.getNamedItem("value");
if (valueNode != null) {
try {
int key;
String nodeValue = valueNode.getNodeValue();
if (attr.getType() == MAttrType.ENUM) {
key = Integer.parseInt(nodeValue);
} else {
nodeValue = nodeValue.replace("0x", "");
key = Integer.parseInt(nodeValue, 16);
}
attr.getValues().put(key, nameNode.getNodeValue());
} catch (NumberFormatException e) {
LOG.debug("Failed parse manifest number", e);
}
}
}
}
}
}
public String decode(String attrName, int value) {
MAttr attr = attrMap.get(attrName);
if (attr == null) {
return null;
}
if (attr.getType() == MAttrType.ENUM) {
String name = attr.getValues().get(value);
if (name != null) {
return name;
}
} else if (attr.getType() == MAttrType.FLAG) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<Integer, String> entry : attr.getValues().entrySet()) {
if ((value & entry.getKey()) != 0) {
sb.append(entry.getValue()).append('|');
}
}
if (sb.length() != 0) {
return sb.deleteCharAt(sb.length() - 1).toString();
}
}
return "UNKNOWN_DATA_" + Integer.toHexString(value);
}
}
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment