Commit f02b99a1 authored by Skylot's avatar Skylot

fix some sonar warnings

parent 9132ef57
package jadx.api; 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.BufferedInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
...@@ -24,8 +15,16 @@ import java.util.zip.ZipFile; ...@@ -24,8 +15,16 @@ import java.util.zip.ZipFile;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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.READ_BUFFER_SIZE;
import static jadx.core.utils.files.FileUtils.close;
import static jadx.core.utils.files.FileUtils.copyStream; import static jadx.core.utils.files.FileUtils.copyStream;
// TODO: move to core package // TODO: move to core package
...@@ -53,44 +52,30 @@ public final class ResourcesLoader { ...@@ -53,44 +52,30 @@ public final class ResourcesLoader {
} }
public static ResContainer decodeStream(ResourceFile rf, ResourceDecoder decoder) throws JadxException { public static ResContainer decodeStream(ResourceFile rf, ResourceDecoder decoder) throws JadxException {
ZipFile zipFile = null;
InputStream inputStream = null;
ResContainer result;
try { try {
long size;
ZipRef zipRef = rf.getZipRef(); ZipRef zipRef = rf.getZipRef();
if (zipRef == null) { if (zipRef == null) {
File file = new File(rf.getName()); File file = new File(rf.getName());
inputStream = new BufferedInputStream(new FileInputStream(file)); try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) {
size = file.length(); return decoder.decode(file.length(), inputStream);
}
} else { } else {
zipFile = new ZipFile(zipRef.getZipFile()); try (ZipFile zipFile = new ZipFile(zipRef.getZipFile())) {
ZipEntry entry = zipFile.getEntry(zipRef.getEntryName()); ZipEntry entry = zipFile.getEntry(zipRef.getEntryName());
if(!ZipSecurity.isValidZipEntry(entry)) {
return null;
}
if (entry == null) { if (entry == null) {
throw new IOException("Zip entry not found: " + zipRef); throw new IOException("Zip entry not found: " + zipRef);
} }
inputStream = new BufferedInputStream(zipFile.getInputStream(entry)); if (!ZipSecurity.isValidZipEntry(entry)) {
size = entry.getSize(); return null;
} }
result = decoder.decode(size, inputStream); try (InputStream inputStream = new BufferedInputStream(zipFile.getInputStream(entry))) {
} catch (Exception e) { return decoder.decode(entry.getSize(), inputStream);
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; } catch (Exception e) {
throw new JadxException("Error decode: " + rf.getName(), e);
}
} }
static ResContainer loadContent(JadxDecompiler jadxRef, ResourceFile rf) { static ResContainer loadContent(JadxDecompiler jadxRef, ResourceFile rf) {
...@@ -118,39 +103,31 @@ public final class ResourcesLoader { ...@@ -118,39 +103,31 @@ public final class ResourcesLoader {
case IMG: case IMG:
return ResContainer.singleImageFile(rf.getName(), inputStream); return ResContainer.singleImageFile(rf.getName(), inputStream);
}
default:
if (size > LOAD_SIZE_LIMIT) { if (size > LOAD_SIZE_LIMIT) {
return ResContainer.singleFile(rf.getName(), return ResContainer.singleFile(rf.getName(),
new CodeWriter().add("File too big, size: " + String.format("%.2f KB", size / 1024.))); new CodeWriter().add("File too big, size: " + String.format("%.2f KB", size / 1024.)));
} }
return ResContainer.singleFile(rf.getName(), loadToCodeWriter(inputStream)); return ResContainer.singleFile(rf.getName(), loadToCodeWriter(inputStream));
} }
}
private void loadFile(List<ResourceFile> list, File file) { private void loadFile(List<ResourceFile> list, File file) {
if (file == null) { if (file == null) {
return; return;
} }
ZipFile zip = null; try (ZipFile zip = new ZipFile(file)) {
try {
zip = new ZipFile(file);
Enumeration<? extends ZipEntry> entries = zip.entries(); Enumeration<? extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements()) { while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement(); ZipEntry entry = entries.nextElement();
if(ZipSecurity.isValidZipEntry(entry)) { if (ZipSecurity.isValidZipEntry(entry)) {
addEntry(list, file, entry); addEntry(list, file, entry);
} }
} }
} catch (IOException e) { } catch (Exception e) {
LOG.debug("Not a zip file: {}", file.getAbsolutePath()); LOG.debug("Not a zip file: {}", file.getAbsolutePath());
addResourceFile(list, file); 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 { ...@@ -158,7 +135,7 @@ public final class ResourcesLoader {
String name = file.getAbsolutePath(); String name = file.getAbsolutePath();
ResourceType type = ResourceType.getFileType(name); ResourceType type = ResourceType.getFileType(name);
ResourceFile rf = ResourceFile.createResourceFileInstance(jadxRef, name, type); ResourceFile rf = ResourceFile.createResourceFileInstance(jadxRef, name, type);
if(rf != null) { if (rf != null) {
list.add(rf); list.add(rf);
} }
} }
...@@ -170,7 +147,7 @@ public final class ResourcesLoader { ...@@ -170,7 +147,7 @@ public final class ResourcesLoader {
String name = entry.getName(); String name = entry.getName();
ResourceType type = ResourceType.getFileType(name); ResourceType type = ResourceType.getFileType(name);
ResourceFile rf = ResourceFile.createResourceFileInstance(jadxRef, name, type); ResourceFile rf = ResourceFile.createResourceFileInstance(jadxRef, name, type);
if(rf != null) { if (rf != null) {
rf.setZipRef(new ZipRef(zipFile, name)); rf.setZipRef(new ZipRef(zipFile, name));
list.add(rf); list.add(rf);
} }
......
...@@ -21,4 +21,7 @@ public class Consts { ...@@ -21,4 +21,7 @@ public class Consts {
public static final String ANONYMOUS_CLASS_PREFIX = "AnonymousClass"; public static final String ANONYMOUS_CLASS_PREFIX = "AnonymousClass";
public static final String MTH_TOSTRING_SIGNATURE = "toString()Ljava/lang/String;"; public static final String MTH_TOSTRING_SIGNATURE = "toString()Ljava/lang/String;";
private Consts() {
}
} }
package jadx.core.codegen; 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.api.IJadxArgs;
import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.AttrNode; import jadx.core.dex.attributes.AttrNode;
import jadx.core.dex.attributes.nodes.EnumClassAttr; import jadx.core.dex.attributes.nodes.EnumClassAttr;
import jadx.core.dex.attributes.nodes.EnumClassAttr.EnumField; 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.attributes.nodes.SourceFileAttr;
import jadx.core.dex.info.AccessInfo; import jadx.core.dex.info.AccessInfo;
import jadx.core.dex.info.ClassInfo; import jadx.core.dex.info.ClassInfo;
...@@ -23,30 +36,7 @@ import jadx.core.utils.ErrorsCounter; ...@@ -23,30 +36,7 @@ import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.Utils; import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.CodegenException; 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 { 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 ClassNode cls;
private final ClassGen parentGen; private final ClassGen parentGen;
...@@ -276,7 +266,7 @@ public class ClassGen { ...@@ -276,7 +266,7 @@ public class ClassGen {
private static List<MethodNode> sortMethodsByLine(List<MethodNode> methods) { private static List<MethodNode> sortMethodsByLine(List<MethodNode> methods) {
List<MethodNode> out = new ArrayList<>(methods); List<MethodNode> out = new ArrayList<>(methods);
Collections.sort(out, METHOD_LINE_COMPARATOR); out.sort(Comparator.comparingInt(LineAttrNode::getSourceLine));
return out; return out;
} }
......
package jadx.core.dex.info; 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.api.IJadxArgs;
import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.AType;
import jadx.core.dex.instructions.args.LiteralArg; import jadx.core.dex.instructions.args.LiteralArg;
...@@ -10,14 +18,6 @@ import jadx.core.dex.nodes.FieldNode; ...@@ -10,14 +18,6 @@ import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.ResRefField; import jadx.core.dex.nodes.ResRefField;
import jadx.core.dex.nodes.parser.FieldInitAttr; 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 { public class ConstStorage {
private static final class ValueStorage { private static final class ValueStorage {
......
package jadx.core.dex.nodes.parser; 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.attributes.nodes.SourceFileAttr;
import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.instructions.args.RegisterArg;
...@@ -9,16 +13,8 @@ import jadx.core.dex.nodes.InsnNode; ...@@ -9,16 +13,8 @@ import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.MethodNode;
import jadx.core.utils.exceptions.DecodeException; 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 { 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_END_SEQUENCE = 0x00;
private static final int DBG_ADVANCE_PC = 0x01; private static final int DBG_ADVANCE_PC = 0x01;
private static final int DBG_ADVANCE_LINE = 0x02; private static final int DBG_ADVANCE_LINE = 0x02;
......
package jadx.core.dex.nodes.parser; 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.ArrayList;
import java.util.List; import java.util.List;
import com.android.dex.Dex.Section; import com.android.dex.Dex.Section;
import com.android.dex.Leb128; 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 { public class EncValueParser {
private static final int ENCODED_BYTE = 0x00; private static final int ENCODED_BYTE = 0x00;
...@@ -91,9 +91,11 @@ public class EncValueParser { ...@@ -91,9 +91,11 @@ public class EncValueParser {
case ENCODED_ANNOTATION: case ENCODED_ANNOTATION:
return AnnotationsParser.readAnnotation(dex, in, false); 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) { private int parseUnsignedInt(int byteCount) {
return (int) parseNumber(byteCount, false, 0); return (int) parseNumber(byteCount, false, 0);
......
package jadx.core.dex.visitors; package jadx.core.dex.visitors;
import java.io.File;
import jadx.api.IJadxArgs; import jadx.api.IJadxArgs;
import jadx.core.codegen.CodeWriter; import jadx.core.codegen.CodeWriter;
import jadx.core.dex.nodes.ClassNode; 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 { public class SaveCode {
private final File dir;
private final IJadxArgs args;
public SaveCode(File dir, IJadxArgs args) { private SaveCode() {}
this.args = args;
this.dir = dir;
}
@Override
public boolean visit(ClassNode cls) throws CodegenException {
save(dir, args, cls);
return false;
}
public static void save(File dir, IJadxArgs args, ClassNode cls) { public static void save(File dir, IJadxArgs args, ClassNode cls) {
CodeWriter clsCode = cls.getCode(); CodeWriter clsCode = cls.getCode();
......
package jadx.core.utils; 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.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
...@@ -17,6 +9,13 @@ import java.util.Set; ...@@ -17,6 +9,13 @@ import java.util.Set;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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 { public class ErrorsCounter {
private static final Logger LOG = LoggerFactory.getLogger(ErrorsCounter.class); private static final Logger LOG = LoggerFactory.getLogger(ErrorsCounter.class);
......
package jadx.core.utils; package jadx.core.utils;
import jadx.api.JadxDecompiler;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
import jadx.api.JadxDecompiler;
public class Utils { public class Utils {
public static final String JADX_API_PACKAGE = JadxDecompiler.class.getPackage().getName(); public static final String JADX_API_PACKAGE = JadxDecompiler.class.getPackage().getName();
...@@ -95,8 +95,4 @@ public class Utils { ...@@ -95,8 +95,4 @@ public class Utils {
th.setStackTrace(Arrays.copyOfRange(stackTrace, 0, cutIndex)); 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; 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.ClassGen;
import jadx.core.codegen.CodeWriter; import jadx.core.codegen.CodeWriter;
import jadx.core.dex.info.ClassInfo; import jadx.core.dex.info.ClassInfo;
...@@ -7,17 +12,15 @@ import jadx.core.dex.nodes.ClassNode; ...@@ -7,17 +12,15 @@ import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.DexNode; import jadx.core.dex.nodes.DexNode;
import jadx.core.dex.nodes.RootNode; import jadx.core.dex.nodes.RootNode;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* Android resources specific handlers * Android resources specific handlers
*/ */
public class AndroidResourcesUtils { public class AndroidResourcesUtils {
private static final Logger LOG = LoggerFactory.getLogger(AndroidResourcesUtils.class); private static final Logger LOG = LoggerFactory.getLogger(AndroidResourcesUtils.class);
private AndroidResourcesUtils() {
}
public static ClassNode searchAppResClass(RootNode root) { public static ClassNode searchAppResClass(RootNode root) {
String appPackage = root.getAppPackage(); String appPackage = root.getAppPackage();
String fullName = appPackage != null ? appPackage + ".R" : "R"; String fullName = appPackage != null ? appPackage + ".R" : "R";
...@@ -49,7 +52,7 @@ public class AndroidResourcesUtils { ...@@ -49,7 +52,7 @@ public class AndroidResourcesUtils {
private static ClassNode makeClass(RootNode root, String clsName) { private static ClassNode makeClass(RootNode root, String clsName) {
List<DexNode> dexNodes = root.getDexNodes(); List<DexNode> dexNodes = root.getDexNodes();
if (dexNodes.size() == 0) { if (dexNodes.isEmpty()) {
return null; return null;
} }
DexNode firstDex = dexNodes.get(0); DexNode firstDex = dexNodes.get(0);
......
/** /**
* Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com> * Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com>
* * <p>
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* * <p>
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* * <p>
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
...@@ -22,7 +22,7 @@ import java.io.IOException; ...@@ -22,7 +22,7 @@ import java.io.IOException;
/** /**
* @author Ryszard Wiśniewski <brut.alll@gmail.com> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/ */
abstract public class DataInputDelegate implements DataInput { public abstract class DataInputDelegate implements DataInput {
protected final DataInput mDelegate; protected final DataInput mDelegate;
public DataInputDelegate(DataInput delegate) { public DataInputDelegate(DataInput delegate) {
......
/** /**
* Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com> * Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com>
* * <p>
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* * <p>
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* * <p>
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
...@@ -16,7 +16,10 @@ ...@@ -16,7 +16,10 @@
package jadx.core.utils.android; 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> * @author Ryszard Wiśniewski <brut.alll@gmail.com>
...@@ -32,7 +35,7 @@ public class ExtDataInput extends DataInputDelegate { ...@@ -32,7 +35,7 @@ public class ExtDataInput extends DataInputDelegate {
public int[] readIntArray(int length) throws IOException { public int[] readIntArray(int length) throws IOException {
int[] array = new int[length]; int[] array = new int[length];
for(int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
array[i] = readInt(); array[i] = readInt();
} }
return array; return array;
...@@ -45,30 +48,26 @@ public class ExtDataInput extends DataInputDelegate { ...@@ -45,30 +48,26 @@ public class ExtDataInput extends DataInputDelegate {
public void skipCheckInt(int expected) throws IOException { public void skipCheckInt(int expected) throws IOException {
int got = readInt(); int got = readInt();
if (got != expected) { if (got != expected) {
throw new IOException(String.format( throw new IOException(String.format("Expected: 0x%08x, got: 0x%08x", expected, got));
"Expected: 0x%08x, got: 0x%08x", expected, got));
} }
} }
public void skipCheckShort(short expected) throws IOException { public void skipCheckShort(short expected) throws IOException {
short got = readShort(); short got = readShort();
if (got != expected) { if (got != expected) {
throw new IOException(String.format( throw new IOException(String.format("Expected: 0x%08x, got: 0x%08x", expected, got));
"Expected: 0x%08x, got: 0x%08x", expected, got));
} }
} }
public void skipCheckByte(byte expected) throws IOException { public void skipCheckByte(byte expected) throws IOException {
byte got = readByte(); byte got = readByte();
if (got != expected) { if (got != expected) {
throw new IOException(String.format( throw new IOException(String.format("Expected: 0x%08x, got: 0x%08x", expected, got));
"Expected: 0x%08x, got: 0x%08x", expected, got));
} }
} }
public void skipCheckChunkTypeInt(int expected, int possible) throws IOException { public void skipCheckChunkTypeInt(int expected, int possible) throws IOException {
int got = readInt(); int got = readInt();
if (got == possible) { if (got == possible) {
skipCheckChunkTypeInt(expected, -1); skipCheckChunkTypeInt(expected, -1);
} else if (got != expected) { } else if (got != expected) {
...@@ -81,21 +80,20 @@ public class ExtDataInput extends DataInputDelegate { ...@@ -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 * 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). * requested (this is similar to DataInputStream's wrapper).
*/ */
@Override
public final int skipBytes(int n) throws IOException { public final int skipBytes(int n) throws IOException {
int total = 0; int total = 0;
int cur = 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; total += cur;
} }
return total; return total;
} }
public String readNullEndedString(int length, boolean fixed) public String readNullEndedString(int length, boolean fixed) throws IOException {
throws IOException {
StringBuilder string = new StringBuilder(16); StringBuilder string = new StringBuilder(16);
while(length-- != 0) { while (length-- != 0) {
short ch = readShort(); short ch = readShort();
if (ch == 0) { if (ch == 0) {
break; break;
...@@ -105,7 +103,6 @@ public class ExtDataInput extends DataInputDelegate { ...@@ -105,7 +103,6 @@ public class ExtDataInput extends DataInputDelegate {
if (fixed) { if (fixed) {
skipBytes(length * 2); skipBytes(length * 2);
} }
return string.toString(); return string.toString();
} }
} }
/** /**
* Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com> * Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com>
* * <p>
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* * <p>
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* * <p>
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
...@@ -16,8 +16,7 @@ ...@@ -16,8 +16,7 @@
package jadx.core.utils.android; package jadx.core.utils.android;
import org.apache.commons.io.IOUtils; import javax.imageio.ImageIO;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.DataInput; import java.io.DataInput;
...@@ -25,7 +24,7 @@ import java.io.IOException; ...@@ -25,7 +24,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import javax.imageio.ImageIO; import org.apache.commons.io.IOUtils;
import jadx.core.utils.exceptions.JadxException; import jadx.core.utils.exceptions.JadxException;
...@@ -39,9 +38,10 @@ public class Res9patchStreamDecoder { ...@@ -39,9 +38,10 @@ public class Res9patchStreamDecoder {
byte[] data = IOUtils.toByteArray(in); byte[] data = IOUtils.toByteArray(in);
BufferedImage im = ImageIO.read(new ByteArrayInputStream(data)); 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); im2.createGraphics().drawImage(im, 1, 1, w, h, null);
NinePatch np = getNinePatch(data); NinePatch np = getNinePatch(data);
...@@ -64,15 +64,13 @@ public class Res9patchStreamDecoder { ...@@ -64,15 +64,13 @@ public class Res9patchStreamDecoder {
} }
} }
private NinePatch getNinePatch(byte[] data) throws JadxException, private NinePatch getNinePatch(byte[] data) throws JadxException, IOException {
IOException {
ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data)); ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data));
find9patchChunk(di); find9patchChunk(di);
return NinePatch.decode(di); return NinePatch.decode(di);
} }
private void find9patchChunk(DataInput di) throws JadxException, private void find9patchChunk(DataInput di) throws JadxException, IOException {
IOException {
di.skipBytes(8); di.skipBytes(8);
while (true) { while (true) {
int size; int size;
...@@ -104,8 +102,12 @@ public class Res9patchStreamDecoder { ...@@ -104,8 +102,12 @@ public class Res9patchStreamDecoder {
private static final int NP_COLOR = 0xff000000; private static final int NP_COLOR = 0xff000000;
private static class NinePatch { private static class NinePatch {
public final int padLeft, padRight, padTop, padBottom; public final int padLeft;
public final int[] xDivs, yDivs; 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, public NinePatch(int padLeft, int padRight, int padTop, int padBottom,
int[] xDivs, int[] yDivs) { int[] xDivs, int[] yDivs) {
...@@ -131,8 +133,7 @@ public class Res9patchStreamDecoder { ...@@ -131,8 +133,7 @@ public class Res9patchStreamDecoder {
int[] xDivs = di.readIntArray(numXDivs); int[] xDivs = di.readIntArray(numXDivs);
int[] yDivs = di.readIntArray(numYDivs); int[] yDivs = di.readIntArray(numYDivs);
return new NinePatch(padLeft, padRight, padTop, padBottom, xDivs, return new NinePatch(padLeft, padRight, padTop, padBottom, xDivs, yDivs);
yDivs);
} }
} }
} }
...@@ -7,6 +7,7 @@ import java.io.FileInputStream; ...@@ -7,6 +7,7 @@ import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.file.Files;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.List; import java.util.List;
...@@ -179,20 +180,10 @@ public class FileUtils { ...@@ -179,20 +180,10 @@ public class FileUtils {
} }
private static boolean isZipFileCanBeOpen(File file) { private static boolean isZipFileCanBeOpen(File file) {
ZipFile zipFile = null; try (ZipFile zipFile = new ZipFile(file)) {
try {
zipFile = new ZipFile(file);
return zipFile.entries().hasMoreElements(); return zipFile.entries().hasMoreElements();
} catch (Exception e) { } catch (Exception e) {
return false; return false;
} finally {
if (zipFile != null) {
try {
zipFile.close();
} catch (IOException e) {
LOG.error(e.getMessage());
}
}
} }
} }
...@@ -214,8 +205,8 @@ public class FileUtils { ...@@ -214,8 +205,8 @@ public class FileUtils {
LOG.debug("Failed to detect filesystem case-sensitivity by file creation", e); LOG.debug("Failed to detect filesystem case-sensitivity by file creation", e);
} finally { } finally {
try { try {
caseCheckUpper.delete(); Files.deleteIfExists(caseCheckUpper.toPath());
caseCheckLow.delete(); Files.deleteIfExists(caseCheckLow.toPath());
} catch (Exception e) { } catch (Exception e) {
// ignore // ignore
} }
......
package jadx.core.utils.files; 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.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
...@@ -16,13 +11,16 @@ import java.util.jar.JarOutputStream; ...@@ -16,13 +11,16 @@ import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipFile; import java.util.zip.ZipFile;
import com.android.dex.Dex;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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.isApkFile;
import static jadx.core.utils.files.FileUtils.isZipDexFile; import static jadx.core.utils.files.FileUtils.isZipDexFile;
...@@ -30,7 +28,7 @@ public class InputFile { ...@@ -30,7 +28,7 @@ public class InputFile {
private static final Logger LOG = LoggerFactory.getLogger(InputFile.class); private static final Logger LOG = LoggerFactory.getLogger(InputFile.class);
private final File file; 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 { public static void addFilesFrom(File file, List<InputFile> list, boolean... skipSources) throws IOException, DecodeException {
InputFile inputFile = new InputFile(file); InputFile inputFile = new InputFile(file);
...@@ -38,7 +36,7 @@ public class InputFile { ...@@ -38,7 +36,7 @@ public class InputFile {
list.add(inputFile); list.add(inputFile);
} }
private InputFile(File file) throws IOException, DecodeException { private InputFile(File file) throws IOException {
if (!file.exists()) { if (!file.exists()) {
throw new IOException("File not found: " + file.getAbsolutePath()); throw new IOException("File not found: " + file.getAbsolutePath());
} }
...@@ -80,11 +78,11 @@ public class InputFile { ...@@ -80,11 +78,11 @@ public class InputFile {
throw new DecodeException("Unsupported input file format: " + file); throw new DecodeException("Unsupported input file format: " + file);
} }
private void addDexFile(Dex dexBuf) throws IOException { private void addDexFile(Dex dexBuf) {
addDexFile("", 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)); dexFiles.add(new DexFile(this, fileName, dexBuf));
} }
...@@ -92,44 +90,41 @@ public class InputFile { ...@@ -92,44 +90,41 @@ public class InputFile {
int index = 0; int index = 0;
try (ZipFile zf = new ZipFile(file)) { try (ZipFile zf = new ZipFile(file)) {
// Input file could be .apk or .zip files // 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; String instantRunDexSuffix = "classes" + ext;
for (Enumeration<? extends ZipEntry> e = zf.entries(); e.hasMoreElements(); ) { for (Enumeration<? extends ZipEntry> e = zf.entries(); e.hasMoreElements(); ) {
ZipEntry entry = e.nextElement(); ZipEntry entry = e.nextElement();
String entryName = entry.getName(); if (!ZipSecurity.isValidZipEntry(entry)) {
// security check
if(!ZipSecurity.isValidZipEntry(entry)) {
continue; continue;
} }
InputStream inputStream = zf.getInputStream(entry); String entryName = entry.getName();
try { try (InputStream inputStream = zf.getInputStream(entry)) {
if ((entryName.startsWith("classes") && entryName.endsWith(ext)) if ((entryName.startsWith("classes") && entryName.endsWith(ext))
|| entryName.endsWith(instantRunDexSuffix)) { || entryName.endsWith(instantRunDexSuffix)) {
if (ext.equals(".dex")) { switch (ext) {
case ".dex":
index++; index++;
addDexFile(entryName, new Dex(inputStream)); addDexFile(entryName, new Dex(inputStream));
} else if (ext.equals(".jar")) { break;
case ".jar":
index++; index++;
File jarFile = FileUtils.createTempFile(entryName); File jarFile = FileUtils.createTempFile(entryName);
FileOutputStream fos = new FileOutputStream(jarFile); try (FileOutputStream fos = new FileOutputStream(jarFile)) {
try {
IOUtils.copy(inputStream, fos); IOUtils.copy(inputStream, fos);
} finally {
close(fos);
} }
addDexFile(entryName, loadFromJar(jarFile)); addDexFile(entryName, loadFromJar(jarFile));
} else { break;
default:
throw new JadxRuntimeException("Unexpected extension in zip: " + ext); throw new JadxRuntimeException("Unexpected extension in zip: " + ext);
} }
} else if (entryName.equals("instant-run.zip") && ext.equals(".dex")) { } else if (entryName.equals("instant-run.zip") && ext.equals(".dex")) {
File jarFile = FileUtils.createTempFile("instant-run.zip"); File jarFile = FileUtils.createTempFile("instant-run.zip");
FileOutputStream fos = new FileOutputStream(jarFile); try (FileOutputStream fos = new FileOutputStream(jarFile)) {
try {
IOUtils.copy(inputStream, fos); IOUtils.copy(inputStream, fos);
} finally {
close(fos);
} }
InputFile tempFile = new InputFile(jarFile); InputFile tempFile = new InputFile(jarFile);
tempFile.loadFromZip(ext); tempFile.loadFromZip(ext);
...@@ -139,8 +134,6 @@ public class InputFile { ...@@ -139,8 +134,6 @@ public class InputFile {
this.dexFiles.addAll(dexFiles); this.dexFiles.addAll(dexFiles);
} }
} }
} finally {
close(inputStream);
} }
} }
} }
...@@ -156,7 +149,7 @@ public class InputFile { ...@@ -156,7 +149,7 @@ public class InputFile {
throw new JadxException("Empty dx output"); throw new JadxException("Empty dx output");
} }
return new Dex(ba); return new Dex(ba);
} catch (Throwable e) { } catch (Exception e) {
throw new DecodeException("java class to dex conversion error:\n " + e.getMessage(), e); throw new DecodeException("java class to dex conversion error:\n " + e.getMessage(), e);
} finally { } finally {
if (j2d.isError()) { if (j2d.isError()) {
...@@ -167,19 +160,12 @@ public class InputFile { ...@@ -167,19 +160,12 @@ public class InputFile {
private static Dex loadFromClassFile(File file) throws IOException, DecodeException { private static Dex loadFromClassFile(File file) throws IOException, DecodeException {
File outFile = FileUtils.createTempFile("cls.jar"); File outFile = FileUtils.createTempFile("cls.jar");
FileOutputStream out = null; try (JarOutputStream jo = new JarOutputStream(new FileOutputStream(outFile))) {
JarOutputStream jo = null;
try {
out = new FileOutputStream(outFile);
jo = new JarOutputStream(out);
String clsName = AsmUtils.getNameFromClassFile(file); String clsName = AsmUtils.getNameFromClassFile(file);
if (clsName == null || ZipSecurity.isValidZipEntryName(clsName)) { if (clsName == null || ZipSecurity.isValidZipEntryName(clsName)) {
throw new IOException("Can't read class name from file: " + file); throw new IOException("Can't read class name from file: " + file);
} }
FileUtils.addFileToJar(jo, file, clsName + ".class"); FileUtils.addFileToJar(jo, file, clsName + ".class");
} finally {
close(jo);
close(out);
} }
return loadFromJar(outFile); return loadFromJar(outFile);
} }
......
...@@ -5,17 +5,14 @@ import java.io.ByteArrayOutputStream; ...@@ -5,17 +5,14 @@ import java.io.ByteArrayOutputStream;
import com.android.dx.command.dexer.DxContext; import com.android.dx.command.dexer.DxContext;
import com.android.dx.command.dexer.Main; import com.android.dx.command.dexer.Main;
import com.android.dx.command.dexer.Main.Arguments; import com.android.dx.command.dexer.Main.Arguments;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.core.utils.exceptions.JadxException; import jadx.core.utils.exceptions.JadxException;
public class JavaToDex { public class JavaToDex {
private static final Logger LOG = LoggerFactory.getLogger(JavaToDex.class);
private static final String CHARSET_NAME = "UTF-8"; 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) { public DxArgs(DxContext context, String dexFile, String[] input) {
super(context); super(context);
outName = dexFile; outName = dexFile;
......
...@@ -12,6 +12,8 @@ public class ZipSecurity { ...@@ -12,6 +12,8 @@ public class ZipSecurity {
// size of uncompressed zip entry shouldn't be bigger of compressed in MAX_SIZE_DIFF times // 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 final int MAX_SIZE_DIFF = 25;
private ZipSecurity() {}
private static boolean isInSubDirectory(File base, File file) { private static boolean isInSubDirectory(File base, File file) {
if (file == null) { if (file == null) {
return false; return false;
...@@ -19,7 +21,6 @@ public class ZipSecurity { ...@@ -19,7 +21,6 @@ public class ZipSecurity {
if (file.equals(base)) { if (file.equals(base)) {
return true; return true;
} }
return isInSubDirectory(base, file.getParentFile()); return isInSubDirectory(base, file.getParentFile());
} }
...@@ -29,13 +30,12 @@ public class ZipSecurity { ...@@ -29,13 +30,12 @@ public class ZipSecurity {
try { try {
File currentPath = new File(".").getCanonicalFile(); File currentPath = new File(".").getCanonicalFile();
File canonical = new File(currentPath, entryName).getCanonicalFile(); File canonical = new File(currentPath, entryName).getCanonicalFile();
if(isInSubDirectory(currentPath, canonical)) { if (isInSubDirectory(currentPath, canonical)) {
return true; return true;
} }
LOG.error("Path traversal attack detected, invalid name: {}", entryName); LOG.error("Path traversal attack detected, invalid name: {}", entryName);
return false; return false;
} } catch (Exception e) {
catch(Exception e) {
LOG.error("Path traversal attack detected, invalid name: {}", entryName); LOG.error("Path traversal attack detected, invalid name: {}", entryName);
return false; return false;
} }
...@@ -44,13 +44,13 @@ public class ZipSecurity { ...@@ -44,13 +44,13 @@ public class ZipSecurity {
public static boolean isZipBomb(ZipEntry entry) { public static boolean isZipBomb(ZipEntry entry) {
long compressedSize = entry.getCompressedSize(); long compressedSize = entry.getCompressedSize();
long uncompressedSize = entry.getSize(); 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 {}", LOG.error("Zip bomp attack detected, invalid sizes: compressed {}, uncompressed {}, name {}",
compressedSize, uncompressedSize, entry.getName()); compressedSize, uncompressedSize, entry.getName());
return true; return true;
} }
if(compressedSize * MAX_SIZE_DIFF < uncompressedSize) { if (compressedSize * MAX_SIZE_DIFF < uncompressedSize) {
LOG.error("Zip bomp attack detected, invalid sizes: compressed {}, uncompressed {}, name {}", LOG.error("Zip bomb attack detected, invalid sizes: compressed {}, uncompressed {}, name {}",
compressedSize, uncompressedSize, entry.getName()); compressedSize, uncompressedSize, entry.getName());
return true; return true;
} }
......
package jadx.core.xmlgen; 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.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
...@@ -18,6 +9,16 @@ import java.util.Map; ...@@ -18,6 +9,16 @@ import java.util.Map;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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: /* TODO:
Don't die when error occurs Don't die when error occurs
Check error cases, maybe checked const values are not always the same Check error cases, maybe checked const values are not always the same
...@@ -34,10 +35,12 @@ public class BinaryXMLParser extends CommonBinaryParser { ...@@ -34,10 +35,12 @@ public class BinaryXMLParser extends CommonBinaryParser {
private static final Logger LOG = LoggerFactory.getLogger(BinaryXMLParser.class); private static final Logger LOG = LoggerFactory.getLogger(BinaryXMLParser.class);
private static final String ANDROID_R_STYLE_CLS = "android.R$style"; private static final String ANDROID_R_STYLE_CLS = "android.R$style";
private static final boolean ATTR_NEW_LINE = false; 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<Integer, String> resNames;
private final Map<String, String> nsMap = new HashMap<>(); private final Map<String, String> nsMap = new HashMap<>();
private CodeWriter writer; private CodeWriter writer;
private String[] strings; private String[] strings;
private String currentTag = "ERROR"; private String currentTag = "ERROR";
...@@ -48,14 +51,7 @@ public class BinaryXMLParser extends CommonBinaryParser { ...@@ -48,14 +51,7 @@ public class BinaryXMLParser extends CommonBinaryParser {
public BinaryXMLParser(RootNode root) { public BinaryXMLParser(RootNode root) {
try { try {
try { readAndroidRStyleClass();
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);
}
// add application constants // add application constants
ConstStorage constStorage = root.getConstValues(); ConstStorage constStorage = root.getConstValues();
Map<Object, FieldNode> constFields = constStorage.getGlobalConstFields(); Map<Object, FieldNode> constFields = constStorage.getGlobalConstFields();
...@@ -72,6 +68,17 @@ public class BinaryXMLParser extends CommonBinaryParser { ...@@ -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 { public synchronized CodeWriter parse(InputStream inputStream) throws IOException {
is = new ParserStream(inputStream); is = new ParserStream(inputStream);
if (!isBinaryXml()) { if (!isBinaryXml()) {
...@@ -186,13 +193,11 @@ public class BinaryXMLParser extends CommonBinaryParser { ...@@ -186,13 +193,11 @@ public class BinaryXMLParser extends CommonBinaryParser {
int strIndex = is.readInt32(); int strIndex = is.readInt32();
String str = strings[strIndex]; String str = strings[strIndex];
writer.startLine().addIndent();
//TODO: what's this for?
/*writer.startLine().addIndent();
writer.attachSourceLine(lineNumber); 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); is.skip(size - 2);
} }
...@@ -236,7 +241,7 @@ public class BinaryXMLParser extends CommonBinaryParser { ...@@ -236,7 +241,7 @@ public class BinaryXMLParser extends CommonBinaryParser {
writer.add(" xmlns:" + entry.getValue() + "=\"").add(entry.getKey()).add("\""); 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++) { for (int i = 0; i < attributeCount; i++) {
parseAttribute(i, attrNewLine); parseAttribute(i, attrNewLine);
} }
...@@ -284,7 +289,6 @@ public class BinaryXMLParser extends CommonBinaryParser { ...@@ -284,7 +289,6 @@ public class BinaryXMLParser extends CommonBinaryParser {
if (attributeNS != -1) { if (attributeNS != -1) {
writer.add(nsMap.get(strings[attributeNS])).add(':'); writer.add(nsMap.get(strings[attributeNS])).add(':');
} }
LOG.debug("decodeAttribute: " + attributeNS + " " + name);
writer.add("style/").add(name.replaceAll("_", ".")); writer.add("style/").add(name.replaceAll("_", "."));
} else { } else {
FieldNode field = localStyleMap.get(attrValData); FieldNode field = localStyleMap.get(attrValData);
...@@ -304,7 +308,7 @@ public class BinaryXMLParser extends CommonBinaryParser { ...@@ -304,7 +308,7 @@ public class BinaryXMLParser extends CommonBinaryParser {
} }
writer.add(resName); writer.add(resName);
} else { } else {
resName = ValuesParser.androidResMap.get(attrValData); resName = ValuesParser.getAndroidResMap().get(attrValData);
if (resName != null) { if (resName != null) {
writer.add("@android:").add(resName); writer.add("@android:").add(resName);
} else if (attrValData == 0) { } else if (attrValData == 0) {
......
...@@ -56,7 +56,7 @@ public class ManifestAttributes { ...@@ -56,7 +56,7 @@ public class ManifestAttributes {
try { try {
instance = new ManifestAttributes(); instance = new ManifestAttributes();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); LOG.error("Failed to create ManifestAttributes", e);
} }
} }
return instance; return instance;
......
package jadx.core.xmlgen; 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 javax.imageio.ImageIO;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
...@@ -17,9 +12,17 @@ import java.util.List; ...@@ -17,9 +12,17 @@ import java.util.List;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; 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> { public class ResContainer implements Comparable<ResContainer> {
private static final Logger LOG = LoggerFactory.getLogger(ResContainer.class);
private final String name; private final String name;
private final List<ResContainer> subFiles; private final List<ResContainer> subFiles;
...@@ -34,21 +37,21 @@ public class ResContainer implements Comparable<ResContainer> { ...@@ -34,21 +37,21 @@ public class ResContainer implements Comparable<ResContainer> {
} }
public static ResContainer singleFile(String name, CodeWriter content) { 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; resContainer.content = content;
return resContainer; return resContainer;
} }
public static ResContainer singleImageFile(String name, InputStream content) { 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; InputStream newContent = content;
if (name.endsWith(".9.png")) { if (name.endsWith(".9.png")) {
Res9patchStreamDecoder decoder = new Res9patchStreamDecoder(); Res9patchStreamDecoder decoder = new Res9patchStreamDecoder();
ByteArrayOutputStream os = new ByteArrayOutputStream(); ByteArrayOutputStream os = new ByteArrayOutputStream();
try { try {
decoder.decode(content, os); decoder.decode(content, os);
} catch (JadxException e) { } catch (Exception e) {
e.printStackTrace(); LOG.error("Failed to decode 9-patch png image", e);
} }
newContent = new ByteArrayInputStream(os.toByteArray()); newContent = new ByteArrayInputStream(os.toByteArray());
} }
...@@ -61,7 +64,7 @@ public class ResContainer implements Comparable<ResContainer> { ...@@ -61,7 +64,7 @@ public class ResContainer implements Comparable<ResContainer> {
} }
public static ResContainer multiFile(String name) { public static ResContainer multiFile(String name) {
return new ResContainer(name, new ArrayList<ResContainer>()); return new ResContainer(name, new ArrayList<>());
} }
public String getName() { public String getName() {
......
package jadx.core.xmlgen; package jadx.core.xmlgen;
import jadx.core.utils.Utils;
import jadx.core.xmlgen.entry.ResourceEntry;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
...@@ -11,14 +8,9 @@ import java.util.HashMap; ...@@ -11,14 +8,9 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public class ResourceStorage { import jadx.core.xmlgen.entry.ResourceEntry;
private static final Comparator<ResourceEntry> COMPARATOR = new Comparator<ResourceEntry>() { public class ResourceStorage {
@Override
public int compare(ResourceEntry a, ResourceEntry b) {
return Utils.compare(a.getId(), b.getId());
}
};
private final List<ResourceEntry> list = new ArrayList<>(); private final List<ResourceEntry> list = new ArrayList<>();
private String appPackage; private String appPackage;
...@@ -32,12 +24,12 @@ public class ResourceStorage { ...@@ -32,12 +24,12 @@ public class ResourceStorage {
} }
public void finish() { public void finish() {
Collections.sort(list, COMPARATOR); list.sort(Comparator.comparingInt(ResourceEntry::getId));
} }
public ResourceEntry getByRef(int refId) { public ResourceEntry getByRef(int refId) {
ResourceEntry key = new ResourceEntry(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) { if (index < 0) {
return null; return null;
} }
......
...@@ -4,11 +4,14 @@ import javax.xml.parsers.DocumentBuilderFactory; ...@@ -4,11 +4,14 @@ import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
public class XmlSecurity { public class XmlSecurity {
private static DocumentBuilderFactory secureDbf = null; private static DocumentBuilderFactory secureDbf = null;
private XmlSecurity() {}
public static DocumentBuilderFactory getSecureDbf() throws ParserConfigurationException { public static DocumentBuilderFactory getSecureDbf() throws ParserConfigurationException {
synchronized(XmlSecurity.class) { synchronized (XmlSecurity.class) {
if(secureDbf == null) { if (secureDbf == null) {
secureDbf = DocumentBuilderFactory.newInstance(); secureDbf = DocumentBuilderFactory.newInstance();
secureDbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); 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://apache.org/xml/features/nonvalidating/load-external-dtd", false);
...@@ -21,6 +24,4 @@ public class XmlSecurity { ...@@ -21,6 +24,4 @@ public class XmlSecurity {
} }
return secureDbf; return secureDbf;
} }
} }
...@@ -18,12 +18,12 @@ import jadx.core.xmlgen.ResTableParser; ...@@ -18,12 +18,12 @@ import jadx.core.xmlgen.ResTableParser;
public class ValuesParser extends ParserConstants { public class ValuesParser extends ParserConstants {
private static final Logger LOG = LoggerFactory.getLogger(ValuesParser.class); 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 String[] strings;
private final Map<Integer, String> resMap; private final Map<Integer, String> resMap;
public static String[] androidStrings;
public static Map<Integer, String> androidResMap;
public ValuesParser(String[] strings, Map<Integer, String> resMap) { public ValuesParser(String[] strings, Map<Integer, String> resMap) {
this.strings = strings; this.strings = strings;
this.resMap = resMap; this.resMap = resMap;
...@@ -31,14 +31,14 @@ public class ValuesParser extends ParserConstants { ...@@ -31,14 +31,14 @@ public class ValuesParser extends ParserConstants {
if (androidStrings == null && androidResMap == null) { if (androidStrings == null && androidResMap == null) {
try { try {
decodeAndroid(); decodeAndroid();
} catch (IOException e) { } catch (Exception e) {
e.printStackTrace(); LOG.error("Failed to decode Android Resource file", e);
} }
} }
} }
private void decodeAndroid() throws IOException { private static void decodeAndroid() throws IOException {
InputStream inputStream = new BufferedInputStream(getClass().getResourceAsStream("/resources.arsc")); InputStream inputStream = new BufferedInputStream(ValuesParser.class.getResourceAsStream("/resources.arsc"));
ResTableParser androidParser = new ResTableParser(); ResTableParser androidParser = new ResTableParser();
androidParser.decode(inputStream); androidParser.decode(inputStream);
androidStrings = androidParser.getStrings(); androidStrings = androidParser.getStrings();
...@@ -212,4 +212,8 @@ public class ValuesParser extends ParserConstants { ...@@ -212,4 +212,8 @@ public class ValuesParser extends ParserConstants {
f.setMinimumIntegerDigits(1); f.setMinimumIntegerDigits(1);
return f.format(value); return f.format(value);
} }
public static Map<Integer, String> getAndroidResMap() {
return androidResMap;
}
} }
...@@ -7,19 +7,11 @@ import java.util.Enumeration; ...@@ -7,19 +7,11 @@ import java.util.Enumeration;
import java.util.List; import java.util.List;
import java.util.regex.Pattern; 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.api.ResourceFile;
import jadx.gui.JadxWrapper; import jadx.gui.JadxWrapper;
import jadx.gui.treemodel.JResource.JResType; import jadx.gui.treemodel.JResource.JResType;
import jadx.gui.utils.Utils; import jadx.gui.utils.Utils;
import jadx.gui.treemodel.JResource.JResType;
import jadx.gui.utils.Utils;
public class JRoot extends JNode { public class JRoot extends JNode {
private static final long serialVersionUID = 8888495789773527342L; private static final long serialVersionUID = 8888495789773527342L;
......
...@@ -6,8 +6,6 @@ import javax.swing.table.TableCellRenderer; ...@@ -6,8 +6,6 @@ import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn; import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel; import javax.swing.table.TableColumnModel;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter; import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
...@@ -90,12 +88,7 @@ public abstract class CommonSearchDialog extends JDialog { ...@@ -90,12 +88,7 @@ public abstract class CommonSearchDialog extends JDialog {
addWindowListener(new WindowAdapter() { addWindowListener(new WindowAdapter() {
@Override @Override
public void windowOpened(WindowEvent e) { public void windowOpened(WindowEvent e) {
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(CommonSearchDialog.this::openInit);
@Override
public void run() {
openInit();
}
});
} }
}); });
} }
...@@ -119,12 +112,7 @@ public abstract class CommonSearchDialog extends JDialog { ...@@ -119,12 +112,7 @@ public abstract class CommonSearchDialog extends JDialog {
protected void initCommon() { protected void initCommon() {
KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
getRootPane().registerKeyboardAction(new ActionListener() { getRootPane().registerKeyboardAction(e -> dispose(), stroke, JComponent.WHEN_IN_FOCUSED_WINDOW);
@Override
public void actionPerformed(ActionEvent e) {
dispose();
}
}, stroke, JComponent.WHEN_IN_FOCUSED_WINDOW);
} }
@NotNull @NotNull
...@@ -132,17 +120,9 @@ public abstract class CommonSearchDialog extends JDialog { ...@@ -132,17 +120,9 @@ public abstract class CommonSearchDialog extends JDialog {
progressPane = new ProgressPanel(mainWindow, false); progressPane = new ProgressPanel(mainWindow, false);
JButton cancelButton = new JButton(NLS.str("search_dialog.cancel")); JButton cancelButton = new JButton(NLS.str("search_dialog.cancel"));
cancelButton.addActionListener(new ActionListener() { cancelButton.addActionListener(event -> dispose());
public void actionPerformed(ActionEvent event) {
dispose();
}
});
JButton openBtn = new JButton(NLS.str("search_dialog.open")); JButton openBtn = new JButton(NLS.str("search_dialog.open"));
openBtn.addActionListener(new ActionListener() { openBtn.addActionListener(event -> openSelectedItem());
public void actionPerformed(ActionEvent event) {
openSelectedItem();
}
});
getRootPane().setDefaultButton(openBtn); getRootPane().setDefaultButton(openBtn);
JPanel buttonPane = new JPanel(); 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