fix: do not reopen zip file on every resource decode
This commit is contained in:
@@ -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();
|
||||
|
||||
+4
-3
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user