diff --git a/.gitignore b/.gitignore index 64a00231e..2a60afc97 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ jadx-output/ *-tmp/ **/tmp/ *.jobf +*.jadx *.class *.dump diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java index a08026bbe..a468f00ad 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java @@ -210,18 +210,13 @@ public class RootNode { if (parser != null) { processResources(parser.getResStorage()); updateObfuscatedFiles(parser, resources); - updateManifestAttribMap(parser); + ManifestAttributes.getInstance().updateAttributes(parser); } } catch (Exception e) { LOG.error("Failed to parse 'resources.pb'/'.arsc' file", e); } } - private void updateManifestAttribMap(IResTableParser parser) { - ManifestAttributes manifestAttributes = ManifestAttributes.getInstance(); - manifestAttributes.updateAttributes(parser); - } - private @Nullable ResourceFile getResourceFile(List resources) { for (ResourceFile rf : resources) { if (rf.getType() == ResourceType.ARSC) { diff --git a/jadx-core/src/main/java/jadx/core/xmlgen/ParserStream.java b/jadx-core/src/main/java/jadx/core/xmlgen/ParserStream.java index 88d0d0ddd..46293f565 100644 --- a/jadx-core/src/main/java/jadx/core/xmlgen/ParserStream.java +++ b/jadx-core/src/main/java/jadx/core/xmlgen/ParserStream.java @@ -4,13 +4,14 @@ import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import org.jetbrains.annotations.NotNull; public class ParserStream { - protected static final Charset STRING_CHARSET_UTF16 = Charset.forName("UTF-16LE"); - protected static final Charset STRING_CHARSET_UTF8 = Charset.forName("UTF-8"); + protected static final Charset STRING_CHARSET_UTF16 = StandardCharsets.UTF_16LE; + protected static final Charset STRING_CHARSET_UTF8 = StandardCharsets.UTF_8; private static final int[] EMPTY_INT_ARRAY = new int[0]; private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; @@ -90,7 +91,7 @@ public class ParserStream { long pos = input.skip(count); while (pos < count) { long skipped = input.skip(count - pos); - if (skipped == -1) { + if (skipped == 0) { throw new IOException("No data, can't skip " + count + " bytes"); } pos += skipped; diff --git a/jadx-core/src/main/java/jadx/core/xmlgen/ResTableBinaryParser.java b/jadx-core/src/main/java/jadx/core/xmlgen/ResTableBinaryParser.java index 08bf4d174..04c0b3eca 100644 --- a/jadx-core/src/main/java/jadx/core/xmlgen/ResTableBinaryParser.java +++ b/jadx-core/src/main/java/jadx/core/xmlgen/ResTableBinaryParser.java @@ -72,7 +72,8 @@ public class ResTableBinaryParser extends CommonBinaryParser implements IResTabl */ private final boolean useRawResName; private final RootNode root; - private final ResourceStorage resStorage = new ResourceStorage(); + + private ResourceStorage resStorage; private BinaryXMLStrings strings; public ResTableBinaryParser(RootNode root) { @@ -88,6 +89,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(); decodeTableChunk(); resStorage.finish(); if (LOG.isDebugEnabled()) { diff --git a/jadx-core/src/main/java/jadx/core/xmlgen/ResTableBinaryParserProvider.java b/jadx-core/src/main/java/jadx/core/xmlgen/ResTableBinaryParserProvider.java index eb3270b59..7cdd13ac8 100644 --- a/jadx-core/src/main/java/jadx/core/xmlgen/ResTableBinaryParserProvider.java +++ b/jadx-core/src/main/java/jadx/core/xmlgen/ResTableBinaryParserProvider.java @@ -7,19 +7,19 @@ import jadx.api.plugins.resources.IResTableParserProvider; import jadx.core.dex.nodes.RootNode; public class ResTableBinaryParserProvider implements IResTableParserProvider { - private IResTableParser parser; + private RootNode root; @Override public void init(RootNode root) { - parser = new ResTableBinaryParser(root); + this.root = root; } @Override - public synchronized @Nullable IResTableParser getParser(ResourceFile resFile) { + public @Nullable IResTableParser getParser(ResourceFile resFile) { String fileName = resFile.getOriginalName(); if (!fileName.endsWith(".arsc")) { return null; } - return parser; + return new ResTableBinaryParser(root); } } diff --git a/jadx-core/src/test/java/jadx/api/JadxDecompilerTest.java b/jadx-core/src/test/java/jadx/api/JadxDecompilerTest.java index 75685655f..c2ff24256 100644 --- a/jadx-core/src/test/java/jadx/api/JadxDecompilerTest.java +++ b/jadx-core/src/test/java/jadx/api/JadxDecompilerTest.java @@ -5,10 +5,12 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; +import java.util.List; import org.junit.jupiter.api.Test; import jadx.core.utils.files.FileUtils; +import jadx.core.xmlgen.ResContainer; import jadx.plugins.input.dex.DexInputPlugin; import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; @@ -54,6 +56,32 @@ public class JadxDecompilerTest { } } + @Test + public void testResourcesLoad() { + File sampleApk = getFileFromSampleDir("app-with-fake-dex.apk"); + File outDir = FileUtils.createTempDir("jadx-usage-example-2").toFile(); + + JadxArgs args = new JadxArgs(); + args.getInputFiles().add(sampleApk); + args.setOutDir(outDir); + args.setSkipSources(true); + try (JadxDecompiler jadx = new JadxDecompiler(args)) { + jadx.load(); + List resources = jadx.getResources(); + assertThat(resources).hasSize(8); + ResourceFile arsc = resources.stream() + .filter(r -> r.getType() == ResourceType.ARSC) + .findFirst().orElseThrow(); + ResContainer resContainer = arsc.loadContent(); + ResContainer xmlRes = resContainer.getSubFiles().stream() + .filter(r -> r.getName().equals("res/values/colors.xml")) + .findFirst().orElseThrow(); + assertThat(xmlRes.getText()) + .code() + .containsOne("#008577"); + } + } + private static final String TEST_SAMPLES_DIR = "test-samples/"; public static File getFileFromSampleDir(String fileName) { diff --git a/jadx-plugins/jadx-aab-input/src/main/java/jadx/plugins/input/aab/ResTableProtoParserProvider.java b/jadx-plugins/jadx-aab-input/src/main/java/jadx/plugins/input/aab/ResTableProtoParserProvider.java index cd06f975e..2f8c6ad69 100644 --- a/jadx-plugins/jadx-aab-input/src/main/java/jadx/plugins/input/aab/ResTableProtoParserProvider.java +++ b/jadx-plugins/jadx-aab-input/src/main/java/jadx/plugins/input/aab/ResTableProtoParserProvider.java @@ -9,19 +9,19 @@ import jadx.core.xmlgen.IResTableParser; import jadx.plugins.input.aab.parsers.ResTableProtoParser; public class ResTableProtoParserProvider implements IResTableParserProvider { - private ResTableProtoParser parser; + private RootNode root; @Override public void init(RootNode root) { - parser = new ResTableProtoParser(root); + this.root = root; } @Override - public synchronized @Nullable IResTableParser getParser(ResourceFile resFile) { + public @Nullable IResTableParser getParser(ResourceFile resFile) { String fileName = resFile.getOriginalName(); if (!fileName.endsWith("resources.pb")) { return null; } - return parser; + return new ResTableProtoParser(root); } } diff --git a/jadx-plugins/jadx-aab-input/src/main/java/jadx/plugins/input/aab/parsers/ResTableProtoParser.java b/jadx-plugins/jadx-aab-input/src/main/java/jadx/plugins/input/aab/parsers/ResTableProtoParser.java index f32b12f80..8d1f46578 100644 --- a/jadx-plugins/jadx-aab-input/src/main/java/jadx/plugins/input/aab/parsers/ResTableProtoParser.java +++ b/jadx-plugins/jadx-aab-input/src/main/java/jadx/plugins/input/aab/parsers/ResTableProtoParser.java @@ -26,7 +26,7 @@ import jadx.core.xmlgen.entry.ValuesParser; public class ResTableProtoParser extends CommonProtoParser implements IResTableParser { private final RootNode root; - private final ResourceStorage resStorage = new ResourceStorage(); + private ResourceStorage resStorage; public ResTableProtoParser(RootNode root) { this.root = root; @@ -34,6 +34,7 @@ public class ResTableProtoParser extends CommonProtoParser implements IResTableP @Override public void decode(InputStream inputStream) throws IOException { + resStorage = new ResourceStorage(); ResourceTable table = ResourceTable.parseFrom(FileUtils.streamToByteArray(inputStream)); for (Package p : table.getPackageList()) { parse(p);