Commit f02b99a1 authored by Skylot's avatar Skylot

fix some sonar warnings

parent 9132ef57
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());
try (ZipFile zipFile = new ZipFile(zipRef.getZipFile())) {
ZipEntry entry = zipFile.getEntry(zipRef.getEntryName());
if(!ZipSecurity.isValidZipEntry(entry)) {
return null;
}
if (entry == null) {
throw new IOException("Zip entry not found: " + zipRef);
}
inputStream = new BufferedInputStream(zipFile.getInputStream(entry));
size = entry.getSize();
if (!ZipSecurity.isValidZipEntry(entry)) {
return null;
}
result = decoder.decode(size, inputStream);
} catch (Exception e) {
throw new JadxException("Error decode: " + rf.getName(), e);
} finally {
try {
if (zipFile != null) {
zipFile.close();
try (InputStream inputStream = new BufferedInputStream(zipFile.getInputStream(entry))) {
return decoder.decode(entry.getSize(), inputStream);
}
} catch (Exception e) {
LOG.error("Error close zip file: {}", rf.getName(), e);
}
close(inputStream);
}
return result;
} catch (Exception e) {
throw new JadxException("Error decode: " + rf.getName(), e);
}
}
static ResContainer loadContent(JadxDecompiler jadxRef, ResourceFile rf) {
......@@ -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));
}
}
private void loadFile(List<ResourceFile> list, File file) {
if (file == null) {
return;
}
ZipFile zip = null;
try {
zip = new ZipFile(file);
try (ZipFile zip = new ZipFile(file)) {
Enumeration<? extends ZipEntry> 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);
}
......
......@@ -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() {
}
}
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<MethodNode> METHOD_LINE_COMPARATOR = new Comparator<MethodNode>() {
@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<MethodNode> sortMethodsByLine(List<MethodNode> methods) {
List<MethodNode> out = new ArrayList<>(methods);
Collections.sort(out, METHOD_LINE_COMPARATOR);
out.sort(Comparator.comparingInt(LineAttrNode::getSourceLine));
return out;
}
......
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 {
......
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;
......
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,9 +91,11 @@ public class EncValueParser {
case ENCODED_ANNOTATION:
return AnnotationsParser.readAnnotation(dex, in, false);
}
default:
throw new DecodeException("Unknown encoded value type: 0x" + Integer.toHexString(type));
}
}
private int parseUnsignedInt(int byteCount) {
return (int) parseNumber(byteCount, false, 0);
......
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 extends AbstractVisitor {
private final File dir;
private final IJadxArgs args;
public class SaveCode {
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();
......
package jadx.core.utils;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.IAttributeNode;
import jadx.core.dex.attributes.nodes.JadxErrorAttr;
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;
......@@ -17,6 +9,13 @@ 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;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.utils.exceptions.JadxOverflowException;
public class ErrorsCounter {
private static final Logger LOG = LoggerFactory.getLogger(ErrorsCounter.class);
......
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;
}
}
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<DexNode> dexNodes = root.getDexNodes();
if (dexNodes.size() == 0) {
if (dexNodes.isEmpty()) {
return null;
}
DexNode firstDex = dexNodes.get(0);
......
/**
* Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com>
*
* <p>
* 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
*
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
*
* <p>
* 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.
......@@ -22,7 +22,7 @@ import java.io.IOException;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
abstract public class DataInputDelegate implements DataInput {
public abstract class DataInputDelegate implements DataInput {
protected final DataInput mDelegate;
public DataInputDelegate(DataInput delegate) {
......
/**
* Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com>
*
* <p>
* 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
*
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
*
* <p>
* 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.
......@@ -16,7 +16,10 @@
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 <brut.alll@gmail.com>
......@@ -32,7 +35,7 @@ public class ExtDataInput extends DataInputDelegate {
public int[] readIntArray(int length) throws IOException {
int[] array = new int[length];
for(int i = 0; i < length; i++) {
for (int i = 0; i < length; i++) {
array[i] = readInt();
}
return array;
......@@ -45,30 +48,26 @@ public class ExtDataInput extends DataInputDelegate {
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));
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));
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));
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();
if (got == possible) {
skipCheckChunkTypeInt(expected, -1);
} else if (got != expected) {
......@@ -81,21 +80,20 @@ public class ExtDataInput extends DataInputDelegate {
* 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;
while ((total < n) && ((cur = (int) super.skipBytes(n - total)) > 0)) {
while ((total < n) && ((cur = super.skipBytes(n - total)) > 0)) {
total += cur;
}
return total;
}
public String readNullEndedString(int length, boolean fixed)
throws IOException {
public String readNullEndedString(int length, boolean fixed) throws IOException {
StringBuilder string = new StringBuilder(16);
while(length-- != 0) {
while (length-- != 0) {
short ch = readShort();
if (ch == 0) {
break;
......@@ -105,7 +103,6 @@ public class ExtDataInput extends DataInputDelegate {
if (fixed) {
skipBytes(length * 2);
}
return string.toString();
}
}
/**
* Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com>
*
* <p>
* 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
*
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
*
* <p>
* 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.
......@@ -16,8 +16,7 @@
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;
......@@ -39,9 +38,10 @@ public class Res9patchStreamDecoder {
byte[] data = IOUtils.toByteArray(in);
BufferedImage im = ImageIO.read(new ByteArrayInputStream(data));
int w = im.getWidth(), h = im.getHeight();
int w = im.getWidth();
int h = im.getHeight();
BufferedImage im2 = new BufferedImage(w+2, h+2, BufferedImage.TYPE_INT_ARGB);
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);
......@@ -64,15 +64,13 @@ public class Res9patchStreamDecoder {
}
}
private NinePatch getNinePatch(byte[] data) throws JadxException,
IOException {
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 {
private void find9patchChunk(DataInput di) throws JadxException, IOException {
di.skipBytes(8);
while (true) {
int size;
......@@ -104,8 +102,12 @@ public class Res9patchStreamDecoder {
private static final int NP_COLOR = 0xff000000;
private static class NinePatch {
public final int padLeft, padRight, padTop, padBottom;
public final int[] xDivs, yDivs;
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) {
......@@ -131,8 +133,7 @@ public class Res9patchStreamDecoder {
int[] xDivs = di.readIntArray(numXDivs);
int[] yDivs = di.readIntArray(numYDivs);
return new NinePatch(padLeft, padRight, padTop, padBottom, xDivs,
yDivs);
return new NinePatch(padLeft, padRight, padTop, padBottom, xDivs, yDivs);
}
}
}
......@@ -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
}
......
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<DexFile> dexFiles = new ArrayList<DexFile>();
private final List<DexFile> dexFiles = new ArrayList<>();
public static void addFilesFrom(File file, List<InputFile> 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<? extends ZipEntry> 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")) {
switch (ext) {
case ".dex":
index++;
addDexFile(entryName, new Dex(inputStream));
} else if (ext.equals(".jar")) {
break;
case ".jar":
index++;
File jarFile = FileUtils.createTempFile(entryName);
FileOutputStream fos = new FileOutputStream(jarFile);
try {
try (FileOutputStream fos = new FileOutputStream(jarFile)) {
IOUtils.copy(inputStream, fos);
} finally {
close(fos);
}
addDexFile(entryName, loadFromJar(jarFile));
} else {
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);
}
......
......@@ -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;
......
......@@ -12,6 +12,8 @@ public class ZipSecurity {
// 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 ZipSecurity() {}
private static boolean isInSubDirectory(File base, File file) {
if (file == null) {
return false;
......@@ -19,7 +21,6 @@ public class ZipSecurity {
if (file.equals(base)) {
return true;
}
return isInSubDirectory(base, file.getParentFile());
}
......@@ -29,13 +30,12 @@ public class ZipSecurity {
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;
}
......@@ -44,13 +44,13 @@ public class ZipSecurity {
public static boolean isZipBomb(ZipEntry entry) {
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;
}
......
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<Integer, String> styleMap = new HashMap<Integer, String>();
private final Map<Integer, FieldNode> localStyleMap = new HashMap<Integer, FieldNode>();
private final Map<Integer, String> styleMap = new HashMap<>();
private final Map<Integer, FieldNode> localStyleMap = new HashMap<>();
private final Map<Integer, String> resNames;
private final Map<String, String> 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<Object, FieldNode> 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];
//TODO: what's this for?
/*writer.startLine().addIndent();
writer.startLine().addIndent();
writer.attachSourceLine(lineNumber);
writer.add(StringUtils.escapeXML(str.trim()));*/
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) {
......
......@@ -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;
......
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<ResContainer> {
private static final Logger LOG = LoggerFactory.getLogger(ResContainer.class);
private final String name;
private final List<ResContainer> subFiles;
......@@ -34,21 +37,21 @@ public class ResContainer implements Comparable<ResContainer> {
}
public static ResContainer singleFile(String name, CodeWriter content) {
ResContainer resContainer = new ResContainer(name, Collections.<ResContainer>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.<ResContainer>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<ResContainer> {
}
public static ResContainer multiFile(String name) {
return new ResContainer(name, new ArrayList<ResContainer>());
return new ResContainer(name, new ArrayList<>());
}
public String getName() {
......
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<ResourceEntry> COMPARATOR = new Comparator<ResourceEntry>() {
@Override
public int compare(ResourceEntry a, ResourceEntry b) {
return Utils.compare(a.getId(), b.getId());
}
};
public class ResourceStorage {
private final List<ResourceEntry> 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;
}
......
......@@ -4,11 +4,14 @@ 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);
......@@ -21,6 +24,4 @@ public class XmlSecurity {
}
return secureDbf;
}
}
......@@ -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<Integer, String> androidResMap;
private final String[] strings;
private final Map<Integer, String> resMap;
public static String[] androidStrings;
public static Map<Integer, String> androidResMap;
public ValuesParser(String[] strings, Map<Integer, String> 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<Integer, String> getAndroidResMap() {
return androidResMap;
}
}
......@@ -7,19 +7,11 @@ 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;
public class JRoot extends JNode {
private static final long serialVersionUID = 8888495789773527342L;
......
......@@ -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();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment