fix: do not reopen zip file on every resource decode

This commit is contained in:
Skylot
2025-03-11 21:56:42 +00:00
parent d84f0389ec
commit 3209dbb7a4
6 changed files with 65 additions and 87 deletions
@@ -99,6 +99,7 @@ public final class JadxDecompiler implements Closeable {
private final List<ICodeLoader> customCodeLoaders = new ArrayList<>();
private final List<CustomResourcesLoader> customResourcesLoaders = new ArrayList<>();
private final Map<JadxPassType, List<JadxPass>> customPasses = new HashMap<>();
private final List<Closeable> closeableList = new ArrayList<>();
private IJadxEvents events = new JadxEventsImpl();
@@ -158,7 +159,7 @@ public final class JadxDecompiler implements Closeable {
loadedInputs.add(loader);
}
} catch (Exception e) {
throw new JadxRuntimeException("Failed to load code for plugin: " + plugin, e);
LOG.warn("Failed to load code for plugin: {}", plugin, e);
}
}
}
@@ -178,32 +179,26 @@ public final class JadxDecompiler implements Closeable {
@Override
public void close() {
reset();
closeInputs();
closeLoaders();
closeAll(loadedInputs);
closeAll(customCodeLoaders);
closeAll(customResourcesLoaders);
closeAll(closeableList);
args.close();
FileUtils.clearTempRootDir();
}
private void closeInputs() {
loadedInputs.forEach(load -> {
try {
load.close();
} catch (Exception e) {
LOG.error("Failed to close input", e);
}
});
loadedInputs.clear();
}
private void closeLoaders() {
for (CustomResourcesLoader resourcesLoader : customResourcesLoaders) {
try {
resourcesLoader.close();
} catch (Exception e) {
LOG.error("Failed to close resource loader: {}", resourcesLoader, e);
private void closeAll(List<? extends Closeable> list) {
try {
for (Closeable closeable : list) {
try {
closeable.close();
} catch (Exception e) {
LOG.warn("Fail to close '{}'", closeable, e);
}
}
} finally {
list.clear();
}
customResourcesLoaders.clear();
}
private void loadPlugins() {
@@ -706,6 +701,10 @@ public final class JadxDecompiler implements Closeable {
return zipReader;
}
public void addCloseable(Closeable closeable) {
closeableList.add(closeable);
}
@Override
public String toString() {
return "jadx decompiler " + getVersion();
@@ -2,38 +2,18 @@ package jadx.api;
import java.io.File;
import org.jetbrains.annotations.Nullable;
import jadx.core.xmlgen.ResContainer;
import jadx.core.xmlgen.entry.ResourceEntry;
import jadx.zip.IZipEntry;
public class ResourceFile {
public static final class ZipRef {
private final File zipFile;
private final String entryName;
public ZipRef(File zipFile, String entryName) {
this.zipFile = zipFile;
this.entryName = entryName;
}
public File getZipFile() {
return zipFile;
}
public String getEntryName() {
return entryName;
}
@Override
public String toString() {
return "ZipRef{" + zipFile + ", '" + entryName + "'}";
}
}
private final JadxDecompiler decompiler;
private final String name;
private final ResourceType type;
private ZipRef zipRef;
private @Nullable IZipEntry zipEntry;
private String deobfName;
public static ResourceFile createResourceFile(JadxDecompiler decompiler, File file, ResourceType type) {
@@ -73,10 +53,6 @@ public class ResourceFile {
return ResourcesLoader.loadContent(decompiler, this);
}
void setZipRef(ZipRef zipRef) {
this.zipRef = zipRef;
}
public boolean setAlias(ResourceEntry ri) {
StringBuilder sb = new StringBuilder();
sb.append("res/").append(ri.getTypeName()).append(ri.getConfig());
@@ -93,8 +69,12 @@ public class ResourceFile {
return false;
}
public ZipRef getZipRef() {
return zipRef;
public @Nullable IZipEntry getZipEntry() {
return zipEntry;
}
void setZipEntry(@Nullable IZipEntry zipEntry) {
this.zipEntry = zipEntry;
}
public JadxDecompiler getDecompiler() {
@@ -13,7 +13,6 @@ import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.ResourceFile.ZipRef;
import jadx.api.impl.SimpleCodeInfo;
import jadx.api.plugins.CustomResourcesLoader;
import jadx.api.plugins.resources.IResContainerFactory;
@@ -31,7 +30,6 @@ import jadx.core.xmlgen.ResContainer;
import jadx.core.xmlgen.ResTableBinaryParserProvider;
import jadx.zip.IZipEntry;
import jadx.zip.ZipContent;
import jadx.zip.ZipReader;
import static jadx.core.utils.files.FileUtils.READ_BUFFER_SIZE;
import static jadx.core.utils.files.FileUtils.copyStream;
@@ -40,21 +38,21 @@ import static jadx.core.utils.files.FileUtils.copyStream;
public final class ResourcesLoader implements IResourcesLoader {
private static final Logger LOG = LoggerFactory.getLogger(ResourcesLoader.class);
private final JadxDecompiler jadxRef;
private final JadxDecompiler decompiler;
private final List<IResTableParserProvider> resTableParserProviders = new ArrayList<>();
private final List<IResContainerFactory> resContainerFactories = new ArrayList<>();
private BinaryXMLParser binaryXmlParser;
ResourcesLoader(JadxDecompiler jadxRef) {
this.jadxRef = jadxRef;
ResourcesLoader(JadxDecompiler decompiler) {
this.decompiler = decompiler;
this.resTableParserProviders.add(new ResTableBinaryParserProvider());
}
List<ResourceFile> load(RootNode root) {
init(root);
List<File> inputFiles = jadxRef.getArgs().getInputFiles();
List<File> inputFiles = decompiler.getArgs().getInputFiles();
List<ResourceFile> list = new ArrayList<>(inputFiles.size());
for (File file : inputFiles) {
loadFile(list, file);
@@ -95,23 +93,16 @@ public final class ResourcesLoader implements IResourcesLoader {
public static <T> T decodeStream(ResourceFile rf, ResourceDecoder<T> decoder) throws JadxException {
try {
ZipRef zipRef = rf.getZipRef();
if (zipRef == null) {
IZipEntry zipEntry = rf.getZipEntry();
if (zipEntry != null) {
try (InputStream inputStream = zipEntry.getInputStream()) {
return decoder.decode(zipEntry.getUncompressedSize(), inputStream);
}
} else {
File file = new File(rf.getOriginalName());
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) {
return decoder.decode(file.length(), inputStream);
}
} else {
ZipReader zipReader = rf.getDecompiler().getZipReader();
try (ZipContent content = zipReader.open(zipRef.getZipFile())) {
IZipEntry entry = content.searchEntry(zipRef.getEntryName());
if (entry == null) {
throw new IOException("Zip entry not found: " + zipRef);
}
try (InputStream inputStream = entry.getInputStream()) {
return decoder.decode(entry.getUncompressedSize(), inputStream);
}
}
}
} catch (Exception e) {
throw new JadxException("Error decode: " + rf.getOriginalName(), e);
@@ -194,7 +185,7 @@ public final class ResourcesLoader implements IResourcesLoader {
}
// Try to load the resources with a custom loader first
for (CustomResourcesLoader loader : jadxRef.getCustomResourcesLoaders()) {
for (CustomResourcesLoader loader : decompiler.getCustomResourcesLoaders()) {
if (loader.load(this, list, file)) {
LOG.debug("Custom loader used for {}", file.getAbsolutePath());
return;
@@ -207,13 +198,19 @@ public final class ResourcesLoader implements IResourcesLoader {
public void defaultLoadFile(List<ResourceFile> list, File file, String subDir) {
if (FileUtils.isZipFile(file)) {
jadxRef.getZipReader().visitEntries(file, entry -> {
addEntry(list, file, entry, subDir);
return null;
});
try {
ZipContent zipContent = decompiler.getZipReader().open(file);
// do not close a zip now, entry content will be read later
decompiler.addCloseable(zipContent);
for (IZipEntry entry : zipContent.getEntries()) {
addEntry(list, file, entry, subDir);
}
} catch (Exception e) {
throw new RuntimeException("Failed to open zip file: " + file.getAbsolutePath(), e);
}
} else {
ResourceType type = ResourceType.getFileType(file.getAbsolutePath());
list.add(ResourceFile.createResourceFile(jadxRef, file, type));
list.add(ResourceFile.createResourceFile(decompiler, file, type));
}
}
@@ -223,9 +220,9 @@ public final class ResourcesLoader implements IResourcesLoader {
}
String name = entry.getName();
ResourceType type = ResourceType.getFileType(name);
ResourceFile rf = ResourceFile.createResourceFile(jadxRef, subDir + name, type);
ResourceFile rf = ResourceFile.createResourceFile(decompiler, subDir + name, type);
if (rf != null) {
rf.setZipRef(new ZipRef(zipFile, name));
rf.setZipEntry(entry);
list.add(rf);
}
}
@@ -238,7 +235,7 @@ public final class ResourcesLoader implements IResourcesLoader {
private synchronized BinaryXMLParser loadBinaryXmlParser() {
if (binaryXmlParser == null) {
binaryXmlParser = new BinaryXMLParser(jadxRef.getRoot());
binaryXmlParser = new BinaryXMLParser(decompiler.getRoot());
}
return binaryXmlParser;
}
@@ -27,6 +27,7 @@ import jadx.gui.ui.tab.TabbedPane;
import jadx.gui.utils.CertificateManager;
import jadx.gui.utils.NLS;
import jadx.gui.utils.UiUtils;
import jadx.zip.IZipEntry;
public class ApkSignature extends JNode {
private static final long serialVersionUID = -9121321926113143407L;
@@ -45,9 +46,9 @@ public class ApkSignature extends JNode {
File apkFile = null;
for (ResourceFile resFile : wrapper.getResources()) {
if (resFile.getType() == ResourceType.MANIFEST) {
ResourceFile.ZipRef zipRef = resFile.getZipRef();
if (zipRef != null) {
apkFile = zipRef.getZipFile();
IZipEntry zipEntry = resFile.getZipEntry();
if (zipEntry != null) {
apkFile = zipEntry.getZipFile();
break;
}
}
@@ -56,7 +56,7 @@ public class JRoot extends JNode {
String splitPathStr = Pattern.quote(File.separator);
for (ResourceFile rf : resources) {
String rfName;
if (rf.getZipRef() != null) {
if (rf.getZipEntry() != null) {
rfName = rf.getDeobfName();
} else {
rfName = new File(rf.getDeobfName()).getName();
@@ -12,6 +12,7 @@ import jadx.api.plugins.resources.IResContainerFactory;
import jadx.core.dex.nodes.RootNode;
import jadx.core.xmlgen.ResContainer;
import jadx.plugins.input.aab.parsers.ResXmlProtoParser;
import jadx.zip.IZipEntry;
public class ProtoXmlResContainerFactory implements IResContainerFactory {
private ResXmlProtoParser xmlParser;
@@ -27,11 +28,11 @@ public class ProtoXmlResContainerFactory implements IResContainerFactory {
if (type != ResourceType.XML && type != ResourceType.MANIFEST) {
return null;
}
ResourceFile.ZipRef ref = resFile.getZipRef();
if (ref == null) {
IZipEntry zipEntry = resFile.getZipEntry();
if (zipEntry == null) {
return null;
}
boolean isFromAab = ref.getZipFile().getPath().contains(".aab");
boolean isFromAab = zipEntry.getZipFile().getPath().contains(".aab");
if (!isFromAab) {
return null;
}