fix(res): prevent duplication of ARSC entries (#2263)
This commit is contained in:
@@ -28,6 +28,7 @@ jadx-output/
|
||||
*-tmp/
|
||||
**/tmp/
|
||||
*.jobf
|
||||
*.jadx
|
||||
|
||||
*.class
|
||||
*.dump
|
||||
|
||||
@@ -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<ResourceFile> resources) {
|
||||
for (ResourceFile rf : resources) {
|
||||
if (rf.getType() == ResourceType.ARSC) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ResourceFile> 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("<color name=\"colorPrimary\">#008577</color>");
|
||||
}
|
||||
}
|
||||
|
||||
private static final String TEST_SAMPLES_DIR = "test-samples/";
|
||||
|
||||
public static File getFileFromSampleDir(String fileName) {
|
||||
|
||||
+4
-4
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user