Skip to content

Commit

Permalink
resources: initial version of .arsc file decode
Browse files Browse the repository at this point in the history
  • Loading branch information
skylot committed Oct 9, 2015
1 parent e4fef40 commit ef8a685
Show file tree
Hide file tree
Showing 18 changed files with 480 additions and 96 deletions.
Binary file modified jadx-core/clsp-data/android-5.1.jar
Binary file not shown.
19 changes: 5 additions & 14 deletions jadx-core/src/main/java/jadx/api/JadxDecompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import jadx.core.Jadx;
import jadx.core.ProcessClass;
import jadx.core.codegen.CodeGen;
import jadx.core.codegen.CodeWriter;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.MethodNode;
Expand All @@ -15,6 +14,7 @@
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.InputFile;
import jadx.core.xmlgen.BinaryXMLParser;
import jadx.core.xmlgen.ResourcesSaver;

import java.io.File;
import java.io.IOException;
Expand Down Expand Up @@ -150,7 +150,7 @@ public ExecutorService getSaveExecutor() {
return getSaveExecutor(!args.isSkipSources(), !args.isSkipResources());
}

private ExecutorService getSaveExecutor(boolean saveSources, boolean saveResources) {
private ExecutorService getSaveExecutor(boolean saveSources, final boolean saveResources) {
if (root == null) {
throw new JadxRuntimeException("No loaded files");
}
Expand All @@ -172,17 +172,7 @@ public void run() {
}
if (saveResources) {
for (final ResourceFile resourceFile : getResources()) {
executor.execute(new Runnable() {
@Override
public void run() {
if (ResourceType.isSupportedForUnpack(resourceFile.getType())) {
CodeWriter cw = resourceFile.getContent();
if (cw != null) {
cw.save(new File(outDir, resourceFile.getName()));
}
}
}
});
executor.execute(new ResourcesSaver(outDir, resourceFile));
}
}
return executor;
Expand Down Expand Up @@ -294,7 +284,7 @@ RootNode getRoot() {
return root;
}

BinaryXMLParser getXmlParser() {
synchronized BinaryXMLParser getXmlParser() {
if (xmlParser == null) {
xmlParser = new BinaryXMLParser(root);
}
Expand All @@ -321,4 +311,5 @@ public IJadxArgs getArgs() {
public String toString() {
return "jadx decompiler " + getVersion();
}

}
4 changes: 2 additions & 2 deletions jadx-core/src/main/java/jadx/api/ResourceFile.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package jadx.api;

import jadx.core.codegen.CodeWriter;
import jadx.core.xmlgen.ResContainer;

import java.io.File;

Expand Down Expand Up @@ -48,7 +48,7 @@ public ResourceType getType() {
return type;
}

public CodeWriter getContent() {
public ResContainer getContent() {
return ResourcesLoader.loadContent(decompiler, this);
}

Expand Down
19 changes: 19 additions & 0 deletions jadx-core/src/main/java/jadx/api/ResourceFileContent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package jadx.api;

import jadx.core.codegen.CodeWriter;
import jadx.core.xmlgen.ResContainer;

public class ResourceFileContent extends ResourceFile {

private final CodeWriter content;

public ResourceFileContent(String name, ResourceType type, CodeWriter content) {
super(null, name, type);
this.content = content;
}

@Override
public ResContainer getContent() {
return ResContainer.singleFile(getName(), content);
}
}
2 changes: 1 addition & 1 deletion jadx-core/src/main/java/jadx/api/ResourceType.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ public static ResourceType getFileType(String fileName) {
public static boolean isSupportedForUnpack(ResourceType type) {
switch (type) {
case CODE:
case ARSC:
case LIB:
case FONT:
case IMG:
Expand All @@ -43,6 +42,7 @@ public static boolean isSupportedForUnpack(ResourceType type) {

case MANIFEST:
case XML:
case ARSC:
return true;
}
return false;
Expand Down
33 changes: 18 additions & 15 deletions jadx-core/src/main/java/jadx/api/ResourcesLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.files.InputFile;
import jadx.core.xmlgen.ResContainer;
import jadx.core.xmlgen.ResTableParser;

import java.io.BufferedInputStream;
Expand Down Expand Up @@ -43,17 +44,17 @@ List<ResourceFile> load(List<InputFile> inputFiles) {
}

public interface ResourceDecoder {
Object decode(long size, InputStream is) throws IOException;
ResContainer decode(long size, InputStream is) throws IOException;
}

public static Object decodeStream(ResourceFile rf, ResourceDecoder decoder) throws JadxException {
public static ResContainer decodeStream(ResourceFile rf, ResourceDecoder decoder) throws JadxException {
ZipRef zipRef = rf.getZipRef();
if (zipRef == null) {
return null;
}
ZipFile zipFile = null;
InputStream inputStream = null;
Object result = null;
ResContainer result = null;
try {
zipFile = new ZipFile(zipRef.getZipFile());
ZipEntry entry = zipFile.getEntry(zipRef.getEntryName());
Expand All @@ -79,38 +80,40 @@ public static Object decodeStream(ResourceFile rf, ResourceDecoder decoder) thro
return result;
}

static CodeWriter loadContent(final JadxDecompiler jadxRef, final ResourceFile rf) {
static ResContainer loadContent(final JadxDecompiler jadxRef, final ResourceFile rf) {
try {
return (CodeWriter) decodeStream(rf, new ResourceDecoder() {
return decodeStream(rf, new ResourceDecoder() {
@Override
public Object decode(long size, InputStream is) throws IOException {
public ResContainer decode(long size, InputStream is) throws IOException {
if (size > LOAD_SIZE_LIMIT) {
return new CodeWriter().add("File too big, size: "
+ String.format("%.2f KB", size / 1024.));
return ResContainer.singleFile(rf.getName(),
new CodeWriter().add("File too big, size: "
+ String.format("%.2f KB", size / 1024.)));
}
return loadContent(jadxRef, rf.getType(), is);
return loadContent(jadxRef, rf, is);
}
});
} catch (JadxException e) {
LOG.error("Decode error", e);
CodeWriter cw = new CodeWriter();
cw.add("Error decode ").add(rf.getType().toString().toLowerCase());
cw.startLine(Utils.getStackTrace(e.getCause()));
return cw;
return ResContainer.singleFile(rf.getName(), cw);
}
}

private static CodeWriter loadContent(JadxDecompiler jadxRef, ResourceType type,
private static ResContainer loadContent(JadxDecompiler jadxRef, ResourceFile rf,
InputStream inputStream) throws IOException {
switch (type) {
switch (rf.getType()) {
case MANIFEST:
case XML:
return jadxRef.getXmlParser().parse(inputStream);
return ResContainer.singleFile(rf.getName(),
jadxRef.getXmlParser().parse(inputStream));

case ARSC:
return new ResTableParser().decodeToCodeWriter(inputStream);
return new ResTableParser().decodeFiles(inputStream);
}
return loadToCodeWriter(inputStream);
return ResContainer.singleFile(rf.getName(), loadToCodeWriter(inputStream));
}

private void loadFile(List<ResourceFile> list, File file) {
Expand Down
3 changes: 2 additions & 1 deletion jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.files.InputFile;
import jadx.core.xmlgen.ResContainer;
import jadx.core.xmlgen.ResTableParser;
import jadx.core.xmlgen.ResourceStorage;

Expand Down Expand Up @@ -74,7 +75,7 @@ public void loadResources(List<ResourceFile> resources) {
try {
ResourcesLoader.decodeStream(arsc, new ResourcesLoader.ResourceDecoder() {
@Override
public Object decode(long size, InputStream is) throws IOException {
public ResContainer decode(long size, InputStream is) throws IOException {
parser.decode(is);
return null;
}
Expand Down
57 changes: 33 additions & 24 deletions jadx-core/src/main/java/jadx/core/utils/StringUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,30 +27,14 @@ public static String unescapeChar(char ch) {

private static void processChar(int c, StringBuilder res) {
switch (c) {
case '\n':
res.append("\\n");
break;
case '\r':
res.append("\\r");
break;
case '\t':
res.append("\\t");
break;
case '\b':
res.append("\\b");
break;
case '\f':
res.append("\\f");
break;
case '\'':
res.append('\'');
break;
case '"':
res.append("\\\"");
break;
case '\\':
res.append("\\\\");
break;
case '\n': res.append("\\n"); break;
case '\r': res.append("\\r"); break;
case '\t': res.append("\\t"); break;
case '\b': res.append("\\b"); break;
case '\f': res.append("\\f"); break;
case '\'': res.append('\''); break;
case '"': res.append("\\\""); break;
case '\\': res.append("\\\\"); break;

default:
if (32 <= c && c <= 126) {
Expand Down Expand Up @@ -114,4 +98,29 @@ public static String escapeXML(String str) {
}
return sb.toString();
}

public static String escapeResValue(String str) {
int len = str.length();
StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < len; i++) {
char c = str.charAt(i);
switch (c) {
case '&': sb.append("&amp;"); break;
case '<': sb.append("&lt;"); break;
case '>': sb.append("&gt;"); break;
case '"': sb.append("&quot;"); break;
case '\'': sb.append("&apos;"); break;

case '\n': sb.append("\\n"); break;
case '\r': sb.append("\\r"); break;
case '\t': sb.append("\\t"); break;
case '\b': sb.append("\\b"); break;
case '\f': sb.append("\\f"); break;
default:
sb.append(c);
break;
}
}
return sb.toString();
}
}
39 changes: 23 additions & 16 deletions jadx-core/src/main/java/jadx/core/xmlgen/BinaryXMLParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public class BinaryXMLParser extends CommonBinaryParser {

private static final Logger LOG = LoggerFactory.getLogger(BinaryXMLParser.class);
private static final String ANDROID_R_STYLE_CLS = "android.R$style";
private static final boolean ATTR_NEW_LINE = false;

private CodeWriter writer;
private String[] strings;
Expand Down Expand Up @@ -76,7 +77,7 @@ public BinaryXMLParser(RootNode root) {
resNames = root.getResourcesNames();

attributes = new ManifestAttributes();
attributes.parse();
attributes.parseAll();
} catch (Exception e) {
throw new JadxRuntimeException("BinaryXMLParser init error", e);
}
Expand Down Expand Up @@ -221,12 +222,13 @@ private void parseElement() throws IOException {
int comment = is.readInt32();
int startNS = is.readInt32();
int startNSName = is.readInt32(); // actually is elementName...
if (!wasOneLiner && !"ERROR".equals(currentTag) && !currentTag.equals(strings[startNSName])) {
if (!wasOneLiner && !"ERROR".equals(currentTag)
&& !currentTag.equals(strings[startNSName])) {
writer.add(">");
}
wasOneLiner = false;
currentTag = strings[startNSName];
writer.startLine("<").add(strings[startNSName]);
writer.startLine("<").add(currentTag);
writer.attachSourceLine(elementBegLineNumber);
int attributeStart = is.readInt16();
if (attributeStart != 0x14) {
Expand All @@ -240,22 +242,16 @@ private void parseElement() throws IOException {
int idIndex = is.readInt16();
int classIndex = is.readInt16();
int styleIndex = is.readInt16();
if ("manifest".equals(strings[startNSName])) {
writer.add(" xmlns:\"").add(nsURI).add("\"");
}
if (attributeCount > 0) {
writer.add(" ");
if ("manifest".equals(currentTag) || writer.getIndent() == 0) {
writer.add(" xmlns:android=\"").add(nsURI).add("\"");
}
boolean attrNewLine = attributeCount == 1 ? false : ATTR_NEW_LINE;
for (int i = 0; i < attributeCount; i++) {
parseAttribute(i);
writer.add('"');
if (i + 1 < attributeCount) {
writer.add(" ");
}
parseAttribute(i, attrNewLine);
}
}

private void parseAttribute(int i) throws IOException {
private void parseAttribute(int i, boolean newLine) throws IOException {
int attributeNS = is.readInt32();
int attributeName = is.readInt32();
int attributeRawValue = is.readInt32();
Expand All @@ -268,17 +264,24 @@ private void parseAttribute(int i) throws IOException {
}
int attrValDataType = is.readInt8();
int attrValData = is.readInt32();

String attrName = strings[attributeName];
if (newLine) {
writer.startLine().addIndent();
} else {
writer.add(' ');
}
if (attributeNS != -1) {
writer.add(nsPrefix).add(':');
}
String attrName = strings[attributeName];
writer.add(attrName).add("=\"");
String decodedAttr = attributes.decode(attrName, attrValData);
if (decodedAttr != null) {
writer.add(decodedAttr);
} else {
decodeAttribute(attributeNS, attrValDataType, attrValData);
}
writer.add('"');
}

private void decodeAttribute(int attributeNS, int attrValDataType, int attrValData) {
Expand All @@ -295,7 +298,11 @@ private void decodeAttribute(int attributeNS, int attrValDataType, int attrValDa
FieldNode field = localStyleMap.get(attrValData);
if (field != null) {
String cls = field.getParentClass().getShortName().toLowerCase();
writer.add("@").add(cls).add("/").add(field.getName());
writer.add("@");
if ("id".equals(cls)) {
writer.add('+');
}
writer.add(cls).add("/").add(field.getName());
} else {
String resName = resNames.get(attrValData);
if (resName != null) {
Expand Down
Loading

0 comments on commit ef8a685

Please sign in to comment.