From 90692d89c59c1ccbf0a99c7992bf6f009dfcb664 Mon Sep 17 00:00:00 2001 From: Sergey Toshin Date: Fri, 1 Jun 2018 17:49:29 +0300 Subject: [PATCH] Xml deobf 2.0 --- .../main/java/jadx/api/ResourcesLoader.java | 3 +- .../java/jadx/core/codegen/CodeWriter.java | 7 +++ .../jadx/core/xmlgen/BinaryXMLParser.java | 54 +++++++++++++++++-- .../main/java/jadx/core/xmlgen/XmlDeobf.java | 41 ++++++++++++++ 4 files changed, 99 insertions(+), 6 deletions(-) create mode 100644 jadx-core/src/main/java/jadx/core/xmlgen/XmlDeobf.java diff --git a/jadx-core/src/main/java/jadx/api/ResourcesLoader.java b/jadx-core/src/main/java/jadx/api/ResourcesLoader.java index 292f72a77..c931964b8 100644 --- a/jadx-core/src/main/java/jadx/api/ResourcesLoader.java +++ b/jadx-core/src/main/java/jadx/api/ResourcesLoader.java @@ -99,7 +99,8 @@ public final class ResourcesLoader { jadxRef.getXmlParser().parse(inputStream)); case ARSC: - return new ResTableParser().decodeFiles(inputStream); + return new ResTableParser() + .decodeFiles(inputStream); case IMG: return ResContainer.singleImageFile(rf.getName(), inputStream); diff --git a/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java b/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java index 592d34ff1..4817aee67 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java +++ b/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java @@ -119,6 +119,13 @@ public class CodeWriter { buf.append(code.buf); return this; } + + public void updateContent(String newData) { + offset = newData.length(); + buf = new StringBuilder(newData); + line = newData.split(NL).length + 1; + code = newData; + } public CodeWriter newLine() { addLine(); diff --git a/jadx-core/src/main/java/jadx/core/xmlgen/BinaryXMLParser.java b/jadx-core/src/main/java/jadx/core/xmlgen/BinaryXMLParser.java index 9c8c4d68b..79f54697f 100644 --- a/jadx-core/src/main/java/jadx/core/xmlgen/BinaryXMLParser.java +++ b/jadx-core/src/main/java/jadx/core/xmlgen/BinaryXMLParser.java @@ -55,12 +55,16 @@ public class BinaryXMLParser extends CommonBinaryParser { private boolean isOneLine = true; private int namespaceDepth = 0; private int[] resourceIds; + + private RootNode rootNode; + private String appPackageName; - public BinaryXMLParser(RootNode root) { + public BinaryXMLParser(RootNode rootNode) { + this.rootNode = rootNode; try { readAndroidRStyleClass(); // add application constants - ConstStorage constStorage = root.getConstValues(); + ConstStorage constStorage = rootNode.getConstValues(); Map constFields = constStorage.getGlobalConstFields(); for (Map.Entry entry : constFields.entrySet()) { Object key = entry.getKey(); @@ -248,6 +252,7 @@ public class BinaryXMLParser extends CommonBinaryParser { isOneLine = true; isLastEnd = false; currentTag = getValidTagAttributeName(getString(startNSName)); + currentTag = deobfClassName(currentTag); writer.startLine("<").add(currentTag); writer.attachSourceLine(elementBegLineNumber); int attributeStart = is.readInt16(); @@ -298,16 +303,23 @@ public class BinaryXMLParser extends CommonBinaryParser { } else { writer.add(' '); } + String shortNsName = null; if (attributeNS != -1) { - writer.add(getAttributeNS(attributeNS)).add(':'); + shortNsName = getAttributeNS(attributeNS); + writer.add(shortNsName).add(':'); } String attrName = getValidTagAttributeName(getAttributeName(attributeName)); writer.add(attrName).add("=\""); String decodedAttr = ManifestAttributes.getInstance().decode(attrName, attrValData); if (decodedAttr != null) { + memorizePackageName(attrName, decodedAttr); + if(isDeobfCandidateAttr(shortNsName, attrName)) { + decodedAttr = deobfClassName(decodedAttr); + } writer.add(StringUtils.escapeXML(decodedAttr)); } else { - decodeAttribute(attributeNS, attrValDataType, attrValData); + decodeAttribute(attributeNS, attrValDataType, attrValData, + shortNsName, attrName); } writer.add('"'); } @@ -367,7 +379,9 @@ public class BinaryXMLParser extends CommonBinaryParser { return "NOT_FOUND_STR_0x" + Integer.toHexString(strId); } - private void decodeAttribute(int attributeNS, int attrValDataType, int attrValData) { + private void decodeAttribute(int attributeNS, int attrValDataType, int attrValData, + String shortNsName, String attrName) { + if (attrValDataType == TYPE_REFERENCE) { // reference custom processing String name = styleMap.get(attrValData); @@ -408,6 +422,10 @@ public class BinaryXMLParser extends CommonBinaryParser { } } else { String str = valuesParser.decodeValue(attrValDataType, attrValData); + memorizePackageName(attrName, str); + if(isDeobfCandidateAttr(shortNsName, attrName)) { + str = deobfClassName(str); + } writer.add(str != null ? StringUtils.escapeXML(str) : "null"); } } @@ -465,4 +483,30 @@ public class BinaryXMLParser extends CommonBinaryParser { } return sb.toString(); } + + private String deobfClassName(String className) { + String newName = XmlDeobf.deobfClassName(rootNode, className, + appPackageName); + if(newName != null) { + return newName; + } + return className; + } + + private boolean isDeobfCandidateAttr(String shortNsName, String attrName) { + String fullName; + if(shortNsName != null) { + fullName = shortNsName + ":" + attrName; + } + else { + return false; + } + return "android:name".equals(fullName); + } + + private void memorizePackageName(String attrName, String attrValue) { + if("manifest".equals(currentTag) && "package".equals(attrName)) { + appPackageName = attrValue; + } + } } diff --git a/jadx-core/src/main/java/jadx/core/xmlgen/XmlDeobf.java b/jadx-core/src/main/java/jadx/core/xmlgen/XmlDeobf.java new file mode 100644 index 000000000..e8a41a729 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/xmlgen/XmlDeobf.java @@ -0,0 +1,41 @@ +package jadx.core.xmlgen; + +import java.util.HashMap; +import java.util.Map; + +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.RootNode; + +/* + * modifies android:name attributes and xml tags which are old class names + * but were changed during deobfuscation + */ +public class XmlDeobf { + private static final Map deobfMap = new HashMap<>(); + + private XmlDeobf() {} + + public static String deobfClassName(RootNode rootNode, String potencialClassName, + String packageName) { + + if(packageName != null && potencialClassName.startsWith(".")) { + potencialClassName = packageName + potencialClassName; + } + return getNewClassName(rootNode, potencialClassName); + } + + private static String getNewClassName(RootNode rootNode, String old) { + if(deobfMap.isEmpty()) { + for(ClassNode classNode : rootNode.getClasses(true)) { + if(classNode.getAlias() != null) { + String oldName = classNode.getClassInfo().getFullName(); + String newName = classNode.getAlias().getFullName(); + if(!oldName.equals(newName)) { + deobfMap.put(oldName, newName); + } + } + } + } + return deobfMap.get(old); + } +}