fix: disable usage of JDK Unsafe class in GSON (#2341)
Some checks failed
Build Artifacts / build (push) Has been cancelled
Build Artifacts / build-win-bundle (push) Has been cancelled
Build Test / tests (ubuntu-latest) (push) Has been cancelled
Build Test / tests (windows-latest) (push) Has been cancelled
CodeQL / Analyze (java) (push) Has been cancelled

This commit is contained in:
Skylot 2024-11-13 18:28:04 +00:00
parent 60dcdc7096
commit 1e1036c049
No known key found for this signature in database
GPG Key ID: 47A4975761262B6A
20 changed files with 138 additions and 108 deletions

View File

@ -9,7 +9,6 @@ import org.jetbrains.annotations.Nullable;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import jadx.api.ICodeInfo;
import jadx.api.ICodeWriter;
@ -32,13 +31,13 @@ import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.GsonUtils;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.JadxRuntimeException;
public class JsonCodeGen {
private static final Gson GSON = new GsonBuilder()
.setPrettyPrinting()
private static final Gson GSON = GsonUtils.defaultGsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES)
.disableHtmlEscaping()
.create();

View File

@ -11,7 +11,6 @@ import org.slf4j.LoggerFactory;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import jadx.api.JadxArgs;
import jadx.core.codegen.json.mapping.JsonClsMapping;
@ -24,14 +23,14 @@ import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.GsonUtils;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.FileUtils;
public class JsonMappingGen {
private static final Logger LOG = LoggerFactory.getLogger(JsonMappingGen.class);
private static final Gson GSON = new GsonBuilder()
.setPrettyPrinting()
private static final Gson GSON = GsonUtils.defaultGsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES)
.disableHtmlEscaping()
.create();

View File

@ -2,6 +2,9 @@ package jadx.core.utils;
import java.lang.reflect.Type;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
@ -11,11 +14,27 @@ import com.google.gson.JsonSerializer;
public class GsonUtils {
public static Gson buildGson() {
return defaultGsonBuilder().create();
}
public static GsonBuilder defaultGsonBuilder() {
return new GsonBuilder()
.disableJdkUnsafe()
.setPrettyPrinting();
}
public static void fillObjectFromJsonString(GsonBuilder builder, Object obj, String jsonStr) {
Class<?> type = obj.getClass();
Gson gson = builder.registerTypeAdapter(type, (InstanceCreator<?>) t -> obj).create();
gson.fromJson(jsonStr, type);
}
public static <T> InterfaceReplace<T> interfaceReplace(Class<T> replaceCls) {
return new InterfaceReplace<>(replaceCls);
}
private static final class InterfaceReplace<T> implements JsonSerializer<T>, JsonDeserializer<T> {
public static final class InterfaceReplace<T> implements JsonSerializer<T>, JsonDeserializer<T> {
private final Class<T> replaceCls;
private InterfaceReplace(Class<T> replaceCls) {

View File

@ -141,10 +141,8 @@ runtime {
"java.desktop",
"java.naming",
"java.xml",
// needed for "https" protocol to get plugins and updates
// needed for "https" protocol to download plugins and updates
"jdk.crypto.cryptoki",
// add Unsafe class, used by GSON
"jdk.unsupported",
)
jpackage {
imageOptions = listOf("--icon", "$projectDir/src/main/resources/logos/jadx-logo.ico")

View File

@ -19,10 +19,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import jadx.api.plugins.utils.CommonFileUtils;
import jadx.core.utils.GsonUtils;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.FileUtils;
@ -34,7 +34,7 @@ import jadx.gui.utils.files.JadxFiles;
public class CacheManager {
private static final Logger LOG = LoggerFactory.getLogger(CacheManager.class);
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private static final Gson GSON = GsonUtils.buildGson();
private static final Type CACHES_TYPE = new TypeToken<List<CacheEntry>>() {
}.getType();

View File

@ -22,17 +22,18 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import jadx.core.dex.nodes.ClassNode;
import jadx.gui.device.debugger.smali.Smali;
import jadx.gui.treemodel.JClass;
import static jadx.core.utils.GsonUtils.buildGson;
public class BreakpointManager {
private static final Logger LOG = LoggerFactory.getLogger(BreakpointManager.class);
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private static final Gson GSON = buildGson();
private static final Type TYPE_TOKEN = new TypeToken<Map<String, List<FileBreakpoint>>>() {
}.getType();
@ -159,9 +160,13 @@ public class BreakpointManager {
}
protected static class FileBreakpoint {
final String cls;
final String mth;
final long codeOffset;
public String cls;
public String mth;
public long codeOffset;
public FileBreakpoint() {
// needed for JSON deserialization
}
private FileBreakpoint(String cls, String mth, long codeOffset) {
this.cls = cls;

View File

@ -12,11 +12,9 @@ import org.apache.commons.text.StringEscapeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import jadx.api.ICodeInfo;
import jadx.api.impl.SimpleCodeInfo;
import jadx.core.utils.GsonUtils;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.panel.ContentPanel;
@ -30,8 +28,6 @@ public class QuarkReportNode extends JNode {
private static final Logger LOG = LoggerFactory.getLogger(QuarkReportNode.class);
private static final Gson GSON = new GsonBuilder().create();
private static final ImageIcon ICON = UiUtils.openSvgIcon("ui/quark");
private final Path reportFile;
@ -62,7 +58,7 @@ public class QuarkReportNode extends JNode {
try {
QuarkReportData data;
try (BufferedReader reader = Files.newBufferedReader(reportFile)) {
data = GSON.fromJson(reader, QuarkReportData.class);
data = GsonUtils.buildGson().fromJson(reader, QuarkReportData.class);
}
data.validate();
return new QuarkReportPanel(tabbedPane, this, data);

View File

@ -21,7 +21,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import jadx.api.JadxArgs;
import jadx.api.data.ICodeComment;
@ -34,7 +33,6 @@ import jadx.api.data.impl.JadxCodeRef;
import jadx.api.data.impl.JadxCodeRename;
import jadx.api.data.impl.JadxNodeRef;
import jadx.api.plugins.utils.CommonFileUtils;
import jadx.core.utils.GsonUtils;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.FileUtils;
import jadx.gui.cache.manager.CacheManager;
@ -44,6 +42,9 @@ import jadx.gui.ui.MainWindow;
import jadx.gui.ui.codearea.EditorViewState;
import jadx.gui.utils.RelativePathTypeAdapter;
import static jadx.core.utils.GsonUtils.defaultGsonBuilder;
import static jadx.core.utils.GsonUtils.interfaceReplace;
public class JadxProject {
private static final Logger LOG = LoggerFactory.getLogger(JadxProject.class);
@ -335,13 +336,12 @@ public class JadxProject {
}
private static Gson buildGson(Path basePath) {
return new GsonBuilder()
return defaultGsonBuilder()
.registerTypeHierarchyAdapter(Path.class, new RelativePathTypeAdapter(basePath))
.registerTypeAdapter(ICodeComment.class, GsonUtils.interfaceReplace(JadxCodeComment.class))
.registerTypeAdapter(ICodeRename.class, GsonUtils.interfaceReplace(JadxCodeRename.class))
.registerTypeAdapter(IJavaNodeRef.class, GsonUtils.interfaceReplace(JadxNodeRef.class))
.registerTypeAdapter(IJavaCodeRef.class, GsonUtils.interfaceReplace(JadxCodeRef.class))
.setPrettyPrinting()
.registerTypeAdapter(ICodeComment.class, interfaceReplace(JadxCodeComment.class))
.registerTypeAdapter(ICodeRename.class, interfaceReplace(JadxCodeRename.class))
.registerTypeAdapter(IJavaNodeRef.class, interfaceReplace(JadxNodeRef.class))
.registerTypeAdapter(IJavaCodeRef.class, interfaceReplace(JadxCodeRef.class))
.create();
}

View File

@ -15,9 +15,9 @@ import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator;
import com.google.gson.JsonObject;
import jadx.core.utils.GsonUtils;
import jadx.gui.utils.PathTypeAdapter;
import jadx.gui.utils.RectangleTypeAdapter;
@ -34,7 +34,7 @@ public class JadxSettingsAdapter {
|| f.hasModifier(Modifier.PUBLIC)
|| f.hasModifier(Modifier.TRANSIENT)
|| f.hasModifier(Modifier.STATIC)
|| (f.getAnnotation(GsonExclude.class) != null);
|| f.getAnnotation(GsonExclude.class) != null;
}
@Override
@ -42,12 +42,8 @@ public class JadxSettingsAdapter {
return false;
}
};
private static final GsonBuilder GSON_BUILDER = new GsonBuilder()
.setExclusionStrategies(EXCLUDE_FIELDS)
.registerTypeHierarchyAdapter(Path.class, PathTypeAdapter.singleton())
.registerTypeHierarchyAdapter(Rectangle.class, RectangleTypeAdapter.singleton())
.setPrettyPrinting();
private static final Gson GSON = GSON_BUILDER.create();
private static final Gson GSON = makeGsonBuilder().create();
private JadxSettingsAdapter() {
}
@ -92,17 +88,18 @@ public class JadxSettingsAdapter {
}
public static void fill(JadxSettings settings, String jsonStr) {
populate(GSON_BUILDER, jsonStr, JadxSettings.class, settings);
GsonUtils.fillObjectFromJsonString(makeGsonBuilder(), settings, jsonStr);
}
private static <T> void populate(GsonBuilder builder, String json, Class<T> type, final T into) {
builder.registerTypeAdapter(type, (InstanceCreator<T>) t -> into)
.create()
.fromJson(json, type);
private static GsonBuilder makeGsonBuilder() {
return GsonUtils.defaultGsonBuilder()
.setExclusionStrategies(EXCLUDE_FIELDS)
.registerTypeHierarchyAdapter(Path.class, PathTypeAdapter.singleton())
.registerTypeHierarchyAdapter(Rectangle.class, RectangleTypeAdapter.singleton());
}
/**
* Annotation for specifying fields that should not be be saved/loaded
* Annotation for specifying fields that should not be saved/loaded
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)

View File

@ -42,7 +42,6 @@ import javax.swing.WindowConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import jadx.api.CommentsLevel;
@ -57,6 +56,7 @@ import jadx.api.args.UseSourceNameAsClassNameAlias;
import jadx.api.plugins.events.JadxEvents;
import jadx.api.plugins.events.types.ReloadSettingsWindow;
import jadx.api.plugins.gui.ISettingsGroup;
import jadx.core.utils.GsonUtils;
import jadx.gui.settings.JadxSettings;
import jadx.gui.settings.JadxSettingsAdapter;
import jadx.gui.settings.JadxUpdateChannel;
@ -747,7 +747,7 @@ public class JadxSettingsWindow extends JDialog {
settingsJson.remove("lastOpenFilePath");
settingsJson.remove("lastSaveFilePath");
settingsJson.remove("recentProjects");
String settingsText = new GsonBuilder().setPrettyPrinting().create().toJson(settingsJson);
String settingsText = GsonUtils.buildGson().toJson(settingsJson);
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
StringSelection selection = new StringSelection(settingsText);
clipboard.setContents(selection, selection);

View File

@ -322,7 +322,7 @@ public class MainWindow extends JFrame {
if (!settings.isCheckForUpdates()) {
return;
}
JadxUpdate.check(settings.getJadxUpdateChannel(), release -> SwingUtilities.invokeLater(() -> {
new JadxUpdate().check(settings.getJadxUpdateChannel(), release -> SwingUtilities.invokeLater(() -> {
switch (settings.getJadxUpdateChannel()) {
case STABLE:
updateLink.setUrl(JadxUpdate.JADX_RELEASES_URL);

View File

@ -1,17 +1,17 @@
package jadx.gui.ui.codearea;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import jadx.core.utils.GsonUtils;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.action.ActionModel;
public class JsonPrettifyAction extends JNodeAction {
private static final long serialVersionUID = -2682529369671695550L;
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private static final Gson GSON = GsonUtils.buildGson();
public JsonPrettifyAction(CodeArea codeArea) {
super(ActionModel.JSON_PRETTIFY, codeArea);

View File

@ -1,11 +1,10 @@
package jadx.gui.update
import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
import io.github.oshai.kotlinlogging.KotlinLogging
import jadx.api.JadxDecompiler
import jadx.core.Jadx
import jadx.core.plugins.versions.VersionComparator
import jadx.core.utils.GsonUtils.buildGson
import jadx.gui.settings.JadxUpdateChannel
import org.jetbrains.kotlin.konan.file.use
import java.io.InputStreamReader
@ -13,25 +12,27 @@ import java.net.HttpURLConnection
import java.net.URI
import kotlin.reflect.KClass
data class Release(val name: String)
data class Release(var name: String = "")
data class ArtifactList(val artifacts: List<Artifact>)
data class ArtifactList(var artifacts: List<Artifact>? = null)
data class Artifact(
val name: String,
@SerializedName("workflow_run") val workflowRun: WorkflowRun,
var name: String = "",
@SerializedName("workflow_run") var workflowRun: WorkflowRun? = null,
)
data class WorkflowRun(
@SerializedName("head_branch") val branch: String,
@SerializedName("head_branch") var branch: String = "",
)
interface IUpdateCallback {
fun onUpdate(r: Release)
}
object JadxUpdate {
private val log = KotlinLogging.logger {}
class JadxUpdate(private val jadxVersion: String = Jadx.getVersion()) {
companion object {
private val LOG = KotlinLogging.logger {}
const val JADX_ARTIFACTS_URL = "https://nightly.link/skylot/jadx/workflows/build-artifacts/master"
const val JADX_RELEASES_URL = "https://github.com/skylot/jadx/releases"
@ -39,17 +40,23 @@ object JadxUpdate {
private const val GITHUB_API_URL = "https://api.github.com/repos/skylot/jadx"
private const val GITHUB_LATEST_ARTIFACTS_URL = "$GITHUB_API_URL/actions/artifacts?per_page=5&page=1"
private const val GITHUB_LATEST_RELEASE_URL = "$GITHUB_API_URL/releases/latest"
}
@JvmStatic
fun check(updateChannel: JadxUpdateChannel, callback: IUpdateCallback) {
if (jadxVersion == Jadx.VERSION_DEV) {
LOG.debug { "Ignore update check: development version" }
return
}
Thread {
try {
val release = checkForNewRelease(updateChannel)
if (release != null) {
callback.onUpdate(release)
} else {
LOG.info { "No updates found" }
}
} catch (e: Exception) {
log.warn(e) { "Jadx update error" }
LOG.warn(e) { "Jadx update error" }
}
}.apply {
name = "Jadx update thread"
@ -58,14 +65,9 @@ object JadxUpdate {
}
}
private fun checkForNewRelease(updateChannel: JadxUpdateChannel): Release? {
if (Jadx.isDevVersion()) {
log.debug { "Ignore check for update: development version" }
return null
}
log.info {
"Checking for updates... Update channel: $updateChannel, current version: ${JadxDecompiler.getVersion()}"
}
fun checkForNewRelease(updateChannel: JadxUpdateChannel): Release? {
LOG.info { "Checking for updates..." }
LOG.info { "Update channel: $updateChannel, current version: $jadxVersion" }
return when (updateChannel) {
JadxUpdateChannel.STABLE -> checkForNewStableRelease()
JadxUpdateChannel.UNSTABLE -> checkForNewUnstableRelease()
@ -73,28 +75,27 @@ object JadxUpdate {
}
private fun checkForNewStableRelease(): Release? {
val currentVersion = JadxDecompiler.getVersion()
if (currentVersion.startsWith("r")) {
if (jadxVersion.startsWith("r")) {
// current version is 'unstable', but update channel set to 'stable'
log.info { "Skip update check: can't compare unstable and stable versions" }
LOG.info { "Skip update check: can't compare unstable and stable versions" }
return null
}
val latestRelease = getAndParse(GITHUB_LATEST_RELEASE_URL, Release::class) ?: return null
if (VersionComparator.checkAndCompare(currentVersion, latestRelease.name) >= 0) return null
log.info { "Found new jadx version: ${latestRelease.name}" }
if (VersionComparator.checkAndCompare(jadxVersion, latestRelease.name) >= 0) return null
LOG.info { "Found new jadx version: ${latestRelease.name}" }
return latestRelease
}
private fun checkForNewUnstableRelease(): Release? {
val artifacts = getAndParse(GITHUB_LATEST_ARTIFACTS_URL, ArtifactList::class)
?.artifacts
?.filter { it.workflowRun.branch == "master" }
?.filter { it.workflowRun?.branch == "master" }
?: return null
if (artifacts.isEmpty()) return null
val latestVersion = artifacts[0].name.removePrefix("jadx-gui-").removePrefix("jadx-").substringBefore('-')
if (VersionComparator.checkAndCompare(JadxDecompiler.getVersion(), latestVersion) >= 0) return null
log.info { "Found new unstable version: $latestVersion" }
if (VersionComparator.checkAndCompare(jadxVersion, latestVersion) >= 0) return null
LOG.info { "Found new unstable version: $latestVersion" }
return Release(latestVersion)
}
@ -105,7 +106,7 @@ object JadxUpdate {
}
return con.inputStream.use { stream ->
InputStreamReader(stream).use { reader ->
Gson().fromJson(reader, klass.java)
buildGson().fromJson(reader, klass.java)
}
}
}

View File

@ -0,0 +1,27 @@
package jadx.gui.update
import jadx.gui.settings.JadxUpdateChannel
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
/**
* Test updates fetch.
* All tests disabled because of network requests, run manually on JadxUpdate changes
*/
@Disabled("Network requests")
class TestJadxUpdate {
@Test
fun testStableCheck() {
JadxUpdate("1.5.0").checkForNewRelease(JadxUpdateChannel.STABLE)?.let {
println("Latest release: $it")
}
}
@Test
fun testUnstableCheck() {
JadxUpdate("r2000").checkForNewRelease(JadxUpdateChannel.UNSTABLE)?.let {
println("Latest unstable: $it")
}
}
}

View File

@ -13,7 +13,6 @@ import java.util.function.Consumer;
import org.jetbrains.annotations.Nullable;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import jadx.api.plugins.utils.ZipSecurity;
@ -26,6 +25,7 @@ import jadx.plugins.tools.resolvers.github.data.Asset;
import jadx.plugins.tools.resolvers.github.data.Release;
import jadx.plugins.tools.utils.PluginUtils;
import static jadx.core.utils.GsonUtils.buildGson;
import static jadx.plugins.tools.utils.PluginFiles.PLUGINS_LIST_CACHE;
public class JadxPluginsList {
@ -101,10 +101,6 @@ public class JadxPluginsList {
loadedList = listCache;
}
private static Gson buildGson() {
return new GsonBuilder().setPrettyPrinting().create();
}
private Release fetchLatestRelease() {
LocationInfo latest = new LocationInfo("jadx-decompiler", "jadx-plugins-list", "list", null);
Release release = GithubTools.fetchRelease(latest);
@ -127,7 +123,7 @@ public class JadxPluginsList {
}
private static List<JadxPluginMetadata> loadListBundle(Path tmpListFile) {
Gson gson = new Gson();
Gson gson = buildGson();
List<JadxPluginMetadata> entries = new ArrayList<>();
ZipSecurity.readZipEntries(tmpListFile.toFile(), (entry, in) -> {
if (entry.getName().endsWith(".json")) {

View File

@ -19,9 +19,6 @@ import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import jadx.api.plugins.JadxPlugin;
import jadx.api.plugins.JadxPluginInfo;
import jadx.core.Jadx;
@ -36,6 +33,7 @@ import jadx.plugins.tools.resolvers.IJadxPluginResolver;
import jadx.plugins.tools.resolvers.ResolversRegistry;
import jadx.plugins.tools.utils.PluginUtils;
import static jadx.core.utils.GsonUtils.buildGson;
import static jadx.plugins.tools.utils.PluginFiles.DROPINS_DIR;
import static jadx.plugins.tools.utils.PluginFiles.INSTALLED_DIR;
import static jadx.plugins.tools.utils.PluginFiles.PLUGINS_JSON;
@ -307,12 +305,6 @@ public class JadxPluginsTools {
}
}
private static Gson buildGson() {
return new GsonBuilder()
.setPrettyPrinting()
.create();
}
private JadxInstalledPlugins loadPluginsJson() {
if (!Files.isRegularFile(PLUGINS_JSON)) {
JadxInstalledPlugins plugins = new JadxInstalledPlugins();

View File

@ -10,11 +10,12 @@ import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.stream.Collectors;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import jadx.plugins.tools.resolvers.github.data.Release;
import static jadx.core.utils.GsonUtils.buildGson;
public class GithubTools {
private static final String GITHUB_API_URL = "https://api.github.com/";
@ -59,7 +60,7 @@ public class GithubTools {
throw new RuntimeException("Request failed, url: " + url, e);
}
try (Reader reader = new InputStreamReader(con.getInputStream(), StandardCharsets.UTF_8)) {
return new Gson().fromJson(reader, type);
return buildGson().fromJson(reader, type);
} catch (Exception e) {
throw new RuntimeException("Failed to parse response, url: " + url, e);
}

View File

@ -15,7 +15,7 @@ class XapkCustomResourcesLoader : CustomResourcesLoader {
if (!XapkUtils.isSupported(manifest)) return false
val apkEntries = manifest.splitApks.map { it.file }.toHashSet()
ZipSecurity.visitZipEntries(file) { zip, entry ->
ZipSecurity.visitZipEntries<Any>(file) { zip, entry ->
if (apkEntries.contains(entry.name)) {
val tmpFile = ZipSecurity.getInputStreamForEntry(zip, entry).use {
CommonFileUtils.saveToTempFile(it, ".apk").toFile()

View File

@ -4,14 +4,14 @@ import com.google.gson.annotations.SerializedName
data class XapkManifest(
@SerializedName("xapk_version")
val xapkVersion: Int,
var xapkVersion: Int = 0,
@SerializedName("split_apks")
val splitApks: List<SplitApk>,
var splitApks: List<SplitApk> = listOf(),
) {
data class SplitApk(
@SerializedName("file")
val file: String,
var file: String = "",
@SerializedName("id")
val id: String,
var id: String = "",
)
}

View File

@ -1,7 +1,7 @@
package jadx.plugins.input.xapk
import com.google.gson.Gson
import jadx.api.plugins.utils.ZipSecurity
import jadx.core.utils.GsonUtils.buildGson
import jadx.core.utils.files.FileUtils
import jadx.core.utils.files.ZipFile
import java.io.File
@ -14,7 +14,7 @@ object XapkUtils {
ZipFile(file).use { zip ->
val manifestEntry = zip.getEntry("manifest.json") ?: return null
return InputStreamReader(ZipSecurity.getInputStreamForEntry(zip, manifestEntry)).use {
Gson().fromJson(it, XapkManifest::class.java)
buildGson().fromJson(it, XapkManifest::class.java)
}
}
} catch (e: Exception) {