From f02b99a1d0f0ec150c4514a58b26d5eb90d4af71 Mon Sep 17 00:00:00 2001 From: Skylot Date: Sat, 6 Jan 2018 10:35:22 +0300 Subject: [PATCH] fix some sonar warnings --- .../main/java/jadx/api/ResourcesLoader.java | 95 +++----- jadx-core/src/main/java/jadx/core/Consts.java | 3 + .../main/java/jadx/core/codegen/ClassGen.java | 38 ++-- .../java/jadx/core/dex/info/ConstStorage.java | 16 +- .../dex/nodes/parser/DebugInfoParser.java | 12 +- .../core/dex/nodes/parser/EncValueParser.java | 14 +- .../java/jadx/core/dex/visitors/SaveCode.java | 23 +- .../java/jadx/core/utils/ErrorsCounter.java | 19 +- .../src/main/java/jadx/core/utils/Utils.java | 8 +- .../utils/android/AndroidResourcesUtils.java | 15 +- .../core/utils/android/DataInputDelegate.java | 128 +++++------ .../jadx/core/utils/android/ExtDataInput.java | 179 ++++++++------- .../utils/android/Res9patchStreamDecoder.java | 207 +++++++++--------- .../java/jadx/core/utils/files/FileUtils.java | 17 +- .../java/jadx/core/utils/files/InputFile.java | 82 +++---- .../java/jadx/core/utils/files/JavaToDex.java | 5 +- .../jadx/core/utils/files/ZipSecurity.java | 40 ++-- .../jadx/core/xmlgen/BinaryXMLParser.java | 60 ++--- .../jadx/core/xmlgen/ManifestAttributes.java | 2 +- .../java/jadx/core/xmlgen/ResContainer.java | 23 +- .../jadx/core/xmlgen/ResourceStorage.java | 16 +- .../java/jadx/core/xmlgen/XmlSecurity.java | 21 +- .../jadx/core/xmlgen/entry/ValuesParser.java | 18 +- .../main/java/jadx/gui/treemodel/JRoot.java | 8 - .../java/jadx/gui/ui/CommonSearchDialog.java | 28 +-- 25 files changed, 489 insertions(+), 588 deletions(-) diff --git a/jadx-core/src/main/java/jadx/api/ResourcesLoader.java b/jadx-core/src/main/java/jadx/api/ResourcesLoader.java index 61a039274..292f72a77 100644 --- a/jadx-core/src/main/java/jadx/api/ResourcesLoader.java +++ b/jadx-core/src/main/java/jadx/api/ResourcesLoader.java @@ -1,14 +1,5 @@ package jadx.api; -import jadx.api.ResourceFile.ZipRef; -import jadx.core.codegen.CodeWriter; -import jadx.core.utils.Utils; -import jadx.core.utils.exceptions.JadxException; -import jadx.core.utils.files.InputFile; -import jadx.core.utils.files.ZipSecurity; -import jadx.core.xmlgen.ResContainer; -import jadx.core.xmlgen.ResTableParser; - import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.File; @@ -24,8 +15,16 @@ import java.util.zip.ZipFile; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import jadx.api.ResourceFile.ZipRef; +import jadx.core.codegen.CodeWriter; +import jadx.core.utils.Utils; +import jadx.core.utils.exceptions.JadxException; +import jadx.core.utils.files.InputFile; +import jadx.core.utils.files.ZipSecurity; +import jadx.core.xmlgen.ResContainer; +import jadx.core.xmlgen.ResTableParser; + import static jadx.core.utils.files.FileUtils.READ_BUFFER_SIZE; -import static jadx.core.utils.files.FileUtils.close; import static jadx.core.utils.files.FileUtils.copyStream; // TODO: move to core package @@ -53,44 +52,30 @@ public final class ResourcesLoader { } public static ResContainer decodeStream(ResourceFile rf, ResourceDecoder decoder) throws JadxException { - ZipFile zipFile = null; - InputStream inputStream = null; - ResContainer result; try { - long size; ZipRef zipRef = rf.getZipRef(); if (zipRef == null) { File file = new File(rf.getName()); - inputStream = new BufferedInputStream(new FileInputStream(file)); - size = file.length(); + try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) { + return decoder.decode(file.length(), inputStream); + } } else { - zipFile = new ZipFile(zipRef.getZipFile()); - ZipEntry entry = zipFile.getEntry(zipRef.getEntryName()); - - if(!ZipSecurity.isValidZipEntry(entry)) { - return null; + try (ZipFile zipFile = new ZipFile(zipRef.getZipFile())) { + ZipEntry entry = zipFile.getEntry(zipRef.getEntryName()); + if (entry == null) { + throw new IOException("Zip entry not found: " + zipRef); + } + if (!ZipSecurity.isValidZipEntry(entry)) { + return null; + } + try (InputStream inputStream = new BufferedInputStream(zipFile.getInputStream(entry))) { + return decoder.decode(entry.getSize(), inputStream); + } } - - if (entry == null) { - throw new IOException("Zip entry not found: " + zipRef); - } - inputStream = new BufferedInputStream(zipFile.getInputStream(entry)); - size = entry.getSize(); } - result = decoder.decode(size, inputStream); } catch (Exception e) { throw new JadxException("Error decode: " + rf.getName(), e); - } finally { - try { - if (zipFile != null) { - zipFile.close(); - } - } catch (Exception e) { - LOG.error("Error close zip file: {}", rf.getName(), e); - } - close(inputStream); } - return result; } static ResContainer loadContent(JadxDecompiler jadxRef, ResourceFile rf) { @@ -106,7 +91,7 @@ public final class ResourcesLoader { } private static ResContainer loadContent(JadxDecompiler jadxRef, ResourceFile rf, - InputStream inputStream, long size) throws IOException { + InputStream inputStream, long size) throws IOException { switch (rf.getType()) { case MANIFEST: case XML: @@ -118,39 +103,31 @@ public final class ResourcesLoader { case IMG: return ResContainer.singleImageFile(rf.getName(), inputStream); + + default: + if (size > LOAD_SIZE_LIMIT) { + return ResContainer.singleFile(rf.getName(), + new CodeWriter().add("File too big, size: " + String.format("%.2f KB", size / 1024.))); + } + return ResContainer.singleFile(rf.getName(), loadToCodeWriter(inputStream)); } - if (size > LOAD_SIZE_LIMIT) { - return ResContainer.singleFile(rf.getName(), - new CodeWriter().add("File too big, size: " + String.format("%.2f KB", size / 1024.))); - } - return ResContainer.singleFile(rf.getName(), loadToCodeWriter(inputStream)); } private void loadFile(List list, File file) { if (file == null) { return; } - ZipFile zip = null; - try { - zip = new ZipFile(file); + try (ZipFile zip = new ZipFile(file)) { Enumeration entries = zip.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); - if(ZipSecurity.isValidZipEntry(entry)) { + if (ZipSecurity.isValidZipEntry(entry)) { addEntry(list, file, entry); } } - } catch (IOException e) { + } catch (Exception e) { LOG.debug("Not a zip file: {}", file.getAbsolutePath()); addResourceFile(list, file); - } finally { - if (zip != null) { - try { - zip.close(); - } catch (Exception e) { - LOG.error("Zip file close error: {}", file.getAbsolutePath(), e); - } - } } } @@ -158,7 +135,7 @@ public final class ResourcesLoader { String name = file.getAbsolutePath(); ResourceType type = ResourceType.getFileType(name); ResourceFile rf = ResourceFile.createResourceFileInstance(jadxRef, name, type); - if(rf != null) { + if (rf != null) { list.add(rf); } } @@ -170,7 +147,7 @@ public final class ResourcesLoader { String name = entry.getName(); ResourceType type = ResourceType.getFileType(name); ResourceFile rf = ResourceFile.createResourceFileInstance(jadxRef, name, type); - if(rf != null) { + if (rf != null) { rf.setZipRef(new ZipRef(zipFile, name)); list.add(rf); } diff --git a/jadx-core/src/main/java/jadx/core/Consts.java b/jadx-core/src/main/java/jadx/core/Consts.java index a63aba92f..ab35e8a2e 100644 --- a/jadx-core/src/main/java/jadx/core/Consts.java +++ b/jadx-core/src/main/java/jadx/core/Consts.java @@ -21,4 +21,7 @@ public class Consts { public static final String ANONYMOUS_CLASS_PREFIX = "AnonymousClass"; public static final String MTH_TOSTRING_SIGNATURE = "toString()Ljava/lang/String;"; + + private Consts() { + } } diff --git a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java index f0c4983c3..1b4a89bd6 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java @@ -1,11 +1,24 @@ package jadx.core.codegen; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import com.android.dx.rop.code.AccessFlags; + import jadx.api.IJadxArgs; import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.AttrNode; import jadx.core.dex.attributes.nodes.EnumClassAttr; import jadx.core.dex.attributes.nodes.EnumClassAttr.EnumField; +import jadx.core.dex.attributes.nodes.LineAttrNode; import jadx.core.dex.attributes.nodes.SourceFileAttr; import jadx.core.dex.info.AccessInfo; import jadx.core.dex.info.ClassInfo; @@ -23,30 +36,7 @@ import jadx.core.utils.ErrorsCounter; import jadx.core.utils.Utils; import jadx.core.utils.exceptions.CodegenException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.android.dx.rop.code.AccessFlags; - public class ClassGen { - private static final Logger LOG = LoggerFactory.getLogger(ClassGen.class); - - public static final Comparator METHOD_LINE_COMPARATOR = new Comparator() { - @Override - public int compare(MethodNode a, MethodNode b) { - return Utils.compare(a.getSourceLine(), b.getSourceLine()); - } - }; private final ClassNode cls; private final ClassGen parentGen; @@ -276,7 +266,7 @@ public class ClassGen { private static List sortMethodsByLine(List methods) { List out = new ArrayList<>(methods); - Collections.sort(out, METHOD_LINE_COMPARATOR); + out.sort(Comparator.comparingInt(LineAttrNode::getSourceLine)); return out; } diff --git a/jadx-core/src/main/java/jadx/core/dex/info/ConstStorage.java b/jadx-core/src/main/java/jadx/core/dex/info/ConstStorage.java index 89bf84356..5468be17f 100644 --- a/jadx-core/src/main/java/jadx/core/dex/info/ConstStorage.java +++ b/jadx-core/src/main/java/jadx/core/dex/info/ConstStorage.java @@ -1,5 +1,13 @@ package jadx.core.dex.info; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.jetbrains.annotations.Nullable; + import jadx.api.IJadxArgs; import jadx.core.dex.attributes.AType; import jadx.core.dex.instructions.args.LiteralArg; @@ -10,14 +18,6 @@ import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.ResRefField; import jadx.core.dex.nodes.parser.FieldInitAttr; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.jetbrains.annotations.Nullable; - public class ConstStorage { private static final class ValueStorage { diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/parser/DebugInfoParser.java b/jadx-core/src/main/java/jadx/core/dex/nodes/parser/DebugInfoParser.java index aef236eb4..3e9ad9982 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/parser/DebugInfoParser.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/parser/DebugInfoParser.java @@ -1,5 +1,9 @@ package jadx.core.dex.nodes.parser; +import java.util.List; + +import com.android.dex.Dex.Section; + import jadx.core.dex.attributes.nodes.SourceFileAttr; import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.RegisterArg; @@ -9,16 +13,8 @@ import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.utils.exceptions.DecodeException; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.android.dex.Dex.Section; - public class DebugInfoParser { - private static final Logger LOG = LoggerFactory.getLogger(DebugInfoParser.class); private static final int DBG_END_SEQUENCE = 0x00; private static final int DBG_ADVANCE_PC = 0x01; private static final int DBG_ADVANCE_LINE = 0x02; diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/parser/EncValueParser.java b/jadx-core/src/main/java/jadx/core/dex/nodes/parser/EncValueParser.java index d5db18448..f1c3ba622 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/parser/EncValueParser.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/parser/EncValueParser.java @@ -1,16 +1,16 @@ package jadx.core.dex.nodes.parser; -import jadx.core.dex.info.FieldInfo; -import jadx.core.dex.info.MethodInfo; -import jadx.core.dex.nodes.DexNode; -import jadx.core.utils.exceptions.DecodeException; - import java.util.ArrayList; import java.util.List; import com.android.dex.Dex.Section; import com.android.dex.Leb128; +import jadx.core.dex.info.FieldInfo; +import jadx.core.dex.info.MethodInfo; +import jadx.core.dex.nodes.DexNode; +import jadx.core.utils.exceptions.DecodeException; + public class EncValueParser { private static final int ENCODED_BYTE = 0x00; @@ -91,8 +91,10 @@ public class EncValueParser { case ENCODED_ANNOTATION: return AnnotationsParser.readAnnotation(dex, in, false); + + default: + throw new DecodeException("Unknown encoded value type: 0x" + Integer.toHexString(type)); } - throw new DecodeException("Unknown encoded value type: 0x" + Integer.toHexString(type)); } private int parseUnsignedInt(int byteCount) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/SaveCode.java b/jadx-core/src/main/java/jadx/core/dex/visitors/SaveCode.java index ec40de3c0..9c2a08907 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/SaveCode.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/SaveCode.java @@ -1,32 +1,19 @@ package jadx.core.dex.visitors; +import java.io.File; + import jadx.api.IJadxArgs; import jadx.core.codegen.CodeWriter; import jadx.core.dex.nodes.ClassNode; -import jadx.core.utils.exceptions.CodegenException; -import jadx.core.utils.files.ZipSecurity; -import java.io.File; +public class SaveCode { -public class SaveCode extends AbstractVisitor { - private final File dir; - private final IJadxArgs args; - - public SaveCode(File dir, IJadxArgs args) { - this.args = args; - this.dir = dir; - } - - @Override - public boolean visit(ClassNode cls) throws CodegenException { - save(dir, args, cls); - return false; - } + private SaveCode() {} public static void save(File dir, IJadxArgs args, ClassNode cls) { CodeWriter clsCode = cls.getCode(); String fileName = cls.getClassInfo().getFullPath() + ".java"; - if (args.isFallbackMode()) { + if (args.isFallbackMode()) { fileName += ".jadx"; } clsCode.save(dir, fileName); diff --git a/jadx-core/src/main/java/jadx/core/utils/ErrorsCounter.java b/jadx-core/src/main/java/jadx/core/utils/ErrorsCounter.java index e0f27d0f3..0a08b7823 100644 --- a/jadx-core/src/main/java/jadx/core/utils/ErrorsCounter.java +++ b/jadx-core/src/main/java/jadx/core/utils/ErrorsCounter.java @@ -1,5 +1,14 @@ package jadx.core.utils; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.IAttributeNode; import jadx.core.dex.attributes.nodes.JadxErrorAttr; @@ -7,16 +16,6 @@ import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.utils.exceptions.JadxOverflowException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class ErrorsCounter { private static final Logger LOG = LoggerFactory.getLogger(ErrorsCounter.class); diff --git a/jadx-core/src/main/java/jadx/core/utils/Utils.java b/jadx-core/src/main/java/jadx/core/utils/Utils.java index afc4d7db2..019e80f83 100644 --- a/jadx-core/src/main/java/jadx/core/utils/Utils.java +++ b/jadx-core/src/main/java/jadx/core/utils/Utils.java @@ -1,12 +1,12 @@ package jadx.core.utils; -import jadx.api.JadxDecompiler; - import java.io.PrintWriter; import java.io.StringWriter; import java.util.Arrays; import java.util.Iterator; +import jadx.api.JadxDecompiler; + public class Utils { public static final String JADX_API_PACKAGE = JadxDecompiler.class.getPackage().getName(); @@ -95,8 +95,4 @@ public class Utils { th.setStackTrace(Arrays.copyOfRange(stackTrace, 0, cutIndex)); } } - - public static int compare(int x, int y) { - return x < y ? -1 : x == y ? 0 : 1; - } } diff --git a/jadx-core/src/main/java/jadx/core/utils/android/AndroidResourcesUtils.java b/jadx-core/src/main/java/jadx/core/utils/android/AndroidResourcesUtils.java index 48aa3d1f6..0e3de84b0 100644 --- a/jadx-core/src/main/java/jadx/core/utils/android/AndroidResourcesUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/android/AndroidResourcesUtils.java @@ -1,5 +1,10 @@ package jadx.core.utils.android; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import jadx.core.codegen.ClassGen; import jadx.core.codegen.CodeWriter; import jadx.core.dex.info.ClassInfo; @@ -7,17 +12,15 @@ import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.DexNode; import jadx.core.dex.nodes.RootNode; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - /** * Android resources specific handlers */ public class AndroidResourcesUtils { private static final Logger LOG = LoggerFactory.getLogger(AndroidResourcesUtils.class); + private AndroidResourcesUtils() { + } + public static ClassNode searchAppResClass(RootNode root) { String appPackage = root.getAppPackage(); String fullName = appPackage != null ? appPackage + ".R" : "R"; @@ -49,7 +52,7 @@ public class AndroidResourcesUtils { private static ClassNode makeClass(RootNode root, String clsName) { List dexNodes = root.getDexNodes(); - if (dexNodes.size() == 0) { + if (dexNodes.isEmpty()) { return null; } DexNode firstDex = dexNodes.get(0); diff --git a/jadx-core/src/main/java/jadx/core/utils/android/DataInputDelegate.java b/jadx-core/src/main/java/jadx/core/utils/android/DataInputDelegate.java index 6597fb1b2..3b64f59ec 100644 --- a/jadx-core/src/main/java/jadx/core/utils/android/DataInputDelegate.java +++ b/jadx-core/src/main/java/jadx/core/utils/android/DataInputDelegate.java @@ -1,17 +1,17 @@ /** - * Copyright 2014 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2014 Ryszard Wiśniewski + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package jadx.core.utils.android; @@ -22,70 +22,70 @@ import java.io.IOException; /** * @author Ryszard Wiśniewski */ -abstract public class DataInputDelegate implements DataInput { - protected final DataInput mDelegate; +public abstract class DataInputDelegate implements DataInput { + protected final DataInput mDelegate; - public DataInputDelegate(DataInput delegate) { - this.mDelegate = delegate; - } + public DataInputDelegate(DataInput delegate) { + this.mDelegate = delegate; + } - public int skipBytes(int n) throws IOException { - return mDelegate.skipBytes(n); - } + public int skipBytes(int n) throws IOException { + return mDelegate.skipBytes(n); + } - public int readUnsignedShort() throws IOException { - return mDelegate.readUnsignedShort(); - } + public int readUnsignedShort() throws IOException { + return mDelegate.readUnsignedShort(); + } - public int readUnsignedByte() throws IOException { - return mDelegate.readUnsignedByte(); - } + public int readUnsignedByte() throws IOException { + return mDelegate.readUnsignedByte(); + } - public String readUTF() throws IOException { - return mDelegate.readUTF(); - } + public String readUTF() throws IOException { + return mDelegate.readUTF(); + } - public short readShort() throws IOException { - return mDelegate.readShort(); - } + public short readShort() throws IOException { + return mDelegate.readShort(); + } - public long readLong() throws IOException { - return mDelegate.readLong(); - } + public long readLong() throws IOException { + return mDelegate.readLong(); + } - public String readLine() throws IOException { - return mDelegate.readLine(); - } + public String readLine() throws IOException { + return mDelegate.readLine(); + } - public int readInt() throws IOException { - return mDelegate.readInt(); - } + public int readInt() throws IOException { + return mDelegate.readInt(); + } - public void readFully(byte[] b, int off, int len) throws IOException { - mDelegate.readFully(b, off, len); - } + public void readFully(byte[] b, int off, int len) throws IOException { + mDelegate.readFully(b, off, len); + } - public void readFully(byte[] b) throws IOException { - mDelegate.readFully(b); - } + public void readFully(byte[] b) throws IOException { + mDelegate.readFully(b); + } - public float readFloat() throws IOException { - return mDelegate.readFloat(); - } + public float readFloat() throws IOException { + return mDelegate.readFloat(); + } - public double readDouble() throws IOException { - return mDelegate.readDouble(); - } + public double readDouble() throws IOException { + return mDelegate.readDouble(); + } - public char readChar() throws IOException { - return mDelegate.readChar(); - } + public char readChar() throws IOException { + return mDelegate.readChar(); + } - public byte readByte() throws IOException { - return mDelegate.readByte(); - } + public byte readByte() throws IOException { + return mDelegate.readByte(); + } - public boolean readBoolean() throws IOException { - return mDelegate.readBoolean(); - } -} \ No newline at end of file + public boolean readBoolean() throws IOException { + return mDelegate.readBoolean(); + } +} diff --git a/jadx-core/src/main/java/jadx/core/utils/android/ExtDataInput.java b/jadx-core/src/main/java/jadx/core/utils/android/ExtDataInput.java index 5a17a6d11..9c7682827 100644 --- a/jadx-core/src/main/java/jadx/core/utils/android/ExtDataInput.java +++ b/jadx-core/src/main/java/jadx/core/utils/android/ExtDataInput.java @@ -1,111 +1,108 @@ /** - * Copyright 2014 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2014 Ryszard Wiśniewski + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package jadx.core.utils.android; -import java.io.*; +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; /** * @author Ryszard Wiśniewski */ public class ExtDataInput extends DataInputDelegate { - public ExtDataInput(InputStream in) { - this((DataInput) new DataInputStream(in)); - } + public ExtDataInput(InputStream in) { + this((DataInput) new DataInputStream(in)); + } - public ExtDataInput(DataInput delegate) { - super(delegate); - } + public ExtDataInput(DataInput delegate) { + super(delegate); + } - public int[] readIntArray(int length) throws IOException { - int[] array = new int[length]; - for(int i = 0; i < length; i++) { - array[i] = readInt(); - } - return array; - } + public int[] readIntArray(int length) throws IOException { + int[] array = new int[length]; + for (int i = 0; i < length; i++) { + array[i] = readInt(); + } + return array; + } - public void skipInt() throws IOException { - skipBytes(4); - } + public void skipInt() throws IOException { + skipBytes(4); + } - public void skipCheckInt(int expected) throws IOException { - int got = readInt(); - if (got != expected) { - throw new IOException(String.format( - "Expected: 0x%08x, got: 0x%08x", expected, got)); - } - } + public void skipCheckInt(int expected) throws IOException { + int got = readInt(); + if (got != expected) { + throw new IOException(String.format("Expected: 0x%08x, got: 0x%08x", expected, got)); + } + } - public void skipCheckShort(short expected) throws IOException { - short got = readShort(); - if (got != expected) { - throw new IOException(String.format( - "Expected: 0x%08x, got: 0x%08x", expected, got)); - } - } + public void skipCheckShort(short expected) throws IOException { + short got = readShort(); + if (got != expected) { + throw new IOException(String.format("Expected: 0x%08x, got: 0x%08x", expected, got)); + } + } - public void skipCheckByte(byte expected) throws IOException { - byte got = readByte(); - if (got != expected) { - throw new IOException(String.format( - "Expected: 0x%08x, got: 0x%08x", expected, got)); - } - } + public void skipCheckByte(byte expected) throws IOException { + byte got = readByte(); + if (got != expected) { + throw new IOException(String.format("Expected: 0x%08x, got: 0x%08x", expected, got)); + } + } - public void skipCheckChunkTypeInt(int expected, int possible) throws IOException { - int got = readInt(); + public void skipCheckChunkTypeInt(int expected, int possible) throws IOException { + int got = readInt(); + if (got == possible) { + skipCheckChunkTypeInt(expected, -1); + } else if (got != expected) { + throw new IOException(String.format("Expected: 0x%08x, got: 0x%08x", expected, got)); + } + } - if (got == possible) { - skipCheckChunkTypeInt(expected, -1); - } else if (got != expected) { - throw new IOException(String.format("Expected: 0x%08x, got: 0x%08x", expected, got)); - } - } + /** + * The general contract of DataInput doesn't guarantee all the bytes requested will be skipped + * and failure can occur for many reasons. We override this to try harder to skip all the bytes + * requested (this is similar to DataInputStream's wrapper). + */ + @Override + public final int skipBytes(int n) throws IOException { + int total = 0; + int cur = 0; - /** - * The general contract of DataInput doesn't guarantee all the bytes requested will be skipped - * and failure can occur for many reasons. We override this to try harder to skip all the bytes - * requested (this is similar to DataInputStream's wrapper). - */ - public final int skipBytes(int n) throws IOException { - int total = 0; - int cur = 0; + while ((total < n) && ((cur = super.skipBytes(n - total)) > 0)) { + total += cur; + } + return total; + } - while ((total < n) && ((cur = (int) super.skipBytes(n - total)) > 0)) { - total += cur; - } - - return total; - } - - public String readNullEndedString(int length, boolean fixed) - throws IOException { - StringBuilder string = new StringBuilder(16); - while(length-- != 0) { - short ch = readShort(); - if (ch == 0) { - break; - } - string.append((char) ch); - } - if (fixed) { - skipBytes(length * 2); - } - - return string.toString(); - } -} \ No newline at end of file + public String readNullEndedString(int length, boolean fixed) throws IOException { + StringBuilder string = new StringBuilder(16); + while (length-- != 0) { + short ch = readShort(); + if (ch == 0) { + break; + } + string.append((char) ch); + } + if (fixed) { + skipBytes(length * 2); + } + return string.toString(); + } +} diff --git a/jadx-core/src/main/java/jadx/core/utils/android/Res9patchStreamDecoder.java b/jadx-core/src/main/java/jadx/core/utils/android/Res9patchStreamDecoder.java index 9d845c421..c57410eef 100644 --- a/jadx-core/src/main/java/jadx/core/utils/android/Res9patchStreamDecoder.java +++ b/jadx-core/src/main/java/jadx/core/utils/android/Res9patchStreamDecoder.java @@ -1,23 +1,22 @@ /** - * Copyright 2014 Ryszard Wiśniewski - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2014 Ryszard Wiśniewski + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package jadx.core.utils.android; -import org.apache.commons.io.IOUtils; - +import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.DataInput; @@ -25,7 +24,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import javax.imageio.ImageIO; +import org.apache.commons.io.IOUtils; import jadx.core.utils.exceptions.JadxException; @@ -34,105 +33,107 @@ import jadx.core.utils.exceptions.JadxException; */ public class Res9patchStreamDecoder { - public void decode(InputStream in, OutputStream out) throws JadxException { - try { - byte[] data = IOUtils.toByteArray(in); + public void decode(InputStream in, OutputStream out) throws JadxException { + try { + byte[] data = IOUtils.toByteArray(in); - BufferedImage im = ImageIO.read(new ByteArrayInputStream(data)); - int w = im.getWidth(), h = im.getHeight(); + BufferedImage im = ImageIO.read(new ByteArrayInputStream(data)); + int w = im.getWidth(); + int h = im.getHeight(); - BufferedImage im2 = new BufferedImage(w+2, h+2, BufferedImage.TYPE_INT_ARGB); - im2.createGraphics().drawImage(im, 1, 1, w, h, null); + BufferedImage im2 = new BufferedImage(w + 2, h + 2, BufferedImage.TYPE_INT_ARGB); + im2.createGraphics().drawImage(im, 1, 1, w, h, null); - NinePatch np = getNinePatch(data); - drawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight); - drawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom); + NinePatch np = getNinePatch(data); + drawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight); + drawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom); - int[] xDivs = np.xDivs; - for (int i = 0; i < xDivs.length; i += 2) { - drawHLine(im2, 0, xDivs[i] + 1, xDivs[i + 1]); - } + int[] xDivs = np.xDivs; + for (int i = 0; i < xDivs.length; i += 2) { + drawHLine(im2, 0, xDivs[i] + 1, xDivs[i + 1]); + } - int[] yDivs = np.yDivs; - for (int i = 0; i < yDivs.length; i += 2) { - drawVLine(im2, 0, yDivs[i] + 1, yDivs[i + 1]); - } + int[] yDivs = np.yDivs; + for (int i = 0; i < yDivs.length; i += 2) { + drawVLine(im2, 0, yDivs[i] + 1, yDivs[i + 1]); + } - ImageIO.write(im2, "png", out); - } catch (IOException | NullPointerException ex) { - throw new JadxException(ex.toString()); - } - } + ImageIO.write(im2, "png", out); + } catch (IOException | NullPointerException ex) { + throw new JadxException(ex.toString()); + } + } - private NinePatch getNinePatch(byte[] data) throws JadxException, - IOException { - ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data)); - find9patchChunk(di); - return NinePatch.decode(di); - } + private NinePatch getNinePatch(byte[] data) throws JadxException, IOException { + ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data)); + find9patchChunk(di); + return NinePatch.decode(di); + } - private void find9patchChunk(DataInput di) throws JadxException, - IOException { - di.skipBytes(8); - while (true) { - int size; - try { - size = di.readInt(); - } catch (IOException ex) { - throw new JadxException("Cant find nine patch chunk", ex); - } - if (di.readInt() == NP_CHUNK_TYPE) { - return; - } - di.skipBytes(size + 4); - } - } + private void find9patchChunk(DataInput di) throws JadxException, IOException { + di.skipBytes(8); + while (true) { + int size; + try { + size = di.readInt(); + } catch (IOException ex) { + throw new JadxException("Cant find nine patch chunk", ex); + } + if (di.readInt() == NP_CHUNK_TYPE) { + return; + } + di.skipBytes(size + 4); + } + } - private void drawHLine(BufferedImage im, int y, int x1, int x2) { - for (int x = x1; x <= x2; x++) { - im.setRGB(x, y, NP_COLOR); - } - } + private void drawHLine(BufferedImage im, int y, int x1, int x2) { + for (int x = x1; x <= x2; x++) { + im.setRGB(x, y, NP_COLOR); + } + } - private void drawVLine(BufferedImage im, int x, int y1, int y2) { - for (int y = y1; y <= y2; y++) { - im.setRGB(x, y, NP_COLOR); - } - } + private void drawVLine(BufferedImage im, int x, int y1, int y2) { + for (int y = y1; y <= y2; y++) { + im.setRGB(x, y, NP_COLOR); + } + } - private static final int NP_CHUNK_TYPE = 0x6e705463; // npTc - private static final int NP_COLOR = 0xff000000; + private static final int NP_CHUNK_TYPE = 0x6e705463; // npTc + private static final int NP_COLOR = 0xff000000; - private static class NinePatch { - public final int padLeft, padRight, padTop, padBottom; - public final int[] xDivs, yDivs; + private static class NinePatch { + public final int padLeft; + public final int padRight; + public final int padTop; + public final int padBottom; + public final int[] xDivs; + public final int[] yDivs; - public NinePatch(int padLeft, int padRight, int padTop, int padBottom, - int[] xDivs, int[] yDivs) { - this.padLeft = padLeft; - this.padRight = padRight; - this.padTop = padTop; - this.padBottom = padBottom; - this.xDivs = xDivs; - this.yDivs = yDivs; - } + public NinePatch(int padLeft, int padRight, int padTop, int padBottom, + int[] xDivs, int[] yDivs) { + this.padLeft = padLeft; + this.padRight = padRight; + this.padTop = padTop; + this.padBottom = padBottom; + this.xDivs = xDivs; + this.yDivs = yDivs; + } - public static NinePatch decode(ExtDataInput di) throws IOException { - di.skipBytes(1); - byte numXDivs = di.readByte(); - byte numYDivs = di.readByte(); - di.skipBytes(1); - di.skipBytes(8); - int padLeft = di.readInt(); - int padRight = di.readInt(); - int padTop = di.readInt(); - int padBottom = di.readInt(); - di.skipBytes(4); - int[] xDivs = di.readIntArray(numXDivs); - int[] yDivs = di.readIntArray(numYDivs); + public static NinePatch decode(ExtDataInput di) throws IOException { + di.skipBytes(1); + byte numXDivs = di.readByte(); + byte numYDivs = di.readByte(); + di.skipBytes(1); + di.skipBytes(8); + int padLeft = di.readInt(); + int padRight = di.readInt(); + int padTop = di.readInt(); + int padBottom = di.readInt(); + di.skipBytes(4); + int[] xDivs = di.readIntArray(numXDivs); + int[] yDivs = di.readIntArray(numYDivs); - return new NinePatch(padLeft, padRight, padTop, padBottom, xDivs, - yDivs); - } - } -} \ No newline at end of file + return new NinePatch(padLeft, padRight, padTop, padBottom, xDivs, yDivs); + } + } +} diff --git a/jadx-core/src/main/java/jadx/core/utils/files/FileUtils.java b/jadx-core/src/main/java/jadx/core/utils/files/FileUtils.java index 71eae79e7..360554a3d 100644 --- a/jadx-core/src/main/java/jadx/core/utils/files/FileUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/files/FileUtils.java @@ -7,6 +7,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; @@ -179,20 +180,10 @@ public class FileUtils { } private static boolean isZipFileCanBeOpen(File file) { - ZipFile zipFile = null; - try { - zipFile = new ZipFile(file); + try (ZipFile zipFile = new ZipFile(file)) { return zipFile.entries().hasMoreElements(); } catch (Exception e) { return false; - } finally { - if (zipFile != null) { - try { - zipFile.close(); - } catch (IOException e) { - LOG.error(e.getMessage()); - } - } } } @@ -214,8 +205,8 @@ public class FileUtils { LOG.debug("Failed to detect filesystem case-sensitivity by file creation", e); } finally { try { - caseCheckUpper.delete(); - caseCheckLow.delete(); + Files.deleteIfExists(caseCheckUpper.toPath()); + Files.deleteIfExists(caseCheckLow.toPath()); } catch (Exception e) { // ignore } diff --git a/jadx-core/src/main/java/jadx/core/utils/files/InputFile.java b/jadx-core/src/main/java/jadx/core/utils/files/InputFile.java index a439cb8bb..a5c8791a4 100644 --- a/jadx-core/src/main/java/jadx/core/utils/files/InputFile.java +++ b/jadx-core/src/main/java/jadx/core/utils/files/InputFile.java @@ -1,10 +1,5 @@ package jadx.core.utils.files; -import jadx.core.utils.AsmUtils; -import jadx.core.utils.exceptions.DecodeException; -import jadx.core.utils.exceptions.JadxException; -import jadx.core.utils.exceptions.JadxRuntimeException; - import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -16,13 +11,16 @@ import java.util.jar.JarOutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; +import com.android.dex.Dex; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.android.dex.Dex; +import jadx.core.utils.AsmUtils; +import jadx.core.utils.exceptions.DecodeException; +import jadx.core.utils.exceptions.JadxException; +import jadx.core.utils.exceptions.JadxRuntimeException; -import static jadx.core.utils.files.FileUtils.close; import static jadx.core.utils.files.FileUtils.isApkFile; import static jadx.core.utils.files.FileUtils.isZipDexFile; @@ -30,7 +28,7 @@ public class InputFile { private static final Logger LOG = LoggerFactory.getLogger(InputFile.class); private final File file; - private final List dexFiles = new ArrayList(); + private final List dexFiles = new ArrayList<>(); public static void addFilesFrom(File file, List list, boolean... skipSources) throws IOException, DecodeException { InputFile inputFile = new InputFile(file); @@ -38,7 +36,7 @@ public class InputFile { list.add(inputFile); } - private InputFile(File file) throws IOException, DecodeException { + private InputFile(File file) throws IOException { if (!file.exists()) { throw new IOException("File not found: " + file.getAbsolutePath()); } @@ -80,11 +78,11 @@ public class InputFile { throw new DecodeException("Unsupported input file format: " + file); } - private void addDexFile(Dex dexBuf) throws IOException { + private void addDexFile(Dex dexBuf) { addDexFile("", dexBuf); } - private void addDexFile(String fileName, Dex dexBuf) throws IOException { + private void addDexFile(String fileName, Dex dexBuf) { dexFiles.add(new DexFile(this, fileName, dexBuf)); } @@ -92,44 +90,41 @@ public class InputFile { int index = 0; try (ZipFile zf = new ZipFile(file)) { // Input file could be .apk or .zip files - // we should consider the input file could contain only one single dex, multi-dex, or instantRun support dex for Android .apk files + // we should consider the input file could contain only one single dex, multi-dex, + // or instantRun support dex for Android .apk files String instantRunDexSuffix = "classes" + ext; for (Enumeration e = zf.entries(); e.hasMoreElements(); ) { ZipEntry entry = e.nextElement(); - String entryName = entry.getName(); - - // security check - if(!ZipSecurity.isValidZipEntry(entry)) { + if (!ZipSecurity.isValidZipEntry(entry)) { continue; } - InputStream inputStream = zf.getInputStream(entry); - try { + String entryName = entry.getName(); + try (InputStream inputStream = zf.getInputStream(entry)) { if ((entryName.startsWith("classes") && entryName.endsWith(ext)) || entryName.endsWith(instantRunDexSuffix)) { - if (ext.equals(".dex")) { - index++; - addDexFile(entryName, new Dex(inputStream)); - } else if (ext.equals(".jar")) { - index++; - File jarFile = FileUtils.createTempFile(entryName); - FileOutputStream fos = new FileOutputStream(jarFile); - try { - IOUtils.copy(inputStream, fos); - } finally { - close(fos); - } - addDexFile(entryName, loadFromJar(jarFile)); - } else { - throw new JadxRuntimeException("Unexpected extension in zip: " + ext); + switch (ext) { + case ".dex": + index++; + addDexFile(entryName, new Dex(inputStream)); + break; + + case ".jar": + index++; + File jarFile = FileUtils.createTempFile(entryName); + try (FileOutputStream fos = new FileOutputStream(jarFile)) { + IOUtils.copy(inputStream, fos); + } + addDexFile(entryName, loadFromJar(jarFile)); + break; + + default: + throw new JadxRuntimeException("Unexpected extension in zip: " + ext); } } else if (entryName.equals("instant-run.zip") && ext.equals(".dex")) { File jarFile = FileUtils.createTempFile("instant-run.zip"); - FileOutputStream fos = new FileOutputStream(jarFile); - try { + try (FileOutputStream fos = new FileOutputStream(jarFile)) { IOUtils.copy(inputStream, fos); - } finally { - close(fos); } InputFile tempFile = new InputFile(jarFile); tempFile.loadFromZip(ext); @@ -139,8 +134,6 @@ public class InputFile { this.dexFiles.addAll(dexFiles); } } - } finally { - close(inputStream); } } } @@ -156,7 +149,7 @@ public class InputFile { throw new JadxException("Empty dx output"); } return new Dex(ba); - } catch (Throwable e) { + } catch (Exception e) { throw new DecodeException("java class to dex conversion error:\n " + e.getMessage(), e); } finally { if (j2d.isError()) { @@ -167,19 +160,12 @@ public class InputFile { private static Dex loadFromClassFile(File file) throws IOException, DecodeException { File outFile = FileUtils.createTempFile("cls.jar"); - FileOutputStream out = null; - JarOutputStream jo = null; - try { - out = new FileOutputStream(outFile); - jo = new JarOutputStream(out); + try (JarOutputStream jo = new JarOutputStream(new FileOutputStream(outFile))) { String clsName = AsmUtils.getNameFromClassFile(file); if (clsName == null || ZipSecurity.isValidZipEntryName(clsName)) { throw new IOException("Can't read class name from file: " + file); } FileUtils.addFileToJar(jo, file, clsName + ".class"); - } finally { - close(jo); - close(out); } return loadFromJar(outFile); } diff --git a/jadx-core/src/main/java/jadx/core/utils/files/JavaToDex.java b/jadx-core/src/main/java/jadx/core/utils/files/JavaToDex.java index 1636c7e3b..c4898b4be 100644 --- a/jadx-core/src/main/java/jadx/core/utils/files/JavaToDex.java +++ b/jadx-core/src/main/java/jadx/core/utils/files/JavaToDex.java @@ -5,17 +5,14 @@ import java.io.ByteArrayOutputStream; import com.android.dx.command.dexer.DxContext; import com.android.dx.command.dexer.Main; import com.android.dx.command.dexer.Main.Arguments; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import jadx.core.utils.exceptions.JadxException; public class JavaToDex { - private static final Logger LOG = LoggerFactory.getLogger(JavaToDex.class); private static final String CHARSET_NAME = "UTF-8"; - public static class DxArgs extends Arguments { + private static class DxArgs extends Arguments { public DxArgs(DxContext context, String dexFile, String[] input) { super(context); outName = dexFile; diff --git a/jadx-core/src/main/java/jadx/core/utils/files/ZipSecurity.java b/jadx-core/src/main/java/jadx/core/utils/files/ZipSecurity.java index 75a9ac146..11dd7f142 100644 --- a/jadx-core/src/main/java/jadx/core/utils/files/ZipSecurity.java +++ b/jadx-core/src/main/java/jadx/core/utils/files/ZipSecurity.java @@ -8,55 +8,55 @@ import org.slf4j.LoggerFactory; public class ZipSecurity { private static final Logger LOG = LoggerFactory.getLogger(ZipSecurity.class); - + // size of uncompressed zip entry shouldn't be bigger of compressed in MAX_SIZE_DIFF times private static final int MAX_SIZE_DIFF = 25; - - private static boolean isInSubDirectory(File base, File file) { - if (file == null) { - return false; - } - if (file.equals(base)) { - return true; - } - return isInSubDirectory(base, file.getParentFile()); + private ZipSecurity() {} + + private static boolean isInSubDirectory(File base, File file) { + if (file == null) { + return false; + } + if (file.equals(base)) { + return true; + } + return isInSubDirectory(base, file.getParentFile()); } - + // checks that entry name contains no any traversals // and prevents cases like "../classes.dex", to limit output only to the specified directory public static boolean isValidZipEntryName(String entryName) { try { File currentPath = new File(".").getCanonicalFile(); File canonical = new File(currentPath, entryName).getCanonicalFile(); - if(isInSubDirectory(currentPath, canonical)) { + if (isInSubDirectory(currentPath, canonical)) { return true; } LOG.error("Path traversal attack detected, invalid name: {}", entryName); return false; - } - catch(Exception e) { + } catch (Exception e) { LOG.error("Path traversal attack detected, invalid name: {}", entryName); return false; } } - + public static boolean isZipBomb(ZipEntry entry) { - long compressedSize = entry.getCompressedSize(); + long compressedSize = entry.getCompressedSize(); long uncompressedSize = entry.getSize(); - if(compressedSize < 0 || uncompressedSize < 0) { + if (compressedSize < 0 || uncompressedSize < 0) { LOG.error("Zip bomp attack detected, invalid sizes: compressed {}, uncompressed {}, name {}", compressedSize, uncompressedSize, entry.getName()); return true; } - if(compressedSize * MAX_SIZE_DIFF < uncompressedSize) { - LOG.error("Zip bomp attack detected, invalid sizes: compressed {}, uncompressed {}, name {}", + if (compressedSize * MAX_SIZE_DIFF < uncompressedSize) { + LOG.error("Zip bomb attack detected, invalid sizes: compressed {}, uncompressed {}, name {}", compressedSize, uncompressedSize, entry.getName()); return true; } return false; } - + public static boolean isValidZipEntry(ZipEntry entry) { return isValidZipEntryName(entry.getName()) && !isZipBomb(entry); diff --git a/jadx-core/src/main/java/jadx/core/xmlgen/BinaryXMLParser.java b/jadx-core/src/main/java/jadx/core/xmlgen/BinaryXMLParser.java index e0ddb3c44..1f20d4033 100644 --- a/jadx-core/src/main/java/jadx/core/xmlgen/BinaryXMLParser.java +++ b/jadx-core/src/main/java/jadx/core/xmlgen/BinaryXMLParser.java @@ -1,14 +1,5 @@ package jadx.core.xmlgen; -import jadx.api.ResourcesLoader; -import jadx.core.codegen.CodeWriter; -import jadx.core.dex.info.ConstStorage; -import jadx.core.dex.instructions.args.ArgType; -import jadx.core.dex.nodes.FieldNode; -import jadx.core.dex.nodes.RootNode; -import jadx.core.utils.exceptions.JadxRuntimeException; -import jadx.core.xmlgen.entry.ValuesParser; - import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; @@ -18,6 +9,16 @@ import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import jadx.api.ResourcesLoader; +import jadx.core.codegen.CodeWriter; +import jadx.core.dex.info.ConstStorage; +import jadx.core.dex.instructions.args.ArgType; +import jadx.core.dex.nodes.FieldNode; +import jadx.core.dex.nodes.RootNode; +import jadx.core.utils.StringUtils; +import jadx.core.utils.exceptions.JadxRuntimeException; +import jadx.core.xmlgen.entry.ValuesParser; + /* TODO: Don't die when error occurs Check error cases, maybe checked const values are not always the same @@ -34,10 +35,12 @@ public class BinaryXMLParser extends CommonBinaryParser { private static final Logger LOG = LoggerFactory.getLogger(BinaryXMLParser.class); private static final String ANDROID_R_STYLE_CLS = "android.R$style"; private static final boolean ATTR_NEW_LINE = false; - private final Map styleMap = new HashMap(); - private final Map localStyleMap = new HashMap(); + + private final Map styleMap = new HashMap<>(); + private final Map localStyleMap = new HashMap<>(); private final Map resNames; private final Map nsMap = new HashMap<>(); + private CodeWriter writer; private String[] strings; private String currentTag = "ERROR"; @@ -48,14 +51,7 @@ public class BinaryXMLParser extends CommonBinaryParser { public BinaryXMLParser(RootNode root) { try { - try { - Class rStyleCls = Class.forName(ANDROID_R_STYLE_CLS); - for (Field f : rStyleCls.getFields()) { - styleMap.put(f.getInt(f.getType()), f.getName()); - } - } catch (Throwable th) { - LOG.error("R class loading failed", th); - } + readAndroidRStyleClass(); // add application constants ConstStorage constStorage = root.getConstValues(); Map constFields = constStorage.getGlobalConstFields(); @@ -72,6 +68,17 @@ public class BinaryXMLParser extends CommonBinaryParser { } } + private void readAndroidRStyleClass() { + try { + Class rStyleCls = Class.forName(ANDROID_R_STYLE_CLS); + for (Field f : rStyleCls.getFields()) { + styleMap.put(f.getInt(f.getType()), f.getName()); + } + } catch (Exception th) { + LOG.error("Android R class loading failed", th); + } + } + public synchronized CodeWriter parse(InputStream inputStream) throws IOException { is = new ParserStream(inputStream); if (!isBinaryXml()) { @@ -186,13 +193,11 @@ public class BinaryXMLParser extends CommonBinaryParser { int strIndex = is.readInt32(); String str = strings[strIndex]; + writer.startLine().addIndent(); + writer.attachSourceLine(lineNumber); + writer.add(StringUtils.escapeXML(str.trim())); - //TODO: what's this for? - /*writer.startLine().addIndent(); - writer.attachSourceLine(lineNumber); - writer.add(StringUtils.escapeXML(str.trim()));*/ - - int size = is.readInt16(); + long size = is.readInt16(); is.skip(size - 2); } @@ -236,7 +241,7 @@ public class BinaryXMLParser extends CommonBinaryParser { writer.add(" xmlns:" + entry.getValue() + "=\"").add(entry.getKey()).add("\""); } } - boolean attrNewLine = attributeCount == 1 ? false : ATTR_NEW_LINE; + boolean attrNewLine = attributeCount != 1 && ATTR_NEW_LINE; for (int i = 0; i < attributeCount; i++) { parseAttribute(i, attrNewLine); } @@ -284,7 +289,6 @@ public class BinaryXMLParser extends CommonBinaryParser { if (attributeNS != -1) { writer.add(nsMap.get(strings[attributeNS])).add(':'); } - LOG.debug("decodeAttribute: " + attributeNS + " " + name); writer.add("style/").add(name.replaceAll("_", ".")); } else { FieldNode field = localStyleMap.get(attrValData); @@ -304,7 +308,7 @@ public class BinaryXMLParser extends CommonBinaryParser { } writer.add(resName); } else { - resName = ValuesParser.androidResMap.get(attrValData); + resName = ValuesParser.getAndroidResMap().get(attrValData); if (resName != null) { writer.add("@android:").add(resName); } else if (attrValData == 0) { diff --git a/jadx-core/src/main/java/jadx/core/xmlgen/ManifestAttributes.java b/jadx-core/src/main/java/jadx/core/xmlgen/ManifestAttributes.java index 3e70d1cf6..1ad467a5b 100644 --- a/jadx-core/src/main/java/jadx/core/xmlgen/ManifestAttributes.java +++ b/jadx-core/src/main/java/jadx/core/xmlgen/ManifestAttributes.java @@ -56,7 +56,7 @@ public class ManifestAttributes { try { instance = new ManifestAttributes(); } catch (Exception e) { - e.printStackTrace(); + LOG.error("Failed to create ManifestAttributes", e); } } return instance; diff --git a/jadx-core/src/main/java/jadx/core/xmlgen/ResContainer.java b/jadx-core/src/main/java/jadx/core/xmlgen/ResContainer.java index 73b3e505e..31c68ea15 100644 --- a/jadx-core/src/main/java/jadx/core/xmlgen/ResContainer.java +++ b/jadx-core/src/main/java/jadx/core/xmlgen/ResContainer.java @@ -1,10 +1,5 @@ package jadx.core.xmlgen; -import jadx.core.codegen.CodeWriter; -import jadx.core.utils.android.Res9patchStreamDecoder; -import jadx.core.utils.exceptions.JadxException; -import jadx.core.utils.exceptions.JadxRuntimeException; - import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; @@ -17,9 +12,17 @@ import java.util.List; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import jadx.core.codegen.CodeWriter; +import jadx.core.utils.android.Res9patchStreamDecoder; +import jadx.core.utils.exceptions.JadxRuntimeException; public class ResContainer implements Comparable { + private static final Logger LOG = LoggerFactory.getLogger(ResContainer.class); + private final String name; private final List subFiles; @@ -34,21 +37,21 @@ public class ResContainer implements Comparable { } public static ResContainer singleFile(String name, CodeWriter content) { - ResContainer resContainer = new ResContainer(name, Collections.emptyList()); + ResContainer resContainer = new ResContainer(name, Collections.emptyList()); resContainer.content = content; return resContainer; } public static ResContainer singleImageFile(String name, InputStream content) { - ResContainer resContainer = new ResContainer(name, Collections.emptyList()); + ResContainer resContainer = new ResContainer(name, Collections.emptyList()); InputStream newContent = content; if (name.endsWith(".9.png")) { Res9patchStreamDecoder decoder = new Res9patchStreamDecoder(); ByteArrayOutputStream os = new ByteArrayOutputStream(); try { decoder.decode(content, os); - } catch (JadxException e) { - e.printStackTrace(); + } catch (Exception e) { + LOG.error("Failed to decode 9-patch png image", e); } newContent = new ByteArrayInputStream(os.toByteArray()); } @@ -61,7 +64,7 @@ public class ResContainer implements Comparable { } public static ResContainer multiFile(String name) { - return new ResContainer(name, new ArrayList()); + return new ResContainer(name, new ArrayList<>()); } public String getName() { diff --git a/jadx-core/src/main/java/jadx/core/xmlgen/ResourceStorage.java b/jadx-core/src/main/java/jadx/core/xmlgen/ResourceStorage.java index 2531e70c5..7102a34df 100644 --- a/jadx-core/src/main/java/jadx/core/xmlgen/ResourceStorage.java +++ b/jadx-core/src/main/java/jadx/core/xmlgen/ResourceStorage.java @@ -1,8 +1,5 @@ package jadx.core.xmlgen; -import jadx.core.utils.Utils; -import jadx.core.xmlgen.entry.ResourceEntry; - import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -11,14 +8,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -public class ResourceStorage { +import jadx.core.xmlgen.entry.ResourceEntry; - private static final Comparator COMPARATOR = new Comparator() { - @Override - public int compare(ResourceEntry a, ResourceEntry b) { - return Utils.compare(a.getId(), b.getId()); - } - }; +public class ResourceStorage { private final List list = new ArrayList<>(); private String appPackage; @@ -32,12 +24,12 @@ public class ResourceStorage { } public void finish() { - Collections.sort(list, COMPARATOR); + list.sort(Comparator.comparingInt(ResourceEntry::getId)); } public ResourceEntry getByRef(int refId) { ResourceEntry key = new ResourceEntry(refId); - int index = Collections.binarySearch(list, key, COMPARATOR); + int index = Collections.binarySearch(list, key, Comparator.comparingInt(ResourceEntry::getId)); if (index < 0) { return null; } diff --git a/jadx-core/src/main/java/jadx/core/xmlgen/XmlSecurity.java b/jadx-core/src/main/java/jadx/core/xmlgen/XmlSecurity.java index 485cdaa9d..defa24867 100644 --- a/jadx-core/src/main/java/jadx/core/xmlgen/XmlSecurity.java +++ b/jadx-core/src/main/java/jadx/core/xmlgen/XmlSecurity.java @@ -4,23 +4,24 @@ import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; public class XmlSecurity { + private static DocumentBuilderFactory secureDbf = null; - + + private XmlSecurity() {} + public static DocumentBuilderFactory getSecureDbf() throws ParserConfigurationException { - synchronized(XmlSecurity.class) { - if(secureDbf == null) { + synchronized (XmlSecurity.class) { + if (secureDbf == null) { secureDbf = DocumentBuilderFactory.newInstance(); secureDbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); secureDbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); - secureDbf.setFeature("http://xml.org/sax/features/external-general-entities", false); - secureDbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); - secureDbf.setFeature("http://apache.org/xml/features/dom/create-entity-ref-nodes", false); - secureDbf.setXIncludeAware(false); - secureDbf.setExpandEntityReferences(false); + secureDbf.setFeature("http://xml.org/sax/features/external-general-entities", false); + secureDbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + secureDbf.setFeature("http://apache.org/xml/features/dom/create-entity-ref-nodes", false); + secureDbf.setXIncludeAware(false); + secureDbf.setExpandEntityReferences(false); } } return secureDbf; } - - } diff --git a/jadx-core/src/main/java/jadx/core/xmlgen/entry/ValuesParser.java b/jadx-core/src/main/java/jadx/core/xmlgen/entry/ValuesParser.java index 8e046187c..ae57fab8c 100644 --- a/jadx-core/src/main/java/jadx/core/xmlgen/entry/ValuesParser.java +++ b/jadx-core/src/main/java/jadx/core/xmlgen/entry/ValuesParser.java @@ -18,12 +18,12 @@ import jadx.core.xmlgen.ResTableParser; public class ValuesParser extends ParserConstants { private static final Logger LOG = LoggerFactory.getLogger(ValuesParser.class); + private static String[] androidStrings; + private static Map androidResMap; + private final String[] strings; private final Map resMap; - public static String[] androidStrings; - public static Map androidResMap; - public ValuesParser(String[] strings, Map resMap) { this.strings = strings; this.resMap = resMap; @@ -31,14 +31,14 @@ public class ValuesParser extends ParserConstants { if (androidStrings == null && androidResMap == null) { try { decodeAndroid(); - } catch (IOException e) { - e.printStackTrace(); + } catch (Exception e) { + LOG.error("Failed to decode Android Resource file", e); } } } - private void decodeAndroid() throws IOException { - InputStream inputStream = new BufferedInputStream(getClass().getResourceAsStream("/resources.arsc")); + private static void decodeAndroid() throws IOException { + InputStream inputStream = new BufferedInputStream(ValuesParser.class.getResourceAsStream("/resources.arsc")); ResTableParser androidParser = new ResTableParser(); androidParser.decode(inputStream); androidStrings = androidParser.getStrings(); @@ -212,4 +212,8 @@ public class ValuesParser extends ParserConstants { f.setMinimumIntegerDigits(1); return f.format(value); } + + public static Map getAndroidResMap() { + return androidResMap; + } } diff --git a/jadx-gui/src/main/java/jadx/gui/treemodel/JRoot.java b/jadx-gui/src/main/java/jadx/gui/treemodel/JRoot.java index 57fd8455c..ac088b294 100644 --- a/jadx-gui/src/main/java/jadx/gui/treemodel/JRoot.java +++ b/jadx-gui/src/main/java/jadx/gui/treemodel/JRoot.java @@ -7,16 +7,8 @@ import java.util.Enumeration; import java.util.List; import java.util.regex.Pattern; -import javax.swing.Icon; -import javax.swing.ImageIcon; - import jadx.api.ResourceFile; import jadx.gui.JadxWrapper; -import jadx.api.ResourceFile; -import jadx.gui.JadxWrapper; -import jadx.gui.treemodel.JResource.JResType; -import jadx.gui.utils.Utils; - import jadx.gui.treemodel.JResource.JResType; import jadx.gui.utils.Utils; diff --git a/jadx-gui/src/main/java/jadx/gui/ui/CommonSearchDialog.java b/jadx-gui/src/main/java/jadx/gui/ui/CommonSearchDialog.java index 57ec24e01..8c792d8e0 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/CommonSearchDialog.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/CommonSearchDialog.java @@ -6,8 +6,6 @@ import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; @@ -90,12 +88,7 @@ public abstract class CommonSearchDialog extends JDialog { addWindowListener(new WindowAdapter() { @Override public void windowOpened(WindowEvent e) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - openInit(); - } - }); + SwingUtilities.invokeLater(CommonSearchDialog.this::openInit); } }); } @@ -119,12 +112,7 @@ public abstract class CommonSearchDialog extends JDialog { protected void initCommon() { KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); - getRootPane().registerKeyboardAction(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - dispose(); - } - }, stroke, JComponent.WHEN_IN_FOCUSED_WINDOW); + getRootPane().registerKeyboardAction(e -> dispose(), stroke, JComponent.WHEN_IN_FOCUSED_WINDOW); } @NotNull @@ -132,17 +120,9 @@ public abstract class CommonSearchDialog extends JDialog { progressPane = new ProgressPanel(mainWindow, false); JButton cancelButton = new JButton(NLS.str("search_dialog.cancel")); - cancelButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent event) { - dispose(); - } - }); + cancelButton.addActionListener(event -> dispose()); JButton openBtn = new JButton(NLS.str("search_dialog.open")); - openBtn.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent event) { - openSelectedItem(); - } - }); + openBtn.addActionListener(event -> openSelectedItem()); getRootPane().setDefaultButton(openBtn); JPanel buttonPane = new JPanel();